OSDN Git Service

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