OSDN Git Service

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