OSDN Git Service

MacGui: move the presets sorting into a new method "-sortPresets" so it can be done...
[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 ([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] == 1)
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                         /* We show the mp4 option checkboxes here since we are mp4 */
2258             [fCreateChapterMarkers setEnabled: YES];
2259                         [fDstMp4LargeFileCheck setHidden: NO];
2260                         [fDstMp4HttpOptFileCheck setHidden: NO];
2261             [fDstMp4iPodFileCheck setHidden: NO];
2262             break;
2263             
2264         case 1:
2265             ext = "mkv";
2266             [fDstCodecsPopUp addItemWithTitle:_( @"MPEG-4 Video / AAC Audio" )];
2267             [fDstCodecsPopUp addItemWithTitle:_( @"MPEG-4 Video / AC-3 Audio" )];
2268                         [fDstCodecsPopUp addItemWithTitle:_( @"MPEG-4 Video / MP3 Audio" )];
2269                         [fDstCodecsPopUp addItemWithTitle:_( @"MPEG-4 Video / Vorbis Audio" )];
2270             
2271                         [fDstCodecsPopUp addItemWithTitle:_( @"AVC/H.264 Video / AAC Audio" )];
2272                         [fDstCodecsPopUp addItemWithTitle:_( @"AVC/H.264 Video / AC-3 Audio" )];
2273                         [fDstCodecsPopUp addItemWithTitle:_( @"AVC/H.264 Video / MP3 Audio" )];
2274                         [fDstCodecsPopUp addItemWithTitle:_( @"AVC/H.264 Video / Vorbis Audio" )];
2275             /* We enable the create chapters checkbox here */
2276                         [fCreateChapterMarkers setEnabled: YES];
2277                         break;
2278             
2279         case 2: 
2280             ext = "avi";
2281             [fDstCodecsPopUp addItemWithTitle:_( @"MPEG-4 Video / MP3 Audio" )];
2282             [fDstCodecsPopUp addItemWithTitle:_( @"MPEG-4 Video / AC-3 Audio" )];
2283             [fDstCodecsPopUp addItemWithTitle:_( @"AVC/H.264 Video / MP3 Audio" )];
2284             [fDstCodecsPopUp addItemWithTitle:_( @"AVC/H.264 Video / AC-3 Audio" )];
2285                         /* We disable the create chapters checkbox here and make sure it is unchecked*/
2286                         [fCreateChapterMarkers setEnabled: NO];
2287                         [fCreateChapterMarkers setState: NSOffState];
2288                         break;
2289             
2290         case 3:
2291             ext = "ogm";
2292             [fDstCodecsPopUp addItemWithTitle:_( @"MPEG-4 Video / Vorbis Audio" )];
2293             [fDstCodecsPopUp addItemWithTitle:_( @"MPEG-4 Video / MP3 Audio" )];
2294             /* We disable the create chapters checkbox here and make sure it is unchecked*/
2295                         [fCreateChapterMarkers setEnabled: NO];
2296                         [fCreateChapterMarkers setState: NSOffState];
2297                         break;
2298     }
2299     
2300     if ( SuccessfulScan ) {
2301         [fDstCodecsPopUp selectItemWithTitle:selectedCodecs];
2302         
2303         /* Add/replace to the correct extension */
2304         if( [string characterAtIndex: [string length] - 4] == '.' )
2305         {
2306             [fDstFile2Field setStringValue: [NSString stringWithFormat:
2307                 @"%@.%s", [string substringToIndex: [string length] - 4],
2308                 ext]];
2309         }
2310         else
2311         {
2312             [fDstFile2Field setStringValue: [NSString stringWithFormat:
2313                 @"%@.%s", string, ext]];
2314         }
2315         
2316         if ( [fDstCodecsPopUp selectedItem] == NULL )
2317         {
2318             [fDstCodecsPopUp selectItemAtIndex:0];
2319             [self codecsPopUpChanged: NULL];
2320             
2321             /* changing the format may mean that we can / can't offer mono or 6ch, */
2322             /* so call audioTrackPopUpChanged for both audio tracks to update the mixdown popups */
2323             [self audioTrackPopUpChanged: fAudLang1PopUp];
2324             [self audioTrackPopUpChanged: fAudLang2PopUp];
2325             /* We call the method to properly enable/disable turbo 2 pass */
2326             [self twoPassCheckboxChanged: sender];
2327             /* We call method method to change UI to reflect whether a preset is used or not*/
2328         }
2329     }
2330     
2331     /* Lets check to see if we want to auto set the .m4v extension for mp4 */
2332     [self autoSetM4vExtension: sender];
2333         [self customSettingUsed: sender];       
2334 }
2335
2336 - (IBAction) codecsPopUpChanged: (id) sender
2337 {
2338     int format = [fDstFormatPopUp indexOfSelectedItem];
2339     int codecs = [fDstCodecsPopUp indexOfSelectedItem];
2340         
2341     [fAdvancedOptions setHidden:YES];
2342
2343     /* Update the encoder popup*/
2344     if( ( FormatSettings[format][codecs] & HB_VCODEC_X264 ) )
2345     {
2346         /* MPEG-4 -> H.264 */
2347         [fVidEncoderPopUp removeAllItems];
2348                 [fVidEncoderPopUp addItemWithTitle: @"x264"];
2349                 [fVidEncoderPopUp selectItemAtIndex: 0];
2350         [fAdvancedOptions setHidden:NO];
2351         [self autoSetM4vExtension: sender];
2352     }
2353     
2354     else if( ( FormatSettings[format][codecs] & HB_VCODEC_FFMPEG ) )
2355     {
2356         /* H.264 -> MPEG-4 */
2357         [fVidEncoderPopUp removeAllItems];
2358         [fVidEncoderPopUp addItemWithTitle: @"FFmpeg"];
2359         [fVidEncoderPopUp addItemWithTitle: @"XviD"];
2360         [fVidEncoderPopUp selectItemAtIndex: 0];
2361                                 
2362     }
2363
2364     if( FormatSettings[format][codecs] & HB_ACODEC_AC3 )
2365     {
2366         /* AC-3 pass-through: disable samplerate and bitrate */
2367         [fAudRatePopUp    setEnabled: NO];
2368         [fAudBitratePopUp setEnabled: NO];
2369     }
2370     else
2371     {
2372         [fAudRatePopUp    setEnabled: YES];
2373         [fAudBitratePopUp setEnabled: YES];
2374     }
2375     /* changing the codecs on offer may mean that we can / can't offer mono or 6ch, */
2376         /* so call audioTrackPopUpChanged for both audio tracks to update the mixdown popups */
2377         [self audioTrackPopUpChanged: fAudLang1PopUp];
2378         [self audioTrackPopUpChanged: fAudLang2PopUp];
2379         [self encoderPopUpChanged: sender];
2380
2381 }
2382
2383 - (IBAction) encoderPopUpChanged: (id) sender
2384 {
2385     hb_job_t * job = fTitle->job;
2386     
2387     /* We need to set loose anamorphic as available depending on whether or not the ffmpeg encoder
2388     is being used as it borks up loose anamorphic .
2389     For convenience lets use the titleOfSelected index. Probably should revisit whether or not we want
2390     to use the index itself but this is easier */
2391     if ([fVidEncoderPopUp titleOfSelectedItem] == @"FFmpeg")
2392     {
2393         if (job->pixel_ratio == 2)
2394         {
2395             job->pixel_ratio = 0;
2396         }
2397         [fPictureController setAllowLooseAnamorphic:NO];
2398         /* We set the iPod atom checkbox to disabled and uncheck it as its only for x264 in the mp4
2399          container. Format is taken care of in formatPopUpChanged method by hiding and unchecking
2400          anything other than MP4.
2401          */ 
2402         [fDstMp4iPodFileCheck setEnabled: NO];
2403         [fDstMp4iPodFileCheck setState: NSOffState];
2404     }
2405     else
2406     {
2407         [fPictureController setAllowLooseAnamorphic:YES];
2408         [fDstMp4iPodFileCheck setEnabled: YES];
2409     }
2410     
2411         [self calculatePictureSizing: sender];
2412         [self twoPassCheckboxChanged: sender];
2413 }
2414
2415
2416     /* if MP4 format and [fDstCodecsPopUp indexOfSelectedItem] > 1 we know that the audio is going to be
2417          * either aac + ac3 passthru, or just ac3 passthru so we need to make sure the output file extension is m4v
2418          * otherwise Quicktime will not play it at all */
2419 - (IBAction) autoSetM4vExtension: (id) sender
2420 {
2421         if ([fDstFormatPopUp indexOfSelectedItem] == 0 && [fDstCodecsPopUp indexOfSelectedItem] > 1)
2422         {
2423             NSString *newpath = [[[fDstFile2Field stringValue] stringByDeletingPathExtension] stringByAppendingPathExtension: @"m4v"];
2424             [fDstFile2Field setStringValue: [NSString stringWithFormat:
2425                                              @"%@", newpath]];
2426         }
2427 }
2428 /* Method to determine if we should change the UI
2429 To reflect whether or not a Preset is being used or if
2430 the user is using "Custom" settings by determining the sender*/
2431 - (IBAction) customSettingUsed: (id) sender
2432 {
2433         if ([sender stringValue] != NULL)
2434         {
2435                 /* Deselect the currently selected Preset if there is one*/
2436                 [fPresetsOutlineView deselectRow:[fPresetsOutlineView selectedRow]];
2437                 [[fPresetsActionMenu itemAtIndex:0] setEnabled: NO];
2438                 /* Change UI to show "Custom" settings are being used */
2439                 [fPresetSelectedDisplay setStringValue: @"Custom"];
2440                 
2441                 curUserPresetChosenNum = nil;
2442         }
2443
2444 }
2445
2446
2447 #pragma mark -
2448 #pragma mark - Video
2449
2450 - (IBAction) twoPassCheckboxChanged: (id) sender
2451 {
2452         /* check to see if x264 is chosen */
2453         int format = [fDstFormatPopUp indexOfSelectedItem];
2454     int codecs = [fDstCodecsPopUp indexOfSelectedItem];
2455         if( ( FormatSettings[format][codecs] & HB_VCODEC_X264 ) )
2456     {
2457                 if( [fVidTwoPassCheck state] == NSOnState)
2458                 {
2459                         [fVidTurboPassCheck setHidden: NO];
2460                 }
2461                 else
2462                 {
2463                         [fVidTurboPassCheck setHidden: YES];
2464                         [fVidTurboPassCheck setState: NSOffState];
2465                 }
2466                 /* Make sure Two Pass is checked if Turbo is checked */
2467                 if( [fVidTurboPassCheck state] == NSOnState)
2468                 {
2469                         [fVidTwoPassCheck setState: NSOnState];
2470                 }
2471         }
2472         else
2473         {
2474                 [fVidTurboPassCheck setHidden: YES];
2475                 [fVidTurboPassCheck setState: NSOffState];
2476         }
2477         
2478         /* We call method method to change UI to reflect whether a preset is used or not*/
2479         [self customSettingUsed: sender];
2480 }
2481
2482 - (IBAction ) videoFrameRateChanged: (id) sender
2483 {
2484     /* We call method method to calculatePictureSizing to error check detelecine*/
2485     [self calculatePictureSizing: sender];
2486
2487     /* We call method method to change UI to reflect whether a preset is used or not*/
2488         [self customSettingUsed: sender];
2489 }
2490 - (IBAction) videoMatrixChanged: (id) sender;
2491 {
2492     bool target, bitrate, quality;
2493
2494     target = bitrate = quality = false;
2495     if( [fVidQualityMatrix isEnabled] )
2496     {
2497         switch( [fVidQualityMatrix selectedRow] )
2498         {
2499             case 0:
2500                 target = true;
2501                 break;
2502             case 1:
2503                 bitrate = true;
2504                 break;
2505             case 2:
2506                 quality = true;
2507                 break;
2508         }
2509     }
2510     [fVidTargetSizeField  setEnabled: target];
2511     [fVidBitrateField     setEnabled: bitrate];
2512     [fVidQualitySlider    setEnabled: quality];
2513     [fVidTwoPassCheck     setEnabled: !quality &&
2514         [fVidQualityMatrix isEnabled]];
2515     if( quality )
2516     {
2517         [fVidTwoPassCheck setState: NSOffState];
2518                 [fVidTurboPassCheck setHidden: YES];
2519                 [fVidTurboPassCheck setState: NSOffState];
2520     }
2521
2522     [self qualitySliderChanged: sender];
2523     [self calculateBitrate: sender];
2524         [self customSettingUsed: sender];
2525 }
2526
2527 - (IBAction) qualitySliderChanged: (id) sender
2528 {
2529     [fVidConstantCell setTitle: [NSString stringWithFormat:
2530         _( @"Constant quality: %.0f %%" ), 100.0 *
2531         [fVidQualitySlider floatValue]]];
2532                 [self customSettingUsed: sender];
2533 }
2534
2535 - (void) controlTextDidChange: (NSNotification *) notification
2536 {
2537     [self calculateBitrate: NULL];
2538 }
2539
2540 - (IBAction) calculateBitrate: (id) sender
2541 {
2542     if( !fHandle || [fVidQualityMatrix selectedRow] != 0 || !SuccessfulScan )
2543     {
2544         return;
2545     }
2546
2547     hb_list_t  * list  = hb_get_titles( fHandle );
2548     hb_title_t * title = (hb_title_t *) hb_list_item( list,
2549             [fSrcTitlePopUp indexOfSelectedItem] );
2550     hb_job_t * job = title->job;
2551
2552     [self prepareJob];
2553
2554     [fVidBitrateField setIntValue: hb_calc_bitrate( job,
2555             [fVidTargetSizeField intValue] )];
2556 }
2557
2558 #pragma mark -
2559 #pragma mark - Picture
2560
2561 /* lets set the picture size back to the max from right after title scan
2562    Lets use an IBAction here as down the road we could always use a checkbox
2563    in the gui to easily take the user back to max. Remember, the compiler
2564    resolves IBActions down to -(void) during compile anyway */
2565 - (IBAction) revertPictureSizeToMax: (id) sender
2566 {
2567         hb_job_t * job = fTitle->job;
2568         /* We use the output picture width and height
2569      as calculated from libhb right after title is set
2570      in TitlePopUpChanged */
2571         job->width = PicOrigOutputWidth;
2572         job->height = PicOrigOutputHeight;
2573     [fPictureController setAutoCrop:YES];
2574         /* Here we use the auto crop values determined right after scan */
2575         job->crop[0] = AutoCropTop;
2576         job->crop[1] = AutoCropBottom;
2577         job->crop[2] = AutoCropLeft;
2578         job->crop[3] = AutoCropRight;
2579     
2580     
2581     [self calculatePictureSizing: sender];
2582     /* We call method to change UI to reflect whether a preset is used or not*/    
2583     [self customSettingUsed: sender];
2584 }
2585
2586 /**
2587  * Registers changes made in the Picture Settings Window.
2588  */
2589
2590 - (void)pictureSettingsDidChange {
2591         [self calculatePictureSizing: NULL];
2592 }
2593
2594 /* Get and Display Current Pic Settings in main window */
2595 - (IBAction) calculatePictureSizing: (id) sender
2596 {
2597         [fPicSettingsOutp setStringValue: [NSString stringWithFormat:@"%d x %d", fTitle->job->width, fTitle->job->height]];
2598         
2599     if (fTitle->job->pixel_ratio == 1)
2600         {
2601         int titlewidth = fTitle->width-fTitle->job->crop[2]-fTitle->job->crop[3];
2602         int arpwidth = fTitle->job->pixel_aspect_width;
2603         int arpheight = fTitle->job->pixel_aspect_height;
2604         int displayparwidth = titlewidth * arpwidth / arpheight;
2605         int displayparheight = fTitle->height-fTitle->job->crop[0]-fTitle->job->crop[1];
2606         [fPicSettingsOutp setStringValue: [NSString stringWithFormat:@"%d x %d", titlewidth, displayparheight]];
2607         [fPicSettingsAnamorphic setStringValue: [NSString stringWithFormat:@"%d x %d Strict", displayparwidth, displayparheight]];
2608         fTitle->job->keep_ratio = 0;
2609         }
2610     else if (fTitle->job->pixel_ratio == 2)
2611     {
2612         hb_job_t * job = fTitle->job;
2613         int output_width, output_height, output_par_width, output_par_height;
2614         hb_set_anamorphic_size(job, &output_width, &output_height, &output_par_width, &output_par_height);
2615         int display_width;
2616         display_width = output_width * output_par_width / output_par_height;
2617         
2618         [fPicSettingsOutp setStringValue: [NSString stringWithFormat:@"%d x %d", output_width, output_height]];
2619         [fPicSettingsAnamorphic setStringValue: [NSString stringWithFormat:@"%d x %d Loose", display_width, output_height]];
2620         
2621         fTitle->job->keep_ratio = 0;
2622     }
2623         else
2624         {
2625         [fPicSettingsAnamorphic setStringValue: [NSString stringWithFormat:@"Off"]];
2626         }
2627     
2628         /* Set ON/Off values for the deinterlace/keep aspect ratio according to boolean */      
2629         if (fTitle->job->keep_ratio > 0)
2630         {
2631                 [fPicSettingARkeep setStringValue: @"On"];
2632         }
2633         else
2634         {
2635                 [fPicSettingARkeep setStringValue: @"Off"];
2636         }       
2637     /* Detelecine */
2638     if ([fPictureController detelecine]) {
2639         [fPicSettingDetelecine setStringValue: @"Yes"];
2640     }
2641     else {
2642         [fPicSettingDetelecine setStringValue: @"No"];
2643     }
2644     
2645     /* VFR (Variable Frame Rate) */
2646     if ([fPictureController vfr]) {
2647         /* We change the string of the fps popup to warn that vfr is on Framerate (FPS): */
2648         [fVidRateField setStringValue: @"Framerate (VFR On):"];  
2649         
2650     }
2651     else {
2652         /* make sure the label for framerate is set to its default */  
2653         [fVidRateField setStringValue: @"Framerate (FPS):"];
2654     }
2655     
2656         /* Deinterlace */
2657         if ([fPictureController deinterlace] == 0)
2658         {
2659                 [fPicSettingDeinterlace setStringValue: @"Off"];
2660         }
2661         else if ([fPictureController deinterlace] == 1)
2662         {
2663                 [fPicSettingDeinterlace setStringValue: @"Fast"];
2664         }
2665         else if ([fPictureController deinterlace] == 2)
2666         {
2667                 [fPicSettingDeinterlace setStringValue: @"Slow"];
2668         }
2669         else if ([fPictureController deinterlace] == 3)
2670         {
2671                 [fPicSettingDeinterlace setStringValue: @"Slower"];
2672         }
2673         /* Denoise */
2674         if ([fPictureController denoise] == 0)
2675         {
2676                 [fPicSettingDenoise setStringValue: @"Off"];
2677         }
2678         else if ([fPictureController denoise] == 1)
2679         {
2680                 [fPicSettingDenoise setStringValue: @"Weak"];
2681         }
2682         else if ([fPictureController denoise] == 2)
2683         {
2684                 [fPicSettingDenoise setStringValue: @"Medium"];
2685         }
2686         else if ([fPictureController denoise] == 3)
2687         {
2688                 [fPicSettingDenoise setStringValue: @"Strong"];
2689         }
2690     
2691     /* Deblock */
2692     if ([fPictureController deblock]) {
2693         [fPicSettingDeblock setStringValue: @"Yes"];
2694     }
2695     else {
2696         [fPicSettingDeblock setStringValue: @"No"];
2697     }
2698         
2699         if (fTitle->job->pixel_ratio > 0)
2700         {
2701                 [fPicSettingPAR setStringValue: @""];
2702         }
2703         else
2704         {
2705                 [fPicSettingPAR setStringValue: @"Off"];
2706         }
2707         /* Set the display field for crop as per boolean */
2708         if (![fPictureController autoCrop])
2709         {
2710             [fPicSettingAutoCrop setStringValue: @"Custom"];
2711         }
2712         else
2713         {
2714                 [fPicSettingAutoCrop setStringValue: @"Auto"];
2715         }       
2716         
2717     
2718 }
2719
2720
2721 #pragma mark -
2722 #pragma mark - Audio and Subtitles
2723
2724 - (IBAction) setEnabledStateOfAudioMixdownControls: (id) sender
2725 {
2726
2727     /* enable/disable the mixdown text and popupbutton for audio track 1 */
2728     [fAudTrack1MixPopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
2729     [fAudTrack1MixLabel setTextColor: ([fAudLang1PopUp indexOfSelectedItem] == 0) ?
2730         [NSColor disabledControlTextColor] : [NSColor controlTextColor]];
2731
2732     /* enable/disable the mixdown text and popupbutton for audio track 2 */
2733     [fAudTrack2MixPopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
2734     [fAudTrack2MixLabel setTextColor: ([fAudLang2PopUp indexOfSelectedItem] == 0) ?
2735         [NSColor disabledControlTextColor] : [NSColor controlTextColor]];
2736
2737 }
2738
2739 - (IBAction) addAllAudioTracksToPopUp: (id) sender
2740 {
2741
2742     hb_list_t  * list  = hb_get_titles( fHandle );
2743     hb_title_t * title = (hb_title_t*)
2744         hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
2745
2746         hb_audio_t * audio;
2747
2748     [sender removeAllItems];
2749     [sender addItemWithTitle: _( @"None" )];
2750     for( int i = 0; i < hb_list_count( title->list_audio ); i++ )
2751     {
2752         audio = (hb_audio_t *) hb_list_item( title->list_audio, i );
2753         [[sender menu] addItemWithTitle:
2754             [NSString stringWithCString: audio->lang]
2755             action: NULL keyEquivalent: @""];
2756     }
2757     [sender selectItemAtIndex: 0];
2758
2759 }
2760
2761 - (IBAction) selectAudioTrackInPopUp: (id) sender searchPrefixString: (NSString *) searchPrefixString selectIndexIfNotFound: (int) selectIndexIfNotFound
2762 {
2763
2764     /* this method can be used to find a language, or a language-and-source-format combination, by passing in the appropriate string */
2765     /* e.g. to find the first French track, pass in an NSString * of "Francais" */
2766     /* e.g. to find the first English 5.1 AC3 track, pass in an NSString * of "English (AC3) (5.1 ch)" */
2767     /* if no matching track is found, then selectIndexIfNotFound is used to choose which track to select instead */
2768     
2769         if (searchPrefixString != NULL) 
2770         {
2771
2772         for( int i = 0; i < [sender numberOfItems]; i++ )
2773         {
2774             /* Try to find the desired search string */
2775             if ([[[sender itemAtIndex: i] title] hasPrefix:searchPrefixString])
2776             {
2777                 [sender selectItemAtIndex: i];
2778                 return;
2779             }
2780         }
2781         /* couldn't find the string, so select the requested "search string not found" item */
2782         /* index of 0 means select the "none" item */
2783         /* index of 1 means select the first audio track */
2784         [sender selectItemAtIndex: selectIndexIfNotFound];
2785         }
2786     else
2787     {
2788         /* if no search string is provided, then select the selectIndexIfNotFound item */
2789         [sender selectItemAtIndex: selectIndexIfNotFound];
2790     }
2791
2792 }
2793
2794 - (IBAction) audioTrackPopUpChanged: (id) sender
2795 {
2796     /* utility function to call audioTrackPopUpChanged without passing in a mixdown-to-use */
2797     [self audioTrackPopUpChanged: sender mixdownToUse: 0];
2798 }
2799
2800 - (IBAction) audioTrackPopUpChanged: (id) sender mixdownToUse: (int) mixdownToUse
2801 {
2802
2803     /* make sure we have a selected title before continuing */
2804     if (fTitle == NULL) return;
2805
2806     /* find out if audio track 1 or 2 was changed - this is passed to us in the tag of the sender */
2807     /* the sender will have been either fAudLang1PopUp (tag = 0) or fAudLang2PopUp (tag = 1) */
2808     int thisAudio = [sender tag];
2809
2810     /* get the index of the selected audio */
2811     int thisAudioIndex = [sender indexOfSelectedItem] - 1;
2812
2813 #if 0
2814     /* Handbrake can't currently cope with ripping the same source track twice */
2815     /* So, if this audio is also selected in the other audio track popup, set that popup's selection to "none" */
2816     /* get a reference to the two audio track popups */
2817     NSPopUpButton * thisAudioPopUp  = (thisAudio == 1 ? fAudLang2PopUp : fAudLang1PopUp);
2818     NSPopUpButton * otherAudioPopUp = (thisAudio == 1 ? fAudLang1PopUp : fAudLang2PopUp);
2819     /* if the same track is selected in the other audio popup, then select "none" in that popup */
2820     /* unless, of course, both are selected as "none!" */
2821     if ([thisAudioPopUp indexOfSelectedItem] != 0 && [thisAudioPopUp indexOfSelectedItem] == [otherAudioPopUp indexOfSelectedItem]) {
2822         [otherAudioPopUp selectItemAtIndex: 0];
2823         [self audioTrackPopUpChanged: otherAudioPopUp];
2824     }
2825 #endif
2826
2827     /* pointer for the hb_audio_s struct we will use later on */
2828     hb_audio_t * audio;
2829
2830     /* find out what the currently-selected output audio codec is */
2831     int format = [fDstFormatPopUp indexOfSelectedItem];
2832     int codecs = [fDstCodecsPopUp indexOfSelectedItem];
2833     int acodec = FormatSettings[format][codecs] & HB_ACODEC_MASK;
2834     
2835     /*HACK: Lets setup a convenience variable to decide whether or not to allow aac hybrid (aac + ac3 passthru )*/
2836     bool mp4AacAc3;
2837     if (format == 0 && codecs == 2) // if mp4 and aac + ac3
2838     {
2839     mp4AacAc3 = 1;
2840     }
2841     else
2842     {
2843     mp4AacAc3 = 0;
2844     }
2845
2846     /* pointer to this track's mixdown NSPopUpButton */
2847     NSTextField   * mixdownTextField;
2848     NSPopUpButton * mixdownPopUp;
2849
2850     /* find our mixdown NSTextField and NSPopUpButton */
2851     if (thisAudio == 0)
2852     {
2853         mixdownTextField = fAudTrack1MixLabel;
2854         mixdownPopUp = fAudTrack1MixPopUp;
2855     }
2856     else
2857     {
2858         mixdownTextField = fAudTrack2MixLabel;
2859         mixdownPopUp = fAudTrack2MixPopUp;
2860     }
2861
2862     /* delete the previous audio mixdown options */
2863     [mixdownPopUp removeAllItems];
2864
2865     /* check if the audio mixdown controls need their enabled state changing */
2866     [self setEnabledStateOfAudioMixdownControls: NULL];
2867
2868     if (thisAudioIndex != -1)
2869     {
2870
2871         /* get the audio */
2872         audio = (hb_audio_t *) hb_list_item( fTitle->list_audio, thisAudioIndex );
2873         if (audio != NULL)
2874         {
2875
2876             /* find out if our selected output audio codec supports mono and / or 6ch */
2877             /* we also check for an input codec of AC3 or DCA,
2878                as they are the only libraries able to do the mixdown to mono / conversion to 6-ch */
2879             /* audioCodecsSupportMono and audioCodecsSupport6Ch are the same for now,
2880                but this may change in the future, so they are separated for flexibility */
2881             int audioCodecsSupportMono = ((audio->codec == HB_ACODEC_AC3 ||
2882                 audio->codec == HB_ACODEC_DCA) && acodec == HB_ACODEC_FAAC);
2883             int audioCodecsSupport6Ch =  ((audio->codec == HB_ACODEC_AC3 ||
2884                 audio->codec == HB_ACODEC_DCA) && (acodec == HB_ACODEC_FAAC ||
2885                 acodec == HB_ACODEC_VORBIS));
2886
2887             /* check for AC-3 passthru */
2888             if (audio->codec == HB_ACODEC_AC3 && acodec == HB_ACODEC_AC3)
2889             {
2890             
2891                     [[mixdownPopUp menu] addItemWithTitle:
2892                         [NSString stringWithCString: "AC3 Passthru"]
2893                         action: NULL keyEquivalent: @""];
2894             }
2895             else
2896             {
2897
2898                 /* add the appropriate audio mixdown menuitems to the popupbutton */
2899                 /* in each case, we set the new menuitem's tag to be the amixdown value for that mixdown,
2900                    so that we can reference the mixdown later */
2901
2902                 /* keep a track of the min and max mixdowns we used, so we can select the best match later */
2903                 int minMixdownUsed = 0;
2904                 int maxMixdownUsed = 0;
2905                 
2906                 /* get the input channel layout without any lfe channels */
2907                 int layout = audio->input_channel_layout & HB_INPUT_CH_LAYOUT_DISCRETE_NO_LFE_MASK;
2908
2909                 /* do we want to add a mono option? */
2910                 if (!mp4AacAc3 && audioCodecsSupportMono == 1) {
2911                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
2912                         [NSString stringWithCString: hb_audio_mixdowns[0].human_readable_name]
2913                         action: NULL keyEquivalent: @""];
2914                     [menuItem setTag: hb_audio_mixdowns[0].amixdown];
2915                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[0].amixdown;
2916                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[0].amixdown);
2917                 }
2918
2919                 /* do we want to add a stereo option? */
2920                 /* offer stereo if we have a mono source and non-mono-supporting codecs, as otherwise we won't have a mixdown at all */
2921                 /* also offer stereo if we have a stereo-or-better source */
2922                 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))) {
2923                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
2924                         [NSString stringWithCString: hb_audio_mixdowns[1].human_readable_name]
2925                         action: NULL keyEquivalent: @""];
2926                     [menuItem setTag: hb_audio_mixdowns[1].amixdown];
2927                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[1].amixdown;
2928                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[1].amixdown);
2929                 }
2930
2931                 /* do we want to add a dolby surround (DPL1) option? */
2932                 if (!mp4AacAc3 && (layout == HB_INPUT_CH_LAYOUT_3F1R || layout == HB_INPUT_CH_LAYOUT_3F2R || layout == HB_INPUT_CH_LAYOUT_DOLBY)) {
2933                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
2934                         [NSString stringWithCString: hb_audio_mixdowns[2].human_readable_name]
2935                         action: NULL keyEquivalent: @""];
2936                     [menuItem setTag: hb_audio_mixdowns[2].amixdown];
2937                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[2].amixdown;
2938                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[2].amixdown);
2939                 }
2940
2941                 /* do we want to add a dolby pro logic 2 (DPL2) option? */
2942                 if ((!mp4AacAc3 || audio->codec == HB_ACODEC_DCA) && layout == HB_INPUT_CH_LAYOUT_3F2R) {
2943                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
2944                         [NSString stringWithCString: hb_audio_mixdowns[3].human_readable_name]
2945                         action: NULL keyEquivalent: @""];
2946                     [menuItem setTag: hb_audio_mixdowns[3].amixdown];
2947                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[3].amixdown;
2948                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[3].amixdown);
2949                 }
2950
2951                 /* do we want to add a 6-channel discrete option? */
2952                 if (!mp4AacAc3 && (audioCodecsSupport6Ch == 1 && layout == HB_INPUT_CH_LAYOUT_3F2R && (audio->input_channel_layout & HB_INPUT_CH_LAYOUT_HAS_LFE))) {
2953                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
2954                         [NSString stringWithCString: hb_audio_mixdowns[4].human_readable_name]
2955                         action: NULL keyEquivalent: @""];
2956                     [menuItem setTag: hb_audio_mixdowns[4].amixdown];
2957                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[4].amixdown;
2958                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[4].amixdown);
2959                 }
2960
2961                 /* do we want to add an AC-3 passthrough option? */
2962                 if (audio->codec == HB_ACODEC_AC3 && acodec == HB_ACODEC_AC3) {
2963                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
2964                         [NSString stringWithCString: hb_audio_mixdowns[5].human_readable_name]
2965                         action: NULL keyEquivalent: @""];
2966                     [menuItem setTag: hb_audio_mixdowns[5].amixdown];
2967                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[5].amixdown;
2968                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[5].amixdown);
2969                 }
2970
2971                 /* do we want to add the DPLII+AC3 passthrough option? */
2972                 if (mp4AacAc3 && audio->codec == HB_ACODEC_AC3) {
2973                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
2974                         [NSString stringWithCString: hb_audio_mixdowns[6].human_readable_name]
2975                         action: NULL keyEquivalent: @""];
2976                     [menuItem setTag: hb_audio_mixdowns[6].amixdown];
2977                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[6].amixdown;
2978                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[6].amixdown);
2979                 }
2980                 /* auto-select the best mixdown based on our saved mixdown preference */
2981                 
2982                 /* for now, this is hard-coded to a "best" mixdown of HB_AMIXDOWN_DOLBYPLII */
2983                 /* ultimately this should be a prefs option */
2984                 int useMixdown;
2985                 
2986                 /* if we passed in a mixdown to use - in order to load a preset - then try and use it */
2987                 if (mixdownToUse > 0)
2988                 {
2989                     useMixdown = mixdownToUse;
2990                 }
2991                 else
2992                 {
2993                     useMixdown = HB_AMIXDOWN_DOLBYPLII;
2994                 }
2995                 
2996                 /* if useMixdown > maxMixdownUsed, then use maxMixdownUsed */
2997                 if (useMixdown > maxMixdownUsed) useMixdown = maxMixdownUsed;
2998
2999                 /* if useMixdown < minMixdownUsed, then use minMixdownUsed */
3000                 if (useMixdown < minMixdownUsed) useMixdown = minMixdownUsed;
3001
3002                 /* select the (possibly-amended) preferred mixdown */
3003                 [mixdownPopUp selectItemWithTag: useMixdown];
3004                                 
3005                                 /* lets call the audioTrackMixdownChanged method here to determine appropriate bitrates, etc. */
3006                 [self audioTrackMixdownChanged: NULL];
3007             }
3008
3009         }
3010         
3011     }
3012
3013         /* see if the new audio track choice will change the bitrate we need */
3014     [self calculateBitrate: sender];    
3015
3016 }
3017 - (IBAction) audioTrackMixdownChanged: (id) sender
3018 {
3019
3020     /* find out what the currently-selected output audio codec is */
3021     int format = [fDstFormatPopUp indexOfSelectedItem];
3022     int codecs = [fDstCodecsPopUp indexOfSelectedItem];
3023     int acodec = FormatSettings[format][codecs] & HB_ACODEC_MASK;
3024     
3025     /* storage variable for the min and max bitrate allowed for this codec */
3026     int minbitrate;
3027     int maxbitrate;
3028     
3029     switch( acodec )
3030     {
3031         case HB_ACODEC_FAAC:
3032             /* check if we have a 6ch discrete conversion in either audio track */
3033             if ([[fAudTrack1MixPopUp selectedItem] tag] == HB_AMIXDOWN_6CH || 
3034                 [[fAudTrack2MixPopUp selectedItem] tag] == HB_AMIXDOWN_6CH || 
3035                 [[fAudTrack1MixPopUp selectedItem] tag] == HB_AMIXDOWN_AC3 || 
3036                 [[fAudTrack2MixPopUp selectedItem] tag] == HB_AMIXDOWN_AC3)
3037             {
3038                 /* FAAC is happy using our min bitrate of 32 kbps, even for 6ch */
3039                 minbitrate = 32;
3040                 /* If either mixdown popup includes 6-channel discrete, then allow up to 384 kbps */
3041                 maxbitrate = 384;
3042                 break;
3043             }
3044             else
3045             {
3046                 /* FAAC is happy using our min bitrate of 32 kbps for stereo or mono */
3047                 minbitrate = 32;
3048                 /* FAAC won't honour anything more than 160 for stereo, so let's not offer it */
3049                 /* note: haven't dealt with mono separately here, FAAC will just use the max it can */
3050                 maxbitrate = 160;
3051                 break;
3052             }
3053
3054         case HB_ACODEC_LAME:
3055             /* Lame is happy using our min bitrate of 32 kbps */
3056             minbitrate = 32;
3057             /* Lame won't encode if the bitrate is higher than 320 kbps */
3058             maxbitrate = 320;
3059             break;
3060
3061         case HB_ACODEC_VORBIS:
3062         if ([[fAudTrack1MixPopUp selectedItem] tag] == HB_AMIXDOWN_6CH || [[fAudTrack2MixPopUp selectedItem] tag] == HB_AMIXDOWN_6CH)
3063             {
3064                 /* Vorbis causes a crash if we use a bitrate below 192 kbps with 6 channel */
3065                 minbitrate = 192;
3066                 /* If either mixdown popup includes 6-channel discrete, then allow up to 384 kbps */
3067                 maxbitrate = 384;
3068                 break;
3069             }
3070             else
3071             {
3072             /* Vorbis causes a crash if we use a bitrate below 48 kbps */
3073             minbitrate = 48;
3074             /* Vorbis can cope with 384 kbps quite happily, even for stereo */
3075             maxbitrate = 384;
3076             break;
3077             }
3078
3079         default:
3080             /* AC3 passthru disables the bitrate dropdown anyway, so we might as well just use the min and max bitrate */
3081             minbitrate = 32;
3082             maxbitrate = 384;
3083         
3084     }
3085
3086     [fAudBitratePopUp removeAllItems];
3087
3088     for( int i = 0; i < hb_audio_bitrates_count; i++ )
3089     {
3090         if (hb_audio_bitrates[i].rate >= minbitrate && hb_audio_bitrates[i].rate <= maxbitrate)
3091         {
3092             /* add a new menuitem for this bitrate */
3093             NSMenuItem *menuItem = [[fAudBitratePopUp menu] addItemWithTitle:
3094                 [NSString stringWithCString: hb_audio_bitrates[i].string]
3095                 action: NULL keyEquivalent: @""];
3096             /* set its tag to be the actual bitrate as an integer, so we can retrieve it later */
3097             [menuItem setTag: hb_audio_bitrates[i].rate];
3098         }
3099     }
3100
3101     /* select the default bitrate (but use 384 for 6-ch AAC) */
3102     if ([[fAudTrack1MixPopUp selectedItem] tag] == HB_AMIXDOWN_6CH || 
3103         [[fAudTrack2MixPopUp selectedItem] tag] == HB_AMIXDOWN_6CH ||
3104         [[fAudTrack1MixPopUp selectedItem] tag] == HB_AMIXDOWN_AC3 || 
3105         [[fAudTrack2MixPopUp selectedItem] tag] == HB_AMIXDOWN_AC3)
3106     {
3107         [fAudBitratePopUp selectItemWithTag: 384];
3108     }
3109     else
3110     {
3111         [fAudBitratePopUp selectItemWithTag: hb_audio_bitrates[hb_audio_bitrates_default].rate];
3112     }
3113
3114 }
3115
3116 - (IBAction) audioDRCSliderChanged: (id) sender
3117 {
3118     [fAudDrcField setStringValue: [NSString stringWithFormat: @"%.2f", [fAudDrcSlider floatValue]]];
3119     [self customSettingUsed: sender];
3120 }
3121
3122 - (IBAction) subtitleSelectionChanged: (id) sender
3123 {
3124         if ([fSubPopUp indexOfSelectedItem] == 0)
3125         {
3126         [fSubForcedCheck setState: NSOffState];
3127         [fSubForcedCheck setEnabled: NO];       
3128         }
3129         else
3130         {
3131         [fSubForcedCheck setEnabled: YES];      
3132         }
3133         
3134 }
3135
3136
3137
3138
3139 #pragma mark -
3140 #pragma mark Open New Windows
3141
3142 - (IBAction) openHomepage: (id) sender
3143 {
3144     [[NSWorkspace sharedWorkspace] openURL: [NSURL
3145         URLWithString:@"http://handbrake.fr/"]];
3146 }
3147
3148 - (IBAction) openForums: (id) sender
3149 {
3150     [[NSWorkspace sharedWorkspace] openURL: [NSURL
3151         URLWithString:@"http://handbrake.fr/forum/"]];
3152 }
3153 - (IBAction) openUserGuide: (id) sender
3154 {
3155     [[NSWorkspace sharedWorkspace] openURL: [NSURL
3156         URLWithString:@"http://handbrake.fr/trac/wiki/HandBrakeGuide"]];
3157 }
3158
3159 /**
3160  * Shows debug output window.
3161  */
3162 - (IBAction)showDebugOutputPanel:(id)sender
3163 {
3164     [outputPanel showOutputPanel:sender];
3165 }
3166
3167 /**
3168  * Shows preferences window.
3169  */
3170 - (IBAction) showPreferencesWindow: (id) sender
3171 {
3172     NSWindow * window = [fPreferencesController window];
3173     if (![window isVisible])
3174         [window center];
3175
3176     [window makeKeyAndOrderFront: nil];
3177 }
3178
3179 /**
3180  * Shows queue window.
3181  */
3182 - (IBAction) showQueueWindow:(id)sender
3183 {
3184     [fQueueController showQueueWindow:sender];
3185 }
3186
3187
3188 - (IBAction) toggleDrawer:(id)sender {
3189     [fPresetDrawer toggle:self];
3190 }
3191
3192 /**
3193  * Shows Picture Settings Window.
3194  */
3195
3196 - (IBAction) showPicturePanel: (id) sender
3197 {
3198         hb_list_t  * list  = hb_get_titles( fHandle );
3199     hb_title_t * title = (hb_title_t *) hb_list_item( list,
3200             [fSrcTitlePopUp indexOfSelectedItem] );
3201     [fPictureController showPanelInWindow:fWindow forTitle:title];
3202 }
3203
3204 #pragma mark -
3205 #pragma mark Preset Outline View Methods
3206 #pragma mark - Required
3207 /* These are required by the NSOutlineView Datasource Delegate */
3208 /* We use this to deterimine children of an item */
3209 - (id)outlineView:(NSOutlineView *)fPresetsOutlineView child:(NSInteger)index ofItem:(id)item
3210 {
3211 if (item == nil)
3212         return [UserPresets objectAtIndex:index];
3213     
3214     // We are only one level deep, so we can't be asked about children
3215     NSAssert (NO, @"Presets View outlineView:child:ofItem: currently can't handle nested items.");
3216     return nil;
3217 }
3218 /* We use this to determine if an item should be expandable */
3219 - (BOOL)outlineView:(NSOutlineView *)fPresetsOutlineView isItemExpandable:(id)item
3220 {
3221
3222     /* For now, we maintain one level, so set to no
3223     * when nested, we set to yes for any preset "folders"
3224     */
3225     return NO;
3226
3227 }
3228 /* used to specify the number of levels to show for each item */
3229 - (int)outlineView:(NSOutlineView *)fPresetsOutlineView numberOfChildrenOfItem:(id)item
3230 {
3231     /* currently use no levels to test outline view viability */
3232     if (item == nil)
3233         return [UserPresets count];
3234     else
3235         return 0;
3236 }
3237 /* Used to tell the outline view which information is to be displayed per item */
3238 - (id)outlineView:(NSOutlineView *)fPresetsOutlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
3239 {
3240         /* We have two columns right now, icon and PresetName */
3241         
3242     if ([[tableColumn identifier] isEqualToString:@"PresetName"])
3243     {
3244         return [item objectForKey:@"PresetName"];
3245     }
3246     else
3247     {
3248         return @"something";
3249     }
3250 }
3251
3252 #pragma mark - Added Functionality (optional)
3253 /* Use to customize the font and display characteristics of the title cell */
3254 - (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
3255 {
3256     if ([[tableColumn identifier] isEqualToString:@"PresetName"])
3257     {
3258         NSDictionary *userPresetDict = item;
3259         NSFont *txtFont;
3260         NSColor *fontColor;
3261         NSColor *shadowColor;
3262         txtFont = [NSFont systemFontOfSize: [NSFont smallSystemFontSize]];
3263         /*check to see if its a selected row */
3264         if ([fPresetsOutlineView selectedRow] == [fPresetsOutlineView rowForItem:item])
3265         {
3266             
3267             fontColor = [NSColor whiteColor];
3268             shadowColor = [NSColor colorWithDeviceRed:(127.0/255.0) green:(140.0/255.0) blue:(160.0/255.0) alpha:1.0];
3269         }
3270         else
3271         {
3272             if ([[userPresetDict objectForKey:@"Type"] intValue] == 0)
3273             {
3274                 fontColor = [NSColor blueColor];
3275             }
3276             else // User created preset, use a black font
3277             {
3278                 fontColor = [NSColor blackColor];
3279             }
3280             shadowColor = nil;
3281         }
3282         /* We use Bold Text for the HB Default */
3283         if ([[userPresetDict objectForKey:@"Default"] intValue] == 1)// 1 is HB default
3284         {
3285             txtFont = [NSFont boldSystemFontOfSize: [NSFont smallSystemFontSize]];
3286         }
3287         /* We use Bold Text for the User Specified Default */
3288         if ([[userPresetDict objectForKey:@"Default"] intValue] == 2)// 2 is User default
3289         {
3290             txtFont = [NSFont boldSystemFontOfSize: [NSFont smallSystemFontSize]];
3291         }
3292         
3293         
3294         [cell setTextColor:fontColor];
3295         [cell setFont:txtFont];
3296         
3297     }
3298 }
3299
3300 /* We use this to edit the name field in the outline view */
3301 - (void)outlineView:(NSOutlineView *)fPresetsOutlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
3302 {
3303     if ([[tableColumn identifier] isEqualToString:@"PresetName"])
3304     {
3305         id theRecord;
3306         
3307         theRecord = item;
3308         [theRecord setObject:object forKey:@"PresetName"];
3309         /* We Sort the Presets By Factory or Custom */
3310         NSSortDescriptor * presetTypeDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"Type" 
3311                                                                              ascending:YES] autorelease];
3312                 /* We Sort the Presets Alphabetically by name */
3313         NSSortDescriptor * presetNameDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"PresetName" 
3314                                                                              ascending:YES selector:@selector(caseInsensitiveCompare:)] autorelease];
3315         NSArray *sortDescriptors=[NSArray arrayWithObjects:presetTypeDescriptor,presetNameDescriptor,nil];
3316         NSArray *sortedArray=[UserPresets sortedArrayUsingDescriptors:sortDescriptors];
3317         [UserPresets setArray:sortedArray];
3318         /* We Reload the New Table data for presets */
3319         //[fPresetsOutlineView reloadData];
3320         /* We save all of the preset data here */
3321         [self savePreset];
3322     }
3323 }
3324 /* We use this to provide tooltips for the items in the presets outline view */
3325 - (NSString *)outlineView:(NSOutlineView *)fPresetsOutlineView toolTipForCell:(NSCell *)cell rect:(NSRectPointer)rect tableColumn:(NSTableColumn *)tc item:(id)item mouseLocation:(NSPoint)mouseLocation
3326 {
3327     //if ([[tc identifier] isEqualToString:@"PresetName"])
3328     //{
3329         /* initialize the tooltip contents variable */
3330         NSString *loc_tip;
3331         /* if there is a description for the preset, we show it in the tooltip */
3332         if ([item valueForKey:@"PresetDescription"])
3333         {
3334             loc_tip = [NSString stringWithFormat: @"%@",[item valueForKey:@"PresetDescription"]];
3335             return (loc_tip);
3336         }
3337         else
3338         {
3339             loc_tip = @"No description available";
3340         }
3341         return (loc_tip);
3342     //}
3343 }
3344
3345
3346 #pragma mark - Functional Preset NSOutlineView Methods
3347
3348 - (IBAction)selectPreset:(id)sender
3349 {
3350     
3351     if ([fPresetsOutlineView selectedRow] >= 0)
3352     {
3353         chosenPreset = [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]];
3354         /* we set the preset display field in main window here */
3355         [fPresetSelectedDisplay setStringValue: [NSString stringWithFormat: @"%@",[chosenPreset valueForKey:@"PresetName"]]];
3356         if ([[chosenPreset objectForKey:@"Default"] intValue] == 1)
3357         {
3358             [fPresetSelectedDisplay setStringValue: [NSString stringWithFormat: @"%@ (Default)",[chosenPreset valueForKey:@"PresetName"]]];
3359         }
3360         else
3361         {
3362             [fPresetSelectedDisplay setStringValue: [NSString stringWithFormat: @"%@",[chosenPreset valueForKey:@"PresetName"]]];
3363         }
3364         /* File Format */
3365         [fDstFormatPopUp selectItemWithTitle: [NSString stringWithFormat:[chosenPreset valueForKey:@"FileFormat"]]];
3366         [self formatPopUpChanged: NULL];
3367         
3368         /* Chapter Markers*/
3369         [fCreateChapterMarkers setState:[[chosenPreset objectForKey:@"ChapterMarkers"] intValue]];
3370         /* Allow Mpeg4 64 bit formatting +4GB file sizes */
3371         [fDstMp4LargeFileCheck setState:[[chosenPreset objectForKey:@"Mp4LargeFile"] intValue]];
3372         /* Mux mp4 with http optimization */
3373         [fDstMp4HttpOptFileCheck setState:[[chosenPreset objectForKey:@"Mp4HttpOptimize"] intValue]];
3374         /* Set the state of ipod compatible with Mp4iPodCompatible */
3375         [fDstMp4iPodFileCheck setState:[[chosenPreset objectForKey:@"Mp4iPodCompatible"] intValue]];
3376         /* Codecs */
3377         [fDstCodecsPopUp selectItemWithTitle: [NSString stringWithFormat:[chosenPreset valueForKey:@"FileCodecs"]]];
3378         [self codecsPopUpChanged: NULL];
3379         
3380         /* Video encoder */
3381         /* We set the advanced opt string here if applicable*/
3382         [fAdvancedOptions setOptions: [NSString stringWithFormat:[chosenPreset valueForKey:@"x264Option"]]];
3383         /* We use a conditional to account for the new x264 encoder dropdown as well as presets made using legacy x264 settings*/
3384         if ([[NSString stringWithFormat:[chosenPreset valueForKey:@"VideoEncoder"]] isEqualToString: @"x264 (h.264 Main)"] || [[NSString stringWithFormat:[chosenPreset valueForKey:@"VideoEncoder"]] isEqualToString: @"x264 (h.264 iPod)"])
3385         {
3386             [fVidEncoderPopUp selectItemWithTitle: [NSString stringWithFormat:@"x264"]];
3387             /* special case for legacy preset to check the new fDstMp4HttpOptFileCheck checkbox to set the ipod atom */
3388             if ([[NSString stringWithFormat:[chosenPreset valueForKey:@"VideoEncoder"]] isEqualToString: @"x264 (h.264 iPod)"])
3389             {
3390                 [fDstMp4iPodFileCheck setState:NSOnState];
3391                 /* We also need to add "level=30:" to the advanced opts string to set the correct level for the iPod when
3392                  encountering a legacy preset as it used to be handled separately from the opt string*/
3393                 [fAdvancedOptions setOptions: [NSString stringWithFormat:[@"level=30:" stringByAppendingString:[fAdvancedOptions optionsString]]]];
3394             }
3395             else
3396             {
3397                 [fDstMp4iPodFileCheck setState:NSOffState];
3398             }
3399         }
3400         else
3401         {
3402             [fVidEncoderPopUp selectItemWithTitle: [NSString stringWithFormat:[chosenPreset valueForKey:@"VideoEncoder"]]];
3403         }
3404         
3405         /* Lets run through the following functions to get variables set there */
3406         [self encoderPopUpChanged: NULL];
3407         
3408         [self calculateBitrate: NULL];
3409         
3410         /* Video quality */
3411         [fVidQualityMatrix selectCellAtRow:[[chosenPreset objectForKey:@"VideoQualityType"] intValue] column:0];
3412         
3413         [fVidTargetSizeField setStringValue: [NSString stringWithFormat:[chosenPreset valueForKey:@"VideoTargetSize"]]];
3414         [fVidBitrateField setStringValue: [NSString stringWithFormat:[chosenPreset valueForKey:@"VideoAvgBitrate"]]];
3415         [fVidQualitySlider setFloatValue: [[chosenPreset valueForKey:@"VideoQualitySlider"] floatValue]];
3416         
3417         [self videoMatrixChanged: NULL];
3418         
3419         /* Video framerate */
3420         /* For video preset video framerate, we want to make sure that Same as source does not conflict with the
3421          detected framerate in the fVidRatePopUp so we use index 0*/
3422         if ([[NSString stringWithFormat:[chosenPreset valueForKey:@"VideoFramerate"]] isEqualToString: @"Same as source"])
3423         {
3424             [fVidRatePopUp selectItemAtIndex: 0];
3425         }
3426         else
3427         {
3428             [fVidRatePopUp selectItemWithTitle: [NSString stringWithFormat:[chosenPreset valueForKey:@"VideoFramerate"]]];
3429         }
3430         
3431         /* GrayScale */
3432         [fVidGrayscaleCheck setState:[[chosenPreset objectForKey:@"VideoGrayScale"] intValue]];
3433         
3434         /* 2 Pass Encoding */
3435         [fVidTwoPassCheck setState:[[chosenPreset objectForKey:@"VideoTwoPass"] intValue]];
3436         [self twoPassCheckboxChanged: NULL];
3437         /* Turbo 1st pass for 2 Pass Encoding */
3438         [fVidTurboPassCheck setState:[[chosenPreset objectForKey:@"VideoTurboTwoPass"] intValue]];
3439         
3440         /*Audio*/
3441         
3442         /* Audio Sample Rate*/
3443         [fAudRatePopUp selectItemWithTitle: [NSString stringWithFormat:[chosenPreset valueForKey:@"AudioSampleRate"]]];
3444         /* Audio Bitrate Rate*/
3445         [fAudBitratePopUp selectItemWithTitle: [NSString stringWithFormat:[chosenPreset valueForKey:@"AudioBitRate"]]];
3446         /*Subtitles*/
3447         [fSubPopUp selectItemWithTitle: [NSString stringWithFormat:[chosenPreset valueForKey:@"Subtitles"]]];
3448         /* Forced Subtitles */
3449         [fSubForcedCheck setState:[[chosenPreset objectForKey:@"SubtitlesForced"] intValue]];
3450             
3451         /* Dynamic Range Control Slider */
3452         [fAudDrcSlider setFloatValue: [[chosenPreset valueForKey:@"AudioDRCSlider"] floatValue]];
3453         [self audioDRCSliderChanged: NULL];
3454         
3455         /* Picture Settings */
3456         /* Note: objectForKey:@"UsesPictureSettings" now refers to picture size, this encompasses:
3457          * height, width, keep ar, anamorphic and crop settings.
3458          * picture filters are now handled separately.
3459          * We will be able to actually change the key names for legacy preset keys when preset file
3460          * update code is done. But for now, lets hang onto the old legacy key name for backwards compatibility.
3461          */
3462         /* Check to see if the objectForKey:@"UsesPictureSettings is greater than 0, as 0 means use picture sizing "None" 
3463          * and the preset completely ignores any picture sizing values in the preset.
3464          */
3465         if ([[chosenPreset objectForKey:@"UsesPictureSettings"]  intValue] > 0)
3466         {
3467             hb_job_t * job = fTitle->job;
3468             /* Check to see if the objectForKey:@"UsesPictureSettings is 2 which is "Use Max for the source */
3469             if ([[chosenPreset objectForKey:@"UsesPictureSettings"]  intValue] == 2 || [[chosenPreset objectForKey:@"UsesMaxPictureSettings"]  intValue] == 1)
3470             {
3471                 /* Use Max Picture settings for whatever the dvd is.*/
3472                 [self revertPictureSizeToMax: NULL];
3473                 job->keep_ratio = [[chosenPreset objectForKey:@"PictureKeepRatio"]  intValue];
3474                 if (job->keep_ratio == 1)
3475                 {
3476                     hb_fix_aspect( job, HB_KEEP_WIDTH );
3477                     if( job->height > fTitle->height )
3478                     {
3479                         job->height = fTitle->height;
3480                         hb_fix_aspect( job, HB_KEEP_HEIGHT );
3481                     }
3482                 }
3483                 job->pixel_ratio = [[chosenPreset objectForKey:@"PicturePAR"]  intValue];
3484             }
3485             else // /* If not 0 or 2 we assume objectForKey:@"UsesPictureSettings is 1 which is "Use picture sizing from when the preset was set" */
3486             {
3487                 /* we check to make sure the presets width/height does not exceed the sources width/height */
3488                 if (fTitle->width < [[chosenPreset objectForKey:@"PictureWidth"]  intValue] || fTitle->height < [[chosenPreset objectForKey:@"PictureHeight"]  intValue])
3489                 {
3490                     /* if so, then we use the sources height and width to avoid scaling up */
3491                     job->width = fTitle->width;
3492                     job->height = fTitle->height;
3493                 }
3494                 else // source width/height is >= the preset height/width
3495                 {
3496                     /* we can go ahead and use the presets values for height and width */
3497                     job->width = [[chosenPreset objectForKey:@"PictureWidth"]  intValue];
3498                     job->height = [[chosenPreset objectForKey:@"PictureHeight"]  intValue];
3499                 }
3500                 job->keep_ratio = [[chosenPreset objectForKey:@"PictureKeepRatio"]  intValue];
3501                 if (job->keep_ratio == 1)
3502                 {
3503                     hb_fix_aspect( job, HB_KEEP_WIDTH );
3504                     if( job->height > fTitle->height )
3505                     {
3506                         job->height = fTitle->height;
3507                         hb_fix_aspect( job, HB_KEEP_HEIGHT );
3508                     }
3509                 }
3510                 job->pixel_ratio = [[chosenPreset objectForKey:@"PicturePAR"]  intValue];
3511                 
3512                 
3513                 /* If Cropping is set to custom, then recall all four crop values from
3514                  when the preset was created and apply them */
3515                 if ([[chosenPreset objectForKey:@"PictureAutoCrop"]  intValue] == 0)
3516                 {
3517                     [fPictureController setAutoCrop:NO];
3518                     
3519                     /* Here we use the custom crop values saved at the time the preset was saved */
3520                     job->crop[0] = [[chosenPreset objectForKey:@"PictureTopCrop"]  intValue];
3521                     job->crop[1] = [[chosenPreset objectForKey:@"PictureBottomCrop"]  intValue];
3522                     job->crop[2] = [[chosenPreset objectForKey:@"PictureLeftCrop"]  intValue];
3523                     job->crop[3] = [[chosenPreset objectForKey:@"PictureRightCrop"]  intValue];
3524                     
3525                 }
3526                 else /* if auto crop has been saved in preset, set to auto and use post scan auto crop */
3527                 {
3528                     [fPictureController setAutoCrop:YES];
3529                     /* Here we use the auto crop values determined right after scan */
3530                     job->crop[0] = AutoCropTop;
3531                     job->crop[1] = AutoCropBottom;
3532                     job->crop[2] = AutoCropLeft;
3533                     job->crop[3] = AutoCropRight;
3534                     
3535                 }
3536                 /* If the preset has no objectForKey:@"UsesPictureFilters", then we know it is a legacy preset
3537                  * and handle the filters here as before.
3538                  * NOTE: This should be removed when the update presets code is done as we can be assured that legacy
3539                  * presets are updated to work properly with new keys.
3540                  */
3541                 if (![chosenPreset objectForKey:@"UsesPictureFilters"])
3542                 {
3543                     /* Filters */
3544                     /* Deinterlace */
3545                     if ([chosenPreset objectForKey:@"PictureDeinterlace"])
3546                     {
3547                         /* We check to see if the preset used the past fourth "Slowest" deinterlaceing and set that to "Slower
3548                         * since we no longer have a fourth "Slowest" deinterlacing due to the mcdeint bug */
3549                         if ([[chosenPreset objectForKey:@"PictureDeinterlace"] intValue] == 4)
3550                         {
3551                             [fPictureController setDeinterlace:3];
3552                         }
3553                         else
3554                         {
3555
3556                             [fPictureController setDeinterlace:[[chosenPreset objectForKey:@"PictureDeinterlace"] intValue]];
3557                         }
3558                     }
3559                     else
3560                     {
3561                         [fPictureController setDeinterlace:0];
3562                     }
3563                     /* VFR */
3564                     if ([[chosenPreset objectForKey:@"VFR"] intValue] == 1)
3565                     {
3566                         [fPictureController setVFR:[[chosenPreset objectForKey:@"VFR"] intValue]];
3567                     }
3568                     else
3569                     {
3570                         [fPictureController setVFR:0];
3571                     }
3572                     /* Detelecine */
3573                     if ([[chosenPreset objectForKey:@"PictureDetelecine"] intValue] == 1)
3574                     {
3575                         [fPictureController setDetelecine:[[chosenPreset objectForKey:@"PictureDetelecine"] intValue]];
3576                     }
3577                     else
3578                     {
3579                         [fPictureController setDetelecine:0];
3580                     }
3581                     /* Denoise */
3582                     if ([chosenPreset objectForKey:@"PictureDenoise"])
3583                     {
3584                         [fPictureController setDenoise:[[chosenPreset objectForKey:@"PictureDenoise"] intValue]];
3585                     }
3586                     else
3587                     {
3588                         [fPictureController setDenoise:0];
3589                     }   
3590                     /* Deblock */
3591                     if ([[chosenPreset objectForKey:@"PictureDeblock"] intValue] == 1)
3592                     {
3593                         [fPictureController setDeblock:[[chosenPreset objectForKey:@"PictureDeblock"] intValue]];
3594                     }
3595                     else
3596                     {
3597                         [fPictureController setDeblock:0];
3598                     }             
3599                     [self calculatePictureSizing: NULL];
3600                 }
3601                 
3602             }
3603             
3604             
3605         }
3606         /* If the preset has an objectForKey:@"UsesPictureFilters", then we know it is a newer style filters preset
3607          * and handle the filters here depending on whether or not the preset specifies applying the filter.
3608          */
3609         if ([chosenPreset objectForKey:@"UsesPictureFilters"] && [[chosenPreset objectForKey:@"UsesPictureFilters"]  intValue] > 0)
3610         {
3611             /* Filters */
3612             /* Deinterlace */
3613             if ([chosenPreset objectForKey:@"PictureDeinterlace"])
3614             {
3615                 /* We check to see if the preset used the past fourth "Slowest" deinterlaceing and set that to "Slower
3616                  * since we no longer have a fourth "Slowest" deinterlacing due to the mcdeint bug */
3617                 if ([[chosenPreset objectForKey:@"PictureDeinterlace"] intValue] == 4)
3618                 {
3619                     [fPictureController setDeinterlace:3];
3620                 }
3621                 else
3622                 {
3623                     [fPictureController setDeinterlace:[[chosenPreset objectForKey:@"PictureDeinterlace"] intValue]];
3624                 }
3625             }
3626             else
3627             {
3628                 [fPictureController setDeinterlace:0];
3629             }
3630             /* VFR */
3631             if ([[chosenPreset objectForKey:@"VFR"] intValue] == 1)
3632             {
3633                 [fPictureController setVFR:[[chosenPreset objectForKey:@"VFR"] intValue]];
3634             }
3635             else
3636             {
3637                 [fPictureController setVFR:0];
3638             }
3639             /* Detelecine */
3640             if ([[chosenPreset objectForKey:@"PictureDetelecine"] intValue] == 1)
3641             {
3642                 [fPictureController setDetelecine:[[chosenPreset objectForKey:@"PictureDetelecine"] intValue]];
3643             }
3644             else
3645             {
3646                 [fPictureController setDetelecine:0];
3647             }
3648             /* Denoise */
3649             if ([chosenPreset objectForKey:@"PictureDenoise"])
3650             {
3651                 [fPictureController setDenoise:[[chosenPreset objectForKey:@"PictureDenoise"] intValue]];
3652             }
3653             else
3654             {
3655                 [fPictureController setDenoise:0];
3656             }   
3657             /* Deblock */
3658             if ([[chosenPreset objectForKey:@"PictureDeblock"] intValue] == 1)
3659             {
3660                 [fPictureController setDeblock:[[chosenPreset objectForKey:@"PictureDeblock"] intValue]];
3661             }
3662             else
3663             {
3664                 [fPictureController setDeblock:0];
3665             }             
3666         }
3667         [self calculatePictureSizing: NULL];
3668         [[fPresetsActionMenu itemAtIndex:0] setEnabled: YES];
3669     }
3670 }
3671
3672
3673 #pragma mark -
3674 #pragma mark Manage Presets
3675
3676 - (void) loadPresets {
3677         /* We declare the default NSFileManager into fileManager */
3678         NSFileManager * fileManager = [NSFileManager defaultManager];
3679         /*We define the location of the user presets file */
3680     UserPresetsFile = @"~/Library/Application Support/HandBrake/UserPresets.plist";
3681         UserPresetsFile = [[UserPresetsFile stringByExpandingTildeInPath]retain];
3682     /* We check for the presets.plist */
3683         if ([fileManager fileExistsAtPath:UserPresetsFile] == 0) 
3684         {
3685                 [fileManager createFileAtPath:UserPresetsFile contents:nil attributes:nil];
3686         }
3687                 
3688         UserPresets = [[NSMutableArray alloc] initWithContentsOfFile:UserPresetsFile];
3689         if (nil == UserPresets) 
3690         {
3691                 UserPresets = [[NSMutableArray alloc] init];
3692                 [self addFactoryPresets:NULL];
3693         }
3694         [fPresetsOutlineView reloadData];
3695 }
3696
3697
3698 - (IBAction) showAddPresetPanel: (id) sender
3699 {
3700     /* Deselect the currently selected Preset if there is one*/
3701     [fPresetsOutlineView deselectRow:[fPresetsOutlineView selectedRow]];
3702
3703     /* Populate the preset picture settings popup here */
3704     [fPresetNewPicSettingsPopUp removeAllItems];
3705     [fPresetNewPicSettingsPopUp addItemWithTitle:@"None"];
3706     [fPresetNewPicSettingsPopUp addItemWithTitle:@"Current"];
3707     [fPresetNewPicSettingsPopUp addItemWithTitle:@"Source Maximum (post source scan)"];
3708     [fPresetNewPicSettingsPopUp selectItemAtIndex: 0];  
3709     /* Uncheck the preset use filters checkbox */
3710     [fPresetNewPicFiltersCheck setState:NSOffState];
3711     /* Erase info from the input fields*/
3712         [fPresetNewName setStringValue: @""];
3713         [fPresetNewDesc setStringValue: @""];
3714         /* Show the panel */
3715         [NSApp beginSheet: fAddPresetPanel modalForWindow: fWindow modalDelegate: NULL didEndSelector: NULL contextInfo: NULL];
3716 }
3717
3718 - (IBAction) closeAddPresetPanel: (id) sender
3719 {
3720     [NSApp endSheet: fAddPresetPanel];
3721     [fAddPresetPanel orderOut: self];
3722 }
3723
3724 - (IBAction)addUserPreset:(id)sender
3725 {
3726     if (![[fPresetNewName stringValue] length])
3727             NSRunAlertPanel(@"Warning!", @"You need to insert a name for the preset.", @"OK", nil , nil);
3728     else
3729     {
3730         /* Here we create a custom user preset */
3731         [UserPresets addObject:[self createPreset]];
3732         [self addPreset];
3733         
3734         [self closeAddPresetPanel:NULL];
3735     }
3736 }
3737 - (void)addPreset
3738 {
3739
3740         [self sortPresets];
3741         /* We Reload the New Table data for presets */
3742     [fPresetsOutlineView reloadData];
3743    /* We save all of the preset data here */
3744     [self savePreset];
3745 }
3746
3747 - (void)sortPresets
3748 {
3749
3750         
3751         /* We Sort the Presets By Factory or Custom */
3752         NSSortDescriptor * presetTypeDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"Type" 
3753                                                     ascending:YES] autorelease];
3754         /* We Sort the Presets Alphabetically by name */
3755         NSSortDescriptor * presetNameDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"PresetName" 
3756                                                     ascending:YES selector:@selector(caseInsensitiveCompare:)] autorelease];
3757         NSArray *sortDescriptors=[NSArray arrayWithObjects:presetTypeDescriptor,presetNameDescriptor,nil];
3758         NSArray *sortedArray=[UserPresets sortedArrayUsingDescriptors:sortDescriptors];
3759         [UserPresets setArray:sortedArray];
3760         
3761
3762 }
3763
3764 - (IBAction)insertPreset:(id)sender
3765 {
3766     int index = [fPresetsOutlineView selectedRow];
3767     [UserPresets insertObject:[self createPreset] atIndex:index];
3768     [fPresetsOutlineView reloadData];
3769     [self savePreset];
3770 }
3771
3772 - (NSDictionary *)createPreset
3773 {
3774     NSMutableDictionary *preset = [[NSMutableDictionary alloc] init];
3775         /* Get the New Preset Name from the field in the AddPresetPanel */
3776     [preset setObject:[fPresetNewName stringValue] forKey:@"PresetName"];
3777         /*Set whether or not this is a user preset or factory 0 is factory, 1 is user*/
3778         [preset setObject:[NSNumber numberWithInt:1] forKey:@"Type"];
3779         /*Set whether or not this is default, at creation set to 0*/
3780         [preset setObject:[NSNumber numberWithInt:0] forKey:@"Default"];
3781         /*Get the whether or not to apply pic Size and Cropping (includes Anamorphic)*/
3782         [preset setObject:[NSNumber numberWithInt:[fPresetNewPicSettingsPopUp indexOfSelectedItem]] forKey:@"UsesPictureSettings"];
3783     /* Get whether or not to use the current Picture Filter settings for the preset */
3784     [preset setObject:[NSNumber numberWithInt:[fPresetNewPicFiltersCheck state]] forKey:@"UsesPictureFilters"];
3785  
3786     /* Get New Preset Description from the field in the AddPresetPanel*/
3787         [preset setObject:[fPresetNewDesc stringValue] forKey:@"PresetDescription"];
3788         /* File Format */
3789     [preset setObject:[fDstFormatPopUp titleOfSelectedItem] forKey:@"FileFormat"];
3790         /* Chapter Markers fCreateChapterMarkers*/
3791         [preset setObject:[NSNumber numberWithInt:[fCreateChapterMarkers state]] forKey:@"ChapterMarkers"];
3792         /* Allow Mpeg4 64 bit formatting +4GB file sizes */
3793         [preset setObject:[NSNumber numberWithInt:[fDstMp4LargeFileCheck state]] forKey:@"Mp4LargeFile"];
3794     /* Mux mp4 with http optimization */
3795     [preset setObject:[NSNumber numberWithInt:[fDstMp4HttpOptFileCheck state]] forKey:@"Mp4HttpOptimize"];
3796     /* Add iPod uuid atom */
3797     [preset setObject:[NSNumber numberWithInt:[fDstMp4iPodFileCheck state]] forKey:@"Mp4iPodCompatible"];
3798     
3799     /* Codecs */
3800         [preset setObject:[fDstCodecsPopUp titleOfSelectedItem] forKey:@"FileCodecs"];
3801         /* Video encoder */
3802         [preset setObject:[fVidEncoderPopUp titleOfSelectedItem] forKey:@"VideoEncoder"];
3803         /* x264 Option String */
3804         [preset setObject:[fAdvancedOptions optionsString] forKey:@"x264Option"];
3805         
3806         [preset setObject:[NSNumber numberWithInt:[fVidQualityMatrix selectedRow]] forKey:@"VideoQualityType"];
3807         [preset setObject:[fVidTargetSizeField stringValue] forKey:@"VideoTargetSize"];
3808         [preset setObject:[fVidBitrateField stringValue] forKey:@"VideoAvgBitrate"];
3809         [preset setObject:[NSNumber numberWithFloat:[fVidQualitySlider floatValue]] forKey:@"VideoQualitySlider"];
3810         
3811         /* Video framerate */
3812     if ([fVidRatePopUp indexOfSelectedItem] == 0) // Same as source is selected
3813         {
3814     [preset setObject:[NSString stringWithFormat: @"Same as source"] forKey:@"VideoFramerate"];
3815     }
3816     else // we can record the actual titleOfSelectedItem
3817     {
3818     [preset setObject:[fVidRatePopUp titleOfSelectedItem] forKey:@"VideoFramerate"];
3819     }
3820         /* GrayScale */
3821         [preset setObject:[NSNumber numberWithInt:[fVidGrayscaleCheck state]] forKey:@"VideoGrayScale"];
3822         /* 2 Pass Encoding */
3823         [preset setObject:[NSNumber numberWithInt:[fVidTwoPassCheck state]] forKey:@"VideoTwoPass"];
3824         /* Turbo 2 pass Encoding fVidTurboPassCheck*/
3825         [preset setObject:[NSNumber numberWithInt:[fVidTurboPassCheck state]] forKey:@"VideoTurboTwoPass"];
3826         /*Picture Settings*/
3827         hb_job_t * job = fTitle->job;
3828         /* Picture Sizing */
3829         /* Use Max Picture settings for whatever the dvd is.*/
3830         [preset setObject:[NSNumber numberWithInt:0] forKey:@"UsesMaxPictureSettings"];
3831         [preset setObject:[NSNumber numberWithInt:fTitle->job->width] forKey:@"PictureWidth"];
3832         [preset setObject:[NSNumber numberWithInt:fTitle->job->height] forKey:@"PictureHeight"];
3833         [preset setObject:[NSNumber numberWithInt:fTitle->job->keep_ratio] forKey:@"PictureKeepRatio"];
3834         [preset setObject:[NSNumber numberWithInt:fTitle->job->pixel_ratio] forKey:@"PicturePAR"];
3835     
3836     /* Set crop settings here */
3837         [preset setObject:[NSNumber numberWithInt:[fPictureController autoCrop]] forKey:@"PictureAutoCrop"];
3838     [preset setObject:[NSNumber numberWithInt:job->crop[0]] forKey:@"PictureTopCrop"];
3839     [preset setObject:[NSNumber numberWithInt:job->crop[1]] forKey:@"PictureBottomCrop"];
3840         [preset setObject:[NSNumber numberWithInt:job->crop[2]] forKey:@"PictureLeftCrop"];
3841         [preset setObject:[NSNumber numberWithInt:job->crop[3]] forKey:@"PictureRightCrop"];
3842     
3843     /* Picture Filters */
3844     [preset setObject:[NSNumber numberWithInt:[fPictureController deinterlace]] forKey:@"PictureDeinterlace"];
3845         [preset setObject:[NSNumber numberWithInt:[fPictureController detelecine]] forKey:@"PictureDetelecine"];
3846     [preset setObject:[NSNumber numberWithInt:[fPictureController vfr]] forKey:@"VFR"];
3847         [preset setObject:[NSNumber numberWithInt:[fPictureController denoise]] forKey:@"PictureDenoise"];
3848     [preset setObject:[NSNumber numberWithInt:[fPictureController deblock]] forKey:@"PictureDeblock"];
3849     
3850
3851         
3852         /*Audio*/
3853         /* Audio Sample Rate*/
3854         [preset setObject:[fAudRatePopUp titleOfSelectedItem] forKey:@"AudioSampleRate"];
3855         /* Audio Bitrate Rate*/
3856         [preset setObject:[fAudBitratePopUp titleOfSelectedItem] forKey:@"AudioBitRate"];
3857         /* Subtitles*/
3858         [preset setObject:[fSubPopUp titleOfSelectedItem] forKey:@"Subtitles"];
3859     /* Forced Subtitles */
3860         [preset setObject:[NSNumber numberWithInt:[fSubForcedCheck state]] forKey:@"SubtitlesForced"];
3861     /* Dynamic Range Control Slider */
3862     [preset setObject:[NSNumber numberWithFloat:[fAudDrcSlider floatValue]] forKey:@"AudioDRCSlider"];
3863         
3864
3865     [preset autorelease];
3866     return preset;
3867
3868 }
3869
3870 - (void)savePreset
3871 {
3872     [UserPresets writeToFile:UserPresetsFile atomically:YES];
3873         /* We get the default preset in case it changed */
3874         [self getDefaultPresets: NULL];
3875
3876 }
3877
3878 - (IBAction)deletePreset:(id)sender
3879 {
3880     int status;
3881     NSEnumerator *enumerator;
3882     NSNumber *index;
3883     NSMutableArray *tempArray;
3884     id tempObject;
3885     
3886     if ( [fPresetsOutlineView numberOfSelectedRows] == 0 )
3887         return;
3888     /* Alert user before deleting preset */
3889         /* Comment out for now, tie to user pref eventually */
3890
3891     //NSBeep();
3892     status = NSRunAlertPanel(@"Warning!", @"Are you sure that you want to delete the selected preset?", @"OK", @"Cancel", nil);
3893     
3894     if ( status == NSAlertDefaultReturn ) {
3895         enumerator = [fPresetsOutlineView selectedRowEnumerator];
3896         tempArray = [NSMutableArray array];
3897         
3898         while ( (index = [enumerator nextObject]) ) {
3899             tempObject = [UserPresets objectAtIndex:[index intValue]];
3900             [tempArray addObject:tempObject];
3901         }
3902         
3903         [UserPresets removeObjectsInArray:tempArray];
3904         [fPresetsOutlineView reloadData];
3905         [self savePreset];   
3906     }
3907 }
3908
3909 #pragma mark -
3910 #pragma mark Manage Default Preset
3911
3912 - (IBAction)getDefaultPresets:(id)sender
3913 {
3914         int i = 0;
3915     NSEnumerator *enumerator = [UserPresets objectEnumerator];
3916         id tempObject;
3917         while (tempObject = [enumerator nextObject])
3918         {
3919                 NSDictionary *thisPresetDict = tempObject;
3920                 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 1) // 1 is HB default
3921                 {
3922                         presetHbDefault = i;    
3923                 }
3924                 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 2) // 2 is User specified default
3925                 {
3926                         presetUserDefault = i;  
3927                 }
3928                 i++;
3929         }
3930 }
3931
3932 - (IBAction)setDefaultPreset:(id)sender
3933 {
3934     int i = 0;
3935     NSEnumerator *enumerator = [UserPresets objectEnumerator];
3936         id tempObject;
3937         /* First make sure the old user specified default preset is removed */
3938         while (tempObject = [enumerator nextObject])
3939         {
3940                 /* make sure we are not removing the default HB preset */
3941                 if ([[[UserPresets objectAtIndex:i] objectForKey:@"Default"] intValue] != 1) // 1 is HB default
3942                 {
3943                         [[UserPresets objectAtIndex:i] setObject:[NSNumber numberWithInt:0] forKey:@"Default"];
3944                 }
3945                 i++;
3946         }
3947         /* Second, go ahead and set the appropriate user specfied preset */
3948         /* we get the chosen preset from the UserPresets array */
3949         if ([[[UserPresets objectAtIndex:[fPresetsOutlineView selectedRow]] objectForKey:@"Default"] intValue] != 1) // 1 is HB default
3950         {
3951                 [[UserPresets objectAtIndex:[fPresetsOutlineView selectedRow]] setObject:[NSNumber numberWithInt:2] forKey:@"Default"];
3952         }
3953         /*FIX ME: I think we now need to use the items not rows in NSOutlineView */
3954     presetUserDefault = [fPresetsOutlineView selectedRow];
3955         
3956         /* We save all of the preset data here */
3957     [self savePreset];
3958         /* We Reload the New Table data for presets */
3959     [fPresetsOutlineView reloadData];
3960 }
3961
3962 - (IBAction)selectDefaultPreset:(id)sender
3963 {
3964         /* if there is a user specified default, we use it */
3965         if (presetUserDefault)
3966         {
3967         [fPresetsOutlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:presetUserDefault] byExtendingSelection:NO];
3968         [self selectPreset:NULL];
3969         }
3970         else if (presetHbDefault) //else we use the built in default presetHbDefault
3971         {
3972         [fPresetsOutlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:presetHbDefault] byExtendingSelection:NO];
3973         [self selectPreset:NULL];
3974         }
3975 }
3976
3977
3978 #pragma mark -
3979 #pragma mark Manage Built In Presets
3980
3981
3982 - (IBAction)deleteFactoryPresets:(id)sender
3983 {
3984     //int status;
3985     NSEnumerator *enumerator = [UserPresets objectEnumerator];
3986         id tempObject;
3987     
3988         //NSNumber *index;
3989     NSMutableArray *tempArray;
3990
3991
3992         tempArray = [NSMutableArray array];
3993         /* we look here to see if the preset is we move on to the next one */
3994         while ( tempObject = [enumerator nextObject] )  
3995                 {
3996                         /* if the preset is "Factory" then we put it in the array of
3997                         presets to delete */
3998                         if ([[tempObject objectForKey:@"Type"] intValue] == 0)
3999                         {
4000                                 [tempArray addObject:tempObject];
4001                         }
4002         }
4003         
4004         [UserPresets removeObjectsInArray:tempArray];
4005         [fPresetsOutlineView reloadData];
4006         [self savePreset];   
4007
4008 }
4009
4010    /* We use this method to recreate new, updated factory
4011    presets */
4012 - (IBAction)addFactoryPresets:(id)sender
4013 {
4014    
4015    /* First, we delete any existing built in presets */
4016     [self deleteFactoryPresets: sender];
4017     /* Then we generate new built in presets programmatically with fPresetsBuiltin
4018     * which is all setup in HBPresets.h and  HBPresets.m*/
4019     [fPresetsBuiltin generateBuiltinPresets:UserPresets];
4020
4021     [self addPreset];
4022 }
4023
4024
4025
4026
4027
4028
4029
4030
4031 @end