OSDN Git Service

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