OSDN Git Service

WinGui:
[handbrake-jp/handbrake-jp-git.git] / macosx / Controller.mm
1 /* $Id: Controller.mm,v 1.79 2005/11/04 19:41:32 titer Exp $
2
3    This file is part of the HandBrake source code.
4    Homepage: <http://handbrake.m0k.org/>.
5    It may be used under the terms of the GNU General Public License. */
6
7 #include "Controller.h"
8 #include "a52dec/a52.h"
9 #import "HBOutputPanelController.h"
10 #import "HBPreferencesController.h"
11 /* Added to integrate scanning into HBController */
12 #include <IOKit/IOKitLib.h>
13 #include <IOKit/storage/IOMedia.h>
14 #include <IOKit/storage/IODVDMedia.h>
15 #include "HBDVDDetector.h"
16 #include "dvdread/dvd_reader.h"
17 #include "HBPresets.h"
18
19 #define _(a) NSLocalizedString(a,NULL)
20
21 static int FormatSettings[4][10] =
22   { { HB_MUX_MP4 | HB_VCODEC_FFMPEG | HB_ACODEC_FAAC,
23           HB_MUX_MP4 | HB_VCODEC_X264   | HB_ACODEC_FAAC,
24           0,
25           0 },
26     { HB_MUX_MKV | HB_VCODEC_FFMPEG | HB_ACODEC_FAAC,
27           HB_MUX_MKV | HB_VCODEC_FFMPEG | HB_ACODEC_AC3,
28           HB_MUX_MKV | HB_VCODEC_FFMPEG | HB_ACODEC_LAME,
29           HB_MUX_MKV | HB_VCODEC_FFMPEG | HB_ACODEC_VORBIS,
30           HB_MUX_MKV | HB_VCODEC_X264   | HB_ACODEC_FAAC,
31           HB_MUX_MKV | HB_VCODEC_X264   | HB_ACODEC_AC3,
32           HB_MUX_MKV | HB_VCODEC_X264   | HB_ACODEC_LAME,
33           HB_MUX_MKV | HB_VCODEC_X264   | HB_ACODEC_VORBIS,
34           0,
35           0 },
36     { HB_MUX_AVI | HB_VCODEC_FFMPEG | HB_ACODEC_LAME,
37           HB_MUX_AVI | HB_VCODEC_FFMPEG | HB_ACODEC_AC3,
38           HB_MUX_AVI | HB_VCODEC_X264   | HB_ACODEC_LAME,
39           HB_MUX_AVI | HB_VCODEC_X264   | HB_ACODEC_AC3},
40     { HB_MUX_OGM | HB_VCODEC_FFMPEG | HB_ACODEC_VORBIS,
41           HB_MUX_OGM | HB_VCODEC_FFMPEG | HB_ACODEC_LAME,
42           0,
43           0 } };
44
45 /* We setup the toolbar values here */
46 static NSString *        ToggleDrawerIdentifier             = @"Toggle Drawer Item Identifier";
47 static NSString *        StartEncodingIdentifier            = @"Start Encoding Item Identifier";
48 static NSString *        PauseEncodingIdentifier            = @"Pause Encoding Item Identifier";
49 static NSString *        ShowQueueIdentifier                = @"Show Queue Item Identifier";
50 static NSString *        AddToQueueIdentifier               = @"Add to Queue Item Identifier";
51 static NSString *        ShowActivityIdentifier             = @"Debug Output Item Identifier";
52 static NSString *        ChooseSourceIdentifier             = @"Choose Source Item Identifier";
53
54
55 /*******************************
56  * HBController implementation *
57  *******************************/
58 @implementation HBController
59
60 - init
61 {
62     self = [super init];
63     [HBPreferencesController registerUserDefaults];
64     fHandle = NULL;
65     /* Check for check for the app support directory here as
66         * outputPanel needs it right away, as may other future methods
67         */
68     /* We declare the default NSFileManager into fileManager */
69         NSFileManager * fileManager = [NSFileManager defaultManager];
70         /* we set the files and support paths here */
71         AppSupportDirectory = @"~/Library/Application Support/HandBrake";
72     AppSupportDirectory = [AppSupportDirectory stringByExpandingTildeInPath];
73     /* We check for the app support directory for handbrake */
74         if ([fileManager fileExistsAtPath:AppSupportDirectory] == 0) 
75         {
76                 // If it doesnt exist yet, we create it here 
77                 [fileManager createDirectoryAtPath:AppSupportDirectory attributes:nil];
78         }
79     
80     outputPanel = [[HBOutputPanelController alloc] init];
81     fPictureController = [[PictureController alloc] initWithDelegate:self];
82     fQueueController = [[HBQueueController alloc] init];
83     fAdvancedOptions = [[HBAdvancedController alloc] init];
84     /* we init the HBPresets class which currently is only used
85     * for updating built in presets, may move more functionality
86     * there in the future
87     */
88     fPresetsBuiltin = [[HBPresets alloc] init];
89     fPreferencesController = [[HBPreferencesController alloc] init];
90     return self;
91 }
92
93
94 - (void) applicationDidFinishLaunching: (NSNotification *) notification
95 {
96     int    build;
97     char * version;
98     
99     // Init libhb
100         int debugLevel = [[NSUserDefaults standardUserDefaults] boolForKey:@"ShowVerboseOutput"] ? HB_DEBUG_ALL : HB_DEBUG_NONE;
101     fHandle = hb_init(debugLevel, [[NSUserDefaults standardUserDefaults] boolForKey:@"CheckForUpdates"]);
102     
103         // Set the Growl Delegate
104     [GrowlApplicationBridge setGrowlDelegate: self];    
105     /* Init others controllers */
106     [fPictureController SetHandle: fHandle];
107     [fQueueController   setHandle: fHandle];
108     [fQueueController   setHBController: self];
109         
110     fChapterTitlesDelegate = [[ChapterTitles alloc] init];
111     [fChapterTable setDataSource:fChapterTitlesDelegate];
112     
113     /* Call UpdateUI every 1/2 sec */
114     [[NSRunLoop currentRunLoop] addTimer: [NSTimer
115         scheduledTimerWithTimeInterval: 0.5 target: self
116                               selector: @selector( updateUI: ) userInfo: NULL repeats: YES]
117                                 forMode: NSEventTrackingRunLoopMode];
118     
119     // Open debug output window now if it was visible when HB was closed
120     if ([[NSUserDefaults standardUserDefaults] boolForKey:@"OutputPanelIsOpen"])
121         [self showDebugOutputPanel:nil];
122     
123     // Open queue window now if it was visible when HB was closed
124     if ([[NSUserDefaults standardUserDefaults] boolForKey:@"QueueWindowIsOpen"])
125         [self showQueueWindow:nil];
126     
127         [self openMainWindow:nil];
128     
129     if( ( build = hb_check_update( fHandle, &version ) ) > -1 )
130     {
131         /* Update available - tell the user */
132         
133         NSBeginInformationalAlertSheet( _( @"Update is available" ),
134                                         _( @"Go get it!" ), _( @"Discard" ), NULL, fWindow, self,
135                                         @selector( updateAlertDone:returnCode:contextInfo: ),
136                                         NULL, NULL, [NSString stringWithFormat:
137                                             _( @"HandBrake %s (build %d) is now available for download." ),
138                                             version, build] );
139         return;
140         
141     }
142         
143     /* Show Browse Sources Window ASAP */
144     [self performSelectorOnMainThread: @selector(browseSources:)
145                            withObject: NULL waitUntilDone: NO];
146                            }
147
148 - (void) updateAlertDone: (NSWindow *) sheet
149               returnCode: (int) returnCode contextInfo: (void *) contextInfo
150 {
151     if( returnCode == NSAlertDefaultReturn )
152     {
153         /* Go to HandBrake homepage and exit */
154         [self openHomepage: NULL];
155         [NSApp terminate: self];
156      
157     }
158     else
159     {
160          /* Show scan panel */
161         [self performSelectorOnMainThread: @selector(showScanPanel:)
162                                withObject: NULL waitUntilDone: NO];
163         return;
164                /* Go to HandBrake homepage and exit */
165         [self openHomepage: NULL];
166         [NSApp terminate: self];
167     }
168 }
169
170 - (NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication *) app
171 {
172     // Warn if encoding a movie
173     hb_state_t s;
174     hb_get_state( fHandle, &s );
175     HBJobGroup * jobGroup = [fQueueController currentJobGroup];
176     if ( jobGroup && ( s.state != HB_STATE_IDLE ) )
177     {
178         int result = NSRunCriticalAlertPanel(
179                 NSLocalizedString(@"Are you sure you want to quit HandBrake?", nil),
180                 NSLocalizedString(@"%@ is currently encoding. If you quit HandBrake, your movie will be lost. Do you want to quit anyway?", nil),
181                 NSLocalizedString(@"Quit", nil), NSLocalizedString(@"Don't Quit", nil), nil,
182                 jobGroup ? [jobGroup name] : @"A movie" );
183         
184         if (result == NSAlertDefaultReturn)
185         {
186             [self doCancelCurrentJob];
187             return NSTerminateNow;
188         }
189         else
190             return NSTerminateCancel;
191     }
192     
193     // Warn if items still in the queue
194     else if ( hb_count( fHandle ) > 0 )
195     {
196         int result = NSRunCriticalAlertPanel(
197                 NSLocalizedString(@"Are you sure you want to quit HandBrake?", nil),
198                 NSLocalizedString(@"One or more encodes are queued for encoding. Do you want to quit anyway?", nil),
199                 NSLocalizedString(@"Quit", nil), NSLocalizedString(@"Don't Quit", nil), nil);
200         
201         if ( result == NSAlertDefaultReturn )
202             return NSTerminateNow;
203         else
204             return NSTerminateCancel;
205     }
206     
207     return NSTerminateNow;
208 }
209
210 - (void)applicationWillTerminate:(NSNotification *)aNotification
211 {
212         [outputPanel release];
213         [fQueueController release];
214         hb_close(&fHandle);
215 }
216
217
218 - (void) awakeFromNib
219 {
220     [fWindow center];
221     [fWindow setExcludedFromWindowsMenu:YES];
222     [fAdvancedOptions setView:fAdvancedView];
223
224     /* Initialize currentScanCount so HB can use it to
225                 evaluate successive scans */
226         currentScanCount = 0;
227         
228     /* Init UserPresets .plist */
229         [self loadPresets];
230                 
231         fRipIndicatorShown = NO;  // initially out of view in the nib
232         
233         /* Show/Dont Show Presets drawer upon launch based
234                 on user preference DefaultPresetsDrawerShow*/
235         if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultPresetsDrawerShow"] > 0)
236         {
237                 [fPresetDrawer open];
238         }
239         
240         
241     /* Destination box*/
242     [fDstFormatPopUp removeAllItems];
243     [fDstFormatPopUp addItemWithTitle: _( @"MP4 file" )];
244         [fDstFormatPopUp addItemWithTitle: _( @"MKV file" )];
245     [fDstFormatPopUp addItemWithTitle: _( @"AVI file" )];
246     [fDstFormatPopUp addItemWithTitle: _( @"OGM file" )];
247     [fDstFormatPopUp selectItemAtIndex: 0];
248         
249     [self formatPopUpChanged: NULL];
250     
251         /* We enable the create chapters checkbox here since we are .mp4 */     
252         [fCreateChapterMarkers setEnabled: YES];
253         if ([fDstFormatPopUp indexOfSelectedItem] == 0 && [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultChapterMarkers"] > 0)
254         {
255                 [fCreateChapterMarkers setState: NSOnState];
256         }
257         
258         
259         
260         
261     [fDstFile2Field setStringValue: [NSString stringWithFormat:
262         @"%@/Desktop/Movie.mp4", NSHomeDirectory()]];
263         
264     /* Video encoder */
265     [fVidEncoderPopUp removeAllItems];
266     [fVidEncoderPopUp addItemWithTitle: @"FFmpeg"];
267     [fVidEncoderPopUp addItemWithTitle: @"XviD"];
268         
269     
270         
271     /* Video quality */
272     [fVidTargetSizeField setIntValue: 700];
273         [fVidBitrateField    setIntValue: 1000];
274         
275     [fVidQualityMatrix   selectCell: fVidBitrateCell];
276     [self videoMatrixChanged: NULL];
277         
278     /* Video framerate */
279     [fVidRatePopUp removeAllItems];
280         [fVidRatePopUp addItemWithTitle: _( @"Same as source" )];
281     for( int i = 0; i < hb_video_rates_count; i++ )
282     {
283         if ([[NSString stringWithCString: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%.3f",23.976]])
284                 {
285                         [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@",
286                                 [NSString stringWithCString: hb_video_rates[i].string], @" (NTSC Film)"]];
287                 }
288                 else if ([[NSString stringWithCString: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%d",25]])
289                 {
290                         [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@",
291                                 [NSString stringWithCString: hb_video_rates[i].string], @" (PAL Film/Video)"]];
292                 }
293                 else if ([[NSString stringWithCString: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%.2f",29.97]])
294                 {
295                         [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@",
296                                 [NSString stringWithCString: hb_video_rates[i].string], @" (NTSC Video)"]];
297                 }
298                 else
299                 {
300                         [fVidRatePopUp addItemWithTitle:
301                                 [NSString stringWithCString: hb_video_rates[i].string]];
302                 }
303     }
304     [fVidRatePopUp selectItemAtIndex: 0];
305         
306         /* Picture Settings */
307         //[fPicLabelPAROutputX setStringValue: @""];
308         //[fPicSettingPARWidth setStringValue: @""];
309         //[fPicSettingPARHeight setStringValue:  @""];
310         
311         /* Set Auto Crop to On at launch */
312     [fPictureController setAutoCrop:YES];
313         
314         /* Audio bitrate */
315     [fAudBitratePopUp removeAllItems];
316     for( int i = 0; i < hb_audio_bitrates_count; i++ )
317     {
318         [fAudBitratePopUp addItemWithTitle:
319                                 [NSString stringWithCString: hb_audio_bitrates[i].string]];
320
321     }
322     [fAudBitratePopUp selectItemAtIndex: hb_audio_bitrates_default];
323         
324     /* Audio samplerate */
325     [fAudRatePopUp removeAllItems];
326     for( int i = 0; i < hb_audio_rates_count; i++ )
327     {
328         [fAudRatePopUp addItemWithTitle:
329             [NSString stringWithCString: hb_audio_rates[i].string]];
330     }
331     [fAudRatePopUp selectItemAtIndex: hb_audio_rates_default];
332         
333     /* Bottom */
334     [fStatusField setStringValue: @""];
335         
336     [self enableUI: NO];
337         [self setupToolbar];
338         
339         [fPresetsActionButton setMenu:fPresetsActionMenu];
340         
341         /* We disable the Turbo 1st pass checkbox since we are not x264 */
342         [fVidTurboPassCheck setEnabled: NO];
343         [fVidTurboPassCheck setState: NSOffState];
344         
345         
346         /* lets get our default prefs here */
347         [self getDefaultPresets: NULL];
348         /* lets initialize the current successful scancount here to 0 */
349         currentSuccessfulScanCount = 0;
350     
351 }
352
353 - (void) TranslateStrings
354 {
355     [fSrcTitleField     setStringValue: _( @"Title:" )];
356     [fSrcChapterField   setStringValue: _( @"Chapters:" )];
357     [fSrcChapterToField setStringValue: _( @"to" )];
358     [fSrcDuration1Field setStringValue: _( @"Duration:" )];
359
360     [fDstFormatField    setStringValue: _( @"Format:" )];
361     [fDstCodecsField    setStringValue: _( @"Codecs:" )];
362     [fDstFile1Field     setStringValue: _( @"File:" )];
363     [fDstBrowseButton   setTitle:       _( @"Browse" )];
364
365     [fVidRateField      setStringValue: _( @"Framerate (fps):" )];
366     [fVidEncoderField   setStringValue: _( @"Encoder:" )];
367     [fVidQualityField   setStringValue: _( @"Quality:" )];
368 }
369
370
371 - (void) enableUI: (bool) b
372 {
373     NSControl * controls[] =
374       { fSrcTitleField, fSrcTitlePopUp,
375         fSrcChapterField, fSrcChapterStartPopUp, fSrcChapterToField,
376         fSrcChapterEndPopUp, fSrcDuration1Field, fSrcDuration2Field,
377         fDstFormatField, fDstFormatPopUp, fDstCodecsField,
378         fDstCodecsPopUp, fDstFile1Field, fDstFile2Field,
379         fDstBrowseButton, fVidRateField, fVidRatePopUp,
380         fVidEncoderField, fVidEncoderPopUp, fVidQualityField,
381         fVidQualityMatrix, fVidGrayscaleCheck, fSubField, fSubPopUp,
382         fAudLang1Field, fAudLang1PopUp, fAudLang2Field, fAudLang2PopUp,
383         fAudTrack1MixLabel, fAudTrack1MixPopUp, fAudTrack2MixLabel, fAudTrack2MixPopUp,
384         fAudRateField, fAudRatePopUp, fAudBitrateField,
385         fAudBitratePopUp, fPictureButton,fQueueStatus,fPicSettingARkeep,
386                 fPicSettingDeinterlace,fPicLabelSettings,fPicLabelSrc,fPicLabelOutp,fPicSettingsSrc,fPicSettingsOutp,fPicSettingsAnamorphic,
387                 fPicLabelAr,fPicLabelDeinterlace,fPicSettingPAR,fPicLabelAnamorphic,fPresetsAdd,fPresetsDelete,
388                 fCreateChapterMarkers,fVidTurboPassCheck,fDstMp4LargeFileCheck,fPicLabelAutoCrop,
389                 fPicSettingAutoCrop,fPicSettingDetelecine,fPicLabelDetelecine,fPicLabelDenoise,fPicSettingDenoise,
390         fSubForcedCheck,fPicSettingDeblock,fPicLabelDeblock,fPresetsOutlineView,fAudDrcSlider,
391         fAudDrcField,fAudDrcLabel,fDstMp4HttpOptFileCheck,fAudDrcDescLabel1,fAudDrcDescLabel2,fAudDrcDescLabel3,
392         fAudDrcDescLabel4};
393
394     for( unsigned i = 0;
395          i < sizeof( controls ) / sizeof( NSControl * ); i++ )
396     {
397         if( [[controls[i] className] isEqualToString: @"NSTextField"] )
398         {
399             NSTextField * tf = (NSTextField *) controls[i];
400             if( ![tf isBezeled] )
401             {
402                 [tf setTextColor: b ? [NSColor controlTextColor] :
403                     [NSColor disabledControlTextColor]];
404                 continue;
405             }
406         }
407         [controls[i] setEnabled: b];
408
409     }
410         
411         if (b) {
412
413         /* if we're enabling the interface, check if the audio mixdown controls need to be enabled or not */
414         /* these will have been enabled by the mass control enablement above anyway, so we're sense-checking it here */
415         [self setEnabledStateOfAudioMixdownControls: NULL];
416         /* we also call calculatePictureSizing here to sense check if we already have vfr selected */
417         [self calculatePictureSizing: NULL];
418         
419         } else {
420
421                 [fPresetsOutlineView setEnabled: NO];
422         
423         }
424
425     [self videoMatrixChanged: NULL];
426     [fAdvancedOptions enableUI:b];
427 }
428
429
430 /***********************************************************************
431  * UpdateDockIcon
432  ***********************************************************************
433  * Shows a progression bar on the dock icon, filled according to
434  * 'progress' (0.0 <= progress <= 1.0).
435  * Called with progress < 0.0 or progress > 1.0, restores the original
436  * icon.
437  **********************************************************************/
438 - (void) UpdateDockIcon: (float) progress
439 {
440     NSImage * icon;
441     NSData * tiff;
442     NSBitmapImageRep * bmp;
443     uint32_t * pen;
444     uint32_t black = htonl( 0x000000FF );
445     uint32_t red   = htonl( 0xFF0000FF );
446     uint32_t white = htonl( 0xFFFFFFFF );
447     int row_start, row_end;
448     int i, j;
449
450     /* Get application original icon */
451     icon = [NSImage imageNamed: @"NSApplicationIcon"];
452
453     if( progress < 0.0 || progress > 1.0 )
454     {
455         [NSApp setApplicationIconImage: icon];
456         return;
457     }
458
459     /* Get it in a raw bitmap form */
460     tiff = [icon TIFFRepresentationUsingCompression:
461             NSTIFFCompressionNone factor: 1.0];
462     bmp = [NSBitmapImageRep imageRepWithData: tiff];
463     
464     /* Draw the progression bar */
465     /* It's pretty simple (ugly?) now, but I'm no designer */
466
467     row_start = 3 * (int) [bmp size].height / 4;
468     row_end   = 7 * (int) [bmp size].height / 8;
469
470     for( i = row_start; i < row_start + 2; i++ )
471     {
472         pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
473         for( j = 0; j < (int) [bmp size].width; j++ )
474         {
475             pen[j] = black;
476         }
477     }
478     for( i = row_start + 2; i < row_end - 2; i++ )
479     {
480         pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
481         pen[0] = black;
482         pen[1] = black;
483         for( j = 2; j < (int) [bmp size].width - 2; j++ )
484         {
485             if( j < 2 + (int) ( ( [bmp size].width - 4.0 ) * progress ) )
486             {
487                 pen[j] = red;
488             }
489             else
490             {
491                 pen[j] = white;
492             }
493         }
494         pen[j]   = black;
495         pen[j+1] = black;
496     }
497     for( i = row_end - 2; i < row_end; i++ )
498     {
499         pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
500         for( j = 0; j < (int) [bmp size].width; j++ )
501         {
502             pen[j] = black;
503         }
504     }
505
506     /* Now update the dock icon */
507     tiff = [bmp TIFFRepresentationUsingCompression:
508             NSTIFFCompressionNone factor: 1.0];
509     icon = [[NSImage alloc] initWithData: tiff];
510     [NSApp setApplicationIconImage: icon];
511     [icon release];
512 }
513
514 - (void) updateUI: (NSTimer *) timer
515 {
516
517     hb_list_t  * list;
518     list = hb_get_titles( fHandle );    
519     /* check to see if there has been a new scan done
520         this bypasses the constraints of HB_STATE_WORKING
521         not allowing setting a newly scanned source */
522         int checkScanCount = hb_get_scancount( fHandle );
523         if (checkScanCount > currentScanCount)
524         {
525                 
526                 currentScanCount = checkScanCount;
527                 [self showNewScan: NULL];
528         }
529         
530     hb_state_t s;
531     hb_get_state( fHandle, &s );
532         
533         
534     switch( s.state )
535     {
536         case HB_STATE_IDLE:
537                 break;
538 #define p s.param.scanning                      
539         case HB_STATE_SCANNING:
540                 {
541             
542             [fSrcDVD2Field setStringValue: [NSString stringWithFormat:
543                                             _( @"Scanning title %d of %d..." ),
544                                             p.title_cur, p.title_count]];
545             float scanprogress_total;
546             scanprogress_total = ( p.title_cur - 1 ) / p.title_count;
547             /* FIX ME: currently having an issue showing progress on the scan
548              * indicator ( fScanIndicator ), for now just set to barber pole (indeterminate) in -performScan
549              * and stop indeterminate in -showNewScan .
550              */
551             //[fScanIndicator setHidden: NO];
552             //[fScanIndicator setDoubleValue: 100.0 * scanprogress_total];
553             break;
554                 }
555 #undef p
556         
557 #define p s.param.scandone
558         case HB_STATE_SCANDONE:
559         {
560                         [self showNewScan: NULL];
561             [toolbar validateVisibleItems];
562                         break;
563         }
564 #undef p
565                         
566 #define p s.param.working
567         case HB_STATE_WORKING:
568         {
569             float progress_total;
570             NSMutableString * string;
571                         /* Currently, p.job_cur and p.job_count get screwed up when adding
572                                 jobs during encoding, if they cannot be fixed in libhb, will implement a
573                                 nasty but working cocoa solution */
574                         /* Update text field */
575                         string = [NSMutableString stringWithFormat: _( @"Encoding: task %d of %d, %.2f %%" ), p.job_cur, p.job_count, 100.0 * p.progress];
576             
577                         if( p.seconds > -1 )
578             {
579                 [string appendFormat:
580                     _( @" (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)" ),
581                     p.rate_cur, p.rate_avg, p.hours, p.minutes, p.seconds];
582             }
583             [fStatusField setStringValue: string];
584                         
585             /* Update slider */
586                         progress_total = ( p.progress + p.job_cur - 1 ) / p.job_count;
587             [fRipIndicator setIndeterminate: NO];
588             [fRipIndicator setDoubleValue: 100.0 * progress_total];
589                         
590             // If progress bar hasn't been revealed at the bottom of the window, do
591             // that now. This code used to be in doRip. I moved it to here to handle
592             // the case where hb_start is called by HBQueueController and not from
593             // HBController.
594             if (!fRipIndicatorShown)
595             {
596                 NSRect frame = [fWindow frame];
597                 if (frame.size.width <= 591)
598                     frame.size.width = 591;
599                 frame.size.height += 36;
600                 frame.origin.y -= 36;
601                 [fWindow setFrame:frame display:YES animate:YES];
602                 fRipIndicatorShown = YES;
603                 /* We check to see if we need to warn the user that the computer will go to sleep
604                    or shut down when encoding is finished */
605                 [self remindUserOfSleepOrShutdown];
606             }
607
608             /* Update dock icon */
609             [self UpdateDockIcon: progress_total];
610                         
611             // Has current job changed? That means the queue has probably changed as
612                         // well so update it
613             [fQueueController hblibStateChanged: s];
614             
615             break;
616         }
617 #undef p
618                         
619 #define p s.param.muxing
620         case HB_STATE_MUXING:
621         {
622             NSMutableString * string;
623                         
624             /* Update text field */
625             string = [NSMutableString stringWithFormat:
626                 _( @"Muxing..." )];
627             [fStatusField setStringValue: string];
628                         
629             /* Update slider */
630             [fRipIndicator setIndeterminate: YES];
631             [fRipIndicator startAnimation: nil];
632                         
633             /* Update dock icon */
634             [self UpdateDockIcon: 1.0];
635                         
636                         // Pass along the info to HBQueueController
637             [fQueueController hblibStateChanged: s];
638                         
639             break;
640         }
641 #undef p
642                         
643         case HB_STATE_PAUSED:
644                     [fStatusField setStringValue: _( @"Paused" )];
645             
646                         // Pass along the info to HBQueueController
647             [fQueueController hblibStateChanged: s];
648
649             break;
650                         
651         case HB_STATE_WORKDONE:
652         {
653             // HB_STATE_WORKDONE happpens as a result of hblib finishing all its jobs
654             // or someone calling hb_stop. In the latter case, hb_stop does not clear
655             // out the remaining passes/jobs in the queue. We'll do that here.
656                         
657             // Delete all remaining jobs of this encode.
658             hb_job_t * job;
659             while( ( job = hb_job( fHandle, 0 ) ) && ( !IsFirstPass(job->sequence_id) ) )
660                 hb_rem( fHandle, job );
661
662             [fStatusField setStringValue: _( @"Done." )];
663             [fRipIndicator setIndeterminate: NO];
664             [fRipIndicator setDoubleValue: 0.0];
665             [toolbar validateVisibleItems];
666
667             /* Restore dock icon */
668             [self UpdateDockIcon: -1.0];
669
670             if (fRipIndicatorShown)
671             {
672                 NSRect frame = [fWindow frame];
673                 if (frame.size.width <= 591)
674                                     frame.size.width = 591;
675                 frame.size.height += -36;
676                 frame.origin.y -= -36;
677                 [fWindow setFrame:frame display:YES animate:YES];
678                                 fRipIndicatorShown = NO;
679                         }
680                         
681                         // Pass along the info to HBQueueController
682             [fQueueController hblibStateChanged: s];
683                         
684             /* Check to see if the encode state has not been cancelled
685                                 to determine if we should check for encode done notifications */
686                         if (fEncodeState != 2)                  {
687                                 /* If Growl Notification or Window and Growl has been selected */
688                                 if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Growl Notification"] || 
689                                         [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window And Growl"])
690                 {
691                                         /*Growl Notification*/
692                                         [self showGrowlDoneNotification: NULL];
693                 }
694                 /* If Alert Window or Window and Growl has been selected */
695                                 if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window"] || 
696                                         [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window And Growl"])
697                 {
698                                         /*On Screen Notification*/
699                                         int status;
700                                         NSBeep();
701                                         status = NSRunAlertPanel(@"Put down that cocktail...",@"Your HandBrake encode is done!", @"OK", nil, nil);
702                                         [NSApp requestUserAttention:NSCriticalRequest];
703                                         if ( status == NSAlertDefaultReturn ) 
704                                         {
705                                                 [self enableUI: YES];
706                                         }
707                 }
708                                 else
709                                 {
710                                         [self enableUI: YES];
711                                 }
712                                    /* If sleep has been selected */ 
713             if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Put Computer To Sleep"]) 
714                 { 
715                /* Sleep */ 
716                NSDictionary* errorDict; 
717                NSAppleEventDescriptor* returnDescriptor = NULL; 
718                NSAppleScript* scriptObject = [[NSAppleScript alloc] initWithSource: 
719                         @"tell application \"Finder\" to sleep"]; 
720                returnDescriptor = [scriptObject executeAndReturnError: &errorDict]; 
721                [scriptObject release]; 
722                [self enableUI: YES]; 
723                 } 
724             /* If Shutdown has been selected */ 
725             if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Shut Down Computer"]) 
726                 { 
727                /* Shut Down */ 
728                NSDictionary* errorDict; 
729                NSAppleEventDescriptor* returnDescriptor = NULL; 
730                NSAppleScript* scriptObject = [[NSAppleScript alloc] initWithSource: 
731                         @"tell application \"Finder\" to shut down"]; 
732                returnDescriptor = [scriptObject executeAndReturnError: &errorDict]; 
733                [scriptObject release]; 
734                [self enableUI: YES]; 
735                 }
736                         
737                                                 // MetaX insertion via AppleScript
738                         if([[NSUserDefaults standardUserDefaults] boolForKey: @"sendToMetaX"] == YES)
739                         {
740                         NSAppleScript *myScript = [[NSAppleScript alloc] initWithSource: [NSString stringWithFormat: @"%@%@%@", @"tell application \"MetaX\" to open (POSIX file \"", [fDstFile2Field stringValue], @"\")"]];
741                         [myScript executeAndReturnError: nil];
742                         [myScript release];
743                         }
744                         
745                         
746                         }
747                         else
748                         {
749                                 [self enableUI: YES];
750                         }
751             break;
752         }
753     }
754         
755     /* Lets show the queue status here in the main window */
756         int queue_count = hb_count( fHandle );
757         if( queue_count )
758         {
759                 [fQueueStatus setStringValue: [NSString stringWithFormat:
760                         @"%d pass%s in the queue",
761                                                  queue_count, ( queue_count > 1 ) ? "es" : ""]];
762         }
763         else
764         {
765                 [fQueueStatus setStringValue: @""];
766         }
767 }
768
769
770 #pragma mark -
771 #pragma mark Toolbar
772 // ============================================================
773 // NSToolbar Related Methods
774 // ============================================================
775
776 - (void) setupToolbar {
777     toolbar = [[[NSToolbar alloc] initWithIdentifier: @"HandBrake Toolbar"] autorelease];
778     
779     [toolbar setAllowsUserCustomization: YES];
780     [toolbar setAutosavesConfiguration: YES];
781     [toolbar setDisplayMode: NSToolbarDisplayModeIconAndLabel];
782     
783     [toolbar setDelegate: self];
784     
785     [fWindow setToolbar: toolbar];
786 }
787
788 - (NSToolbarItem *) toolbar: (NSToolbar *)toolbar itemForItemIdentifier:
789     (NSString *) itemIdent willBeInsertedIntoToolbar:(BOOL) willBeInserted {
790     NSToolbarItem * item = [[NSToolbarItem alloc] initWithItemIdentifier: itemIdent];
791     
792     if ([itemIdent isEqualToString: ToggleDrawerIdentifier])
793     {
794         [item setLabel: @"Toggle Presets"];
795         [item setPaletteLabel: @"Toggler Presets"];
796         [item setToolTip: @"Open/Close Preset Drawer"];
797         [item setImage: [NSImage imageNamed: @"Drawer"]];
798         [item setTarget: self];
799         [item setAction: @selector(toggleDrawer:)];
800         [item setAutovalidates: NO];
801     }
802     else if ([itemIdent isEqualToString: StartEncodingIdentifier])
803     {
804         [item setLabel: @"Start"];
805         [item setPaletteLabel: @"Start Encoding"];
806         [item setToolTip: @"Start Encoding"];
807         [item setImage: [NSImage imageNamed: @"Play"]];
808         [item setTarget: self];
809         [item setAction: @selector(Rip:)];
810     }
811     else if ([itemIdent isEqualToString: ShowQueueIdentifier])
812     {
813         [item setLabel: @"Show Queue"];
814         [item setPaletteLabel: @"Show Queue"];
815         [item setToolTip: @"Show Queue"];
816         [item setImage: [NSImage imageNamed: @"Queue"]];
817         [item setTarget: self];
818         [item setAction: @selector(showQueueWindow:)];
819         [item setAutovalidates: NO];
820     }
821     else if ([itemIdent isEqualToString: AddToQueueIdentifier])
822     {
823         [item setLabel: @"Add to Queue"];
824         [item setPaletteLabel: @"Add to Queue"];
825         [item setToolTip: @"Add to Queue"];
826         [item setImage: [NSImage imageNamed: @"AddToQueue"]];
827         [item setTarget: self];
828         [item setAction: @selector(addToQueue:)];
829     }
830     else if ([itemIdent isEqualToString: PauseEncodingIdentifier])
831     {
832         [item setLabel: @"Pause"];
833         [item setPaletteLabel: @"Pause Encoding"];
834         [item setToolTip: @"Pause Encoding"];
835         [item setImage: [NSImage imageNamed: @"Pause"]];
836         [item setTarget: self];
837         [item setAction: @selector(Pause:)];
838     }
839     else if ([itemIdent isEqualToString: ShowActivityIdentifier]) {
840         [item setLabel: @"Activity Window"];
841         [item setPaletteLabel: @"Show Activity Window"];
842         [item setToolTip: @"Show Activity Window"];
843         [item setImage: [NSImage imageNamed: @"ActivityWindow"]];
844         [item setTarget: self];
845         [item setAction: @selector(showDebugOutputPanel:)];
846         [item setAutovalidates: NO];
847     }
848     else if ([itemIdent isEqualToString: ChooseSourceIdentifier])
849     {
850         [item setLabel: @"Source"];
851         [item setPaletteLabel: @"Source"];
852         [item setToolTip: @"Choose Video Source"];
853         [item setImage: [NSImage imageNamed: @"Source"]];
854         [item setTarget: self];
855         [item setAction: @selector(browseSources:)];
856     }
857     else
858     {
859         [item release];
860         return nil;
861     }
862
863     return item;
864 }
865
866 - (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar
867 {
868     return [NSArray arrayWithObjects: ChooseSourceIdentifier, NSToolbarSeparatorItemIdentifier, StartEncodingIdentifier,
869         PauseEncodingIdentifier, AddToQueueIdentifier, ShowQueueIdentifier, NSToolbarFlexibleSpaceItemIdentifier, 
870                 NSToolbarSpaceItemIdentifier, ShowActivityIdentifier, ToggleDrawerIdentifier, nil];
871 }
872
873 - (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) toolbar
874 {
875     return [NSArray arrayWithObjects:  StartEncodingIdentifier, PauseEncodingIdentifier, AddToQueueIdentifier,
876         ChooseSourceIdentifier, ShowQueueIdentifier, ShowActivityIdentifier, ToggleDrawerIdentifier,
877         NSToolbarCustomizeToolbarItemIdentifier, NSToolbarFlexibleSpaceItemIdentifier,
878         NSToolbarSpaceItemIdentifier, NSToolbarSeparatorItemIdentifier, nil];
879 }
880
881 - (BOOL) validateToolbarItem: (NSToolbarItem *) toolbarItem
882 {
883     NSString * ident = [toolbarItem itemIdentifier];
884         
885     if (fHandle)
886     {
887         hb_state_t s;
888         hb_get_state2( fHandle, &s );
889         
890         if (s.state == HB_STATE_WORKING || s.state == HB_STATE_MUXING)
891         {
892             if ([ident isEqualToString: StartEncodingIdentifier])
893             {
894                 [toolbarItem setImage: [NSImage imageNamed: @"Stop"]];
895                 [toolbarItem setLabel: @"Stop"];
896                 [toolbarItem setPaletteLabel: @"Stop"];
897                 [toolbarItem setToolTip: @"Stop Encoding"];
898                 return YES;
899             }
900             if ([ident isEqualToString: PauseEncodingIdentifier])
901             {
902                 [toolbarItem setImage: [NSImage imageNamed: @"Pause"]];
903                 [toolbarItem setLabel: @"Pause"];
904                 [toolbarItem setPaletteLabel: @"Pause Encoding"];
905                 [toolbarItem setToolTip: @"Pause Encoding"];
906                 return YES;
907             }
908             if (SuccessfulScan)
909                 if ([ident isEqualToString: AddToQueueIdentifier])
910                     return YES;
911         }
912         else if (s.state == HB_STATE_PAUSED)
913         {
914             if ([ident isEqualToString: PauseEncodingIdentifier])
915             {
916                 [toolbarItem setImage: [NSImage imageNamed: @"Play"]];
917                 [toolbarItem setLabel: @"Resume"];
918                 [toolbarItem setPaletteLabel: @"Resume Encoding"];
919                 [toolbarItem setToolTip: @"Resume Encoding"];
920                 return YES;
921             }
922             if ([ident isEqualToString: StartEncodingIdentifier])
923                 return YES;
924             if ([ident isEqualToString: AddToQueueIdentifier])
925                 return YES;
926         }
927         else if (s.state == HB_STATE_SCANNING)
928             return NO;
929         else if (s.state == HB_STATE_WORKDONE || s.state == HB_STATE_SCANDONE || SuccessfulScan)
930         {
931             if ([ident isEqualToString: StartEncodingIdentifier])
932             {
933                 [toolbarItem setImage: [NSImage imageNamed: @"Play"]];
934                 if (hb_count(fHandle) > 0)
935                     [toolbarItem setLabel: @"Start Queue"];
936                 else
937                     [toolbarItem setLabel: @"Start"];
938                 [toolbarItem setPaletteLabel: @"Start Encoding"];
939                 [toolbarItem setToolTip: @"Start Encoding"];
940                 return YES;
941             }
942             if ([ident isEqualToString: AddToQueueIdentifier])
943                 return YES;
944         }
945
946     }
947     
948     if ([ident isEqualToString: ShowQueueIdentifier])
949         return YES;
950     if ([ident isEqualToString: ToggleDrawerIdentifier])
951         return YES;
952     if ([ident isEqualToString: ChooseSourceIdentifier])
953         return YES;
954     if ([ident isEqualToString: ShowActivityIdentifier])
955         return YES;
956     
957     return NO;
958 }
959
960 - (BOOL) validateMenuItem: (NSMenuItem *) menuItem
961 {
962     SEL action = [menuItem action];
963     
964     hb_state_t s;
965     hb_get_state2( fHandle, &s );
966     
967     if (fHandle)
968     {
969         if (action == @selector(addToQueue:) || action == @selector(showPicturePanel:) || action == @selector(showAddPresetPanel:))
970             return SuccessfulScan && [fWindow attachedSheet] == nil;
971         
972         if (action == @selector(browseSources:))
973         {
974             if (s.state == HB_STATE_SCANNING)
975                 return NO;
976             else
977                 return [fWindow attachedSheet] == nil;
978         }
979         if (action == @selector(selectDefaultPreset:))
980             return [fPresetsOutlineView selectedRow] >= 0 && [fWindow attachedSheet] == nil;
981         if (action == @selector(Pause:))
982         {
983             if (s.state == HB_STATE_WORKING)
984             {
985                 if(![[menuItem title] isEqualToString:@"Pause Encoding"])
986                     [menuItem setTitle:@"Pause Encoding"];
987                 return YES;
988             }
989             else if (s.state == HB_STATE_PAUSED)
990             {
991                 if(![[menuItem title] isEqualToString:@"Resume Encoding"])
992                     [menuItem setTitle:@"Resume Encoding"];
993                 return YES;
994             }
995             else
996                 return NO;
997         }
998         if (action == @selector(Rip:))
999             if (s.state == HB_STATE_WORKING || s.state == HB_STATE_MUXING || s.state == HB_STATE_PAUSED)
1000             {
1001                 if(![[menuItem title] isEqualToString:@"Stop Encoding"])
1002                     [menuItem setTitle:@"Stop Encoding"];
1003                 return YES;
1004             }
1005             else if (SuccessfulScan)
1006             {
1007                 if(![[menuItem title] isEqualToString:@"Start Encoding"])
1008                     [menuItem setTitle:@"Start Encoding"];
1009                 return [fWindow attachedSheet] == nil;
1010             }
1011             else
1012                 return NO;
1013         }
1014     
1015     return YES;
1016 }
1017
1018 #pragma mark -
1019 #pragma mark Growl
1020 // register a test notification and make
1021 // it enabled by default
1022 #define SERVICE_NAME @"Encode Done"
1023 - (NSDictionary *)registrationDictionaryForGrowl 
1024
1025     NSDictionary *registrationDictionary = [NSDictionary dictionaryWithObjectsAndKeys: 
1026     [NSArray arrayWithObjects:SERVICE_NAME,nil], GROWL_NOTIFICATIONS_ALL, 
1027     [NSArray arrayWithObjects:SERVICE_NAME,nil], GROWL_NOTIFICATIONS_DEFAULT, 
1028     nil]; 
1029
1030     return registrationDictionary; 
1031
1032
1033 -(IBAction)showGrowlDoneNotification:(id)sender
1034 {
1035   [GrowlApplicationBridge 
1036             notifyWithTitle:@"Put down that cocktail..." 
1037                 description:@"your HandBrake encode is done!" 
1038            notificationName:SERVICE_NAME
1039                    iconData:nil 
1040                    priority:0 
1041                    isSticky:1 
1042                clickContext:nil];
1043 }
1044
1045 #pragma mark -
1046 #pragma mark Get New Source
1047
1048 /*Opens the source browse window, called from Open Source widgets */
1049 - (IBAction) browseSources: (id) sender
1050 {
1051     [self enableUI: NO];
1052     NSOpenPanel * panel;
1053         
1054     panel = [NSOpenPanel openPanel];
1055     [panel setAllowsMultipleSelection: NO];
1056     [panel setCanChooseFiles: YES];
1057     [panel setCanChooseDirectories: YES ];
1058     NSString * sourceDirectory;
1059         if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastSourceDirectory"])
1060         {
1061                 sourceDirectory = [[NSUserDefaults standardUserDefaults] stringForKey:@"LastSourceDirectory"];
1062         }
1063         else
1064         {
1065                 sourceDirectory = @"~/Desktop";
1066                 sourceDirectory = [sourceDirectory stringByExpandingTildeInPath];
1067         }
1068     /* we open up the browse sources sheet here and call for browseSourcesDone after the sheet is closed
1069         * to evaluate whether we want to specify a title, we pass the sender in the contextInfo variable
1070         */
1071     [panel beginSheetForDirectory: sourceDirectory file: nil types: nil
1072                    modalForWindow: fWindow modalDelegate: self
1073                    didEndSelector: @selector( browseSourcesDone:returnCode:contextInfo: )
1074                       contextInfo: sender]; 
1075 }
1076
1077 - (void) browseSourcesDone: (NSOpenPanel *) sheet
1078                 returnCode: (int) returnCode contextInfo: (void *) contextInfo
1079 {
1080     /* we convert the sender content of contextInfo back into a variable called sender
1081      * mostly just for consistency for evaluation later
1082      */
1083     id sender = (id)contextInfo;
1084     /* User selected a file to open */
1085         if( returnCode == NSOKButton )
1086     {
1087         
1088         NSString *scanPath = [[sheet filenames] objectAtIndex: 0];
1089         /* we set the last searched source directory in the prefs here */
1090         NSString *sourceDirectory = [scanPath stringByDeletingLastPathComponent];
1091         [[NSUserDefaults standardUserDefaults] setObject:sourceDirectory forKey:@"LastSourceDirectory"];
1092         /* we order out sheet, which is the browse window as we need to open
1093          * the title selection sheet right away
1094          */
1095         [sheet orderOut: self];
1096         
1097         if (sender == fOpenSourceTitleMMenu)
1098         {
1099             /* We put the chosen source path in the source display text field for the
1100              * source title selection sheet in which the user specifies the specific title to be
1101              * scanned  as well as the short source name in fSrcDsplyNameTitleScan just for display
1102              * purposes in the title panel
1103              */
1104             /* Full Path */
1105             [fScanSrcTitlePathField setStringValue: [NSString stringWithFormat:@"%@", scanPath]];
1106             NSString *displayTitlescanSourceName;
1107             
1108             if ([[scanPath lastPathComponent] isEqualToString: @"VIDEO_TS"])
1109             {
1110                 /* If VIDEO_TS Folder is chosen, choose its parent folder for the source display name 
1111                  we have to use the title->dvd value so we get the proper name of the volume if a physical dvd is the source*/
1112                 displayTitlescanSourceName = [NSString stringWithFormat:[[scanPath stringByDeletingLastPathComponent] lastPathComponent]];
1113             }
1114             else
1115             {
1116                 /* if not the VIDEO_TS Folder, we can assume the chosen folder is the source name */
1117                 displayTitlescanSourceName = [NSString stringWithFormat:[scanPath lastPathComponent]];
1118             }
1119             /* we set the source display name in the title selection dialogue */
1120             [fSrcDsplyNameTitleScan setStringValue: [NSString stringWithFormat:@"%@", displayTitlescanSourceName]];
1121             /* We show the actual sheet where the user specifies the title to be scanned 
1122              * as we are going to do a title specific scan
1123              */
1124             [self showSourceTitleScanPanel:NULL];
1125         }
1126         else
1127         {
1128             /* We are just doing a standard full source scan, so we specify "0" to libhb */
1129             NSString *path = [[sheet filenames] objectAtIndex: 0];
1130             
1131             /* We check to see if the chosen file at path is a package */
1132             if ([[NSWorkspace sharedWorkspace] isFilePackageAtPath:path])
1133             {
1134                 fprintf( stdout, "MacGui: Trying to open a package\n");
1135                 /* We check to see if this is an .eyetv package */
1136                 if ([[path pathExtension] isEqualToString: @"eyetv"])
1137                 {
1138                     fprintf( stdout, "MacGui: Trying to open eyetv package\n");
1139                     /* We're looking at an EyeTV package - try to open its enclosed
1140                      .mpg media file */
1141                     NSString *mpgname;
1142                     int n = [[path stringByAppendingString: @"/"]
1143                              completePathIntoString: &mpgname caseSensitive: NO
1144                              matchesIntoArray: nil
1145                              filterTypes: [NSArray arrayWithObject: @"mpg"]];
1146                     if (n > 0)
1147                     {
1148                         /* Found an mpeg inside the eyetv package, make it our scan path 
1149                         and call performScan on the enclosed mpeg */
1150                         path = mpgname;
1151                         fprintf( stdout, "MacGui: found mpeg in eyetv package\n");
1152                         [self performScan:path scanTitleNum:0];
1153                     }
1154                     else
1155                     {
1156                         /* We did not find an mpeg file in our package, so we do not call performScan */
1157                         fprintf( stdout, "MacGui: no valid mpeg in eyetv package\n");
1158                     }
1159                 }
1160                 else
1161                 {
1162                     /* The package is not an eyetv package, so we do not call performScan */
1163                     fprintf( stdout, "MacGui: Unable to open package\n");
1164                 }
1165             }
1166             else
1167             {
1168                 /* path is not a package, so we call perform scan directly on our file */
1169                 [self performScan:path scanTitleNum:0];
1170             }
1171             
1172         }
1173         
1174     }
1175     else // User clicked Cancel in browse window
1176     {
1177         /* if we have a title loaded up */
1178         if ([[fSrcDVD2Field stringValue] length] > 0)
1179         {
1180             [self enableUI: YES];
1181         }
1182     }
1183 }
1184
1185 /* Here we open the title selection sheet where we can specify an exact title to be scanned */
1186 - (IBAction) showSourceTitleScanPanel: (id) sender
1187 {
1188     /* We default the title number to be scanned to "0" which results in a full source scan, unless the
1189     * user changes it
1190     */
1191     [fScanSrcTitleNumField setStringValue: @"0"];
1192         /* Show the panel */
1193         [NSApp beginSheet: fScanSrcTitlePanel modalForWindow: fWindow modalDelegate: NULL didEndSelector: NULL contextInfo: NULL];
1194 }
1195
1196 - (IBAction) closeSourceTitleScanPanel: (id) sender
1197 {
1198     [NSApp endSheet: fScanSrcTitlePanel];
1199     [fScanSrcTitlePanel orderOut: self];
1200     
1201     
1202     
1203     if(sender == fScanSrcTitleOpenButton)
1204     {
1205         /* We setup the scan status in the main window to indicate a source title scan */
1206         [fSrcDVD2Field setStringValue: _( @"Opening a new source title ..." )];
1207                 //[fScanIndicator setHidden: NO];
1208             [fScanIndicator setIndeterminate: YES];
1209         [fScanIndicator startAnimation: nil];
1210                 
1211         /* We use the performScan method to actually perform the specified scan passing the path and the title
1212             * to be scanned
1213             */
1214         [self performScan:[fScanSrcTitlePathField stringValue] scanTitleNum:[fScanSrcTitleNumField intValue]];
1215     }
1216 }
1217
1218
1219 /* Here we actually tell hb_scan to perform the source scan, using the path to source and title number*/
1220 - (void) performScan:(NSString *) scanPath scanTitleNum: (int) scanTitleNum
1221 {
1222     NSString *path = scanPath;
1223     HBDVDDetector *detector = [HBDVDDetector detectorForPath:path];
1224     if( [detector isVideoDVD] )
1225     {
1226         // The chosen path was actually on a DVD, so use the raw block
1227         // device path instead.
1228         path = [detector devicePath];
1229     }
1230     /* If there is no title number passed to scan, we use "0"
1231         * which causes the default behavior of a full source scan
1232     */
1233     if (!scanTitleNum)
1234     {
1235         scanTitleNum = 0;
1236     }
1237     
1238     [fSrcDVD2Field setStringValue: [NSString stringWithFormat: @"Scanning new source ..."]];
1239     /* due to issue with progress in the scan indicator, we are starting the
1240     indeterminate animation here */
1241     [fScanIndicator startAnimation: self];
1242     /* we actually pass the scan off to libhb here */
1243     hb_scan( fHandle, [path UTF8String], scanTitleNum );
1244     
1245 }
1246
1247 - (IBAction) showNewScan:(id)sender
1248 {
1249         /* due to issue with progress in the scan indicator, we are stopping the
1250     indeterminate animation here */
1251     [fScanIndicator stopAnimation: self];
1252     
1253     hb_list_t  * list;
1254         hb_title_t * title;
1255         int indxpri=0;    // Used to search the longuest title (default in combobox)
1256         int longuestpri=0; // Used to search the longuest title (default in combobox)
1257         
1258         list = hb_get_titles( fHandle );
1259         
1260         if( !hb_list_count( list ) )
1261         {
1262                 /* We display a message if a valid dvd source was not chosen */
1263                 [fSrcDVD2Field setStringValue: @"No Valid Title Found"];
1264         SuccessfulScan = NO;
1265         }
1266         else
1267         {
1268         /* We increment the successful scancount here by one,
1269         which we use at the end of this function to tell the gui
1270         if this is the first successful scan since launch and whether
1271         or not we should set all settings to the defaults */
1272                 
1273         currentSuccessfulScanCount++;
1274         
1275         [toolbar validateVisibleItems];
1276                 
1277                 [fSrcTitlePopUp removeAllItems];
1278                 for( int i = 0; i < hb_list_count( list ); i++ )
1279                 {
1280                         title = (hb_title_t *) hb_list_item( list, i );
1281                         
1282             currentSource = [NSString stringWithUTF8String: title->name];
1283             
1284             /* To get the source name as well as the default output name, first we check to see if
1285                 the selected directory is the VIDEO_TS Directory */
1286             if ([[currentSource lastPathComponent] isEqualToString: @"VIDEO_TS"])
1287             {
1288                 /* If VIDEO_TS Folder is chosen, choose its parent folder for the source display name 
1289                 we have to use the title->dvd value so we get the proper name of the volume if a physical dvd is the source*/
1290                 sourceDisplayName = [NSString stringWithFormat:[[[NSString stringWithUTF8String: title->dvd] stringByDeletingLastPathComponent] lastPathComponent]];
1291             }
1292             else if ([[NSString stringWithUTF8String: title->dvd] rangeOfString: @".eyetv/"].length != 0)
1293             {
1294                 /* Use the name of the EyeTV package instead of the media file */
1295                 sourceDisplayName = [NSString stringWithFormat: @"%@",
1296                                      [[[[NSString stringWithUTF8String: title->dvd] stringByDeletingLastPathComponent] lastPathComponent] stringByDeletingPathExtension]];
1297             }
1298             else
1299             {
1300                 /* if not the VIDEO_TS Folder, we can assume the chosen folder is the source name */
1301                 sourceDisplayName = [NSString stringWithFormat:[currentSource lastPathComponent]];
1302             }
1303                         /*Set DVD Name at top of window*/
1304                         [fSrcDVD2Field setStringValue:[NSString stringWithFormat: @"%@", sourceDisplayName]];
1305                         
1306                         /* Use the dvd name in the default output field here 
1307                                 May want to add code to remove blank spaces for some dvd names*/
1308                         /* Check to see if the last destination has been set,use if so, if not, use Desktop */
1309                         if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastDestinationDirectory"])
1310                         {
1311                                 [fDstFile2Field setStringValue: [NSString stringWithFormat:
1312                                         @"%@/%@.mp4", [[NSUserDefaults standardUserDefaults] stringForKey:@"LastDestinationDirectory"],sourceDisplayName]];
1313                         }
1314                         else
1315                         {
1316                                 [fDstFile2Field setStringValue: [NSString stringWithFormat:
1317                                         @"%@/Desktop/%@.mp4", NSHomeDirectory(),sourceDisplayName]];
1318                         }
1319                         
1320                         if (longuestpri < title->hours*60*60 + title->minutes *60 + title->seconds)
1321                         {
1322                                 longuestpri=title->hours*60*60 + title->minutes *60 + title->seconds;
1323                                 indxpri=i;
1324                         }
1325                         
1326                         [self formatPopUpChanged:NULL];
1327                         
1328             [fSrcTitlePopUp addItemWithTitle: [NSString
1329                 stringWithFormat: @"%d - %02dh%02dm%02ds",
1330                 title->index, title->hours, title->minutes,
1331                 title->seconds]];
1332                 }
1333         
1334                 // Select the longuest title
1335                 [fSrcTitlePopUp selectItemAtIndex: indxpri];
1336                 [self titlePopUpChanged: NULL];
1337                 
1338         SuccessfulScan = YES;
1339                 [self enableUI: YES];
1340                 
1341                 /* if its the initial successful scan after awakeFromNib */
1342         if (currentSuccessfulScanCount == 1)
1343         {
1344             [self selectDefaultPreset: NULL];
1345             /* if Deinterlace upon launch is specified in the prefs, then set to 1 for "Fast",
1346              if not, then set to 0 for none */
1347             if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultDeinterlaceOn"] > 0)
1348             {
1349                 [fPictureController setDeinterlace:1];
1350             }
1351             else
1352             {
1353                 [fPictureController setDeinterlace:0];
1354             }
1355             /* lets set Denoise to index 0 or "None" since this is the first scan */
1356             [fPictureController setDenoise:0];
1357             
1358             [fPictureController setInitialPictureFilters];
1359         }
1360         
1361         }
1362 }
1363
1364
1365 #pragma mark -
1366 #pragma mark New Output Destination
1367
1368 - (IBAction) browseFile: (id) sender
1369 {
1370     /* Open a panel to let the user choose and update the text field */
1371     NSSavePanel * panel = [NSSavePanel savePanel];
1372         /* We get the current file name and path from the destination field here */
1373         [panel beginSheetForDirectory: [[fDstFile2Field stringValue] stringByDeletingLastPathComponent] file: [[fDstFile2Field stringValue] lastPathComponent]
1374                                    modalForWindow: fWindow modalDelegate: self
1375                                    didEndSelector: @selector( browseFileDone:returnCode:contextInfo: )
1376                                           contextInfo: NULL];
1377 }
1378
1379 - (void) browseFileDone: (NSSavePanel *) sheet
1380     returnCode: (int) returnCode contextInfo: (void *) contextInfo
1381 {
1382     if( returnCode == NSOKButton )
1383     {
1384         [fDstFile2Field setStringValue: [sheet filename]];
1385     }
1386 }
1387
1388
1389 #pragma mark -
1390 #pragma mark Main Window Control
1391
1392 - (IBAction) openMainWindow: (id) sender
1393 {
1394     [fWindow  makeKeyAndOrderFront:nil];
1395 }
1396
1397 - (BOOL) windowShouldClose: (id) sender
1398 {
1399     return YES;
1400 }
1401
1402 - (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag
1403 {
1404     if( !flag ) {
1405         [fWindow  makeKeyAndOrderFront:nil];
1406                 
1407         return YES;
1408     }
1409     
1410     return NO;
1411 }
1412
1413 #pragma mark -
1414 #pragma mark Job Handling
1415
1416
1417 - (void) prepareJob
1418 {
1419     hb_list_t  * list  = hb_get_titles( fHandle );
1420     hb_title_t * title = (hb_title_t *) hb_list_item( list,
1421             [fSrcTitlePopUp indexOfSelectedItem] );
1422     hb_job_t * job = title->job;
1423     //int i;
1424
1425     /* Chapter selection */
1426     job->chapter_start = [fSrcChapterStartPopUp indexOfSelectedItem] + 1;
1427     job->chapter_end   = [fSrcChapterEndPopUp   indexOfSelectedItem] + 1;
1428         
1429     /* Format and codecs */
1430     int format = [fDstFormatPopUp indexOfSelectedItem];
1431     int codecs = [fDstCodecsPopUp indexOfSelectedItem];
1432     job->mux    = FormatSettings[format][codecs] & HB_MUX_MASK;
1433     job->vcodec = FormatSettings[format][codecs] & HB_VCODEC_MASK;
1434     job->acodec = FormatSettings[format][codecs] & HB_ACODEC_MASK;
1435     /* If mpeg-4, then set mpeg-4 specific options like chapters and > 4gb file sizes */
1436         if ([fDstFormatPopUp indexOfSelectedItem] == 0)
1437         {
1438         /* We set the largeFileSize (64 bit formatting) variable here to allow for > 4gb files based on the format being
1439                 mpeg4 and the checkbox being checked 
1440                 *Note: this will break compatibility with some target devices like iPod, etc.!!!!*/
1441                 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"AllowLargeFiles"] > 0 && [fDstMp4LargeFileCheck state] == NSOnState)
1442                 {
1443                         job->largeFileSize = 1;
1444                 }
1445                 else
1446                 {
1447                         job->largeFileSize = 0;
1448                 }
1449         /* We set http optimized mp4 here */
1450         if ([fDstMp4HttpOptFileCheck state] == NSOnState)
1451                 {
1452         job->mp4_optimize = 1;
1453         }
1454         else
1455         {
1456         job->mp4_optimize = 0;
1457         }
1458     }
1459         if ([fDstFormatPopUp indexOfSelectedItem] == 0 || [fDstFormatPopUp indexOfSelectedItem] == 3)
1460         {
1461           /* We set the chapter marker extraction here based on the format being
1462                 mpeg4 or mkv and the checkbox being checked */
1463                 if ([fCreateChapterMarkers state] == NSOnState)
1464                 {
1465                         job->chapter_markers = 1;
1466                 }
1467                 else
1468                 {
1469                         job->chapter_markers = 0;
1470                 }
1471         }
1472         if( ( job->vcodec & HB_VCODEC_FFMPEG ) &&
1473         [fVidEncoderPopUp indexOfSelectedItem] > 0 )
1474     {
1475         job->vcodec = HB_VCODEC_XVID;
1476     }
1477     if( job->vcodec & HB_VCODEC_X264 )
1478     {
1479                 if ([fVidEncoderPopUp indexOfSelectedItem] > 0 )
1480             {
1481                         /* Just use new Baseline Level 3.0 
1482                         Lets Deprecate Baseline Level 1.3h264_level*/
1483                         job->h264_level = 30;
1484                         job->mux = HB_MUX_IPOD;
1485                         /* move sanity check for iPod Encoding here */
1486                         job->pixel_ratio = 0 ;
1487                         
1488                 }
1489                 
1490                 /* Set this flag to switch from Constant Quantizer(default) to Constant Rate Factor Thanks jbrjake
1491                 Currently only used with Constant Quality setting*/
1492                 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultCrf"] > 0 && [fVidQualityMatrix selectedRow] == 2)
1493                 {
1494                 job->crf = 1;
1495                 }
1496                 
1497                 /* Below Sends x264 options to the core library if x264 is selected*/
1498                 /* Lets use this as per Nyx, Thanks Nyx!*/
1499                 job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */
1500                 /* Turbo first pass if two pass and Turbo First pass is selected */
1501                 if( [fVidTwoPassCheck state] == NSOnState && [fVidTurboPassCheck state] == NSOnState )
1502                 {
1503                         /* pass the "Turbo" string to be appended to the existing x264 opts string into a variable for the first pass */
1504                         NSString *firstPassOptStringTurbo = @":ref=1:subme=1:me=dia:analyse=none:trellis=0:no-fast-pskip=0:8x8dct=0";
1505                         /* append the "Turbo" string variable to the existing opts string.
1506                         Note: the "Turbo" string must be appended, not prepended to work properly*/
1507                         NSString *firstPassOptStringCombined = [[fAdvancedOptions optionsString] stringByAppendingString:firstPassOptStringTurbo];
1508                         strcpy(job->x264opts, [firstPassOptStringCombined UTF8String]);
1509                 }
1510                 else
1511                 {
1512                         strcpy(job->x264opts, [[fAdvancedOptions optionsString] UTF8String]);
1513                 }
1514                 
1515         job->h264_13 = [fVidEncoderPopUp indexOfSelectedItem];
1516     }
1517
1518     /* Video settings */
1519     if( [fVidRatePopUp indexOfSelectedItem] > 0 )
1520     {
1521         job->vrate      = 27000000;
1522         job->vrate_base = hb_video_rates[[fVidRatePopUp
1523             indexOfSelectedItem]-1].rate;
1524     }
1525     else
1526     {
1527         job->vrate      = title->rate;
1528         job->vrate_base = title->rate_base;
1529     }
1530
1531     switch( [fVidQualityMatrix selectedRow] )
1532     {
1533         case 0:
1534             /* Target size.
1535                Bitrate should already have been calculated and displayed
1536                in fVidBitrateField, so let's just use it */
1537         case 1:
1538             job->vquality = -1.0;
1539             job->vbitrate = [fVidBitrateField intValue];
1540             break;
1541         case 2:
1542             job->vquality = [fVidQualitySlider floatValue];
1543             job->vbitrate = 0;
1544             break;
1545     }
1546
1547     job->grayscale = ( [fVidGrayscaleCheck state] == NSOnState );
1548
1549     /* Subtitle settings */
1550     job->subtitle = [fSubPopUp indexOfSelectedItem] - 2;
1551
1552     /* Audio tracks and mixdowns */
1553     /* check for the condition where track 2 has an audio selected, but track 1 does not */
1554     /* we will use track 2 as track 1 in this scenario */
1555     if ([fAudLang1PopUp indexOfSelectedItem] > 0)
1556     {
1557         job->audios[0] = [fAudLang1PopUp indexOfSelectedItem] - 1;
1558         job->audios[1] = [fAudLang2PopUp indexOfSelectedItem] - 1; /* will be -1 if "none" is selected */
1559         job->audios[2] = -1;
1560         job->audio_mixdowns[0] = [[fAudTrack1MixPopUp selectedItem] tag];
1561         job->audio_mixdowns[1] = [[fAudTrack2MixPopUp selectedItem] tag];
1562     }
1563     else if ([fAudLang2PopUp indexOfSelectedItem] > 0)
1564     {
1565         job->audios[0] = [fAudLang2PopUp indexOfSelectedItem] - 1;
1566         job->audio_mixdowns[0] = [[fAudTrack2MixPopUp selectedItem] tag];
1567         job->audios[1] = -1;
1568     }
1569     else
1570     {
1571         job->audios[0] = -1;
1572     }
1573
1574     /* Audio settings */
1575     job->arate = hb_audio_rates[[fAudRatePopUp
1576                      indexOfSelectedItem]].rate;
1577     job->abitrate = [[fAudBitratePopUp selectedItem] tag];
1578     
1579     /* Dynamic Range Compression */
1580     job->dynamic_range_compression = [fAudDrcField floatValue];
1581     
1582     /* set vfr according to the Picture Window */
1583     if ([fPictureController vfr])
1584     {
1585     job->vfr = 1;
1586     }
1587     else
1588     {
1589     job->vfr = 0;
1590     }
1591     
1592     /* Filters */ 
1593     job->filters = hb_list_init();
1594    
1595         /* Detelecine */
1596     if ([fPictureController detelecine])
1597     {
1598         hb_list_add( job->filters, &hb_filter_detelecine );
1599     }
1600    
1601     /* Deinterlace */
1602     if ([fPictureController deinterlace] == 1)
1603     {
1604         /* Run old deinterlacer by default */
1605         hb_filter_deinterlace.settings = "-1"; 
1606         hb_list_add( job->filters, &hb_filter_deinterlace );
1607     }
1608     else if ([fPictureController deinterlace] == 2)
1609     {
1610         /* Yadif mode 0 (1-pass with spatial deinterlacing.) */
1611         hb_filter_deinterlace.settings = "0"; 
1612         hb_list_add( job->filters, &hb_filter_deinterlace );            
1613     }
1614     else if ([fPictureController deinterlace] == 3)
1615     {
1616         /* Yadif (1-pass w/o spatial deinterlacing) and Mcdeint */
1617         hb_filter_deinterlace.settings = "2:-1:1"; 
1618         hb_list_add( job->filters, &hb_filter_deinterlace );            
1619     }
1620     else if ([fPictureController deinterlace] == 4)
1621     {
1622         /* Yadif (2-pass w/ spatial deinterlacing) and Mcdeint*/
1623         hb_filter_deinterlace.settings = "1:-1:1"; 
1624         hb_list_add( job->filters, &hb_filter_deinterlace );            
1625     }
1626         
1627         /* Denoise */
1628         
1629         if ([fPictureController denoise] == 1) // Weak in popup
1630         {
1631                 hb_filter_denoise.settings = "2:1:2:3"; 
1632         hb_list_add( job->filters, &hb_filter_denoise );        
1633         }
1634         else if ([fPictureController denoise] == 2) // Medium in popup
1635         {
1636                 hb_filter_denoise.settings = "3:2:2:3"; 
1637         hb_list_add( job->filters, &hb_filter_denoise );        
1638         }
1639         else if ([fPictureController denoise] == 3) // Strong in popup
1640         {
1641                 hb_filter_denoise.settings = "7:7:5:5"; 
1642         hb_list_add( job->filters, &hb_filter_denoise );        
1643         }
1644     
1645     /* Deblock  (uses pp7 default) */
1646     if ([fPictureController deblock])
1647     {
1648         hb_list_add( job->filters, &hb_filter_deblock );
1649     }
1650
1651 }
1652
1653
1654
1655 /* addToQueue: puts up an alert before ultimately calling doAddToQueue
1656 */
1657 - (IBAction) addToQueue: (id) sender
1658 {
1659         /* We get the destination directory from the destination field here */
1660         NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
1661         /* We check for a valid destination here */
1662         if ([[NSFileManager defaultManager] fileExistsAtPath:destinationDirectory] == 0) 
1663         {
1664                 NSRunAlertPanel(@"Warning!", @"This is not a valid destination directory!", @"OK", nil, nil);
1665         return;
1666         }
1667
1668     /* We check for duplicate name here */
1669         if( [[NSFileManager defaultManager] fileExistsAtPath:
1670             [fDstFile2Field stringValue]] )
1671     {
1672         NSBeginCriticalAlertSheet( _( @"File already exists" ),
1673             _( @"Cancel" ), _( @"Overwrite" ), NULL, fWindow, self,
1674             @selector( overwriteAddToQueueAlertDone:returnCode:contextInfo: ),
1675             NULL, NULL, [NSString stringWithFormat:
1676             _( @"Do you want to overwrite %@?" ),
1677             [fDstFile2Field stringValue]] );
1678         // overwriteAddToQueueAlertDone: will be called when the alert is dismissed.
1679     }
1680     else
1681     {
1682         [self doAddToQueue];
1683     }
1684 }
1685
1686 /* overwriteAddToQueueAlertDone: called from the alert posted by addToQueue that asks
1687    the user if they want to overwrite an exiting movie file.
1688 */
1689 - (void) overwriteAddToQueueAlertDone: (NSWindow *) sheet
1690     returnCode: (int) returnCode contextInfo: (void *) contextInfo
1691 {
1692     if( returnCode == NSAlertAlternateReturn )
1693         [self doAddToQueue];
1694 }
1695
1696 - (void) doAddToQueue
1697 {
1698     hb_list_t  * list  = hb_get_titles( fHandle );
1699     hb_title_t * title = (hb_title_t *) hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
1700     hb_job_t * job = title->job;
1701
1702     // Assign a unique job group ID for all passes of the same encode. This is how the
1703     // UI lumps together jobs to form encodes. libhb does not use this id.
1704     static int jobGroupID = 0;
1705     jobGroupID++;
1706     
1707     // A sequence number, starting at zero, is also used to identifiy to each pass.
1708     // This is used by the UI to determine if a pass if the first pass of an encode.
1709     int sequenceNum = -1;
1710     
1711     [self prepareJob];
1712
1713     /* Destination file */
1714     job->file = [[fDstFile2Field stringValue] UTF8String];
1715
1716     if( [fSubForcedCheck state] == NSOnState )
1717         job->subtitle_force = 1;
1718     else
1719         job->subtitle_force = 0;
1720
1721     /*
1722     * subtitle of -1 is a scan
1723     */
1724     if( job->subtitle == -1 )
1725     {
1726         char *x264opts_tmp;
1727
1728         /*
1729         * When subtitle scan is enabled do a fast pre-scan job
1730         * which will determine which subtitles to enable, if any.
1731         */
1732         job->pass = -1;
1733         x264opts_tmp = job->x264opts;
1734         job->subtitle = -1;
1735
1736         job->x264opts = NULL;
1737
1738         job->indepth_scan = 1;  
1739
1740         job->select_subtitle = (hb_subtitle_t**)malloc(sizeof(hb_subtitle_t*));
1741         *(job->select_subtitle) = NULL;
1742
1743         /*
1744         * Add the pre-scan job
1745         */
1746         job->sequence_id = MakeJobID(jobGroupID, ++sequenceNum);
1747         hb_add( fHandle, job );
1748
1749         job->x264opts = x264opts_tmp;
1750     }
1751     else
1752         job->select_subtitle = NULL;
1753
1754     /* No subtitle were selected, so reset the subtitle to -1 (which before
1755     * this point meant we were scanning
1756     */
1757     if( job->subtitle == -2 )
1758         job->subtitle = -1;
1759
1760     if( [fVidTwoPassCheck state] == NSOnState )
1761     {
1762         hb_subtitle_t **subtitle_tmp = job->select_subtitle;
1763         job->indepth_scan = 0;
1764
1765         /*
1766          * Do not autoselect subtitles on the first pass of a two pass
1767          */
1768         job->select_subtitle = NULL;
1769         
1770         job->pass = 1;
1771         job->sequence_id = MakeJobID(jobGroupID, ++sequenceNum);
1772         hb_add( fHandle, job );
1773
1774         job->pass = 2;
1775         job->sequence_id = MakeJobID(jobGroupID, ++sequenceNum);
1776
1777         job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */  
1778         strcpy(job->x264opts, [[fAdvancedOptions optionsString] UTF8String]);
1779
1780         job->select_subtitle = subtitle_tmp;
1781
1782         hb_add( fHandle, job );
1783     }
1784     else
1785     {
1786         job->indepth_scan = 0;
1787         job->pass = 0;
1788         job->sequence_id = MakeJobID(jobGroupID, ++sequenceNum);
1789         hb_add( fHandle, job );
1790     }
1791         
1792     NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
1793         [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];
1794         
1795     // Notify the queue
1796         [fQueueController hblibJobListChanged];
1797 }
1798
1799 /* Rip: puts up an alert before ultimately calling doRip
1800 */
1801 - (IBAction) Rip: (id) sender
1802 {
1803     /* Rip or Cancel ? */
1804     hb_state_t s;
1805     hb_get_state2( fHandle, &s );
1806
1807     if(s.state == HB_STATE_WORKING || s.state == HB_STATE_PAUSED)
1808         {
1809         [self Cancel: sender];
1810         return;
1811     }
1812     
1813     // If there are jobs in the queue, then this is a rip the queue
1814     
1815     if (hb_count( fHandle ) > 0)
1816     {
1817         [self doRip];
1818         return;
1819     }
1820
1821     // Before adding jobs to the queue, check for a valid destination.
1822
1823     NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
1824     if ([[NSFileManager defaultManager] fileExistsAtPath:destinationDirectory] == 0) 
1825     {
1826         NSRunAlertPanel(@"Warning!", @"This is not a valid destination directory!", @"OK", nil, nil);
1827         return;
1828     }
1829
1830     /* We check for duplicate name here */
1831     if( [[NSFileManager defaultManager] fileExistsAtPath:[fDstFile2Field stringValue]] )
1832     {
1833         NSBeginCriticalAlertSheet( _( @"File already exists" ),
1834             _( @"Cancel" ), _( @"Overwrite" ), NULL, fWindow, self,
1835             @selector( overWriteAlertDone:returnCode:contextInfo: ),
1836             NULL, NULL, [NSString stringWithFormat:
1837             _( @"Do you want to overwrite %@?" ),
1838             [fDstFile2Field stringValue]] );
1839             
1840         // overWriteAlertDone: will be called when the alert is dismissed. It will call doRip.
1841     }
1842     else
1843     {
1844         /* if there are no jobs in the queue, then add this one to the queue and rip 
1845         otherwise, just rip the queue */
1846         if( hb_count( fHandle ) == 0)
1847         {
1848             [self doAddToQueue];
1849         }
1850
1851         NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
1852         [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];
1853         [self doRip];
1854     }
1855 }
1856
1857 /* overWriteAlertDone: called from the alert posted by Rip: that asks the user if they
1858    want to overwrite an exiting movie file.
1859 */
1860 - (void) overWriteAlertDone: (NSWindow *) sheet
1861     returnCode: (int) returnCode contextInfo: (void *) contextInfo
1862 {
1863     if( returnCode == NSAlertAlternateReturn )
1864     {
1865         /* if there are no jobs in the queue, then add this one to the queue and rip 
1866         otherwise, just rip the queue */
1867         if( hb_count( fHandle ) == 0 )
1868         {
1869             [self doAddToQueue];
1870         }
1871
1872         NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
1873         [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];
1874         [self doRip];
1875     }
1876 }
1877
1878 - (void) remindUserOfSleepOrShutdown
1879 {
1880        if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Put Computer To Sleep"])
1881        {
1882                /*Warn that computer will sleep after encoding*/
1883                int reminduser;
1884                NSBeep();
1885                reminduser = NSRunAlertPanel(@"The computer will sleep after encoding is done.",@"You have selected to sleep the computer after encoding. To turn off sleeping, go to the HandBrake preferences.", @"OK", @"Preferences...", nil);
1886                [NSApp requestUserAttention:NSCriticalRequest];
1887                if ( reminduser == NSAlertAlternateReturn ) 
1888                {
1889                        [self showPreferencesWindow:NULL];
1890                }
1891        } 
1892        else if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Shut Down Computer"])
1893        {
1894                /*Warn that computer will shut down after encoding*/
1895                int reminduser;
1896                NSBeep();
1897                reminduser = NSRunAlertPanel(@"The computer will shut down after encoding is done.",@"You have selected to shut down the computer after encoding. To turn off shut down, go to the HandBrake preferences.", @"OK", @"Preferences...", nil);
1898                [NSApp requestUserAttention:NSCriticalRequest];
1899                if ( reminduser == NSAlertAlternateReturn ) 
1900                {
1901                        [self showPreferencesWindow:NULL];
1902                }
1903        }
1904
1905 }
1906
1907
1908 - (void) doRip
1909 {
1910     /* Let libhb do the job */
1911     hb_start( fHandle );
1912         /*set the fEncodeState State */
1913         fEncodeState = 1;
1914 }
1915
1916
1917
1918
1919 //------------------------------------------------------------------------------------
1920 // Removes all jobs from the queue. Does not cancel the current processing job.
1921 //------------------------------------------------------------------------------------
1922 - (void) doDeleteQueuedJobs
1923 {
1924     hb_job_t * job;
1925     while( ( job = hb_job( fHandle, 0 ) ) )
1926         hb_rem( fHandle, job );
1927 }
1928
1929 //------------------------------------------------------------------------------------
1930 // Cancels and deletes the current job and stops libhb from processing the remaining
1931 // encodes.
1932 //------------------------------------------------------------------------------------
1933 - (void) doCancelCurrentJob
1934 {
1935     // Stop the current job. hb_stop will only cancel the current pass and then set
1936     // its state to HB_STATE_WORKDONE. It also does this asynchronously. So when we
1937     // see the state has changed to HB_STATE_WORKDONE (in updateUI), we'll delete the
1938     // remaining passes of the job and then start the queue back up if there are any
1939     // remaining jobs.
1940      
1941     [fQueueController hblibWillStop];
1942     hb_stop( fHandle );
1943     fEncodeState = 2;   // don't alert at end of processing since this was a cancel
1944     
1945 }
1946
1947 //------------------------------------------------------------------------------------
1948 // Displays an alert asking user if the want to cancel encoding of current job.
1949 // Cancel: returns immediately after posting the alert. Later, when the user
1950 // acknowledges the alert, doCancelCurrentJob is called.
1951 //------------------------------------------------------------------------------------
1952 - (IBAction)Cancel: (id)sender
1953 {
1954     if (!fHandle) return;
1955     
1956     HBJobGroup * jobGroup = [fQueueController currentJobGroup];
1957     if (!jobGroup) return;
1958
1959     NSString * alertTitle = [NSString stringWithFormat:NSLocalizedString(@"Stop encoding %@?", nil),
1960             [jobGroup name]];
1961     
1962     // Which window to attach the sheet to?
1963     NSWindow * docWindow;
1964     if ([sender respondsToSelector: @selector(window)])
1965         docWindow = [sender window];
1966     else
1967         docWindow = fWindow;
1968         
1969     NSBeginCriticalAlertSheet(
1970             alertTitle,
1971             NSLocalizedString(@"Keep Encoding", nil),
1972             nil,
1973             NSLocalizedString(@"Stop Encoding", nil),
1974             docWindow, self,
1975             nil, @selector(didDimissCancelCurrentJob:returnCode:contextInfo:), nil,
1976             NSLocalizedString(@"Your movie will be lost if you don't continue encoding.", nil));
1977     
1978     // didDimissCancelCurrentJob:returnCode:contextInfo: will be called when the dialog is dismissed
1979 }
1980
1981 - (void) didDimissCancelCurrentJob: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo
1982 {
1983     if (returnCode == NSAlertOtherReturn)
1984         [self doCancelCurrentJob];  // <- this also stops libhb
1985 }
1986
1987
1988
1989
1990
1991 - (IBAction) Pause: (id) sender
1992 {
1993     hb_state_t s;
1994     hb_get_state2( fHandle, &s );
1995
1996     if( s.state == HB_STATE_PAUSED )
1997     {
1998         hb_resume( fHandle );
1999     }
2000     else
2001     {
2002         hb_pause( fHandle );
2003     }
2004 }
2005
2006 #pragma mark -
2007 #pragma mark GUI Controls Changed Methods
2008
2009 - (IBAction) titlePopUpChanged: (id) sender
2010 {
2011     hb_list_t  * list  = hb_get_titles( fHandle );
2012     hb_title_t * title = (hb_title_t*)
2013         hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
2014                 
2015                 
2016     /* If Auto Naming is on. We create an output filename of dvd name - title number */
2017     if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultAutoNaming"] > 0)
2018         {
2019                 [fDstFile2Field setStringValue: [NSString stringWithFormat:
2020                         @"%@/%@-%d.%@", [[fDstFile2Field stringValue] stringByDeletingLastPathComponent],
2021                         [NSString stringWithUTF8String: title->name],
2022                           title->index,
2023                         [[fDstFile2Field stringValue] pathExtension]]]; 
2024         }
2025
2026     /* Update chapter popups */
2027     [fSrcChapterStartPopUp removeAllItems];
2028     [fSrcChapterEndPopUp   removeAllItems];
2029     for( int i = 0; i < hb_list_count( title->list_chapter ); i++ )
2030     {
2031         [fSrcChapterStartPopUp addItemWithTitle: [NSString
2032             stringWithFormat: @"%d", i + 1]];
2033         [fSrcChapterEndPopUp addItemWithTitle: [NSString
2034             stringWithFormat: @"%d", i + 1]];
2035     }
2036     [fSrcChapterStartPopUp selectItemAtIndex: 0];
2037     [fSrcChapterEndPopUp   selectItemAtIndex:
2038         hb_list_count( title->list_chapter ) - 1];
2039     [self chapterPopUpChanged: NULL];
2040
2041 /* Start Get and set the initial pic size for display */
2042         hb_job_t * job = title->job;
2043         fTitle = title; 
2044
2045         /* Pixel Ratio Setting */
2046         if ([[NSUserDefaults standardUserDefaults] boolForKey:@"PixelRatio"])
2047     {
2048                 job->pixel_ratio = 1 ;
2049         }
2050         else
2051         {
2052                 job->pixel_ratio = 0 ;
2053         }
2054         /*Set Source Size Field Here */
2055     [fPicSettingsSrc setStringValue: [NSString stringWithFormat: @"%d x %d", fTitle->width, fTitle->height]];
2056         
2057         /* Set Auto Crop to on upon selecting a new title */
2058     [fPictureController setAutoCrop:YES];
2059     
2060         /* We get the originial output picture width and height and put them
2061         in variables for use with some presets later on */
2062         PicOrigOutputWidth = job->width;
2063         PicOrigOutputHeight = job->height;
2064         AutoCropTop = job->crop[0];
2065         AutoCropBottom = job->crop[1];
2066         AutoCropLeft = job->crop[2];
2067         AutoCropRight = job->crop[3];
2068
2069         /* we run the picture size values through
2070         calculatePictureSizing to get all picture size
2071         information*/
2072         [self calculatePictureSizing: NULL];
2073         /* Run Through encoderPopUpChanged to see if there
2074                 needs to be any pic value modifications based on encoder settings */
2075         //[self encoderPopUpChanged: NULL];
2076         /* END Get and set the initial pic size for display */ 
2077
2078     /* Update subtitle popups */
2079     hb_subtitle_t * subtitle;
2080     [fSubPopUp removeAllItems];
2081     [fSubPopUp addItemWithTitle: @"None"];
2082     [fSubPopUp addItemWithTitle: @"Autoselect"];
2083     for( int i = 0; i < hb_list_count( title->list_subtitle ); i++ )
2084     {
2085         subtitle = (hb_subtitle_t *) hb_list_item( title->list_subtitle, i );
2086
2087         /* We cannot use NSPopUpButton's addItemWithTitle because
2088            it checks for duplicate entries */
2089         [[fSubPopUp menu] addItemWithTitle: [NSString stringWithCString:
2090             subtitle->lang] action: NULL keyEquivalent: @""];
2091     }
2092     [fSubPopUp selectItemAtIndex: 0];
2093         
2094         [self subtitleSelectionChanged: NULL];
2095     
2096     /* Update chapter table */
2097     [fChapterTitlesDelegate resetWithTitle:title];
2098     [fChapterTable reloadData];
2099
2100     /* Update audio popups */
2101     [self addAllAudioTracksToPopUp: fAudLang1PopUp];
2102     [self addAllAudioTracksToPopUp: fAudLang2PopUp];
2103     /* search for the first instance of our prefs default language for track 1, and set track 2 to "none" */
2104         NSString * audioSearchPrefix = [[NSUserDefaults standardUserDefaults] stringForKey:@"DefaultLanguage"];
2105         [self selectAudioTrackInPopUp: fAudLang1PopUp searchPrefixString: audioSearchPrefix selectIndexIfNotFound: 1];
2106     [self selectAudioTrackInPopUp: fAudLang2PopUp searchPrefixString: NULL selectIndexIfNotFound: 0];
2107         
2108         /* changing the title may have changed the audio channels on offer, */
2109         /* so call audioTrackPopUpChanged for both audio tracks to update the mixdown popups */
2110         [self audioTrackPopUpChanged: fAudLang1PopUp];
2111         [self audioTrackPopUpChanged: fAudLang2PopUp];
2112     
2113     /* We repopulate the Video Framerate popup and show the detected framerate along with "Same as Source"*/
2114     [fVidRatePopUp removeAllItems];
2115     if (fTitle->rate_base == 1126125) // 23.976 NTSC Film
2116     {
2117         [fVidRatePopUp addItemWithTitle: @"Same as source (23.976)"];
2118     }
2119     else if (fTitle->rate_base == 1080000) // 25 PAL Film/Video
2120     {
2121         [fVidRatePopUp addItemWithTitle: @"Same as source (25)"];
2122     }
2123     else if (fTitle->rate_base == 900900) // 29.97 NTSC Video
2124     {
2125         [fVidRatePopUp addItemWithTitle: @"Same as source (29.97)"];
2126     }
2127     else
2128     {
2129         /* if none of the common dvd source framerates is detected, just use "Same as source" */
2130         [fVidRatePopUp addItemWithTitle: @"Same as source"];
2131     }
2132         for( int i = 0; i < hb_video_rates_count; i++ )
2133     {
2134         if ([[NSString stringWithCString: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%.3f",23.976]])
2135                 {
2136                         [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@",
2137                                 [NSString stringWithCString: hb_video_rates[i].string], @" (NTSC Film)"]];
2138                 }
2139                 else if ([[NSString stringWithCString: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%d",25]])
2140                 {
2141                         [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@",
2142                                 [NSString stringWithCString: hb_video_rates[i].string], @" (PAL Film/Video)"]];
2143                 }
2144                 else if ([[NSString stringWithCString: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%.2f",29.97]])
2145                 {
2146                         [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@",
2147                                 [NSString stringWithCString: hb_video_rates[i].string], @" (NTSC Video)"]];
2148                 }
2149                 else
2150                 {
2151                         [fVidRatePopUp addItemWithTitle:
2152                                 [NSString stringWithCString: hb_video_rates[i].string]];
2153                 }
2154     }   
2155     [fVidRatePopUp selectItemAtIndex: 0];
2156     
2157    /* lets call tableViewSelected to make sure that any preset we have selected is enforced after a title change */
2158         [self selectPreset:NULL]; 
2159         
2160 }
2161
2162 - (IBAction) chapterPopUpChanged: (id) sender
2163 {
2164     
2165         /* If start chapter popup is greater than end chapter popup,
2166         we set the end chapter popup to the same as start chapter popup */
2167         if ([fSrcChapterStartPopUp indexOfSelectedItem] > [fSrcChapterEndPopUp indexOfSelectedItem])
2168         {
2169                 [fSrcChapterEndPopUp selectItemAtIndex: [fSrcChapterStartPopUp indexOfSelectedItem]];
2170     }
2171
2172                 
2173         hb_list_t  * list  = hb_get_titles( fHandle );
2174     hb_title_t * title = (hb_title_t *)
2175         hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
2176
2177     hb_chapter_t * chapter;
2178     int64_t        duration = 0;
2179     for( int i = [fSrcChapterStartPopUp indexOfSelectedItem];
2180          i <= [fSrcChapterEndPopUp indexOfSelectedItem]; i++ )
2181     {
2182         chapter = (hb_chapter_t *) hb_list_item( title->list_chapter, i );
2183         duration += chapter->duration;
2184     }
2185     
2186     duration /= 90000; /* pts -> seconds */
2187     [fSrcDuration2Field setStringValue: [NSString stringWithFormat:
2188         @"%02lld:%02lld:%02lld", duration / 3600, ( duration / 60 ) % 60,
2189         duration % 60]];
2190
2191     [self calculateBitrate: sender];
2192 }
2193
2194 - (IBAction) formatPopUpChanged: (id) sender
2195 {
2196     NSString * string = [fDstFile2Field stringValue];
2197     NSString * selectedCodecs = [fDstCodecsPopUp titleOfSelectedItem];
2198     int format = [fDstFormatPopUp indexOfSelectedItem];
2199     char * ext = NULL;
2200         /* Initially set the large file (64 bit formatting) output checkbox to hidden */
2201     [fDstMp4LargeFileCheck setHidden: YES];
2202     [fDstMp4HttpOptFileCheck setHidden: YES];
2203     
2204     /* Update the codecs popup */
2205     [fDstCodecsPopUp removeAllItems];
2206     switch( format )
2207     {
2208         case 0:
2209                         /*Get Default MP4 File Extension*/
2210                         if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultMpegName"] > 0)
2211                         {
2212                                 ext = "m4v";
2213                         }
2214                         else
2215                         {
2216                                 ext = "mp4";
2217                         }
2218             
2219             [fDstCodecsPopUp addItemWithTitle:_( @"MPEG-4 Video / AAC Audio" )];
2220             [fDstCodecsPopUp addItemWithTitle:_( @"AVC/H.264 Video / AAC Audio" )];
2221             
2222                         /* We enable the create chapters checkbox here since we are .mp4*/
2223                         [fCreateChapterMarkers setEnabled: YES];
2224                         /* We show the Large File (64 bit formatting) checkbox since we are .mp4 
2225              if we have enabled the option in the global preferences*/
2226                         if ([[NSUserDefaults standardUserDefaults] boolForKey:@"AllowLargeFiles"] > 0)
2227                         {
2228                                 [fDstMp4LargeFileCheck setHidden: NO];
2229                         }
2230             else
2231             {
2232                 /* if not enable in global preferences, we additionaly sanity check that the
2233                  hidden checkbox is set to off. */
2234                 [fDstMp4LargeFileCheck setState: NSOffState];
2235             }
2236             /* We show the HTTP Optimized checkbox here since we are mp4 */
2237             [fDstMp4HttpOptFileCheck setHidden: NO];
2238             break;
2239             
2240         case 1:
2241             ext = "mkv";
2242             [fDstCodecsPopUp addItemWithTitle:_( @"MPEG-4 Video / AAC Audio" )];
2243             [fDstCodecsPopUp addItemWithTitle:_( @"MPEG-4 Video / AC-3 Audio" )];
2244                         [fDstCodecsPopUp addItemWithTitle:_( @"MPEG-4 Video / MP3 Audio" )];
2245                         [fDstCodecsPopUp addItemWithTitle:_( @"MPEG-4 Video / Vorbis Audio" )];
2246             
2247                         [fDstCodecsPopUp addItemWithTitle:_( @"AVC/H.264 Video / AAC Audio" )];
2248                         [fDstCodecsPopUp addItemWithTitle:_( @"AVC/H.264 Video / AC-3 Audio" )];
2249                         [fDstCodecsPopUp addItemWithTitle:_( @"AVC/H.264 Video / MP3 Audio" )];
2250                         [fDstCodecsPopUp addItemWithTitle:_( @"AVC/H.264 Video / Vorbis Audio" )];
2251             /* We enable the create chapters checkbox here */
2252                         [fCreateChapterMarkers setEnabled: YES];
2253                         break;
2254             
2255         case 2: 
2256             ext = "avi";
2257             [fDstCodecsPopUp addItemWithTitle:_( @"MPEG-4 Video / MP3 Audio" )];
2258             [fDstCodecsPopUp addItemWithTitle:_( @"MPEG-4 Video / AC-3 Audio" )];
2259             [fDstCodecsPopUp addItemWithTitle:_( @"AVC/H.264 Video / MP3 Audio" )];
2260             [fDstCodecsPopUp addItemWithTitle:_( @"AVC/H.264 Video / AC-3 Audio" )];
2261                         /* We disable the create chapters checkbox here and make sure it is unchecked*/
2262                         [fCreateChapterMarkers setEnabled: NO];
2263                         [fCreateChapterMarkers setState: NSOffState];
2264                         break;
2265             
2266         case 3:
2267             ext = "ogm";
2268             [fDstCodecsPopUp addItemWithTitle:_( @"MPEG-4 Video / Vorbis Audio" )];
2269             [fDstCodecsPopUp addItemWithTitle:_( @"MPEG-4 Video / MP3 Audio" )];
2270             /* We disable the create chapters checkbox here and make sure it is unchecked*/
2271                         [fCreateChapterMarkers setEnabled: NO];
2272                         [fCreateChapterMarkers setState: NSOffState];
2273                         break;
2274     }
2275     
2276     if ( SuccessfulScan ) {
2277         [fDstCodecsPopUp selectItemWithTitle:selectedCodecs];
2278         
2279         /* Add/replace to the correct extension */
2280         if( [string characterAtIndex: [string length] - 4] == '.' )
2281         {
2282             [fDstFile2Field setStringValue: [NSString stringWithFormat:
2283                 @"%@.%s", [string substringToIndex: [string length] - 4],
2284                 ext]];
2285         }
2286         else
2287         {
2288             [fDstFile2Field setStringValue: [NSString stringWithFormat:
2289                 @"%@.%s", string, ext]];
2290         }
2291         
2292         if ( [fDstCodecsPopUp selectedItem] == NULL )
2293         {
2294             [fDstCodecsPopUp selectItemAtIndex:0];
2295             [self codecsPopUpChanged: NULL];
2296             
2297             /* changing the format may mean that we can / can't offer mono or 6ch, */
2298             /* so call audioTrackPopUpChanged for both audio tracks to update the mixdown popups */
2299             [self audioTrackPopUpChanged: fAudLang1PopUp];
2300             [self audioTrackPopUpChanged: fAudLang2PopUp];
2301             /* We call the method to properly enable/disable turbo 2 pass */
2302             [self twoPassCheckboxChanged: sender];
2303             /* We call method method to change UI to reflect whether a preset is used or not*/
2304         }
2305     }
2306     
2307         [self customSettingUsed: sender];       
2308 }
2309
2310 - (IBAction) codecsPopUpChanged: (id) sender
2311 {
2312     int format = [fDstFormatPopUp indexOfSelectedItem];
2313     int codecs = [fDstCodecsPopUp indexOfSelectedItem];
2314         
2315     [fAdvancedOptions setHidden:YES];
2316
2317     /* Update the encoder popup*/
2318     if( ( FormatSettings[format][codecs] & HB_VCODEC_X264 ) )
2319     {
2320         /* MPEG-4 -> H.264 */
2321         [fVidEncoderPopUp removeAllItems];
2322                 [fVidEncoderPopUp addItemWithTitle: @"x264 (h.264 Main)"];
2323                 [fVidEncoderPopUp addItemWithTitle: @"x264 (h.264 iPod)"];
2324                 [fVidEncoderPopUp selectItemAtIndex: 0];
2325         [fAdvancedOptions setHidden:NO];
2326     }
2327     
2328     else if( ( FormatSettings[format][codecs] & HB_VCODEC_FFMPEG ) )
2329     {
2330         /* H.264 -> MPEG-4 */
2331         [fVidEncoderPopUp removeAllItems];
2332         [fVidEncoderPopUp addItemWithTitle: @"FFmpeg"];
2333         [fVidEncoderPopUp addItemWithTitle: @"XviD"];
2334         [fVidEncoderPopUp selectItemAtIndex: 0];
2335                                 
2336     }
2337
2338     if( FormatSettings[format][codecs] & HB_ACODEC_AC3 )
2339     {
2340         /* AC-3 pass-through: disable samplerate and bitrate */
2341         [fAudRatePopUp    setEnabled: NO];
2342         [fAudBitratePopUp setEnabled: NO];
2343     }
2344     else
2345     {
2346         [fAudRatePopUp    setEnabled: YES];
2347         [fAudBitratePopUp setEnabled: YES];
2348     }
2349     /* changing the codecs on offer may mean that we can / can't offer mono or 6ch, */
2350         /* so call audioTrackPopUpChanged for both audio tracks to update the mixdown popups */
2351         [self audioTrackPopUpChanged: fAudLang1PopUp];
2352         [self audioTrackPopUpChanged: fAudLang2PopUp];
2353         [self encoderPopUpChanged: sender];
2354
2355 }
2356
2357 - (IBAction) encoderPopUpChanged: (id) sender
2358 {
2359     hb_job_t * job = fTitle->job;
2360         /* Check to see if we need to modify the job pic values based on x264 (iPod) encoder selection */
2361     if ([fDstFormatPopUp indexOfSelectedItem] == 0 && [fDstCodecsPopUp indexOfSelectedItem] == 1 && [fVidEncoderPopUp indexOfSelectedItem] == 1)
2362     {
2363                 
2364                 job->pixel_ratio = 0 ;
2365                 
2366                 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultPicSizeAutoiPod"] > 0)
2367                 {
2368                         
2369                         if (job->width > 640)
2370                         {
2371                                 job->width = 640;
2372                         }
2373                         job->keep_ratio = 1;
2374                         hb_fix_aspect( job, HB_KEEP_WIDTH );
2375                         
2376                 }
2377                 /* Make sure the 64bit formatting checkbox is off */
2378                 [fDstMp4LargeFileCheck setState: NSOffState];
2379         }
2380     
2381     /* We need to set loose anamorphic as available depending on whether or not the ffmpeg encoder
2382     is being used as it borks up loose anamorphic .
2383     For convenience lets use the titleOfSelected index. Probably should revisit whether or not we want
2384     to use the index itself but this is easier */
2385     if ([fVidEncoderPopUp titleOfSelectedItem] == @"FFmpeg")
2386     {
2387         if (job->pixel_ratio == 2)
2388         {
2389             job->pixel_ratio = 0;
2390         }
2391         [fPictureController setAllowLooseAnamorphic:NO];
2392     }
2393     else
2394     {
2395         [fPictureController setAllowLooseAnamorphic:YES];
2396     }
2397     
2398         [self calculatePictureSizing: sender];
2399         [self twoPassCheckboxChanged: sender];
2400 }
2401
2402 /* Method to determine if we should change the UI
2403 To reflect whether or not a Preset is being used or if
2404 the user is using "Custom" settings by determining the sender*/
2405 - (IBAction) customSettingUsed: (id) sender
2406 {
2407         if ([sender stringValue] != NULL)
2408         {
2409                 /* Deselect the currently selected Preset if there is one*/
2410                 [fPresetsOutlineView deselectRow:[fPresetsOutlineView selectedRow]];
2411                 [[fPresetsActionMenu itemAtIndex:0] setEnabled: NO];
2412                 /* Change UI to show "Custom" settings are being used */
2413                 [fPresetSelectedDisplay setStringValue: @"Custom"];
2414                 
2415                 curUserPresetChosenNum = nil;
2416         }
2417
2418 }
2419
2420
2421 #pragma mark -
2422 #pragma mark - Video
2423
2424 - (IBAction) twoPassCheckboxChanged: (id) sender
2425 {
2426         /* check to see if x264 is chosen */
2427         int format = [fDstFormatPopUp indexOfSelectedItem];
2428     int codecs = [fDstCodecsPopUp indexOfSelectedItem];
2429         if( ( FormatSettings[format][codecs] & HB_VCODEC_X264 ) )
2430     {
2431                 if( [fVidTwoPassCheck state] == NSOnState)
2432                 {
2433                         [fVidTurboPassCheck setHidden: NO];
2434                 }
2435                 else
2436                 {
2437                         [fVidTurboPassCheck setHidden: YES];
2438                         [fVidTurboPassCheck setState: NSOffState];
2439                 }
2440                 /* Make sure Two Pass is checked if Turbo is checked */
2441                 if( [fVidTurboPassCheck state] == NSOnState)
2442                 {
2443                         [fVidTwoPassCheck setState: NSOnState];
2444                 }
2445         }
2446         else
2447         {
2448                 [fVidTurboPassCheck setHidden: YES];
2449                 [fVidTurboPassCheck setState: NSOffState];
2450         }
2451         
2452         /* We call method method to change UI to reflect whether a preset is used or not*/
2453         [self customSettingUsed: sender];
2454 }
2455
2456 - (IBAction ) videoFrameRateChanged: (id) sender
2457 {
2458     /* We call method method to calculatePictureSizing to error check detelecine*/
2459     [self calculatePictureSizing: sender];
2460
2461     /* We call method method to change UI to reflect whether a preset is used or not*/
2462         [self customSettingUsed: sender];
2463 }
2464 - (IBAction) videoMatrixChanged: (id) sender;
2465 {
2466     bool target, bitrate, quality;
2467
2468     target = bitrate = quality = false;
2469     if( [fVidQualityMatrix isEnabled] )
2470     {
2471         switch( [fVidQualityMatrix selectedRow] )
2472         {
2473             case 0:
2474                 target = true;
2475                 break;
2476             case 1:
2477                 bitrate = true;
2478                 break;
2479             case 2:
2480                 quality = true;
2481                 break;
2482         }
2483     }
2484     [fVidTargetSizeField  setEnabled: target];
2485     [fVidBitrateField     setEnabled: bitrate];
2486     [fVidQualitySlider    setEnabled: quality];
2487     [fVidTwoPassCheck     setEnabled: !quality &&
2488         [fVidQualityMatrix isEnabled]];
2489     if( quality )
2490     {
2491         [fVidTwoPassCheck setState: NSOffState];
2492                 [fVidTurboPassCheck setHidden: YES];
2493                 [fVidTurboPassCheck setState: NSOffState];
2494     }
2495
2496     [self qualitySliderChanged: sender];
2497     [self calculateBitrate: sender];
2498         [self customSettingUsed: sender];
2499 }
2500
2501 - (IBAction) qualitySliderChanged: (id) sender
2502 {
2503     [fVidConstantCell setTitle: [NSString stringWithFormat:
2504         _( @"Constant quality: %.0f %%" ), 100.0 *
2505         [fVidQualitySlider floatValue]]];
2506                 [self customSettingUsed: sender];
2507 }
2508
2509 - (void) controlTextDidChange: (NSNotification *) notification
2510 {
2511     [self calculateBitrate: NULL];
2512 }
2513
2514 - (IBAction) calculateBitrate: (id) sender
2515 {
2516     if( !fHandle || [fVidQualityMatrix selectedRow] != 0 || !SuccessfulScan )
2517     {
2518         return;
2519     }
2520
2521     hb_list_t  * list  = hb_get_titles( fHandle );
2522     hb_title_t * title = (hb_title_t *) hb_list_item( list,
2523             [fSrcTitlePopUp indexOfSelectedItem] );
2524     hb_job_t * job = title->job;
2525
2526     [self prepareJob];
2527
2528     [fVidBitrateField setIntValue: hb_calc_bitrate( job,
2529             [fVidTargetSizeField intValue] )];
2530 }
2531
2532 #pragma mark -
2533 #pragma mark - Picture
2534
2535 /* lets set the picture size back to the max from right after title scan
2536    Lets use an IBAction here as down the road we could always use a checkbox
2537    in the gui to easily take the user back to max. Remember, the compiler
2538    resolves IBActions down to -(void) during compile anyway */
2539 - (IBAction) revertPictureSizeToMax: (id) sender
2540 {
2541         hb_job_t * job = fTitle->job;
2542         /* We use the output picture width and height
2543      as calculated from libhb right after title is set
2544      in TitlePopUpChanged */
2545         job->width = PicOrigOutputWidth;
2546         job->height = PicOrigOutputHeight;
2547     [fPictureController setAutoCrop:YES];
2548         /* Here we use the auto crop values determined right after scan */
2549         job->crop[0] = AutoCropTop;
2550         job->crop[1] = AutoCropBottom;
2551         job->crop[2] = AutoCropLeft;
2552         job->crop[3] = AutoCropRight;
2553     
2554     
2555     [self calculatePictureSizing: sender];
2556     /* We call method to change UI to reflect whether a preset is used or not*/    
2557     [self customSettingUsed: sender];
2558 }
2559
2560 /**
2561  * Registers changes made in the Picture Settings Window.
2562  */
2563
2564 - (void)pictureSettingsDidChange {
2565         [self calculatePictureSizing: NULL];
2566 }
2567
2568 /* Get and Display Current Pic Settings in main window */
2569 - (IBAction) calculatePictureSizing: (id) sender
2570 {
2571         [fPicSettingsOutp setStringValue: [NSString stringWithFormat:@"%d x %d", fTitle->job->width, fTitle->job->height]];
2572         
2573     if (fTitle->job->pixel_ratio == 1)
2574         {
2575         int titlewidth = fTitle->width-fTitle->job->crop[2]-fTitle->job->crop[3];
2576         int arpwidth = fTitle->job->pixel_aspect_width;
2577         int arpheight = fTitle->job->pixel_aspect_height;
2578         int displayparwidth = titlewidth * arpwidth / arpheight;
2579         int displayparheight = fTitle->height-fTitle->job->crop[0]-fTitle->job->crop[1];
2580         [fPicSettingsOutp setStringValue: [NSString stringWithFormat:@"%d x %d", titlewidth, displayparheight]];
2581         [fPicSettingsAnamorphic setStringValue: [NSString stringWithFormat:@"%d x %d Strict", displayparwidth, displayparheight]];
2582         fTitle->job->keep_ratio = 0;
2583         }
2584     else if (fTitle->job->pixel_ratio == 2)
2585     {
2586         hb_job_t * job = fTitle->job;
2587         int output_width, output_height, output_par_width, output_par_height;
2588         hb_set_anamorphic_size(job, &output_width, &output_height, &output_par_width, &output_par_height);
2589         int display_width;
2590         display_width = output_width * output_par_width / output_par_height;
2591         
2592         [fPicSettingsOutp setStringValue: [NSString stringWithFormat:@"%d x %d", output_width, output_height]];
2593         [fPicSettingsAnamorphic setStringValue: [NSString stringWithFormat:@"%d x %d Loose", display_width, output_height]];
2594         
2595         fTitle->job->keep_ratio = 0;
2596     }
2597         else
2598         {
2599         [fPicSettingsAnamorphic setStringValue: [NSString stringWithFormat:@"Off"]];
2600         }
2601     
2602         /* Set ON/Off values for the deinterlace/keep aspect ratio according to boolean */      
2603         if (fTitle->job->keep_ratio > 0)
2604         {
2605                 [fPicSettingARkeep setStringValue: @"On"];
2606         }
2607         else
2608         {
2609                 [fPicSettingARkeep setStringValue: @"Off"];
2610         }       
2611     /* Detelecine */
2612     if ([fPictureController detelecine]) {
2613         [fPicSettingDetelecine setStringValue: @"Yes"];
2614     }
2615     else {
2616         [fPicSettingDetelecine setStringValue: @"No"];
2617     }
2618     
2619     /* VFR (Variable Frame Rate) */
2620     if ([fPictureController vfr]) {
2621         /* vfr has to set the framerate to 29.97 (ntsc video)
2622          and disable the framerate popup */
2623         [fVidRatePopUp selectItemAtIndex: 8];
2624         [fVidRatePopUp setEnabled: NO];
2625         /* We change the string of the fps popup to warn that vfr is on Framerate (FPS): */
2626         [fVidRateField setStringValue: @"Framerate (VFR On):"];  
2627         
2628     }
2629     else {
2630         /* vfr is off, make sure the framerate popup is enabled */
2631         [fVidRatePopUp setEnabled: YES];
2632         /* and make sure the label for framerate is set to its default */  
2633         [fVidRateField setStringValue: @"Framerate (FPS):"];
2634     }
2635     
2636         /* Deinterlace */
2637         if ([fPictureController deinterlace] == 0)
2638         {
2639                 [fPicSettingDeinterlace setStringValue: @"Off"];
2640         }
2641         else if ([fPictureController deinterlace] == 1)
2642         {
2643                 [fPicSettingDeinterlace setStringValue: @"Fast"];
2644         }
2645         else if ([fPictureController deinterlace] == 2)
2646         {
2647                 [fPicSettingDeinterlace setStringValue: @"Slow"];
2648         }
2649         else if ([fPictureController deinterlace] == 3)
2650         {
2651                 [fPicSettingDeinterlace setStringValue: @"Slower"];
2652         }
2653         else if ([fPictureController deinterlace] ==4)
2654         {
2655                 [fPicSettingDeinterlace setStringValue: @"Slowest"];
2656         }
2657         /* Denoise */
2658         if ([fPictureController denoise] == 0)
2659         {
2660                 [fPicSettingDenoise setStringValue: @"Off"];
2661         }
2662         else if ([fPictureController denoise] == 1)
2663         {
2664                 [fPicSettingDenoise setStringValue: @"Weak"];
2665         }
2666         else if ([fPictureController denoise] == 2)
2667         {
2668                 [fPicSettingDenoise setStringValue: @"Medium"];
2669         }
2670         else if ([fPictureController denoise] == 3)
2671         {
2672                 [fPicSettingDenoise setStringValue: @"Strong"];
2673         }
2674     
2675     /* Deblock */
2676     if ([fPictureController deblock]) {
2677         [fPicSettingDeblock setStringValue: @"Yes"];
2678     }
2679     else {
2680         [fPicSettingDeblock setStringValue: @"No"];
2681     }
2682         
2683         if (fTitle->job->pixel_ratio > 0)
2684         {
2685                 [fPicSettingPAR setStringValue: @""];
2686         }
2687         else
2688         {
2689                 [fPicSettingPAR setStringValue: @"Off"];
2690         }
2691         /* Set the display field for crop as per boolean */
2692         if (![fPictureController autoCrop])
2693         {
2694             [fPicSettingAutoCrop setStringValue: @"Custom"];
2695         }
2696         else
2697         {
2698                 [fPicSettingAutoCrop setStringValue: @"Auto"];
2699         }       
2700         
2701     
2702 }
2703
2704
2705 #pragma mark -
2706 #pragma mark - Audio and Subtitles
2707
2708 - (IBAction) setEnabledStateOfAudioMixdownControls: (id) sender
2709 {
2710
2711     /* enable/disable the mixdown text and popupbutton for audio track 1 */
2712     [fAudTrack1MixPopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
2713     [fAudTrack1MixLabel setTextColor: ([fAudLang1PopUp indexOfSelectedItem] == 0) ?
2714         [NSColor disabledControlTextColor] : [NSColor controlTextColor]];
2715
2716     /* enable/disable the mixdown text and popupbutton for audio track 2 */
2717     [fAudTrack2MixPopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
2718     [fAudTrack2MixLabel setTextColor: ([fAudLang2PopUp indexOfSelectedItem] == 0) ?
2719         [NSColor disabledControlTextColor] : [NSColor controlTextColor]];
2720
2721 }
2722
2723 - (IBAction) addAllAudioTracksToPopUp: (id) sender
2724 {
2725
2726     hb_list_t  * list  = hb_get_titles( fHandle );
2727     hb_title_t * title = (hb_title_t*)
2728         hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
2729
2730         hb_audio_t * audio;
2731
2732     [sender removeAllItems];
2733     [sender addItemWithTitle: _( @"None" )];
2734     for( int i = 0; i < hb_list_count( title->list_audio ); i++ )
2735     {
2736         audio = (hb_audio_t *) hb_list_item( title->list_audio, i );
2737         [[sender menu] addItemWithTitle:
2738             [NSString stringWithCString: audio->lang]
2739             action: NULL keyEquivalent: @""];
2740     }
2741     [sender selectItemAtIndex: 0];
2742
2743 }
2744
2745 - (IBAction) selectAudioTrackInPopUp: (id) sender searchPrefixString: (NSString *) searchPrefixString selectIndexIfNotFound: (int) selectIndexIfNotFound
2746 {
2747
2748     /* this method can be used to find a language, or a language-and-source-format combination, by passing in the appropriate string */
2749     /* e.g. to find the first French track, pass in an NSString * of "Francais" */
2750     /* e.g. to find the first English 5.1 AC3 track, pass in an NSString * of "English (AC3) (5.1 ch)" */
2751     /* if no matching track is found, then selectIndexIfNotFound is used to choose which track to select instead */
2752     
2753         if (searchPrefixString != NULL) 
2754         {
2755
2756         for( int i = 0; i < [sender numberOfItems]; i++ )
2757         {
2758             /* Try to find the desired search string */
2759             if ([[[sender itemAtIndex: i] title] hasPrefix:searchPrefixString])
2760             {
2761                 [sender selectItemAtIndex: i];
2762                 return;
2763             }
2764         }
2765         /* couldn't find the string, so select the requested "search string not found" item */
2766         /* index of 0 means select the "none" item */
2767         /* index of 1 means select the first audio track */
2768         [sender selectItemAtIndex: selectIndexIfNotFound];
2769         }
2770     else
2771     {
2772         /* if no search string is provided, then select the selectIndexIfNotFound item */
2773         [sender selectItemAtIndex: selectIndexIfNotFound];
2774     }
2775
2776 }
2777
2778 - (IBAction) audioTrackPopUpChanged: (id) sender
2779 {
2780     /* utility function to call audioTrackPopUpChanged without passing in a mixdown-to-use */
2781     [self audioTrackPopUpChanged: sender mixdownToUse: 0];
2782 }
2783
2784 - (IBAction) audioTrackPopUpChanged: (id) sender mixdownToUse: (int) mixdownToUse
2785 {
2786
2787     /* make sure we have a selected title before continuing */
2788     if (fTitle == NULL) return;
2789
2790     /* find out if audio track 1 or 2 was changed - this is passed to us in the tag of the sender */
2791     /* the sender will have been either fAudLang1PopUp (tag = 0) or fAudLang2PopUp (tag = 1) */
2792     int thisAudio = [sender tag];
2793
2794     /* get the index of the selected audio */
2795     int thisAudioIndex = [sender indexOfSelectedItem] - 1;
2796
2797     /* Handbrake can't currently cope with ripping the same source track twice */
2798     /* So, if this audio is also selected in the other audio track popup, set that popup's selection to "none" */
2799     /* get a reference to the two audio track popups */
2800     NSPopUpButton * thisAudioPopUp  = (thisAudio == 1 ? fAudLang2PopUp : fAudLang1PopUp);
2801     NSPopUpButton * otherAudioPopUp = (thisAudio == 1 ? fAudLang1PopUp : fAudLang2PopUp);
2802     /* if the same track is selected in the other audio popup, then select "none" in that popup */
2803     /* unless, of course, both are selected as "none!" */
2804     if ([thisAudioPopUp indexOfSelectedItem] != 0 && [thisAudioPopUp indexOfSelectedItem] == [otherAudioPopUp indexOfSelectedItem]) {
2805         [otherAudioPopUp selectItemAtIndex: 0];
2806         [self audioTrackPopUpChanged: otherAudioPopUp];
2807     }
2808
2809     /* pointer for the hb_audio_s struct we will use later on */
2810     hb_audio_t * audio;
2811
2812     /* find out what the currently-selected output audio codec is */
2813     int format = [fDstFormatPopUp indexOfSelectedItem];
2814     int codecs = [fDstCodecsPopUp indexOfSelectedItem];
2815     int acodec = FormatSettings[format][codecs] & HB_ACODEC_MASK;
2816
2817     /* pointer to this track's mixdown NSPopUpButton */
2818     NSTextField   * mixdownTextField;
2819     NSPopUpButton * mixdownPopUp;
2820
2821     /* find our mixdown NSTextField and NSPopUpButton */
2822     if (thisAudio == 0)
2823     {
2824         mixdownTextField = fAudTrack1MixLabel;
2825         mixdownPopUp = fAudTrack1MixPopUp;
2826     }
2827     else
2828     {
2829         mixdownTextField = fAudTrack2MixLabel;
2830         mixdownPopUp = fAudTrack2MixPopUp;
2831     }
2832
2833     /* delete the previous audio mixdown options */
2834     [mixdownPopUp removeAllItems];
2835
2836     /* check if the audio mixdown controls need their enabled state changing */
2837     [self setEnabledStateOfAudioMixdownControls: NULL];
2838
2839     if (thisAudioIndex != -1)
2840     {
2841
2842         /* get the audio */
2843         audio = (hb_audio_t *) hb_list_item( fTitle->list_audio, thisAudioIndex );
2844         if (audio != NULL)
2845         {
2846
2847             /* find out if our selected output audio codec supports mono and / or 6ch */
2848             /* we also check for an input codec of AC3 or DCA,
2849                as they are the only libraries able to do the mixdown to mono / conversion to 6-ch */
2850             /* audioCodecsSupportMono and audioCodecsSupport6Ch are the same for now,
2851                but this may change in the future, so they are separated for flexibility */
2852             int audioCodecsSupportMono = ((audio->codec == HB_ACODEC_AC3 ||
2853                 audio->codec == HB_ACODEC_DCA) && acodec == HB_ACODEC_FAAC);
2854             int audioCodecsSupport6Ch =  ((audio->codec == HB_ACODEC_AC3 ||
2855                 audio->codec == HB_ACODEC_DCA) && (acodec == HB_ACODEC_FAAC ||
2856                 acodec == HB_ACODEC_VORBIS));
2857
2858             /* check for AC-3 passthru */
2859             if (audio->codec == HB_ACODEC_AC3 && acodec == HB_ACODEC_AC3)
2860             {
2861                     [[mixdownPopUp menu] addItemWithTitle:
2862                         [NSString stringWithCString: "AC3 Passthru"]
2863                         action: NULL keyEquivalent: @""];
2864             }
2865             else
2866             {
2867
2868                 /* add the appropriate audio mixdown menuitems to the popupbutton */
2869                 /* in each case, we set the new menuitem's tag to be the amixdown value for that mixdown,
2870                    so that we can reference the mixdown later */
2871
2872                 /* keep a track of the min and max mixdowns we used, so we can select the best match later */
2873                 int minMixdownUsed = 0;
2874                 int maxMixdownUsed = 0;
2875                 
2876                 /* get the input channel layout without any lfe channels */
2877                 int layout = audio->input_channel_layout & HB_INPUT_CH_LAYOUT_DISCRETE_NO_LFE_MASK;
2878
2879                 /* do we want to add a mono option? */
2880                 if (audioCodecsSupportMono == 1) {
2881                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
2882                         [NSString stringWithCString: hb_audio_mixdowns[0].human_readable_name]
2883                         action: NULL keyEquivalent: @""];
2884                     [menuItem setTag: hb_audio_mixdowns[0].amixdown];
2885                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[0].amixdown;
2886                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[0].amixdown);
2887                 }
2888
2889                 /* do we want to add a stereo option? */
2890                 /* offer stereo if we have a mono source and non-mono-supporting codecs, as otherwise we won't have a mixdown at all */
2891                 /* also offer stereo if we have a stereo-or-better source */
2892                 if ((layout == HB_INPUT_CH_LAYOUT_MONO && audioCodecsSupportMono == 0) || layout >= HB_INPUT_CH_LAYOUT_STEREO) {
2893                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
2894                         [NSString stringWithCString: hb_audio_mixdowns[1].human_readable_name]
2895                         action: NULL keyEquivalent: @""];
2896                     [menuItem setTag: hb_audio_mixdowns[1].amixdown];
2897                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[1].amixdown;
2898                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[1].amixdown);
2899                 }
2900
2901                 /* do we want to add a dolby surround (DPL1) option? */
2902                 if (layout == HB_INPUT_CH_LAYOUT_3F1R || layout == HB_INPUT_CH_LAYOUT_3F2R || layout == HB_INPUT_CH_LAYOUT_DOLBY) {
2903                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
2904                         [NSString stringWithCString: hb_audio_mixdowns[2].human_readable_name]
2905                         action: NULL keyEquivalent: @""];
2906                     [menuItem setTag: hb_audio_mixdowns[2].amixdown];
2907                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[2].amixdown;
2908                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[2].amixdown);
2909                 }
2910
2911                 /* do we want to add a dolby pro logic 2 (DPL2) option? */
2912                 if (layout == HB_INPUT_CH_LAYOUT_3F2R) {
2913                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
2914                         [NSString stringWithCString: hb_audio_mixdowns[3].human_readable_name]
2915                         action: NULL keyEquivalent: @""];
2916                     [menuItem setTag: hb_audio_mixdowns[3].amixdown];
2917                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[3].amixdown;
2918                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[3].amixdown);
2919                 }
2920
2921                 /* do we want to add a 6-channel discrete option? */
2922                 if (audioCodecsSupport6Ch == 1 && layout == HB_INPUT_CH_LAYOUT_3F2R && (audio->input_channel_layout & HB_INPUT_CH_LAYOUT_HAS_LFE)) {
2923                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
2924                         [NSString stringWithCString: hb_audio_mixdowns[4].human_readable_name]
2925                         action: NULL keyEquivalent: @""];
2926                     [menuItem setTag: hb_audio_mixdowns[4].amixdown];
2927                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[4].amixdown;
2928                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[4].amixdown);
2929                 }
2930
2931                 /* auto-select the best mixdown based on our saved mixdown preference */
2932                 
2933                 /* for now, this is hard-coded to a "best" mixdown of HB_AMIXDOWN_DOLBYPLII */
2934                 /* ultimately this should be a prefs option */
2935                 int useMixdown;
2936                 
2937                 /* if we passed in a mixdown to use - in order to load a preset - then try and use it */
2938                 if (mixdownToUse > 0)
2939                 {
2940                     useMixdown = mixdownToUse;
2941                 }
2942                 else
2943                 {
2944                     useMixdown = HB_AMIXDOWN_DOLBYPLII;
2945                 }
2946                 
2947                 /* if useMixdown > maxMixdownUsed, then use maxMixdownUsed */
2948                 if (useMixdown > maxMixdownUsed) useMixdown = maxMixdownUsed;
2949
2950                 /* if useMixdown < minMixdownUsed, then use minMixdownUsed */
2951                 if (useMixdown < minMixdownUsed) useMixdown = minMixdownUsed;
2952
2953                 /* select the (possibly-amended) preferred mixdown */
2954                 [mixdownPopUp selectItemWithTag: useMixdown];
2955                                 
2956                                 /* lets call the audioTrackMixdownChanged method here to determine appropriate bitrates, etc. */
2957                 [self audioTrackMixdownChanged: NULL];
2958             }
2959
2960         }
2961         
2962     }
2963
2964         /* see if the new audio track choice will change the bitrate we need */
2965     [self calculateBitrate: sender];    
2966
2967 }
2968 - (IBAction) audioTrackMixdownChanged: (id) sender
2969 {
2970
2971     /* find out what the currently-selected output audio codec is */
2972     int format = [fDstFormatPopUp indexOfSelectedItem];
2973     int codecs = [fDstCodecsPopUp indexOfSelectedItem];
2974     int acodec = FormatSettings[format][codecs] & HB_ACODEC_MASK;
2975     
2976     /* storage variable for the min and max bitrate allowed for this codec */
2977     int minbitrate;
2978     int maxbitrate;
2979     
2980     switch( acodec )
2981     {
2982         case HB_ACODEC_FAAC:
2983             /* check if we have a 6ch discrete conversion in either audio track */
2984             if ([[fAudTrack1MixPopUp selectedItem] tag] == HB_AMIXDOWN_6CH || [[fAudTrack2MixPopUp selectedItem] tag] == HB_AMIXDOWN_6CH)
2985             {
2986                 /* FAAC is happy using our min bitrate of 32 kbps, even for 6ch */
2987                 minbitrate = 32;
2988                 /* If either mixdown popup includes 6-channel discrete, then allow up to 384 kbps */
2989                 maxbitrate = 384;
2990                 break;
2991             }
2992             else
2993             {
2994                 /* FAAC is happy using our min bitrate of 32 kbps for stereo or mono */
2995                 minbitrate = 32;
2996                 /* FAAC won't honour anything more than 160 for stereo, so let's not offer it */
2997                 /* note: haven't dealt with mono separately here, FAAC will just use the max it can */
2998                 maxbitrate = 160;
2999                 break;
3000             }
3001
3002         case HB_ACODEC_LAME:
3003             /* Lame is happy using our min bitrate of 32 kbps */
3004             minbitrate = 32;
3005             /* Lame won't encode if the bitrate is higher than 320 kbps */
3006             maxbitrate = 320;
3007             break;
3008
3009         case HB_ACODEC_VORBIS:
3010         if ([[fAudTrack1MixPopUp selectedItem] tag] == HB_AMIXDOWN_6CH || [[fAudTrack2MixPopUp selectedItem] tag] == HB_AMIXDOWN_6CH)
3011             {
3012                 /* Vorbis causes a crash if we use a bitrate below 192 kbps with 6 channel */
3013                 minbitrate = 192;
3014                 /* If either mixdown popup includes 6-channel discrete, then allow up to 384 kbps */
3015                 maxbitrate = 384;
3016                 break;
3017             }
3018             else
3019             {
3020             /* Vorbis causes a crash if we use a bitrate below 48 kbps */
3021             minbitrate = 48;
3022             /* Vorbis can cope with 384 kbps quite happily, even for stereo */
3023             maxbitrate = 384;
3024             break;
3025             }
3026
3027         default:
3028             /* AC3 passthru disables the bitrate dropdown anyway, so we might as well just use the min and max bitrate */
3029             minbitrate = 32;
3030             maxbitrate = 384;
3031         
3032     }
3033
3034     [fAudBitratePopUp removeAllItems];
3035
3036     for( int i = 0; i < hb_audio_bitrates_count; i++ )
3037     {
3038         if (hb_audio_bitrates[i].rate >= minbitrate && hb_audio_bitrates[i].rate <= maxbitrate)
3039         {
3040             /* add a new menuitem for this bitrate */
3041             NSMenuItem *menuItem = [[fAudBitratePopUp menu] addItemWithTitle:
3042                 [NSString stringWithCString: hb_audio_bitrates[i].string]
3043                 action: NULL keyEquivalent: @""];
3044             /* set its tag to be the actual bitrate as an integer, so we can retrieve it later */
3045             [menuItem setTag: hb_audio_bitrates[i].rate];
3046         }
3047     }
3048
3049     /* select the default bitrate (but use 384 for 6-ch AAC) */
3050     if ([[fAudTrack1MixPopUp selectedItem] tag] == HB_AMIXDOWN_6CH || [[fAudTrack2MixPopUp selectedItem] tag] == HB_AMIXDOWN_6CH)
3051     {
3052         [fAudBitratePopUp selectItemWithTag: 384];
3053     }
3054     else
3055     {
3056         [fAudBitratePopUp selectItemWithTag: hb_audio_bitrates[hb_audio_bitrates_default].rate];
3057     }
3058
3059 }
3060
3061 - (IBAction) audioDRCSliderChanged: (id) sender
3062 {
3063     [fAudDrcField setStringValue: [NSString stringWithFormat: @"%.2f", [fAudDrcSlider floatValue]]];
3064     [self customSettingUsed: sender];
3065 }
3066
3067 - (IBAction) subtitleSelectionChanged: (id) sender
3068 {
3069         if ([fSubPopUp indexOfSelectedItem] == 0)
3070         {
3071         [fSubForcedCheck setState: NSOffState];
3072         [fSubForcedCheck setEnabled: NO];       
3073         }
3074         else
3075         {
3076         [fSubForcedCheck setEnabled: YES];      
3077         }
3078         
3079 }
3080
3081
3082
3083
3084 #pragma mark -
3085 #pragma mark Open New Windows
3086
3087 - (IBAction) openHomepage: (id) sender
3088 {
3089     [[NSWorkspace sharedWorkspace] openURL: [NSURL
3090         URLWithString:@"http://handbrake.m0k.org/"]];
3091 }
3092
3093 - (IBAction) openForums: (id) sender
3094 {
3095     [[NSWorkspace sharedWorkspace] openURL: [NSURL
3096         URLWithString:@"http://handbrake.m0k.org/forum/"]];
3097 }
3098 - (IBAction) openUserGuide: (id) sender
3099 {
3100     [[NSWorkspace sharedWorkspace] openURL: [NSURL
3101         URLWithString:@"http://handbrake.m0k.org/trac/wiki/HandBrakeGuide"]];
3102 }
3103
3104 /**
3105  * Shows debug output window.
3106  */
3107 - (IBAction)showDebugOutputPanel:(id)sender
3108 {
3109     [outputPanel showOutputPanel:sender];
3110 }
3111
3112 /**
3113  * Shows preferences window.
3114  */
3115 - (IBAction) showPreferencesWindow: (id) sender
3116 {
3117     NSWindow * window = [fPreferencesController window];
3118     if (![window isVisible])
3119         [window center];
3120
3121     [window makeKeyAndOrderFront: nil];
3122 }
3123
3124 /**
3125  * Shows queue window.
3126  */
3127 - (IBAction) showQueueWindow:(id)sender
3128 {
3129     [fQueueController showQueueWindow:sender];
3130 }
3131
3132
3133 - (IBAction) toggleDrawer:(id)sender {
3134     [fPresetDrawer toggle:self];
3135 }
3136
3137 /**
3138  * Shows Picture Settings Window.
3139  */
3140
3141 - (IBAction) showPicturePanel: (id) sender
3142 {
3143         hb_list_t  * list  = hb_get_titles( fHandle );
3144     hb_title_t * title = (hb_title_t *) hb_list_item( list,
3145             [fSrcTitlePopUp indexOfSelectedItem] );
3146     [fPictureController showPanelInWindow:fWindow forTitle:title];
3147 }
3148
3149 #pragma mark -
3150 #pragma mark Preset Outline View Methods
3151 #pragma mark - Required
3152 /* These are required by the NSOutlineView Datasource Delegate */
3153 /* We use this to deterimine children of an item */
3154 - (id)outlineView:(NSOutlineView *)fPresetsOutlineView child:(NSInteger)index ofItem:(id)item
3155 {
3156 if (item == nil)
3157         return [UserPresets objectAtIndex:index];
3158     
3159     // We are only one level deep, so we can't be asked about children
3160     NSAssert (NO, @"Presets View outlineView:child:ofItem: currently can't handle nested items.");
3161     return nil;
3162 }
3163 /* We use this to determine if an item should be expandable */
3164 - (BOOL)outlineView:(NSOutlineView *)fPresetsOutlineView isItemExpandable:(id)item
3165 {
3166
3167     /* For now, we maintain one level, so set to no
3168     * when nested, we set to yes for any preset "folders"
3169     */
3170     return NO;
3171
3172 }
3173 /* used to specify the number of levels to show for each item */
3174 - (int)outlineView:(NSOutlineView *)fPresetsOutlineView numberOfChildrenOfItem:(id)item
3175 {
3176     /* currently use no levels to test outline view viability */
3177     if (item == nil)
3178         return [UserPresets count];
3179     else
3180         return 0;
3181 }
3182 /* Used to tell the outline view which information is to be displayed per item */
3183 - (id)outlineView:(NSOutlineView *)fPresetsOutlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
3184 {
3185         /* We have two columns right now, icon and PresetName */
3186         
3187     if ([[tableColumn identifier] isEqualToString:@"PresetName"])
3188     {
3189         return [item objectForKey:@"PresetName"];
3190     }
3191     else
3192     {
3193         return @"something";
3194     }
3195 }
3196
3197 #pragma mark - Added Functionality (optional)
3198 /* Use to customize the font and display characteristics of the title cell */
3199 - (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
3200 {
3201     if ([[tableColumn identifier] isEqualToString:@"PresetName"])
3202     {
3203         NSDictionary *userPresetDict = item;
3204         NSFont *txtFont;
3205         NSColor *fontColor;
3206         NSColor *shadowColor;
3207         txtFont = [NSFont systemFontOfSize: [NSFont smallSystemFontSize]];
3208         /*check to see if its a selected row */
3209         if ([fPresetsOutlineView selectedRow] == [fPresetsOutlineView rowForItem:item])
3210         {
3211             
3212             fontColor = [NSColor whiteColor];
3213             shadowColor = [NSColor colorWithDeviceRed:(127.0/255.0) green:(140.0/255.0) blue:(160.0/255.0) alpha:1.0];
3214         }
3215         else
3216         {
3217             if ([[userPresetDict objectForKey:@"Type"] intValue] == 0)
3218             {
3219                 fontColor = [NSColor blueColor];
3220             }
3221             else // User created preset, use a black font
3222             {
3223                 fontColor = [NSColor blackColor];
3224             }
3225             shadowColor = nil;
3226         }
3227         /* We use Bold Text for the HB Default */
3228         if ([[userPresetDict objectForKey:@"Default"] intValue] == 1)// 1 is HB default
3229         {
3230             txtFont = [NSFont boldSystemFontOfSize: [NSFont smallSystemFontSize]];
3231         }
3232         /* We use Bold Text for the User Specified Default */
3233         if ([[userPresetDict objectForKey:@"Default"] intValue] == 2)// 2 is User default
3234         {
3235             txtFont = [NSFont boldSystemFontOfSize: [NSFont smallSystemFontSize]];
3236         }
3237         
3238         
3239         [cell setTextColor:fontColor];
3240         [cell setFont:txtFont];
3241         
3242     }
3243 }
3244
3245 /* We use this to edit the name field in the outline view */
3246 - (void)outlineView:(NSOutlineView *)fPresetsOutlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
3247 {
3248     if ([[tableColumn identifier] isEqualToString:@"PresetName"])
3249     {
3250         id theRecord;
3251         
3252         theRecord = item;
3253         [theRecord setObject:object forKey:@"PresetName"];
3254         /* We Sort the Presets By Factory or Custom */
3255         NSSortDescriptor * presetTypeDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"Type" 
3256                                                                              ascending:YES] autorelease];
3257                 /* We Sort the Presets Alphabetically by name */
3258         NSSortDescriptor * presetNameDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"PresetName" 
3259                                                                              ascending:YES selector:@selector(caseInsensitiveCompare:)] autorelease];
3260         NSArray *sortDescriptors=[NSArray arrayWithObjects:presetTypeDescriptor,presetNameDescriptor,nil];
3261         NSArray *sortedArray=[UserPresets sortedArrayUsingDescriptors:sortDescriptors];
3262         [UserPresets setArray:sortedArray];
3263         /* We Reload the New Table data for presets */
3264         //[fPresetsOutlineView reloadData];
3265         /* We save all of the preset data here */
3266         [self savePreset];
3267     }
3268 }
3269 /* We use this to provide tooltips for the items in the presets outline view */
3270 - (NSString *)outlineView:(NSOutlineView *)fPresetsOutlineView toolTipForCell:(NSCell *)cell rect:(NSRectPointer)rect tableColumn:(NSTableColumn *)tc item:(id)item mouseLocation:(NSPoint)mouseLocation
3271 {
3272     //if ([[tc identifier] isEqualToString:@"PresetName"])
3273     //{
3274         /* initialize the tooltip contents variable */
3275         NSString *loc_tip;
3276         /* if there is a description for the preset, we show it in the tooltip */
3277         if ([item valueForKey:@"PresetDescription"])
3278         {
3279             loc_tip = [NSString stringWithFormat: @"%@",[item valueForKey:@"PresetDescription"]];
3280             return (loc_tip);
3281         }
3282         else
3283         {
3284             loc_tip = @"No description available";
3285         }
3286         return (loc_tip);
3287     //}
3288 }
3289
3290
3291 #pragma mark - Functional Preset NSOutlineView Methods
3292
3293 - (IBAction)selectPreset:(id)sender
3294 {
3295     
3296     if ([fPresetsOutlineView selectedRow] >= 0)
3297     {
3298         chosenPreset = [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]];
3299         /* we set the preset display field in main window here */
3300         [fPresetSelectedDisplay setStringValue: [NSString stringWithFormat: @"%@",[chosenPreset valueForKey:@"PresetName"]]];
3301         if ([[chosenPreset objectForKey:@"Default"] intValue] == 1)
3302         {
3303             [fPresetSelectedDisplay setStringValue: [NSString stringWithFormat: @"%@ (Default)",[chosenPreset valueForKey:@"PresetName"]]];
3304         }
3305         else
3306         {
3307             [fPresetSelectedDisplay setStringValue: [NSString stringWithFormat: @"%@",[chosenPreset valueForKey:@"PresetName"]]];
3308         }
3309         /* File Format */
3310         [fDstFormatPopUp selectItemWithTitle: [NSString stringWithFormat:[chosenPreset valueForKey:@"FileFormat"]]];
3311         [self formatPopUpChanged: NULL];
3312         
3313         /* Chapter Markers*/
3314         [fCreateChapterMarkers setState:[[chosenPreset objectForKey:@"ChapterMarkers"] intValue]];
3315         /* Allow Mpeg4 64 bit formatting +4GB file sizes */
3316         [fDstMp4LargeFileCheck setState:[[chosenPreset objectForKey:@"Mp4LargeFile"] intValue]];
3317         /* Mux mp4 with http optimization */
3318         [fDstMp4HttpOptFileCheck setState:[[chosenPreset objectForKey:@"Mp4HttpOptimize"] intValue]];
3319         /* Codecs */
3320         [fDstCodecsPopUp selectItemWithTitle: [NSString stringWithFormat:[chosenPreset valueForKey:@"FileCodecs"]]];
3321         [self codecsPopUpChanged: NULL];
3322         /* Video encoder */
3323         [fVidEncoderPopUp selectItemWithTitle: [NSString stringWithFormat:[chosenPreset valueForKey:@"VideoEncoder"]]];
3324         
3325         /* We can show the preset options here in the gui if we want to
3326          so we check to see it the user has specified it in the prefs */
3327         [fAdvancedOptions setOptions: [NSString stringWithFormat:[chosenPreset valueForKey:@"x264Option"]]];
3328         
3329         /* Lets run through the following functions to get variables set there */
3330         [self encoderPopUpChanged: NULL];
3331         
3332         [self calculateBitrate: NULL];
3333         
3334         /* Video quality */
3335         [fVidQualityMatrix selectCellAtRow:[[chosenPreset objectForKey:@"VideoQualityType"] intValue] column:0];
3336         
3337         [fVidTargetSizeField setStringValue: [NSString stringWithFormat:[chosenPreset valueForKey:@"VideoTargetSize"]]];
3338         [fVidBitrateField setStringValue: [NSString stringWithFormat:[chosenPreset valueForKey:@"VideoAvgBitrate"]]];
3339         [fVidQualitySlider setFloatValue: [[chosenPreset valueForKey:@"VideoQualitySlider"] floatValue]];
3340         
3341         [self videoMatrixChanged: NULL];
3342         
3343         /* Video framerate */
3344         /* For video preset video framerate, we want to make sure that Same as source does not conflict with the
3345          detected framerate in the fVidRatePopUp so we use index 0*/
3346         if ([[NSString stringWithFormat:[chosenPreset valueForKey:@"VideoFramerate"]] isEqualToString: @"Same as source"])
3347         {
3348             [fVidRatePopUp selectItemAtIndex: 0];
3349         }
3350         else
3351         {
3352             [fVidRatePopUp selectItemWithTitle: [NSString stringWithFormat:[chosenPreset valueForKey:@"VideoFramerate"]]];
3353         }
3354         
3355         /* GrayScale */
3356         [fVidGrayscaleCheck setState:[[chosenPreset objectForKey:@"VideoGrayScale"] intValue]];
3357         
3358         /* 2 Pass Encoding */
3359         [fVidTwoPassCheck setState:[[chosenPreset objectForKey:@"VideoTwoPass"] intValue]];
3360         [self twoPassCheckboxChanged: NULL];
3361         /* Turbo 1st pass for 2 Pass Encoding */
3362         [fVidTurboPassCheck setState:[[chosenPreset objectForKey:@"VideoTurboTwoPass"] intValue]];
3363         
3364         /*Audio*/
3365         
3366         /* Audio Sample Rate*/
3367         [fAudRatePopUp selectItemWithTitle: [NSString stringWithFormat:[chosenPreset valueForKey:@"AudioSampleRate"]]];
3368         /* Audio Bitrate Rate*/
3369         [fAudBitratePopUp selectItemWithTitle: [NSString stringWithFormat:[chosenPreset valueForKey:@"AudioBitRate"]]];
3370         /*Subtitles*/
3371         [fSubPopUp selectItemWithTitle: [NSString stringWithFormat:[chosenPreset valueForKey:@"Subtitles"]]];
3372         /* Dynamic Range Control Slider */
3373         [fAudDrcSlider setFloatValue: [[chosenPreset valueForKey:@"AudioDRCSlider"] floatValue]];
3374         [self audioDRCSliderChanged: NULL];
3375         
3376         /* Picture Settings */
3377         /* Note: objectForKey:@"UsesPictureSettings" now refers to picture size, this encompasses:
3378          * height, width, keep ar, anamorphic and crop settings.
3379          * picture filters are now handled separately.
3380          * We will be able to actually change the key names for legacy preset keys when preset file
3381          * update code is done. But for now, lets hang onto the old legacy key name for backwards compatibility.
3382          */
3383         /* Check to see if the objectForKey:@"UsesPictureSettings is greater than 0, as 0 means use picture sizing "None" 
3384          * and the preset completely ignores any picture sizing values in the preset.
3385          */
3386         if ([[chosenPreset objectForKey:@"UsesPictureSettings"]  intValue] > 0)
3387         {
3388             hb_job_t * job = fTitle->job;
3389             /* Check to see if the objectForKey:@"UsesPictureSettings is 2 which is "Use Max for the source */
3390             if ([[chosenPreset objectForKey:@"UsesPictureSettings"]  intValue] == 2 || [[chosenPreset objectForKey:@"UsesMaxPictureSettings"]  intValue] == 1)
3391             {
3392                 /* Use Max Picture settings for whatever the dvd is.*/
3393                 [self revertPictureSizeToMax: NULL];
3394                 job->keep_ratio = [[chosenPreset objectForKey:@"PictureKeepRatio"]  intValue];
3395                 if (job->keep_ratio == 1)
3396                 {
3397                     hb_fix_aspect( job, HB_KEEP_WIDTH );
3398                     if( job->height > fTitle->height )
3399                     {
3400                         job->height = fTitle->height;
3401                         hb_fix_aspect( job, HB_KEEP_HEIGHT );
3402                     }
3403                 }
3404                 job->pixel_ratio = [[chosenPreset objectForKey:@"PicturePAR"]  intValue];
3405             }
3406             else // /* If not 0 or 2 we assume objectForKey:@"UsesPictureSettings is 1 which is "Use picture sizing from when the preset was set" */
3407             {
3408                 job->width = [[chosenPreset objectForKey:@"PictureWidth"]  intValue];
3409                 job->height = [[chosenPreset objectForKey:@"PictureHeight"]  intValue];
3410                 job->keep_ratio = [[chosenPreset objectForKey:@"PictureKeepRatio"]  intValue];
3411                 if (job->keep_ratio == 1)
3412                 {
3413                     hb_fix_aspect( job, HB_KEEP_WIDTH );
3414                     if( job->height > fTitle->height )
3415                     {
3416                         job->height = fTitle->height;
3417                         hb_fix_aspect( job, HB_KEEP_HEIGHT );
3418                     }
3419                 }
3420                 job->pixel_ratio = [[chosenPreset objectForKey:@"PicturePAR"]  intValue];
3421                 
3422                 
3423                 /* If Cropping is set to custom, then recall all four crop values from
3424                  when the preset was created and apply them */
3425                 if ([[chosenPreset objectForKey:@"PictureAutoCrop"]  intValue] == 0)
3426                 {
3427                     [fPictureController setAutoCrop:NO];
3428                     
3429                     /* Here we use the custom crop values saved at the time the preset was saved */
3430                     job->crop[0] = [[chosenPreset objectForKey:@"PictureTopCrop"]  intValue];
3431                     job->crop[1] = [[chosenPreset objectForKey:@"PictureBottomCrop"]  intValue];
3432                     job->crop[2] = [[chosenPreset objectForKey:@"PictureLeftCrop"]  intValue];
3433                     job->crop[3] = [[chosenPreset objectForKey:@"PictureRightCrop"]  intValue];
3434                     
3435                 }
3436                 else /* if auto crop has been saved in preset, set to auto and use post scan auto crop */
3437                 {
3438                     [fPictureController setAutoCrop:YES];
3439                     /* Here we use the auto crop values determined right after scan */
3440                     job->crop[0] = AutoCropTop;
3441                     job->crop[1] = AutoCropBottom;
3442                     job->crop[2] = AutoCropLeft;
3443                     job->crop[3] = AutoCropRight;
3444                     
3445                 }
3446                 /* If the preset has no objectForKey:@"UsesPictureFilters", then we know it is a legacy preset
3447                  * and handle the filters here as before.
3448                  * NOTE: This should be removed when the update presets code is done as we can be assured that legacy
3449                  * presets are updated to work properly with new keys.
3450                  */
3451                 if (![chosenPreset objectForKey:@"UsesPictureFilters"])
3452                 {
3453                     /* Filters */
3454                     /* Deinterlace */
3455                     if ([chosenPreset objectForKey:@"PictureDeinterlace"])
3456                     {
3457                         [fPictureController setDeinterlace:[[chosenPreset objectForKey:@"PictureDeinterlace"] intValue]];
3458                     }
3459                     else
3460                     {
3461                         [fPictureController setDeinterlace:0];
3462                     }
3463                     /* VFR */
3464                     if ([[chosenPreset objectForKey:@"VFR"] intValue] == 1)
3465                     {
3466                         [fPictureController setVFR:[[chosenPreset objectForKey:@"VFR"] intValue]];
3467                     }
3468                     else
3469                     {
3470                         [fPictureController setVFR:0];
3471                     }
3472                     /* Detelecine */
3473                     if ([[chosenPreset objectForKey:@"PictureDetelecine"] intValue] == 1)
3474                     {
3475                         [fPictureController setDetelecine:[[chosenPreset objectForKey:@"PictureDetelecine"] intValue]];
3476                     }
3477                     else
3478                     {
3479                         [fPictureController setDetelecine:0];
3480                     }
3481                     /* Denoise */
3482                     if ([chosenPreset objectForKey:@"PictureDenoise"])
3483                     {
3484                         [fPictureController setDenoise:[[chosenPreset objectForKey:@"PictureDenoise"] intValue]];
3485                     }
3486                     else
3487                     {
3488                         [fPictureController setDenoise:0];
3489                     }   
3490                     /* Deblock */
3491                     if ([[chosenPreset objectForKey:@"PictureDeblock"] intValue] == 1)
3492                     {
3493                         [fPictureController setDeblock:[[chosenPreset objectForKey:@"PictureDeblock"] intValue]];
3494                     }
3495                     else
3496                     {
3497                         [fPictureController setDeblock:0];
3498                     }             
3499                     [self calculatePictureSizing: NULL];
3500                 }
3501                 
3502             }
3503             
3504             
3505         }
3506         /* If the preset has an objectForKey:@"UsesPictureFilters", then we know it is a newer style filters preset
3507          * and handle the filters here depending on whether or not the preset specifies applying the filter.
3508          */
3509         if ([chosenPreset objectForKey:@"UsesPictureFilters"] && [[chosenPreset objectForKey:@"UsesPictureFilters"]  intValue] > 0)
3510         {
3511             /* Filters */
3512             /* Deinterlace */
3513             if ([chosenPreset objectForKey:@"PictureDeinterlace"])
3514             {
3515                 [fPictureController setDeinterlace:[[chosenPreset objectForKey:@"PictureDeinterlace"] intValue]];
3516             }
3517             else
3518             {
3519                 [fPictureController setDeinterlace:0];
3520             }
3521             /* VFR */
3522             if ([[chosenPreset objectForKey:@"VFR"] intValue] == 1)
3523             {
3524                 [fPictureController setVFR:[[chosenPreset objectForKey:@"VFR"] intValue]];
3525             }
3526             else
3527             {
3528                 [fPictureController setVFR:0];
3529             }
3530             /* Detelecine */
3531             if ([[chosenPreset objectForKey:@"PictureDetelecine"] intValue] == 1)
3532             {
3533                 [fPictureController setDetelecine:[[chosenPreset objectForKey:@"PictureDetelecine"] intValue]];
3534             }
3535             else
3536             {
3537                 [fPictureController setDetelecine:0];
3538             }
3539             /* Denoise */
3540             if ([chosenPreset objectForKey:@"PictureDenoise"])
3541             {
3542                 [fPictureController setDenoise:[[chosenPreset objectForKey:@"PictureDenoise"] intValue]];
3543             }
3544             else
3545             {
3546                 [fPictureController setDenoise:0];
3547             }   
3548             /* Deblock */
3549             if ([[chosenPreset objectForKey:@"PictureDeblock"] intValue] == 1)
3550             {
3551                 [fPictureController setDeblock:[[chosenPreset objectForKey:@"PictureDeblock"] intValue]];
3552             }
3553             else
3554             {
3555                 [fPictureController setDeblock:0];
3556             }             
3557         }
3558         [self calculatePictureSizing: NULL];
3559         [[fPresetsActionMenu itemAtIndex:0] setEnabled: YES];
3560     }
3561 }
3562
3563
3564 #pragma mark -
3565 #pragma mark Manage Presets
3566
3567 - (void) loadPresets {
3568         /* We declare the default NSFileManager into fileManager */
3569         NSFileManager * fileManager = [NSFileManager defaultManager];
3570         /*We define the location of the user presets file */
3571     UserPresetsFile = @"~/Library/Application Support/HandBrake/UserPresets.plist";
3572         UserPresetsFile = [[UserPresetsFile stringByExpandingTildeInPath]retain];
3573     /* We check for the presets.plist */
3574         if ([fileManager fileExistsAtPath:UserPresetsFile] == 0) 
3575         {
3576                 [fileManager createFileAtPath:UserPresetsFile contents:nil attributes:nil];
3577         }
3578                 
3579         UserPresets = [[NSMutableArray alloc] initWithContentsOfFile:UserPresetsFile];
3580         if (nil == UserPresets) 
3581         {
3582                 UserPresets = [[NSMutableArray alloc] init];
3583                 [self addFactoryPresets:NULL];
3584         }
3585         [fPresetsOutlineView reloadData];
3586 }
3587
3588
3589 - (IBAction) showAddPresetPanel: (id) sender
3590 {
3591     /* Deselect the currently selected Preset if there is one*/
3592     [fPresetsOutlineView deselectRow:[fPresetsOutlineView selectedRow]];
3593
3594     /* Populate the preset picture settings popup here */
3595     [fPresetNewPicSettingsPopUp removeAllItems];
3596     [fPresetNewPicSettingsPopUp addItemWithTitle:@"None"];
3597     [fPresetNewPicSettingsPopUp addItemWithTitle:@"Current"];
3598     [fPresetNewPicSettingsPopUp addItemWithTitle:@"Source Maximum (post source scan)"];
3599     [fPresetNewPicSettingsPopUp selectItemAtIndex: 0];  
3600     /* Uncheck the preset use filters checkbox */
3601     [fPresetNewPicFiltersCheck setState:NSOffState];
3602     /* Erase info from the input fields*/
3603         [fPresetNewName setStringValue: @""];
3604         [fPresetNewDesc setStringValue: @""];
3605         /* Show the panel */
3606         [NSApp beginSheet: fAddPresetPanel modalForWindow: fWindow modalDelegate: NULL didEndSelector: NULL contextInfo: NULL];
3607 }
3608
3609 - (IBAction) closeAddPresetPanel: (id) sender
3610 {
3611     [NSApp endSheet: fAddPresetPanel];
3612     [fAddPresetPanel orderOut: self];
3613 }
3614
3615 - (IBAction)addUserPreset:(id)sender
3616 {
3617     if (![[fPresetNewName stringValue] length])
3618             NSRunAlertPanel(@"Warning!", @"You need to insert a name for the preset.", @"OK", nil , nil);
3619     else
3620     {
3621         /* Here we create a custom user preset */
3622         [UserPresets addObject:[self createPreset]];
3623         [self addPreset];
3624         
3625         [self closeAddPresetPanel:NULL];
3626     }
3627 }
3628 - (void)addPreset
3629 {
3630
3631         
3632         /* We Sort the Presets By Factory or Custom */
3633         NSSortDescriptor * presetTypeDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"Type" 
3634                                                     ascending:YES] autorelease];
3635         /* We Sort the Presets Alphabetically by name */
3636         NSSortDescriptor * presetNameDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"PresetName" 
3637                                                     ascending:YES selector:@selector(caseInsensitiveCompare:)] autorelease];
3638         NSArray *sortDescriptors=[NSArray arrayWithObjects:presetTypeDescriptor,presetNameDescriptor,nil];
3639         NSArray *sortedArray=[UserPresets sortedArrayUsingDescriptors:sortDescriptors];
3640         [UserPresets setArray:sortedArray];
3641         
3642         
3643         /* We Reload the New Table data for presets */
3644     [fPresetsOutlineView reloadData];
3645    /* We save all of the preset data here */
3646     [self savePreset];
3647 }
3648
3649 - (IBAction)insertPreset:(id)sender
3650 {
3651     int index = [fPresetsOutlineView selectedRow];
3652     [UserPresets insertObject:[self createPreset] atIndex:index];
3653     [fPresetsOutlineView reloadData];
3654     [self savePreset];
3655 }
3656
3657 - (NSDictionary *)createPreset
3658 {
3659     NSMutableDictionary *preset = [[NSMutableDictionary alloc] init];
3660         /* Get the New Preset Name from the field in the AddPresetPanel */
3661     [preset setObject:[fPresetNewName stringValue] forKey:@"PresetName"];
3662         /*Set whether or not this is a user preset or factory 0 is factory, 1 is user*/
3663         [preset setObject:[NSNumber numberWithInt:1] forKey:@"Type"];
3664         /*Set whether or not this is default, at creation set to 0*/
3665         [preset setObject:[NSNumber numberWithInt:0] forKey:@"Default"];
3666         /*Get the whether or not to apply pic Size and Cropping (includes Anamorphic)*/
3667         [preset setObject:[NSNumber numberWithInt:[fPresetNewPicSettingsPopUp indexOfSelectedItem]] forKey:@"UsesPictureSettings"];
3668     /* Get whether or not to use the current Picture Filter settings for the preset */
3669     [preset setObject:[NSNumber numberWithInt:[fPresetNewPicFiltersCheck state]] forKey:@"UsesPictureFilters"];
3670  
3671     /* Get New Preset Description from the field in the AddPresetPanel*/
3672         [preset setObject:[fPresetNewDesc stringValue] forKey:@"PresetDescription"];
3673         /* File Format */
3674     [preset setObject:[fDstFormatPopUp titleOfSelectedItem] forKey:@"FileFormat"];
3675         /* Chapter Markers fCreateChapterMarkers*/
3676         [preset setObject:[NSNumber numberWithInt:[fCreateChapterMarkers state]] forKey:@"ChapterMarkers"];
3677         /* Allow Mpeg4 64 bit formatting +4GB file sizes */
3678         [preset setObject:[NSNumber numberWithInt:[fDstMp4LargeFileCheck state]] forKey:@"Mp4LargeFile"];
3679     /* Mux mp4 with http optimization */
3680     [preset setObject:[NSNumber numberWithInt:[fDstMp4HttpOptFileCheck state]] forKey:@"Mp4HttpOptimize"];
3681     /* Codecs */
3682         [preset setObject:[fDstCodecsPopUp titleOfSelectedItem] forKey:@"FileCodecs"];
3683         /* Video encoder */
3684         [preset setObject:[fVidEncoderPopUp titleOfSelectedItem] forKey:@"VideoEncoder"];
3685         /* x264 Option String */
3686         [preset setObject:[fAdvancedOptions optionsString] forKey:@"x264Option"];
3687         
3688         [preset setObject:[NSNumber numberWithInt:[fVidQualityMatrix selectedRow]] forKey:@"VideoQualityType"];
3689         [preset setObject:[fVidTargetSizeField stringValue] forKey:@"VideoTargetSize"];
3690         [preset setObject:[fVidBitrateField stringValue] forKey:@"VideoAvgBitrate"];
3691         [preset setObject:[NSNumber numberWithFloat:[fVidQualitySlider floatValue]] forKey:@"VideoQualitySlider"];
3692         
3693         /* Video framerate */
3694     if ([fVidRatePopUp indexOfSelectedItem] == 0) // Same as source is selected
3695         {
3696     [preset setObject:[NSString stringWithFormat: @"Same as source"] forKey:@"VideoFramerate"];
3697     }
3698     else // we can record the actual titleOfSelectedItem
3699     {
3700     [preset setObject:[fVidRatePopUp titleOfSelectedItem] forKey:@"VideoFramerate"];
3701     }
3702         /* GrayScale */
3703         [preset setObject:[NSNumber numberWithInt:[fVidGrayscaleCheck state]] forKey:@"VideoGrayScale"];
3704         /* 2 Pass Encoding */
3705         [preset setObject:[NSNumber numberWithInt:[fVidTwoPassCheck state]] forKey:@"VideoTwoPass"];
3706         /* Turbo 2 pass Encoding fVidTurboPassCheck*/
3707         [preset setObject:[NSNumber numberWithInt:[fVidTurboPassCheck state]] forKey:@"VideoTurboTwoPass"];
3708         /*Picture Settings*/
3709         hb_job_t * job = fTitle->job;
3710         /* Picture Sizing */
3711         /* Use Max Picture settings for whatever the dvd is.*/
3712         [preset setObject:[NSNumber numberWithInt:0] forKey:@"UsesMaxPictureSettings"];
3713         [preset setObject:[NSNumber numberWithInt:fTitle->job->width] forKey:@"PictureWidth"];
3714         [preset setObject:[NSNumber numberWithInt:fTitle->job->height] forKey:@"PictureHeight"];
3715         [preset setObject:[NSNumber numberWithInt:fTitle->job->keep_ratio] forKey:@"PictureKeepRatio"];
3716         [preset setObject:[NSNumber numberWithInt:fTitle->job->pixel_ratio] forKey:@"PicturePAR"];
3717     
3718     /* Set crop settings here */
3719         [preset setObject:[NSNumber numberWithInt:[fPictureController autoCrop]] forKey:@"PictureAutoCrop"];
3720     [preset setObject:[NSNumber numberWithInt:job->crop[0]] forKey:@"PictureTopCrop"];
3721     [preset setObject:[NSNumber numberWithInt:job->crop[1]] forKey:@"PictureBottomCrop"];
3722         [preset setObject:[NSNumber numberWithInt:job->crop[2]] forKey:@"PictureLeftCrop"];
3723         [preset setObject:[NSNumber numberWithInt:job->crop[3]] forKey:@"PictureRightCrop"];
3724     
3725     /* Picture Filters */
3726     [preset setObject:[NSNumber numberWithInt:[fPictureController deinterlace]] forKey:@"PictureDeinterlace"];
3727         [preset setObject:[NSNumber numberWithInt:[fPictureController detelecine]] forKey:@"PictureDetelecine"];
3728     [preset setObject:[NSNumber numberWithInt:[fPictureController vfr]] forKey:@"VFR"];
3729         [preset setObject:[NSNumber numberWithInt:[fPictureController denoise]] forKey:@"PictureDenoise"];
3730     [preset setObject:[NSNumber numberWithInt:[fPictureController deblock]] forKey:@"PictureDeblock"];
3731     
3732
3733         
3734         /*Audio*/
3735         /* Audio Sample Rate*/
3736         [preset setObject:[fAudRatePopUp titleOfSelectedItem] forKey:@"AudioSampleRate"];
3737         /* Audio Bitrate Rate*/
3738         [preset setObject:[fAudBitratePopUp titleOfSelectedItem] forKey:@"AudioBitRate"];
3739         /* Subtitles*/
3740         [preset setObject:[fSubPopUp titleOfSelectedItem] forKey:@"Subtitles"];
3741     /* Dynamic Range Control Slider */
3742     [preset setObject:[NSNumber numberWithFloat:[fAudDrcSlider floatValue]] forKey:@"AudioDRCSlider"];
3743         
3744
3745     [preset autorelease];
3746     return preset;
3747
3748 }
3749
3750 - (void)savePreset
3751 {
3752     [UserPresets writeToFile:UserPresetsFile atomically:YES];
3753         /* We get the default preset in case it changed */
3754         [self getDefaultPresets: NULL];
3755
3756 }
3757
3758 - (IBAction)deletePreset:(id)sender
3759 {
3760     int status;
3761     NSEnumerator *enumerator;
3762     NSNumber *index;
3763     NSMutableArray *tempArray;
3764     id tempObject;
3765     
3766     if ( [fPresetsOutlineView numberOfSelectedRows] == 0 )
3767         return;
3768     /* Alert user before deleting preset */
3769         /* Comment out for now, tie to user pref eventually */
3770
3771     //NSBeep();
3772     status = NSRunAlertPanel(@"Warning!", @"Are you sure that you want to delete the selected preset?", @"OK", @"Cancel", nil);
3773     
3774     if ( status == NSAlertDefaultReturn ) {
3775         enumerator = [fPresetsOutlineView selectedRowEnumerator];
3776         tempArray = [NSMutableArray array];
3777         
3778         while ( (index = [enumerator nextObject]) ) {
3779             tempObject = [UserPresets objectAtIndex:[index intValue]];
3780             [tempArray addObject:tempObject];
3781         }
3782         
3783         [UserPresets removeObjectsInArray:tempArray];
3784         [fPresetsOutlineView reloadData];
3785         [self savePreset];   
3786     }
3787 }
3788
3789 #pragma mark -
3790 #pragma mark Manage Default Preset
3791
3792 - (IBAction)getDefaultPresets:(id)sender
3793 {
3794         int i = 0;
3795     NSEnumerator *enumerator = [UserPresets objectEnumerator];
3796         id tempObject;
3797         while (tempObject = [enumerator nextObject])
3798         {
3799                 NSDictionary *thisPresetDict = tempObject;
3800                 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 1) // 1 is HB default
3801                 {
3802                         presetHbDefault = i;    
3803                 }
3804                 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 2) // 2 is User specified default
3805                 {
3806                         presetUserDefault = i;  
3807                 }
3808                 i++;
3809         }
3810 }
3811
3812 - (IBAction)setDefaultPreset:(id)sender
3813 {
3814     int i = 0;
3815     NSEnumerator *enumerator = [UserPresets objectEnumerator];
3816         id tempObject;
3817         /* First make sure the old user specified default preset is removed */
3818         while (tempObject = [enumerator nextObject])
3819         {
3820                 /* make sure we are not removing the default HB preset */
3821                 if ([[[UserPresets objectAtIndex:i] objectForKey:@"Default"] intValue] != 1) // 1 is HB default
3822                 {
3823                         [[UserPresets objectAtIndex:i] setObject:[NSNumber numberWithInt:0] forKey:@"Default"];
3824                 }
3825                 i++;
3826         }
3827         /* Second, go ahead and set the appropriate user specfied preset */
3828         /* we get the chosen preset from the UserPresets array */
3829         if ([[[UserPresets objectAtIndex:[fPresetsOutlineView selectedRow]] objectForKey:@"Default"] intValue] != 1) // 1 is HB default
3830         {
3831                 [[UserPresets objectAtIndex:[fPresetsOutlineView selectedRow]] setObject:[NSNumber numberWithInt:2] forKey:@"Default"];
3832         }
3833         /*FIX ME: I think we now need to use the items not rows in NSOutlineView */
3834     presetUserDefault = [fPresetsOutlineView selectedRow];
3835         
3836         /* We save all of the preset data here */
3837     [self savePreset];
3838         /* We Reload the New Table data for presets */
3839     [fPresetsOutlineView reloadData];
3840 }
3841
3842 - (IBAction)selectDefaultPreset:(id)sender
3843 {
3844         /* if there is a user specified default, we use it */
3845         if (presetUserDefault)
3846         {
3847         [fPresetsOutlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:presetUserDefault] byExtendingSelection:NO];
3848         [self selectPreset:NULL];
3849         }
3850         else if (presetHbDefault) //else we use the built in default presetHbDefault
3851         {
3852         [fPresetsOutlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:presetHbDefault] byExtendingSelection:NO];
3853         [self selectPreset:NULL];
3854         }
3855 }
3856
3857
3858 #pragma mark -
3859 #pragma mark Manage Built In Presets
3860
3861
3862 - (IBAction)deleteFactoryPresets:(id)sender
3863 {
3864     //int status;
3865     NSEnumerator *enumerator = [UserPresets objectEnumerator];
3866         id tempObject;
3867     
3868         //NSNumber *index;
3869     NSMutableArray *tempArray;
3870
3871
3872         tempArray = [NSMutableArray array];
3873         /* we look here to see if the preset is we move on to the next one */
3874         while ( tempObject = [enumerator nextObject] )  
3875                 {
3876                         /* if the preset is "Factory" then we put it in the array of
3877                         presets to delete */
3878                         if ([[tempObject objectForKey:@"Type"] intValue] == 0)
3879                         {
3880                                 [tempArray addObject:tempObject];
3881                         }
3882         }
3883         
3884         [UserPresets removeObjectsInArray:tempArray];
3885         [fPresetsOutlineView reloadData];
3886         [self savePreset];   
3887
3888 }
3889
3890    /* We use this method to recreate new, updated factory
3891    presets */
3892 - (IBAction)addFactoryPresets:(id)sender
3893 {
3894    
3895    /* First, we delete any existing built in presets */
3896     [self deleteFactoryPresets: sender];
3897     /* Then we generate new built in presets programmatically with fPresetsBuiltin
3898     * which is all setup in HBPresets.h and  HBPresets.m*/
3899     [fPresetsBuiltin generateBuiltinPresets:UserPresets];
3900
3901     [self addPreset];
3902 }
3903
3904
3905
3906
3907
3908
3909
3910
3911 @end