OSDN Git Service

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