OSDN Git Service

0685d13b93d700ca6348e7e286c3b058cc6da219
[handbrake-jp/handbrake-jp-git.git] / macosx / Controller.m
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.fr/>.
5    It may be used under the terms of the GNU General Public License. */
6
7 #import "Controller.h"
8 #import "HBOutputPanelController.h"
9 #import "HBPreferencesController.h"
10 #import "HBDVDDetector.h"
11 #import "HBPresets.h"
12 #import "HBPreviewController.h"
13
14 #define DragDropSimplePboardType        @"MyCustomOutlineViewPboardType"
15
16 /* We setup the toolbar values here ShowPreviewIdentifier */
17 static NSString *        ToggleDrawerIdentifier             = @"Toggle Drawer Item Identifier";
18 static NSString *        StartEncodingIdentifier            = @"Start Encoding Item Identifier";
19 static NSString *        PauseEncodingIdentifier            = @"Pause Encoding Item Identifier";
20 static NSString *        ShowQueueIdentifier                = @"Show Queue Item Identifier";
21 static NSString *        AddToQueueIdentifier               = @"Add to Queue Item Identifier";
22 static NSString *        ShowPictureIdentifier             = @"Show Picture Window Item Identifier";
23 static NSString *        ShowPreviewIdentifier             = @"Show Preview Window Item Identifier";
24 static NSString *        ShowActivityIdentifier             = @"Debug Output Item Identifier";
25 static NSString *        ChooseSourceIdentifier             = @"Choose Source Item Identifier";
26
27
28 /*******************************
29  * HBController implementation *
30  *******************************/
31 @implementation HBController
32
33 - (id)init
34 {
35     self = [super init];
36     if( !self )
37     {
38         return nil;
39     }
40
41     /* replace bundled app icon with one which is 32/64-bit savvy */
42 #if defined( __LP64__ )
43     fApplicationIcon = [[NSImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForImageResource:@"HandBrake-64.icns"]];
44 #else
45     fApplicationIcon = [[NSImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForImageResource:@"HandBrake.icns"]];
46 #endif
47     if( fApplicationIcon != nil )
48         [NSApp setApplicationIconImage:fApplicationIcon];
49     
50     [HBPreferencesController registerUserDefaults];
51     fHandle = NULL;
52     fQueueEncodeLibhb = NULL;
53     /* Check for check for the app support directory here as
54      * outputPanel needs it right away, as may other future methods
55      */
56     NSString *libraryDir = [NSSearchPathForDirectoriesInDomains( NSLibraryDirectory,
57                                                                 NSUserDomainMask,
58                                                                 YES ) objectAtIndex:0];
59     AppSupportDirectory = [[libraryDir stringByAppendingPathComponent:@"Application Support"]
60                            stringByAppendingPathComponent:@"HandBrake"];
61     if( ![[NSFileManager defaultManager] fileExistsAtPath:AppSupportDirectory] )
62     {
63         [[NSFileManager defaultManager] createDirectoryAtPath:AppSupportDirectory
64                                                    attributes:nil];
65     }
66     /* Check for and create the App Support Preview directory if necessary */
67     NSString *PreviewDirectory = [AppSupportDirectory stringByAppendingPathComponent:@"Previews"];
68     if( ![[NSFileManager defaultManager] fileExistsAtPath:PreviewDirectory] )
69     {
70         [[NSFileManager defaultManager] createDirectoryAtPath:PreviewDirectory
71                                                    attributes:nil];
72     }                                                            
73     outputPanel = [[HBOutputPanelController alloc] init];
74     fPictureController = [[PictureController alloc] init];
75     fQueueController = [[HBQueueController alloc] init];
76     fAdvancedOptions = [[HBAdvancedController alloc] init];
77     /* we init the HBPresets class which currently is only used
78      * for updating built in presets, may move more functionality
79      * there in the future
80      */
81     fPresetsBuiltin = [[HBPresets alloc] init];
82     fPreferencesController = [[HBPreferencesController alloc] init];
83     /* Lets report the HandBrake version number here to the activity log and text log file */
84     NSString *versionStringFull = [[NSString stringWithFormat: @"Handbrake Version: %@", [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"]] stringByAppendingString: [NSString stringWithFormat: @" (%@)", [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"]]];
85     [self writeToActivityLog: "%s", [versionStringFull UTF8String]];    
86     
87     return self;
88 }
89
90
91 - (void) applicationDidFinishLaunching: (NSNotification *) notification
92 {
93     /* Init libhb with check for updates libhb style set to "0" so its ignored and lets sparkle take care of it */
94     int loggingLevel = [[[NSUserDefaults standardUserDefaults] objectForKey:@"LoggingLevel"] intValue];
95     fHandle = hb_init(loggingLevel, 0);
96     /* Optional dvd nav UseDvdNav*/
97     hb_dvd_set_dvdnav([[[NSUserDefaults standardUserDefaults] objectForKey:@"UseDvdNav"] boolValue]);
98     /* Init a separate instance of libhb for user scanning and setting up jobs */
99     fQueueEncodeLibhb = hb_init(loggingLevel, 0);
100     
101         // Set the Growl Delegate
102     [GrowlApplicationBridge setGrowlDelegate: self];
103     /* Init others controllers */
104     [fPictureController SetHandle: fHandle];
105     [fPictureController   setHBController: self];
106     
107     [fQueueController   setHandle: fQueueEncodeLibhb];
108     [fQueueController   setHBController: self];
109
110     fChapterTitlesDelegate = [[ChapterTitles alloc] init];
111     [fChapterTable setDataSource:fChapterTitlesDelegate];
112     [fChapterTable setDelegate:fChapterTitlesDelegate];
113     
114     /* setup the subtitles delegate and connections to table */
115     fSubtitlesDelegate = [[HBSubtitles alloc] init];
116     [fSubtitlesTable setDataSource:fSubtitlesDelegate];
117     [fSubtitlesTable setDelegate:fSubtitlesDelegate];
118     [fSubtitlesTable setRowHeight:25.0];
119     
120     [fPresetsOutlineView setAutosaveName:@"Presets View"];
121     [fPresetsOutlineView setAutosaveExpandedItems:YES];
122     
123     dockIconProgress = 0;
124
125     /* Call UpdateUI every 1/2 sec */
126     [[NSRunLoop currentRunLoop] addTimer:[NSTimer
127                                           scheduledTimerWithTimeInterval:0.5 target:self
128                                           selector:@selector(updateUI:) userInfo:nil repeats:YES]
129                                  forMode:NSDefaultRunLoopMode];
130
131     // Open debug output window now if it was visible when HB was closed
132     if ([[NSUserDefaults standardUserDefaults] boolForKey:@"OutputPanelIsOpen"])
133         [self showDebugOutputPanel:nil];
134
135     // Open queue window now if it was visible when HB was closed
136     if ([[NSUserDefaults standardUserDefaults] boolForKey:@"QueueWindowIsOpen"])
137         [self showQueueWindow:nil];
138
139         [self openMainWindow:nil];
140     
141     /* We have to set the bool to tell hb what to do after a scan
142      * Initially we set it to NO until we start processing the queue
143      */
144      applyQueueToScan = NO;
145     
146     /* Now we re-check the queue array to see if there are
147      * any remaining encodes to be done in it and ask the
148      * user if they want to reload the queue */
149     if ([QueueFileArray count] > 0)
150         {
151         /* run  getQueueStats to see whats in the queue file */
152         [self getQueueStats];
153         /* this results in these values
154          * fEncodingQueueItem = 0;
155          * fPendingCount = 0;
156          * fCompletedCount = 0;
157          * fCanceledCount = 0;
158          * fWorkingCount = 0;
159          */
160         
161         /*On Screen Notification*/
162         NSString * alertTitle;
163         
164         /* We check to see if there is already another instance of hb running.
165          * Note: hbInstances == 1 means we are the only instance of HandBrake.app
166          */
167         if ([self hbInstances] > 1)
168         {
169         alertTitle = [NSString stringWithFormat:
170                          NSLocalizedString(@"There is already an instance of HandBrake running.", @"")];
171         NSBeginCriticalAlertSheet(
172                                       alertTitle,
173                                       NSLocalizedString(@"Reload Queue", nil),
174                                       nil,
175                                       nil,
176                                       fWindow, self,
177                                       nil, @selector(didDimissReloadQueue:returnCode:contextInfo:), nil,
178                                       NSLocalizedString(@" HandBrake will now load up the existing queue.", nil));    
179         }
180         else
181         {
182             if (fWorkingCount > 0)
183             {
184                 alertTitle = [NSString stringWithFormat:
185                               NSLocalizedString(@"HandBrake Has Detected %d Previously Encoding Item and %d Pending Item(s) In Your Queue.", @""),
186                               fWorkingCount,fPendingCount];
187             }
188             else
189             {
190                 alertTitle = [NSString stringWithFormat:
191                               NSLocalizedString(@"HandBrake Has Detected %d Pending Item(s) In Your Queue.", @""),
192                               fPendingCount];
193             }
194             
195             NSBeginCriticalAlertSheet(
196                                       alertTitle,
197                                       NSLocalizedString(@"Reload Queue", nil),
198                                       nil,
199                                       NSLocalizedString(@"Empty Queue", nil),
200                                       fWindow, self,
201                                       nil, @selector(didDimissReloadQueue:returnCode:contextInfo:), nil,
202                                       NSLocalizedString(@" Do you want to reload them ?", nil));
203         }
204         
205         // call didDimissReloadQueue: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo
206         // right below to either clear the old queue or keep it loaded up.
207     }
208     else
209     {
210         /* We show whichever open source window specified in LaunchSourceBehavior preference key */
211         if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"LaunchSourceBehavior"] isEqualToString: @"Open Source"])
212         {
213             [self browseSources:nil];
214         }
215         
216         if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"LaunchSourceBehavior"] isEqualToString: @"Open Source (Title Specific)"])
217         {
218             [self browseSources:(id)fOpenSourceTitleMMenu];
219         }
220     }
221 }
222
223 - (int) hbInstances
224 {
225     /* check to see if another instance of HandBrake.app is running */
226     NSArray *runningAppDictionaries = [[NSWorkspace sharedWorkspace] launchedApplications];
227     NSDictionary *aDictionary;
228     int hbInstances = 0;
229     for (aDictionary in runningAppDictionaries)
230         {
231         //      NSLog(@"Open App: %@", [aDictionary valueForKey:@"NSApplicationName"]);
232         
233         if ([[aDictionary valueForKey:@"NSApplicationName"] isEqualToString:@"HandBrake"])
234                 {
235             hbInstances++;
236                 }
237         }
238     return hbInstances;
239 }
240
241 - (void) didDimissReloadQueue: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo
242 {
243     if (returnCode == NSAlertOtherReturn)
244     {
245         [self clearQueueAllItems];
246         /* We show whichever open source window specified in LaunchSourceBehavior preference key */
247         if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"LaunchSourceBehavior"] isEqualToString: @"Open Source"])
248         {
249             [self browseSources:nil];
250         }
251         
252         if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"LaunchSourceBehavior"] isEqualToString: @"Open Source (Title Specific)"])
253         {
254             [self browseSources:(id)fOpenSourceTitleMMenu];
255         }
256     }
257     else
258     {
259         if ([self hbInstances] == 1)
260         {
261             [self setQueueEncodingItemsAsPending];
262         }
263         [self showQueueWindow:NULL];
264     }
265 }
266
267 - (NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication *) app
268 {
269     /* if we are in preview full screen mode, we need to go to
270      * windowed mode and release the display before we terminate.
271      * We do it here (instead of applicationWillTerminate) so we 
272      * release the displays and can then see the alerts below.
273      */
274     if ([fPictureController previewFullScreenMode] == YES)
275     {
276         [fPictureController previewGoWindowed:nil];
277     }
278     
279     hb_state_t s;
280     hb_get_state( fQueueEncodeLibhb, &s );
281     
282     if ( s.state != HB_STATE_IDLE )
283     {
284         int result = NSRunCriticalAlertPanel(
285                                              NSLocalizedString(@"Are you sure you want to quit HandBrake?", nil),
286                                              NSLocalizedString(@"If you quit HandBrake your current encode will be reloaded into your queue at next launch. Do you want to quit anyway?", nil),
287                                              NSLocalizedString(@"Quit", nil), NSLocalizedString(@"Don't Quit", nil), nil, @"A movie" );
288         
289         if (result == NSAlertDefaultReturn)
290         {
291             return NSTerminateNow;
292         }
293         else
294             return NSTerminateCancel;
295     }
296     
297     // Warn if items still in the queue
298     else if ( fPendingCount > 0 )
299     {
300         int result = NSRunCriticalAlertPanel(
301                                              NSLocalizedString(@"Are you sure you want to quit HandBrake?", nil),
302                                              NSLocalizedString(@"There are pending encodes in your queue. Do you want to quit anyway?",nil),
303                                              NSLocalizedString(@"Quit", nil), NSLocalizedString(@"Don't Quit", nil), nil);
304         
305         if ( result == NSAlertDefaultReturn )
306             return NSTerminateNow;
307         else
308             return NSTerminateCancel;
309     }
310     
311     return NSTerminateNow;
312 }
313
314 - (void)applicationWillTerminate:(NSNotification *)aNotification
315 {
316     
317     [browsedSourceDisplayName release];
318     [outputPanel release];
319         [fQueueController release];
320     [fPreviewController release];
321     [fPictureController release];
322     [fApplicationIcon release];
323
324         hb_close(&fHandle);
325     hb_close(&fQueueEncodeLibhb);
326 }
327
328
329 - (void) awakeFromNib
330 {
331     [fWindow center];
332     [fWindow setExcludedFromWindowsMenu:YES];
333     [fAdvancedOptions setView:fAdvancedView];
334     
335     /* lets setup our presets drawer for drag and drop here */
336     [fPresetsOutlineView registerForDraggedTypes: [NSArray arrayWithObject:DragDropSimplePboardType] ];
337     [fPresetsOutlineView setDraggingSourceOperationMask:NSDragOperationEvery forLocal:YES];
338     [fPresetsOutlineView setVerticalMotionCanBeginDrag: YES];
339     
340     /* Initialize currentScanCount so HB can use it to
341      evaluate successive scans */
342         currentScanCount = 0;
343     
344     
345     /* Init UserPresets .plist */
346         [self loadPresets];
347     
348     /* Init QueueFile .plist */
349     [self loadQueueFile];
350         
351     fRipIndicatorShown = NO;  // initially out of view in the nib
352     
353     /* For 64 bit builds, the threaded animation in the progress
354      * indicators conflicts with the animation in the advanced tab
355      * for reasons not completely clear. jbrjake found a note in the
356      * 10.5 dev notes regarding this possiblility. It was also noted
357      * that unless specified, setUsesThreadedAnimation defaults to true.
358      * So, at least for now we set the indicator animation to NO for
359      * both the scan and regular progress indicators for both 32 and 64 bit
360      * as it test out fine on both and there is no reason our progress indicators
361      * should require their own thread.
362      */
363
364     [fScanIndicator setUsesThreadedAnimation:NO];
365     [fRipIndicator setUsesThreadedAnimation:NO];
366   
367     
368     
369         /* Show/Dont Show Presets drawer upon launch based
370      on user preference DefaultPresetsDrawerShow*/
371         if( [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultPresetsDrawerShow"] > 0 )
372         {
373         [fPresetDrawer setDelegate:self];
374         NSSize drawerSize = NSSizeFromString( [[NSUserDefaults standardUserDefaults] 
375                                                stringForKey:@"Drawer Size"] );
376         if( drawerSize.width )
377             [fPresetDrawer setContentSize: drawerSize];
378                 [fPresetDrawer open];
379         }
380     
381     /* Initially set the dvd angle widgets to hidden (dvdnav only) */
382     [fSrcAngleLabel setHidden:YES];
383     [fSrcAnglePopUp setHidden:YES];
384     
385     /* Setup the start / stop popup */
386     [fEncodeStartStopPopUp removeAllItems];
387     [fEncodeStartStopPopUp addItemWithTitle: @"Chapters"];
388     [fEncodeStartStopPopUp addItemWithTitle: @"Seconds"];
389     [fEncodeStartStopPopUp addItemWithTitle: @"Frames"];
390     /* Align the start / stop widgets with the chapter popups */
391     [fSrcTimeStartEncodingField setFrameOrigin:[fSrcChapterStartPopUp frame].origin];
392     [fSrcTimeEndEncodingField setFrameOrigin:[fSrcChapterEndPopUp frame].origin];
393     
394     [fSrcFrameStartEncodingField setFrameOrigin:[fSrcChapterStartPopUp frame].origin];
395     [fSrcFrameEndEncodingField setFrameOrigin:[fSrcChapterEndPopUp frame].origin];
396     
397     /* Destination box*/
398     NSMenuItem *menuItem;
399     [fDstFormatPopUp removeAllItems];
400     // MP4 file
401     menuItem = [[fDstFormatPopUp menu] addItemWithTitle:@"MP4 file" action: NULL keyEquivalent: @""];
402     [menuItem setTag: HB_MUX_MP4];
403         // MKV file
404     menuItem = [[fDstFormatPopUp menu] addItemWithTitle:@"MKV file" action: NULL keyEquivalent: @""];
405     [menuItem setTag: HB_MUX_MKV];
406     
407     [fDstFormatPopUp selectItemAtIndex: 0];
408     
409     [self formatPopUpChanged:nil];
410     
411         /* We enable the create chapters checkbox here since we are .mp4 */
412         [fCreateChapterMarkers setEnabled: YES];
413         if ([fDstFormatPopUp indexOfSelectedItem] == 0 && [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultChapterMarkers"] > 0)
414         {
415                 [fCreateChapterMarkers setState: NSOnState];
416         }
417     
418     
419     
420     
421     [fDstFile2Field setStringValue: [NSString stringWithFormat:
422                                      @"%@/Desktop/Movie.mp4", NSHomeDirectory()]];
423     
424     /* Video encoder */
425     [fVidEncoderPopUp removeAllItems];
426     [fVidEncoderPopUp addItemWithTitle: @"FFmpeg"];
427     
428     
429     /* Video quality */
430     [fVidTargetSizeField setIntValue: 700];
431         [fVidBitrateField    setIntValue: 1000];
432     
433     [fVidQualityMatrix   selectCell: fVidBitrateCell];
434     [self videoMatrixChanged:nil];
435     
436     /* Video framerate */
437     [fVidRatePopUp removeAllItems];
438         [fVidRatePopUp addItemWithTitle: NSLocalizedString( @"Same as source", @"" )];
439     for( int i = 0; i < hb_video_rates_count; i++ )
440     {
441         if ([[NSString stringWithUTF8String: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%.3f",23.976]])
442                 {
443                         [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@",
444                                              [NSString stringWithUTF8String: hb_video_rates[i].string], @" (NTSC Film)"]];
445                 }
446                 else if ([[NSString stringWithUTF8String: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%d",25]])
447                 {
448                         [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@",
449                                              [NSString stringWithUTF8String: hb_video_rates[i].string], @" (PAL Film/Video)"]];
450                 }
451                 else if ([[NSString stringWithUTF8String: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%.2f",29.97]])
452                 {
453                         [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@",
454                                              [NSString stringWithUTF8String: hb_video_rates[i].string], @" (NTSC Video)"]];
455                 }
456                 else
457                 {
458                         [fVidRatePopUp addItemWithTitle:
459              [NSString stringWithUTF8String: hb_video_rates[i].string]];
460                 }
461     }
462     [fVidRatePopUp selectItemAtIndex: 0];
463         
464         /* Set Auto Crop to On at launch */
465     [fPictureController setAutoCrop:YES];
466         
467         /* Audio bitrate */
468     [fAudTrack1BitratePopUp removeAllItems];
469     for( int i = 0; i < hb_audio_bitrates_count; i++ )
470     {
471         [fAudTrack1BitratePopUp addItemWithTitle:
472          [NSString stringWithUTF8String: hb_audio_bitrates[i].string]];
473         
474     }
475     [fAudTrack1BitratePopUp selectItemAtIndex: hb_audio_bitrates_default];
476         
477     /* Audio samplerate */
478     [fAudTrack1RatePopUp removeAllItems];
479     for( int i = 0; i < hb_audio_rates_count; i++ )
480     {
481         [fAudTrack1RatePopUp addItemWithTitle:
482          [NSString stringWithUTF8String: hb_audio_rates[i].string]];
483     }
484     [fAudTrack1RatePopUp selectItemAtIndex: hb_audio_rates_default];
485         
486     /* Bottom */
487     [fStatusField setStringValue: @""];
488     
489     [self enableUI: NO];
490         [self setupToolbar];
491     
492         /* We disable the Turbo 1st pass checkbox since we are not x264 */
493         [fVidTurboPassCheck setEnabled: NO];
494         [fVidTurboPassCheck setState: NSOffState];
495     
496     
497         /* lets get our default prefs here */
498         [self getDefaultPresets:nil];
499         /* lets initialize the current successful scancount here to 0 */
500         currentSuccessfulScanCount = 0;
501     
502     
503 }
504
505 - (void) enableUI: (bool) b
506 {
507     NSControl * controls[] =
508     { fSrcTitleField, fSrcTitlePopUp,
509         fSrcChapterField, fSrcChapterStartPopUp, fSrcChapterToField,
510         fSrcChapterEndPopUp, fSrcDuration1Field, fSrcDuration2Field,
511         fDstFormatField, fDstFormatPopUp, fDstFile1Field, fDstFile2Field,
512         fDstBrowseButton, fVidRateField, fVidRatePopUp,fVidEncoderField, fVidEncoderPopUp, fVidQualityField,
513         fPictureSizeField,fPictureCroppingField, fVideoFiltersField,fVidQualityMatrix, fSubField, fSubPopUp,
514         fAudSourceLabel, fAudCodecLabel, fAudMixdownLabel, fAudSamplerateLabel, fAudBitrateLabel,
515         fAudTrack1Label, fAudTrack2Label, fAudTrack3Label, fAudTrack4Label,
516         fAudLang1PopUp, fAudLang2PopUp, fAudLang3PopUp, fAudLang4PopUp,
517         fAudTrack1CodecPopUp, fAudTrack2CodecPopUp, fAudTrack3CodecPopUp, fAudTrack4CodecPopUp,
518         fAudTrack1MixPopUp, fAudTrack2MixPopUp, fAudTrack3MixPopUp, fAudTrack4MixPopUp,
519         fAudTrack1RatePopUp, fAudTrack2RatePopUp, fAudTrack3RatePopUp, fAudTrack4RatePopUp,
520         fAudTrack1BitratePopUp, fAudTrack2BitratePopUp, fAudTrack3BitratePopUp, fAudTrack4BitratePopUp,
521         fAudDrcLabel, fAudTrack1DrcSlider, fAudTrack1DrcField, fAudTrack2DrcSlider,
522         fAudTrack2DrcField, fAudTrack3DrcSlider, fAudTrack3DrcField, fAudTrack4DrcSlider,fAudTrack4DrcField,
523         fQueueStatus,fPresetsAdd,fPresetsDelete,fSrcAngleLabel,fSrcAnglePopUp,
524                 fCreateChapterMarkers,fVidTurboPassCheck,fDstMp4LargeFileCheck,fSubForcedCheck,fPresetsOutlineView,
525         fAudDrcLabel,fDstMp4HttpOptFileCheck,fDstMp4iPodFileCheck,fVidQualityRFField,fVidQualityRFLabel,
526         fEncodeStartStopPopUp,fSrcTimeStartEncodingField,fSrcTimeEndEncodingField,fSrcFrameStartEncodingField,fSrcFrameEndEncodingField};
527     
528     for( unsigned i = 0;
529         i < sizeof( controls ) / sizeof( NSControl * ); i++ )
530     {
531         if( [[controls[i] className] isEqualToString: @"NSTextField"] )
532         {
533             NSTextField * tf = (NSTextField *) controls[i];
534             if( ![tf isBezeled] )
535             {
536                 [tf setTextColor: b ? [NSColor controlTextColor] :
537                  [NSColor disabledControlTextColor]];
538                 continue;
539             }
540         }
541         [controls[i] setEnabled: b];
542         
543     }
544     
545         if (b) {
546         
547         /* if we're enabling the interface, check if the audio mixdown controls need to be enabled or not */
548         /* these will have been enabled by the mass control enablement above anyway, so we're sense-checking it here */
549         [self setEnabledStateOfAudioMixdownControls:nil];
550         /* we also call calculatePictureSizing here to sense check if we already have vfr selected */
551         [self calculatePictureSizing:nil];
552         
553         } else {
554         
555                 [fPresetsOutlineView setEnabled: NO];
556         
557         }
558     
559     [self videoMatrixChanged:nil];
560     [fAdvancedOptions enableUI:b];
561 }
562
563
564 /***********************************************************************
565  * UpdateDockIcon
566  ***********************************************************************
567  * Shows a progression bar on the dock icon, filled according to
568  * 'progress' (0.0 <= progress <= 1.0).
569  * Called with progress < 0.0 or progress > 1.0, restores the original
570  * icon.
571  **********************************************************************/
572 - (void) UpdateDockIcon: (float) progress
573 {
574     NSData * tiff;
575     NSBitmapImageRep * bmp;
576     uint32_t * pen;
577     uint32_t black = htonl( 0x000000FF );
578     uint32_t red   = htonl( 0xFF0000FF );
579     uint32_t white = htonl( 0xFFFFFFFF );
580     int row_start, row_end;
581     int i, j;
582
583     if( progress < 0.0 || progress > 1.0 )
584     {
585         [NSApp setApplicationIconImage: fApplicationIcon];
586         return;
587     }
588
589     /* Get it in a raw bitmap form */
590     tiff = [fApplicationIcon TIFFRepresentationUsingCompression:
591             NSTIFFCompressionNone factor: 1.0];
592     bmp = [NSBitmapImageRep imageRepWithData: tiff];
593     
594     /* Draw the progression bar */
595     /* It's pretty simple (ugly?) now, but I'm no designer */
596
597     row_start = 3 * (int) [bmp size].height / 4;
598     row_end   = 7 * (int) [bmp size].height / 8;
599
600     for( i = row_start; i < row_start + 2; i++ )
601     {
602         pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
603         for( j = 0; j < (int) [bmp size].width; j++ )
604         {
605             pen[j] = black;
606         }
607     }
608     for( i = row_start + 2; i < row_end - 2; i++ )
609     {
610         pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
611         pen[0] = black;
612         pen[1] = black;
613         for( j = 2; j < (int) [bmp size].width - 2; j++ )
614         {
615             if( j < 2 + (int) ( ( [bmp size].width - 4.0 ) * progress ) )
616             {
617                 pen[j] = red;
618             }
619             else
620             {
621                 pen[j] = white;
622             }
623         }
624         pen[j]   = black;
625         pen[j+1] = black;
626     }
627     for( i = row_end - 2; i < row_end; i++ )
628     {
629         pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
630         for( j = 0; j < (int) [bmp size].width; j++ )
631         {
632             pen[j] = black;
633         }
634     }
635
636     /* Now update the dock icon */
637     tiff = [bmp TIFFRepresentationUsingCompression:
638             NSTIFFCompressionNone factor: 1.0];
639     NSImage* icon = [[NSImage alloc] initWithData: tiff];
640     [NSApp setApplicationIconImage: icon];
641     [icon release];
642 }
643
644 - (void) updateUI: (NSTimer *) timer
645 {
646     
647     /* Update UI for fHandle (user scanning instance of libhb ) */
648     
649     hb_list_t  * list;
650     list = hb_get_titles( fHandle );
651     /* check to see if there has been a new scan done
652      this bypasses the constraints of HB_STATE_WORKING
653      not allowing setting a newly scanned source */
654         int checkScanCount = hb_get_scancount( fHandle );
655         if( checkScanCount > currentScanCount )
656         {
657                 currentScanCount = checkScanCount;
658         [fScanIndicator setIndeterminate: NO];
659         [fScanIndicator setDoubleValue: 0.0];
660         [fScanIndicator setHidden: YES];
661                 [self showNewScan:nil];
662         }
663     
664     hb_state_t s;
665     hb_get_state( fHandle, &s );
666     
667     switch( s.state )
668     {
669         case HB_STATE_IDLE:
670             break;
671 #define p s.param.scanning
672         case HB_STATE_SCANNING:
673                 {
674             [fSrcDVD2Field setStringValue: [NSString stringWithFormat:
675                                             NSLocalizedString( @"Scanning title %d of %d...", @"" ),
676                                             p.title_cur, p.title_count]];
677             [fScanIndicator setHidden: NO];
678             [fScanIndicator setDoubleValue: 100.0 * ((double)( p.title_cur - 1 ) / p.title_count)];
679             break;
680                 }
681 #undef p
682             
683 #define p s.param.scandone
684         case HB_STATE_SCANDONE:
685         {
686             [fScanIndicator setIndeterminate: NO];
687             [fScanIndicator setDoubleValue: 0.0];
688             [fScanIndicator setHidden: YES];
689                         [self writeToActivityLog:"ScanDone state received from fHandle"];
690             [self showNewScan:nil];
691             [[fWindow toolbar] validateVisibleItems];
692             
693                         break;
694         }
695 #undef p
696             
697 #define p s.param.working
698         case HB_STATE_WORKING:
699         {
700             
701             break;
702         }
703 #undef p
704             
705 #define p s.param.muxing
706         case HB_STATE_MUXING:
707         {
708             
709             break;
710         }
711 #undef p
712             
713         case HB_STATE_PAUSED:
714             break;
715             
716         case HB_STATE_WORKDONE:
717         {
718             break;
719         }
720     }
721     
722     
723     /* Update UI for fQueueEncodeLibhb */
724     // hb_list_t  * list;
725     // list = hb_get_titles( fQueueEncodeLibhb ); //fQueueEncodeLibhb
726     /* check to see if there has been a new scan done
727      this bypasses the constraints of HB_STATE_WORKING
728      not allowing setting a newly scanned source */
729         
730     checkScanCount = hb_get_scancount( fQueueEncodeLibhb );
731         if( checkScanCount > currentScanCount )
732         {
733                 currentScanCount = checkScanCount;
734         }
735     
736     //hb_state_t s;
737     hb_get_state( fQueueEncodeLibhb, &s );
738     
739     switch( s.state )
740     {
741         case HB_STATE_IDLE:
742             break;
743 #define p s.param.scanning
744         case HB_STATE_SCANNING:
745                 {
746             [fStatusField setStringValue: [NSString stringWithFormat:
747                                            NSLocalizedString( @"Queue Scanning title %d of %d...", @"" ),
748                                            p.title_cur, p.title_count]];
749             
750             /* Set the status string in fQueueController as well */                               
751             [fQueueController setQueueStatusString: [NSString stringWithFormat:
752                                                      NSLocalizedString( @"Queue Scanning title %d of %d...", @"" ),
753                                                      p.title_cur, p.title_count]];
754             break;
755                 }
756 #undef p
757             
758 #define p s.param.scandone
759         case HB_STATE_SCANDONE:
760         {
761                         [self writeToActivityLog:"ScanDone state received from fQueueEncodeLibhb"];
762             [self processNewQueueEncode];
763             [[fWindow toolbar] validateVisibleItems];
764             
765                         break;
766         }
767 #undef p
768
769             
770 #define p s.param.working
771         
772         case HB_STATE_SEARCHING:
773                 {
774             NSMutableString * string;
775             NSString * pass_desc;
776             
777             /* Update text field */
778             pass_desc = @"";
779             //string = [NSMutableString stringWithFormat: NSLocalizedString( @"Searching for start point: pass %d %@ of %d, %.2f %%", @"" ), p.job_cur, pass_desc, p.job_count, 100.0 * p.progress];
780             /* For now, do not announce "pass x of x for the search phase ... */
781             string = [NSMutableString stringWithFormat: NSLocalizedString( @"Searching for start point ... :  %.2f %%", @"" ), 100.0 * p.progress];
782             
783                         if( p.seconds > -1 )
784             {
785                 [string appendFormat:
786                  NSLocalizedString( @" (ETA %02dh%02dm%02ds)", @"" ), p.hours, p.minutes, p.seconds];
787             }
788             
789             [fStatusField setStringValue: string];
790             /* Set the status string in fQueueController as well */
791             [fQueueController setQueueStatusString: string];
792             /* Update slider */
793             CGFloat progress_total = ( p.progress + p.job_cur - 1 ) / p.job_count;
794             [fRipIndicator setIndeterminate: NO];
795             [fRipIndicator setDoubleValue:100.0 * progress_total];
796             
797             // If progress bar hasn't been revealed at the bottom of the window, do
798             // that now. This code used to be in doRip. I moved it to here to handle
799             // the case where hb_start is called by HBQueueController and not from
800             // HBController.
801             if( !fRipIndicatorShown )
802             {
803                 NSRect frame = [fWindow frame];
804                 if( frame.size.width <= 591 )
805                     frame.size.width = 591;
806                 frame.size.height += 36;
807                 frame.origin.y -= 36;
808                 [fWindow setFrame:frame display:YES animate:YES];
809                 fRipIndicatorShown = YES;
810                 
811             }
812             
813             /* Update dock icon */
814             /* Note not done yet */
815             break;  
816         }
817             
818
819         case HB_STATE_WORKING:
820         {
821             NSMutableString * string;
822             NSString * pass_desc;
823                         /* Update text field */
824             if (p.job_cur == 1 && p.job_count > 1)
825             {
826                 if ([[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SubtitleList"] && [[[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex]objectForKey:@"SubtitleList"] objectAtIndex:0] objectForKey:@"subtitleSourceTrackNum"] intValue] == 1)
827                 {
828                     pass_desc = @"(subtitle scan)";   
829                 }
830                 else
831                 {
832                     pass_desc = @"";
833                 }
834             }
835             else
836             {
837                 pass_desc = @"";
838             }
839             
840                         string = [NSMutableString stringWithFormat: NSLocalizedString( @"Encoding: pass %d %@ of %d, %.2f %%", @"" ), p.job_cur, pass_desc, p.job_count, 100.0 * p.progress];
841             
842                         if( p.seconds > -1 )
843             {
844                 [string appendFormat:
845                  NSLocalizedString( @" (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)", @"" ),
846                  p.rate_cur, p.rate_avg, p.hours, p.minutes, p.seconds];
847             }
848             
849             [fStatusField setStringValue: string];
850             /* Set the status string in fQueueController as well */
851             [fQueueController setQueueStatusString: string];
852             /* Update slider */
853             CGFloat progress_total = ( p.progress + p.job_cur - 1 ) / p.job_count;
854             [fRipIndicator setIndeterminate: NO];
855             [fRipIndicator setDoubleValue:100.0 * progress_total];
856             
857             // If progress bar hasn't been revealed at the bottom of the window, do
858             // that now. This code used to be in doRip. I moved it to here to handle
859             // the case where hb_start is called by HBQueueController and not from
860             // HBController.
861             if( !fRipIndicatorShown )
862             {
863                 NSRect frame = [fWindow frame];
864                 if( frame.size.width <= 591 )
865                     frame.size.width = 591;
866                 frame.size.height += 36;
867                 frame.origin.y -= 36;
868                 [fWindow setFrame:frame display:YES animate:YES];
869                 fRipIndicatorShown = YES;
870                 
871             }
872
873             /* Update dock icon */
874             if( dockIconProgress < 100.0 * progress_total )
875             {
876                 [self UpdateDockIcon: progress_total];
877                 dockIconProgress += 5;
878             }
879
880             break;
881         }
882 #undef p
883             
884 #define p s.param.muxing
885         case HB_STATE_MUXING:
886         {
887             /* Update text field */
888             [fStatusField setStringValue: NSLocalizedString( @"Muxing...", @"" )];
889             /* Set the status string in fQueueController as well */
890             [fQueueController setQueueStatusString: NSLocalizedString( @"Muxing...", @"" )];
891             /* Update slider */
892             [fRipIndicator setIndeterminate: YES];
893             [fRipIndicator startAnimation: nil];
894             
895             /* Update dock icon */
896             [self UpdateDockIcon: 1.0];
897             
898                         break;
899         }
900 #undef p
901             
902         case HB_STATE_PAUSED:
903                     [fStatusField setStringValue: NSLocalizedString( @"Paused", @"" )];
904             [fQueueController setQueueStatusString: NSLocalizedString( @"Paused", @"" )];
905             
906                         break;
907             
908         case HB_STATE_WORKDONE:
909         {
910             // HB_STATE_WORKDONE happpens as a result of libhb finishing all its jobs
911             // or someone calling hb_stop. In the latter case, hb_stop does not clear
912             // out the remaining passes/jobs in the queue. We'll do that here.
913             
914             // Delete all remaining jobs of this encode.
915             [fStatusField setStringValue: NSLocalizedString( @"Encode Finished.", @"" )];
916             /* Set the status string in fQueueController as well */
917             [fQueueController setQueueStatusString: NSLocalizedString( @"Encode Finished.", @"" )];
918             [fRipIndicator setIndeterminate: NO];
919             [fRipIndicator stopAnimation: nil];
920             [fRipIndicator setDoubleValue: 0.0];
921             [[fWindow toolbar] validateVisibleItems];
922             
923             /* Restore dock icon */
924             [self UpdateDockIcon: -1.0];
925             dockIconProgress = 0;
926             
927             if( fRipIndicatorShown )
928             {
929                 NSRect frame = [fWindow frame];
930                 if( frame.size.width <= 591 )
931                                     frame.size.width = 591;
932                 frame.size.height += -36;
933                 frame.origin.y -= -36;
934                 [fWindow setFrame:frame display:YES animate:YES];
935                                 fRipIndicatorShown = NO;
936                         }
937             /* Since we are done with this encode, tell output to stop writing to the
938              * individual encode log
939              */
940                         [outputPanel endEncodeLog];
941             /* Check to see if the encode state has not been cancelled
942              to determine if we should check for encode done notifications */
943                         if( fEncodeState != 2 )
944             {
945                 NSString *pathOfFinishedEncode;
946                 /* Get the output file name for the finished encode */
947                 pathOfFinishedEncode = [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"DestinationPath"];
948                 
949                 /* Both the Growl Alert and Sending to MetaX can be done as encodes roll off the queue */
950                 /* Growl alert */
951                 [self showGrowlDoneNotification:pathOfFinishedEncode];
952                 /* Send to MetaX */
953                 [self sendToMetaX:pathOfFinishedEncode];
954                 
955                 /* since we have successfully completed an encode, we increment the queue counter */
956                 [self incrementQueueItemDone:nil]; 
957                 
958                 /* all end of queue actions below need to be done after all queue encodes have finished 
959                  * and there are no pending jobs left to process
960                  */
961                 if (fPendingCount == 0)
962                 {
963                     /* If Alert Window or Window and Growl has been selected */
964                     if( [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window"] ||
965                        [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window And Growl"] )
966                     {
967                         /*On Screen Notification*/
968                         int status;
969                         NSBeep();
970                         status = NSRunAlertPanel(@"Put down that cocktail...",@"Your HandBrake queue is done!", @"OK", nil, nil);
971                         [NSApp requestUserAttention:NSCriticalRequest];
972                     }
973                     
974                     /* If sleep has been selected */
975                     if( [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Put Computer To Sleep"] )
976                     {
977                         /* Sleep */
978                         NSDictionary* errorDict;
979                         NSAppleEventDescriptor* returnDescriptor = nil;
980                         NSAppleScript* scriptObject = [[NSAppleScript alloc] initWithSource:
981                                                        @"tell application \"Finder\" to sleep"];
982                         returnDescriptor = [scriptObject executeAndReturnError: &errorDict];
983                         [scriptObject release];
984                     }
985                     /* If Shutdown has been selected */
986                     if( [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Shut Down Computer"] )
987                     {
988                         /* Shut Down */
989                         NSDictionary* errorDict;
990                         NSAppleEventDescriptor* returnDescriptor = nil;
991                         NSAppleScript* scriptObject = [[NSAppleScript alloc] initWithSource:
992                                                        @"tell application \"Finder\" to shut down"];
993                         returnDescriptor = [scriptObject executeAndReturnError: &errorDict];
994                         [scriptObject release];
995                     }
996                     
997                 }
998                 
999                 
1000             }
1001             
1002             break;
1003         }
1004     }
1005     
1006 }
1007
1008 /* We use this to write messages to stderr from the macgui which show up in the activity window and log*/
1009 - (void) writeToActivityLog:(const char *) format, ...
1010 {
1011     va_list args;
1012     va_start(args, format);
1013     if (format != nil)
1014     {
1015         char str[1024];
1016         vsnprintf( str, 1024, format, args );
1017
1018         time_t _now = time( NULL );
1019         struct tm * now  = localtime( &_now );
1020         fprintf(stderr, "[%02d:%02d:%02d] macgui: %s\n", now->tm_hour, now->tm_min, now->tm_sec, str );
1021     }
1022     va_end(args);
1023 }
1024
1025 #pragma mark -
1026 #pragma mark Toolbar
1027 // ============================================================
1028 // NSToolbar Related Methods
1029 // ============================================================
1030
1031 - (void) setupToolbar {
1032     NSToolbar *toolbar = [[[NSToolbar alloc] initWithIdentifier: @"HandBrake Toolbar"] autorelease];
1033
1034     [toolbar setAllowsUserCustomization: YES];
1035     [toolbar setAutosavesConfiguration: YES];
1036     [toolbar setDisplayMode: NSToolbarDisplayModeIconAndLabel];
1037
1038     [toolbar setDelegate: self];
1039
1040     [fWindow setToolbar: toolbar];
1041 }
1042
1043 - (NSToolbarItem *) toolbar: (NSToolbar *)toolbar itemForItemIdentifier:
1044     (NSString *) itemIdent willBeInsertedIntoToolbar:(BOOL) willBeInserted {
1045     NSToolbarItem * item = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdent] autorelease];
1046
1047     if ([itemIdent isEqualToString: ToggleDrawerIdentifier])
1048     {
1049         [item setLabel: @"Toggle Presets"];
1050         [item setPaletteLabel: @"Toggler Presets"];
1051         [item setToolTip: @"Open/Close Preset Drawer"];
1052         [item setImage: [NSImage imageNamed: @"Drawer"]];
1053         [item setTarget: self];
1054         [item setAction: @selector(toggleDrawer:)];
1055         [item setAutovalidates: NO];
1056     }
1057     else if ([itemIdent isEqualToString: StartEncodingIdentifier])
1058     {
1059         [item setLabel: @"Start"];
1060         [item setPaletteLabel: @"Start Encoding"];
1061         [item setToolTip: @"Start Encoding"];
1062         [item setImage: [NSImage imageNamed: @"Play"]];
1063         [item setTarget: self];
1064         [item setAction: @selector(Rip:)];
1065     }
1066     else if ([itemIdent isEqualToString: ShowQueueIdentifier])
1067     {
1068         [item setLabel: @"Show Queue"];
1069         [item setPaletteLabel: @"Show Queue"];
1070         [item setToolTip: @"Show Queue"];
1071         [item setImage: [NSImage imageNamed: @"Queue"]];
1072         [item setTarget: self];
1073         [item setAction: @selector(showQueueWindow:)];
1074         [item setAutovalidates: NO];
1075     }
1076     else if ([itemIdent isEqualToString: AddToQueueIdentifier])
1077     {
1078         [item setLabel: @"Add to Queue"];
1079         [item setPaletteLabel: @"Add to Queue"];
1080         [item setToolTip: @"Add to Queue"];
1081         [item setImage: [NSImage imageNamed: @"AddToQueue"]];
1082         [item setTarget: self];
1083         [item setAction: @selector(addToQueue:)];
1084     }
1085     else if ([itemIdent isEqualToString: PauseEncodingIdentifier])
1086     {
1087         [item setLabel: @"Pause"];
1088         [item setPaletteLabel: @"Pause Encoding"];
1089         [item setToolTip: @"Pause Encoding"];
1090         [item setImage: [NSImage imageNamed: @"Pause"]];
1091         [item setTarget: self];
1092         [item setAction: @selector(Pause:)];
1093     }
1094     else if ([itemIdent isEqualToString: ShowPictureIdentifier])
1095     {
1096         [item setLabel: @"Picture Settings"];
1097         [item setPaletteLabel: @"Show Picture Settings"];
1098         [item setToolTip: @"Show Picture Settings"];
1099         [item setImage: [NSImage imageNamed: @"pref-picture"]];
1100         [item setTarget: self];
1101         [item setAction: @selector(showPicturePanel:)];
1102     }
1103     else if ([itemIdent isEqualToString: ShowPreviewIdentifier])
1104     {
1105         [item setLabel: @"Preview Window"];
1106         [item setPaletteLabel: @"Show Preview"];
1107         [item setToolTip: @"Show Preview"];
1108         //[item setImage: [NSImage imageNamed: @"pref-picture"]];
1109         [item setImage: [NSImage imageNamed: @"Brushed_Window"]];
1110         [item setTarget: self];
1111         [item setAction: @selector(showPreviewWindow:)];
1112     }
1113     else if ([itemIdent isEqualToString: ShowActivityIdentifier]) 
1114     {
1115         [item setLabel: @"Activity Window"];
1116         [item setPaletteLabel: @"Show Activity Window"];
1117         [item setToolTip: @"Show Activity Window"];
1118         [item setImage: [NSImage imageNamed: @"ActivityWindow"]];
1119         [item setTarget: self];
1120         [item setAction: @selector(showDebugOutputPanel:)];
1121         [item setAutovalidates: NO];
1122     }
1123     else if ([itemIdent isEqualToString: ChooseSourceIdentifier])
1124     {
1125         [item setLabel: @"Source"];
1126         [item setPaletteLabel: @"Source"];
1127         [item setToolTip: @"Choose Video Source"];
1128         [item setImage: [NSImage imageNamed: @"Source"]];
1129         [item setTarget: self];
1130         [item setAction: @selector(browseSources:)];
1131     }
1132     else
1133     {
1134         return nil;
1135     }
1136
1137     return item;
1138 }
1139
1140 - (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar
1141 {
1142     return [NSArray arrayWithObjects: ChooseSourceIdentifier, NSToolbarSeparatorItemIdentifier, StartEncodingIdentifier,
1143         PauseEncodingIdentifier, AddToQueueIdentifier, ShowQueueIdentifier, NSToolbarFlexibleSpaceItemIdentifier, 
1144                 NSToolbarSpaceItemIdentifier, ShowPictureIdentifier, ShowPreviewIdentifier, ShowActivityIdentifier, ToggleDrawerIdentifier, nil];
1145 }
1146
1147 - (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) toolbar
1148 {
1149     return [NSArray arrayWithObjects:  StartEncodingIdentifier, PauseEncodingIdentifier, AddToQueueIdentifier,
1150         ChooseSourceIdentifier, ShowQueueIdentifier, ShowPictureIdentifier, ShowPreviewIdentifier, ShowActivityIdentifier, ToggleDrawerIdentifier,
1151         NSToolbarCustomizeToolbarItemIdentifier, NSToolbarFlexibleSpaceItemIdentifier,
1152         NSToolbarSpaceItemIdentifier, NSToolbarSeparatorItemIdentifier, nil];
1153 }
1154
1155 - (BOOL) validateToolbarItem: (NSToolbarItem *) toolbarItem
1156 {
1157     NSString * ident = [toolbarItem itemIdentifier];
1158         
1159     if (fHandle)
1160     {
1161         hb_state_t s;
1162         
1163         hb_get_state( fHandle, &s );
1164         if (s.state == HB_STATE_SCANNING)
1165         {
1166             
1167             if ([ident isEqualToString: ChooseSourceIdentifier])
1168             {
1169                 [toolbarItem setImage: [NSImage imageNamed: @"Stop"]];
1170                 [toolbarItem setLabel: @"Cancel Scan"];
1171                 [toolbarItem setPaletteLabel: @"Cancel Scanning"];
1172                 [toolbarItem setToolTip: @"Cancel Scanning Source"];
1173                 return YES;
1174             }
1175             
1176             if ([ident isEqualToString: StartEncodingIdentifier] || [ident isEqualToString: AddToQueueIdentifier])
1177                 return NO;
1178         }
1179         else
1180         {
1181             if ([ident isEqualToString: ChooseSourceIdentifier])
1182             {
1183                 [toolbarItem setImage: [NSImage imageNamed: @"Source"]];
1184                 [toolbarItem setLabel: @"Source"];
1185                 [toolbarItem setPaletteLabel: @"Source"];
1186                 [toolbarItem setToolTip: @"Choose Video Source"];
1187                 return YES;
1188             }
1189         }
1190
1191         hb_get_state2( fQueueEncodeLibhb, &s );
1192         
1193         if (s.state == HB_STATE_WORKING || s.state == HB_STATE_SEARCHING || s.state == HB_STATE_MUXING)
1194         {
1195             if ([ident isEqualToString: StartEncodingIdentifier])
1196             {
1197                 [toolbarItem setImage: [NSImage imageNamed: @"Stop"]];
1198                 [toolbarItem setLabel: @"Stop"];
1199                 [toolbarItem setPaletteLabel: @"Stop"];
1200                 [toolbarItem setToolTip: @"Stop Encoding"];
1201                 return YES;
1202             }
1203             if ([ident isEqualToString: PauseEncodingIdentifier])
1204             {
1205                 [toolbarItem setImage: [NSImage imageNamed: @"Pause"]];
1206                 [toolbarItem setLabel: @"Pause"];
1207                 [toolbarItem setPaletteLabel: @"Pause Encoding"];
1208                 [toolbarItem setToolTip: @"Pause Encoding"];
1209                 return YES;
1210             }
1211             if (SuccessfulScan)
1212             {
1213                 if ([ident isEqualToString: AddToQueueIdentifier])
1214                     return YES;
1215                 if ([ident isEqualToString: ShowPictureIdentifier])
1216                     return YES;
1217                 if ([ident isEqualToString: ShowPreviewIdentifier])
1218                     return YES;
1219             }
1220         }
1221         else if (s.state == HB_STATE_PAUSED)
1222         {
1223             if ([ident isEqualToString: PauseEncodingIdentifier])
1224             {
1225                 [toolbarItem setImage: [NSImage imageNamed: @"Play"]];
1226                 [toolbarItem setLabel: @"Resume"];
1227                 [toolbarItem setPaletteLabel: @"Resume Encoding"];
1228                 [toolbarItem setToolTip: @"Resume Encoding"];
1229                 return YES;
1230             }
1231             if ([ident isEqualToString: StartEncodingIdentifier])
1232                 return YES;
1233             if ([ident isEqualToString: AddToQueueIdentifier])
1234                 return YES;
1235             if ([ident isEqualToString: ShowPictureIdentifier])
1236                 return YES;
1237             if ([ident isEqualToString: ShowPreviewIdentifier])
1238                 return YES;
1239         }
1240         else if (s.state == HB_STATE_SCANNING)
1241             return NO;
1242         else if (s.state == HB_STATE_WORKDONE || s.state == HB_STATE_SCANDONE || SuccessfulScan)
1243         {
1244             if ([ident isEqualToString: StartEncodingIdentifier])
1245             {
1246                 [toolbarItem setImage: [NSImage imageNamed: @"Play"]];
1247                 if (hb_count(fHandle) > 0)
1248                     [toolbarItem setLabel: @"Start Queue"];
1249                 else
1250                     [toolbarItem setLabel: @"Start"];
1251                 [toolbarItem setPaletteLabel: @"Start Encoding"];
1252                 [toolbarItem setToolTip: @"Start Encoding"];
1253                 return YES;
1254             }
1255             if ([ident isEqualToString: AddToQueueIdentifier])
1256                 return YES;
1257             if ([ident isEqualToString: ShowPictureIdentifier])
1258                 return YES;
1259             if ([ident isEqualToString: ShowPreviewIdentifier])
1260                 return YES;
1261         }
1262
1263     }
1264     /* If there are any pending queue items, make sure the start/stop button is active */
1265     if ([ident isEqualToString: StartEncodingIdentifier] && fPendingCount > 0)
1266         return YES;
1267     if ([ident isEqualToString: ShowQueueIdentifier])
1268         return YES;
1269     if ([ident isEqualToString: ToggleDrawerIdentifier])
1270         return YES;
1271     if ([ident isEqualToString: ChooseSourceIdentifier])
1272         return YES;
1273     if ([ident isEqualToString: ShowActivityIdentifier])
1274         return YES;
1275     
1276     return NO;
1277 }
1278
1279 - (BOOL) validateMenuItem: (NSMenuItem *) menuItem
1280 {
1281     SEL action = [menuItem action];
1282     
1283     hb_state_t s;
1284     hb_get_state2( fHandle, &s );
1285     
1286     if (fHandle)
1287     {
1288         if (action == @selector(addToQueue:) || action == @selector(showPicturePanel:) || action == @selector(showAddPresetPanel:))
1289             return SuccessfulScan && [fWindow attachedSheet] == nil;
1290         
1291         if (action == @selector(browseSources:))
1292         {
1293             if (s.state == HB_STATE_SCANNING)
1294                 return NO;
1295             else
1296                 return [fWindow attachedSheet] == nil;
1297         }
1298         if (action == @selector(selectDefaultPreset:))
1299             return [fPresetsOutlineView selectedRow] >= 0 && [fWindow attachedSheet] == nil;
1300         if (action == @selector(Pause:))
1301         {
1302             if (s.state == HB_STATE_WORKING)
1303             {
1304                 if(![[menuItem title] isEqualToString:@"Pause Encoding"])
1305                     [menuItem setTitle:@"Pause Encoding"];
1306                 return YES;
1307             }
1308             else if (s.state == HB_STATE_PAUSED)
1309             {
1310                 if(![[menuItem title] isEqualToString:@"Resume Encoding"])
1311                     [menuItem setTitle:@"Resume Encoding"];
1312                 return YES;
1313             }
1314             else
1315                 return NO;
1316         }
1317         if (action == @selector(Rip:))
1318         {
1319             if (s.state == HB_STATE_WORKING || s.state == HB_STATE_MUXING || s.state == HB_STATE_PAUSED)
1320             {
1321                 if(![[menuItem title] isEqualToString:@"Stop Encoding"])
1322                     [menuItem setTitle:@"Stop Encoding"];
1323                 return YES;
1324             }
1325             else if (SuccessfulScan)
1326             {
1327                 if(![[menuItem title] isEqualToString:@"Start Encoding"])
1328                     [menuItem setTitle:@"Start Encoding"];
1329                 return [fWindow attachedSheet] == nil;
1330             }
1331             else
1332                 return NO;
1333         }
1334     }
1335     if( action == @selector(setDefaultPreset:) )
1336     {
1337         return [fPresetsOutlineView selectedRow] != -1;
1338     }
1339
1340     return YES;
1341 }
1342
1343 #pragma mark -
1344 #pragma mark Encode Done Actions
1345 // register a test notification and make
1346 // it enabled by default
1347 #define SERVICE_NAME @"Encode Done"
1348 - (NSDictionary *)registrationDictionaryForGrowl 
1349
1350     NSDictionary *registrationDictionary = [NSDictionary dictionaryWithObjectsAndKeys: 
1351     [NSArray arrayWithObjects:SERVICE_NAME,nil], GROWL_NOTIFICATIONS_ALL, 
1352     [NSArray arrayWithObjects:SERVICE_NAME,nil], GROWL_NOTIFICATIONS_DEFAULT, 
1353     nil]; 
1354
1355     return registrationDictionary; 
1356
1357
1358 -(void)showGrowlDoneNotification:(NSString *) filePath
1359 {
1360     /* This end of encode action is called as each encode rolls off of the queue */
1361     NSString * finishedEncode = filePath;
1362     /* strip off the path to just show the file name */
1363     finishedEncode = [finishedEncode lastPathComponent];
1364     if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Growl Notification"] || 
1365         [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window And Growl"])
1366     {
1367         NSString * growlMssg = [NSString stringWithFormat: @"your HandBrake encode %@ is done!",finishedEncode];
1368         [GrowlApplicationBridge 
1369          notifyWithTitle:@"Put down that cocktail..." 
1370          description:growlMssg 
1371          notificationName:SERVICE_NAME
1372          iconData:nil 
1373          priority:0 
1374          isSticky:1 
1375          clickContext:nil];
1376     }
1377     
1378 }
1379 -(void)sendToMetaX:(NSString *) filePath
1380 {
1381     /* This end of encode action is called as each encode rolls off of the queue */
1382     if([[NSUserDefaults standardUserDefaults] boolForKey: @"sendToMetaX"] == YES)
1383     {
1384         NSAppleScript *myScript = [[NSAppleScript alloc] initWithSource: [NSString stringWithFormat: @"%@%@%@", @"tell application \"MetaX\" to open (POSIX file \"", filePath, @"\")"]];
1385         [myScript executeAndReturnError: nil];
1386         [myScript release];
1387     }
1388 }
1389 #pragma mark -
1390 #pragma mark Get New Source
1391
1392 /*Opens the source browse window, called from Open Source widgets */
1393 - (IBAction) browseSources: (id) sender
1394 {
1395     
1396     hb_state_t s;
1397     hb_get_state( fHandle, &s );
1398     if (s.state == HB_STATE_SCANNING)
1399     {
1400         [self cancelScanning:nil];
1401         return;
1402     }
1403     
1404     
1405     NSOpenPanel * panel;
1406         
1407     panel = [NSOpenPanel openPanel];
1408     [panel setAllowsMultipleSelection: NO];
1409     [panel setCanChooseFiles: YES];
1410     [panel setCanChooseDirectories: YES ];
1411     NSString * sourceDirectory;
1412         if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastSourceDirectory"])
1413         {
1414                 sourceDirectory = [[NSUserDefaults standardUserDefaults] stringForKey:@"LastSourceDirectory"];
1415         }
1416         else
1417         {
1418                 sourceDirectory = @"~/Desktop";
1419                 sourceDirectory = [sourceDirectory stringByExpandingTildeInPath];
1420         }
1421     /* we open up the browse sources sheet here and call for browseSourcesDone after the sheet is closed
1422         * to evaluate whether we want to specify a title, we pass the sender in the contextInfo variable
1423         */
1424     [panel beginSheetForDirectory: sourceDirectory file: nil types: nil
1425                    modalForWindow: fWindow modalDelegate: self
1426                    didEndSelector: @selector( browseSourcesDone:returnCode:contextInfo: )
1427                       contextInfo: sender]; 
1428 }
1429
1430 - (void) browseSourcesDone: (NSOpenPanel *) sheet
1431                 returnCode: (int) returnCode contextInfo: (void *) contextInfo
1432 {
1433     /* we convert the sender content of contextInfo back into a variable called sender
1434      * mostly just for consistency for evaluation later
1435      */
1436     id sender = (id)contextInfo;
1437     /* User selected a file to open */
1438         if( returnCode == NSOKButton )
1439     {
1440             /* Free display name allocated previously by this code */
1441         [browsedSourceDisplayName release];
1442        
1443         NSString *scanPath = [[sheet filenames] objectAtIndex: 0];
1444         /* we set the last searched source directory in the prefs here */
1445         NSString *sourceDirectory = [scanPath stringByDeletingLastPathComponent];
1446         [[NSUserDefaults standardUserDefaults] setObject:sourceDirectory forKey:@"LastSourceDirectory"];
1447         /* we order out sheet, which is the browse window as we need to open
1448          * the title selection sheet right away
1449          */
1450         [sheet orderOut: self];
1451         
1452         if (sender == fOpenSourceTitleMMenu || [[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask)
1453         {
1454             /* We put the chosen source path in the source display text field for the
1455              * source title selection sheet in which the user specifies the specific title to be
1456              * scanned  as well as the short source name in fSrcDsplyNameTitleScan just for display
1457              * purposes in the title panel
1458              */
1459             /* Full Path */
1460             [fScanSrcTitlePathField setStringValue:scanPath];
1461             NSString *displayTitlescanSourceName;
1462
1463             if ([[scanPath lastPathComponent] isEqualToString: @"VIDEO_TS"])
1464             {
1465                 /* If VIDEO_TS Folder is chosen, choose its parent folder for the source display name
1466                  we have to use the title->path value so we get the proper name of the volume if a physical dvd is the source*/
1467                 displayTitlescanSourceName = [[scanPath stringByDeletingLastPathComponent] lastPathComponent];
1468             }
1469             else
1470             {
1471                 /* if not the VIDEO_TS Folder, we can assume the chosen folder is the source name */
1472                 displayTitlescanSourceName = [scanPath lastPathComponent];
1473             }
1474             /* we set the source display name in the title selection dialogue */
1475             [fSrcDsplyNameTitleScan setStringValue:displayTitlescanSourceName];
1476             /* we set the attempted scans display name for main window to displayTitlescanSourceName*/
1477             browsedSourceDisplayName = [displayTitlescanSourceName retain];
1478             /* We show the actual sheet where the user specifies the title to be scanned
1479              * as we are going to do a title specific scan
1480              */
1481             [self showSourceTitleScanPanel:nil];
1482         }
1483         else
1484         {
1485             /* We are just doing a standard full source scan, so we specify "0" to libhb */
1486             NSString *path = [[sheet filenames] objectAtIndex: 0];
1487             
1488             /* We check to see if the chosen file at path is a package */
1489             if ([[NSWorkspace sharedWorkspace] isFilePackageAtPath:path])
1490             {
1491                 [self writeToActivityLog: "trying to open a package at: %s", [path UTF8String]];
1492                 /* We check to see if this is an .eyetv package */
1493                 if ([[path pathExtension] isEqualToString: @"eyetv"])
1494                 {
1495                     [self writeToActivityLog:"trying to open eyetv package"];
1496                     /* We're looking at an EyeTV package - try to open its enclosed
1497                      .mpg media file */
1498                      browsedSourceDisplayName = [[[path stringByDeletingPathExtension] lastPathComponent] retain];
1499                     NSString *mpgname;
1500                     int n = [[path stringByAppendingString: @"/"]
1501                              completePathIntoString: &mpgname caseSensitive: YES
1502                              matchesIntoArray: nil
1503                              filterTypes: [NSArray arrayWithObject: @"mpg"]];
1504                     if (n > 0)
1505                     {
1506                         /* Found an mpeg inside the eyetv package, make it our scan path 
1507                         and call performScan on the enclosed mpeg */
1508                         path = mpgname;
1509                         [self writeToActivityLog:"found mpeg in eyetv package"];
1510                         [self performScan:path scanTitleNum:0];
1511                     }
1512                     else
1513                     {
1514                         /* We did not find an mpeg file in our package, so we do not call performScan */
1515                         [self writeToActivityLog:"no valid mpeg in eyetv package"];
1516                     }
1517                 }
1518                 /* We check to see if this is a .dvdmedia package */
1519                 else if ([[path pathExtension] isEqualToString: @"dvdmedia"])
1520                 {
1521                     /* path IS a package - but dvdmedia packages can be treaded like normal directories */
1522                     browsedSourceDisplayName = [[[path stringByDeletingPathExtension] lastPathComponent] retain];
1523                     [self writeToActivityLog:"trying to open dvdmedia package"];
1524                     [self performScan:path scanTitleNum:0];
1525                 }
1526                 else
1527                 {
1528                     /* The package is not an eyetv package, so we do not call performScan */
1529                     [self writeToActivityLog:"unable to open package"];
1530                 }
1531             }
1532             else // path is not a package, so we treat it as a dvd parent folder or VIDEO_TS folder
1533             {
1534                 /* path is not a package, so we call perform scan directly on our file */
1535                 if ([[path lastPathComponent] isEqualToString: @"VIDEO_TS"])
1536                 {
1537                     [self writeToActivityLog:"trying to open video_ts folder (video_ts folder chosen)"];
1538                     /* If VIDEO_TS Folder is chosen, choose its parent folder for the source display name*/
1539                     browsedSourceDisplayName = [[[path stringByDeletingLastPathComponent] lastPathComponent] retain];
1540                 }
1541                 else
1542                 {
1543                     [self writeToActivityLog:"trying to open video_ts folder (parent directory chosen)"];
1544                     /* if not the VIDEO_TS Folder, we can assume the chosen folder is the source name */
1545                     /* make sure we remove any path extension as this can also be an '.mpg' file */
1546                     browsedSourceDisplayName = [[path lastPathComponent] retain];
1547                 }
1548                 [self performScan:path scanTitleNum:0];
1549             }
1550
1551         }
1552
1553     }
1554 }
1555
1556 - (IBAction)showAboutPanel:(id)sender
1557 {
1558     NSMutableDictionary* d = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
1559         fApplicationIcon, @"ApplicationIcon",
1560         nil ];
1561     [NSApp orderFrontStandardAboutPanelWithOptions:d];
1562     [d release];
1563 }
1564
1565 /* Here we open the title selection sheet where we can specify an exact title to be scanned */
1566 - (IBAction) showSourceTitleScanPanel: (id) sender
1567 {
1568     /* We default the title number to be scanned to "0" which results in a full source scan, unless the
1569     * user changes it
1570     */
1571     [fScanSrcTitleNumField setStringValue: @"0"];
1572         /* Show the panel */
1573         [NSApp beginSheet:fScanSrcTitlePanel modalForWindow:fWindow modalDelegate:nil didEndSelector:NULL contextInfo:NULL];
1574 }
1575
1576 - (IBAction) closeSourceTitleScanPanel: (id) sender
1577 {
1578     [NSApp endSheet: fScanSrcTitlePanel];
1579     [fScanSrcTitlePanel orderOut: self];
1580
1581     if(sender == fScanSrcTitleOpenButton)
1582     {
1583         /* We setup the scan status in the main window to indicate a source title scan */
1584         [fSrcDVD2Field setStringValue: @"Opening a new source title ..."];
1585                 [fScanIndicator setHidden: NO];
1586         [fScanIndicator setIndeterminate: YES];
1587         [fScanIndicator startAnimation: nil];
1588                 
1589         /* We use the performScan method to actually perform the specified scan passing the path and the title
1590             * to be scanned
1591             */
1592         [self performScan:[fScanSrcTitlePathField stringValue] scanTitleNum:[fScanSrcTitleNumField intValue]];
1593     }
1594 }
1595
1596 /* Here we actually tell hb_scan to perform the source scan, using the path to source and title number*/
1597 - (void) performScan:(NSString *) scanPath scanTitleNum: (int) scanTitleNum
1598 {
1599     /* set the bool applyQueueToScan so that we dont apply a queue setting to the final scan */
1600     applyQueueToScan = NO;
1601     /* use a bool to determine whether or not we can decrypt using vlc */
1602     BOOL cancelScanDecrypt = 0;
1603     BOOL vlcFound = 0;
1604     NSString *path = scanPath;
1605     HBDVDDetector *detector = [HBDVDDetector detectorForPath:path];
1606     
1607     // Notify ChapterTitles that there's no title
1608     [fChapterTitlesDelegate resetWithTitle:nil];
1609     [fChapterTable reloadData];
1610     
1611     // Notify Subtitles that there's no title
1612     [fSubtitlesDelegate resetWithTitle:nil];
1613     [fSubtitlesTable reloadData];
1614     
1615     [self enableUI: NO];
1616     
1617     if( [detector isVideoDVD] )
1618     {
1619         int hb_arch;
1620 #if defined( __LP64__ )
1621         /* we are 64 bit */
1622         hb_arch = 64;
1623 #else
1624         /* we are 32 bit */
1625         hb_arch = 32;
1626 #endif 
1627         
1628         
1629         // The chosen path was actually on a DVD, so use the raw block
1630         // device path instead.
1631         path = [detector devicePath];
1632         [self writeToActivityLog: "trying to open a physical dvd at: %s", [scanPath UTF8String]];
1633         
1634         /* lets check for vlc here to make sure we have a dylib available to use for decrypting */
1635         NSString *vlcPath = @"/Applications/VLC.app/Contents/MacOS/lib/libdvdcss.2.dylib";
1636         NSFileManager * fileManager = [NSFileManager defaultManager];
1637             if ([fileManager fileExistsAtPath:vlcPath] == 0) 
1638             {
1639             /*vlc not found in /Applications so we set the bool to cancel scanning to 1 */
1640             cancelScanDecrypt = 1;
1641             [self writeToActivityLog: "VLC app not found for decrypting physical dvd"];
1642             int status;
1643             status = NSRunAlertPanel(@"HandBrake could not find VLC or your VLC is out of date.",@"Please download and install VLC media player in your /Applications folder if you wish to read encrypted DVDs.", @"Get VLC", @"Cancel Scan", @"Attempt Scan Anyway");
1644             [NSApp requestUserAttention:NSCriticalRequest];
1645             
1646             if (status == NSAlertDefaultReturn)
1647             {
1648                 /* User chose to go download vlc (as they rightfully should) so we send them to the vlc site */
1649                 [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://www.videolan.org/vlc/download-macosx.html"]];
1650             }
1651             else if (status == NSAlertAlternateReturn)
1652             {
1653                 /* User chose to cancel the scan */
1654                 [self writeToActivityLog: "cannot open physical dvd , scan cancelled"];
1655             }
1656             else
1657             {
1658                 /* User chose to override our warning and scan the physical dvd anyway, at their own peril. on an encrypted dvd this produces massive log files and fails */
1659                 cancelScanDecrypt = 0;
1660                 [self writeToActivityLog: "user overrode vlc warning -trying to open physical dvd without decryption"];
1661             }
1662             
1663         }
1664         else
1665         {
1666             /* VLC was found in /Applications so all is well, we can carry on using vlc's libdvdcss.dylib for decrypting if needed */
1667             [self writeToActivityLog: "VLC app found for decrypting physical dvd"];
1668             vlcFound = 1;
1669         }
1670         /* test for architecture of the vlc app */
1671         NSArray *vlc_architecturesArray = [[NSBundle bundleWithPath:@"/Applications/VLC.app"] executableArchitectures];
1672         BOOL vlcIntel32bit = NO;
1673         BOOL vlcIntel64bit = NO;
1674         BOOL vlcPPC32bit = NO;
1675         BOOL vlcPPC64bit = NO;
1676         /* check the available architectures for vlc and note accordingly */
1677         NSEnumerator *enumerator = [vlc_architecturesArray objectEnumerator];
1678         id tempObject;
1679         while (tempObject = [enumerator nextObject])
1680         {
1681             
1682             if ([tempObject intValue] == NSBundleExecutableArchitectureI386)
1683             {
1684                 vlcIntel32bit = YES;   
1685             }
1686             if ([tempObject intValue] == NSBundleExecutableArchitectureX86_64)
1687             {
1688                 vlcIntel64bit = YES;   
1689             }
1690             if ([tempObject intValue] == NSBundleExecutableArchitecturePPC)
1691             {
1692                 vlcPPC32bit = YES;   
1693             }
1694             if ([tempObject intValue] == NSBundleExecutableArchitecturePPC64)
1695             {
1696                 vlcPPC64bit = YES;   
1697             }
1698             
1699         }
1700         /* Write vlc architecture findings to activity window */
1701         if (vlcIntel32bit)
1702         {
1703             [self writeToActivityLog: " 32-Bit VLC app found for decrypting physical dvd"];
1704         }
1705         if (vlcIntel64bit)
1706         {
1707             [self writeToActivityLog: " 64-Bit VLC app found for decrypting physical dvd"];
1708         }
1709         
1710         
1711         
1712         if (vlcFound && hb_arch == 64 && !vlcIntel64bit && cancelScanDecrypt != 1)
1713         {
1714             
1715             /* we are 64 bit */
1716             
1717             /* Appropriate VLC not found, so cancel */
1718             cancelScanDecrypt = 1;
1719             [self writeToActivityLog: "This version of HandBrake is 64 bit, 64 bit version of vlc not found, scan cancelled"];
1720             /*On Screen Notification*/
1721             int status;
1722             NSBeep();
1723             status = NSRunAlertPanel(@"This version of HandBrake is 64 bit, VLC found but not 64 bit!",@"", @"Cancel Scan", @"Attempt Scan Anyway", @"Get 64 bit VLC", nil);
1724             [NSApp requestUserAttention:NSCriticalRequest];
1725             
1726             if (status == NSAlertDefaultReturn)
1727             {
1728                 /* User chose to cancel the scan */
1729                 [self writeToActivityLog: "cannot open physical dvd VLC found but not 64 bit, scan cancelled"];
1730                 cancelScanDecrypt = 1;
1731             }
1732             else if (status == NSAlertAlternateReturn)
1733             {
1734                 [self writeToActivityLog: "user overrode 64-bit warning trying to open physical dvd without proper decryption"];
1735                 cancelScanDecrypt = 0;
1736             }
1737             else
1738             {
1739                 /* User chose to go download vlc (as they rightfully should) so we send them to the vlc site */
1740                 [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://www.videolan.org/vlc/download-macosx.html"]];
1741             }
1742             
1743         }    
1744         else if (vlcFound && hb_arch == 32 && !vlcIntel32bit && cancelScanDecrypt != 1)
1745         {
1746             /* we are 32 bit */
1747             /* Appropriate VLC not found, so cancel */
1748             cancelScanDecrypt = 1;
1749             [self writeToActivityLog: "This version of HandBrake is 32 bit, 32 bit version of vlc not found, scan cancelled"];
1750             /*On Screen Notification*/
1751             int status;
1752             NSBeep();
1753             status = NSRunAlertPanel(@"This version of HandBrake is 32 bit, VLC found but not 32 bit!",@"", @"Cancel Scan", @"Attempt Scan Anyway", @"Get 32 bit VLC", nil);
1754             [NSApp requestUserAttention:NSCriticalRequest];
1755             
1756             if (status == NSAlertDefaultReturn)
1757             {
1758                 /* User chose to cancel the scan */
1759                 [self writeToActivityLog: "cannot open physical dvd VLC found but not 32 bit, scan cancelled"];
1760                 cancelScanDecrypt = 1;
1761             }
1762             else if (status == NSAlertAlternateReturn)
1763             {
1764                 [self writeToActivityLog: "user overrode 32-bit warning trying to open physical dvd without proper decryption"];
1765                 cancelScanDecrypt = 0;
1766             }
1767             else
1768             {
1769                 /* User chose to go download vlc (as they rightfully should) so we send them to the vlc site */
1770                 [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://www.videolan.org/vlc/download-macosx.html"]];
1771             }
1772             
1773         } 
1774     }
1775     
1776     if (cancelScanDecrypt == 0)
1777     {
1778         /* we actually pass the scan off to libhb here */
1779         /* If there is no title number passed to scan, we use "0"
1780          * which causes the default behavior of a full source scan
1781          */
1782         if (!scanTitleNum)
1783         {
1784             scanTitleNum = 0;
1785         }
1786         if (scanTitleNum > 0)
1787         {
1788             [self writeToActivityLog: "scanning specifically for title: %d", scanTitleNum];
1789         }
1790         /* We use our advance pref to determine how many previews to scan */
1791         int hb_num_previews = [[[NSUserDefaults standardUserDefaults] objectForKey:@"PreviewsNumber"] intValue];
1792         /* set title to NULL */
1793         //fTitle = NULL;
1794         hb_scan( fHandle, [path UTF8String], scanTitleNum, hb_num_previews, 1 );
1795         [fSrcDVD2Field setStringValue:@"Scanning new source ..."];
1796     }
1797 }
1798
1799 - (IBAction) cancelScanning:(id)sender
1800 {
1801     hb_scan_stop(fHandle);
1802 }
1803
1804 - (IBAction) showNewScan:(id)sender
1805 {
1806     hb_list_t  * list;
1807         hb_title_t * title;
1808         int indxpri=0;    // Used to search the longuest title (default in combobox)
1809         int longuestpri=0; // Used to search the longuest title (default in combobox)
1810     
1811
1812         list = hb_get_titles( fHandle );
1813         
1814         if( !hb_list_count( list ) )
1815         {
1816             /* We display a message if a valid dvd source was not chosen */
1817             [fSrcDVD2Field setStringValue: @"No Valid Source Found"];
1818             SuccessfulScan = NO;
1819             
1820             // Notify ChapterTitles that there's no title
1821             [fSubtitlesDelegate resetWithTitle:nil];
1822             [fSubtitlesTable reloadData];
1823             
1824             // Notify Subtitles that there's no title
1825             [fChapterTitlesDelegate resetWithTitle:nil];
1826             [fChapterTable reloadData];
1827         }
1828         else
1829         {
1830             /* We increment the successful scancount here by one,
1831              which we use at the end of this function to tell the gui
1832              if this is the first successful scan since launch and whether
1833              or not we should set all settings to the defaults */
1834             
1835             currentSuccessfulScanCount++;
1836             
1837             [[fWindow toolbar] validateVisibleItems];
1838             
1839             [fSrcTitlePopUp removeAllItems];
1840             for( int i = 0; i < hb_list_count( list ); i++ )
1841             {
1842                 title = (hb_title_t *) hb_list_item( list, i );
1843                 
1844                 currentSource = [NSString stringWithUTF8String: title->name];
1845                 /*Set DVD Name at top of window with the browsedSourceDisplayName grokked right before -performScan */
1846                 [fSrcDVD2Field setStringValue:browsedSourceDisplayName];
1847                 
1848                 /* Use the dvd name in the default output field here
1849                  May want to add code to remove blank spaces for some dvd names*/
1850                 /* Check to see if the last destination has been set,use if so, if not, use Desktop */
1851                 if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastDestinationDirectory"])
1852                 {
1853                     [fDstFile2Field setStringValue: [NSString stringWithFormat:
1854                                                      @"%@/%@.mp4", [[NSUserDefaults standardUserDefaults] stringForKey:@"LastDestinationDirectory"],[browsedSourceDisplayName stringByDeletingPathExtension]]];
1855                 }
1856                 else
1857                 {
1858                     [fDstFile2Field setStringValue: [NSString stringWithFormat:
1859                                                      @"%@/Desktop/%@.mp4", NSHomeDirectory(),[browsedSourceDisplayName stringByDeletingPathExtension]]];
1860                 }
1861                 
1862                 
1863                 if (longuestpri < title->hours*60*60 + title->minutes *60 + title->seconds)
1864                 {
1865                     longuestpri=title->hours*60*60 + title->minutes *60 + title->seconds;
1866                     indxpri=i;
1867                 }
1868                 
1869                 [fSrcTitlePopUp addItemWithTitle: [NSString
1870                                                    stringWithFormat: @"%s %d - %02dh%02dm%02ds",
1871                                                    title->name,title->index, title->hours, title->minutes,
1872                                                    title->seconds]];
1873             }
1874             
1875             /* if we are a stream, select the first title */
1876             if (title->type == HB_STREAM_TYPE)
1877             {
1878                 [fSrcTitlePopUp selectItemAtIndex: 0];
1879             }
1880             else
1881             {
1882                 /* if not then select the longest title (dvd) */
1883                 [fSrcTitlePopUp selectItemAtIndex: indxpri];
1884             }
1885             [self titlePopUpChanged:nil];
1886             
1887             SuccessfulScan = YES;
1888             [self enableUI: YES];
1889
1890             /* if its the initial successful scan after awakeFromNib */
1891             if (currentSuccessfulScanCount == 1)
1892             {
1893                 [self encodeStartStopPopUpChanged:nil];
1894                 
1895                 [self selectDefaultPreset:nil];
1896                 
1897                 // Open preview window now if it was visible when HB was closed
1898                 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"PreviewWindowIsOpen"])
1899                     [self showPreviewWindow:nil];
1900                 
1901                 // Open picture sizing window now if it was visible when HB was closed
1902                 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"PictureSizeWindowIsOpen"])
1903                     [self showPicturePanel:nil];
1904                 
1905             }
1906
1907             
1908         }
1909
1910 }
1911
1912
1913 #pragma mark -
1914 #pragma mark New Output Destination
1915
1916 - (IBAction) browseFile: (id) sender
1917 {
1918     /* Open a panel to let the user choose and update the text field */
1919     NSSavePanel * panel = [NSSavePanel savePanel];
1920         /* We get the current file name and path from the destination field here */
1921         [panel beginSheetForDirectory: [[fDstFile2Field stringValue] stringByDeletingLastPathComponent] file: [[fDstFile2Field stringValue] lastPathComponent]
1922                                    modalForWindow: fWindow modalDelegate: self
1923                                    didEndSelector: @selector( browseFileDone:returnCode:contextInfo: )
1924                                           contextInfo: NULL];
1925 }
1926
1927 - (void) browseFileDone: (NSSavePanel *) sheet
1928              returnCode: (int) returnCode contextInfo: (void *) contextInfo
1929 {
1930     if( returnCode == NSOKButton )
1931     {
1932         [fDstFile2Field setStringValue: [sheet filename]];
1933         /* Save this path to the prefs so that on next browse destination window it opens there */
1934         NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
1935         [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];   
1936     }
1937 }
1938
1939
1940 #pragma mark -
1941 #pragma mark Main Window Control
1942
1943 - (IBAction) openMainWindow: (id) sender
1944 {
1945     [fWindow  makeKeyAndOrderFront:nil];
1946 }
1947
1948 - (BOOL) windowShouldClose: (id) sender
1949 {
1950     return YES;
1951 }
1952
1953 - (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag
1954 {
1955     if( !flag ) {
1956         [fWindow  makeKeyAndOrderFront:nil];
1957                 
1958         return YES;
1959     }
1960     
1961     return NO;
1962 }
1963
1964 - (NSSize) drawerWillResizeContents:(NSDrawer *) drawer toSize:(NSSize) contentSize {
1965         [[NSUserDefaults standardUserDefaults] setObject:NSStringFromSize( contentSize ) forKey:@"Drawer Size"];
1966         return contentSize;
1967 }
1968
1969 #pragma mark -
1970 #pragma mark Queue File
1971
1972 - (void) loadQueueFile {
1973         /* We declare the default NSFileManager into fileManager */
1974         NSFileManager * fileManager = [NSFileManager defaultManager];
1975         /*We define the location of the user presets file */
1976     QueueFile = @"~/Library/Application Support/HandBrake/Queue.plist";
1977         QueueFile = [[QueueFile stringByExpandingTildeInPath]retain];
1978     /* We check for the presets.plist */
1979         if ([fileManager fileExistsAtPath:QueueFile] == 0)
1980         {
1981                 [fileManager createFileAtPath:QueueFile contents:nil attributes:nil];
1982         }
1983
1984         QueueFileArray = [[NSMutableArray alloc] initWithContentsOfFile:QueueFile];
1985         /* lets check to see if there is anything in the queue file .plist */
1986     if (nil == QueueFileArray)
1987         {
1988         /* if not, then lets initialize an empty array */
1989                 QueueFileArray = [[NSMutableArray alloc] init];
1990         
1991      /* Initialize our curQueueEncodeIndex to 0
1992      * so we can use it to track which queue
1993      * item is to be used to track our encodes */
1994      /* NOTE: this should be changed if and when we
1995       * are able to get the last unfinished encode
1996       * in the case of a crash or shutdown */
1997     
1998         }
1999     else
2000     {
2001     [self clearQueueEncodedItems];
2002     }
2003     currentQueueEncodeIndex = 0;
2004 }
2005
2006 - (void)addQueueFileItem
2007 {
2008         [QueueFileArray addObject:[self createQueueFileItem]];
2009         [self saveQueueFileItem];
2010
2011 }
2012
2013 - (void) removeQueueFileItem:(int) queueItemToRemove
2014 {
2015    
2016    /* Find out if the item we are removing is a cancelled (3) or a finished (0) item*/
2017    if ([[[QueueFileArray objectAtIndex:queueItemToRemove] objectForKey:@"Status"] intValue] == 3 || [[[QueueFileArray objectAtIndex:queueItemToRemove] objectForKey:@"Status"] intValue] == 0)
2018     {
2019     /* Since we are removing a cancelled or finished item, WE need to decrement the currentQueueEncodeIndex
2020      * by one to keep in sync with the queue array
2021      */
2022     currentQueueEncodeIndex--;
2023     [self writeToActivityLog: "removeQueueFileItem: Removing a cancelled/finished encode, decrement currentQueueEncodeIndex to %d", currentQueueEncodeIndex];
2024     }
2025     [QueueFileArray removeObjectAtIndex:queueItemToRemove];
2026     [self saveQueueFileItem];
2027
2028 }
2029
2030 - (void)saveQueueFileItem
2031 {
2032     [QueueFileArray writeToFile:QueueFile atomically:YES];
2033     [fQueueController setQueueArray: QueueFileArray];
2034     [self getQueueStats];
2035 }
2036
2037 - (void)getQueueStats
2038 {
2039 /* lets get the stats on the status of the queue array */
2040
2041 fEncodingQueueItem = 0;
2042 fPendingCount = 0;
2043 fCompletedCount = 0;
2044 fCanceledCount = 0;
2045 fWorkingCount = 0;
2046
2047     /* We use a number system to set the encode status of the queue item
2048      * in controller.mm
2049      * 0 == already encoded
2050      * 1 == is being encoded
2051      * 2 == is yet to be encoded
2052      * 3 == cancelled
2053      */
2054
2055         int i = 0;
2056     NSEnumerator *enumerator = [QueueFileArray objectEnumerator];
2057         id tempObject;
2058         while (tempObject = [enumerator nextObject])
2059         {
2060                 NSDictionary *thisQueueDict = tempObject;
2061                 if ([[thisQueueDict objectForKey:@"Status"] intValue] == 0) // Completed
2062                 {
2063                         fCompletedCount++;      
2064                 }
2065                 if ([[thisQueueDict objectForKey:@"Status"] intValue] == 1) // being encoded
2066                 {
2067                         fWorkingCount++;
2068             fEncodingQueueItem = i;     
2069                 }
2070         if ([[thisQueueDict objectForKey:@"Status"] intValue] == 2) // pending          
2071         {
2072                         fPendingCount++;
2073                 }
2074         if ([[thisQueueDict objectForKey:@"Status"] intValue] == 3) // cancelled                
2075         {
2076                         fCanceledCount++;
2077                 }
2078                 i++;
2079         }
2080
2081     /* Set the queue status field in the main window */
2082     NSMutableString * string;
2083     if (fPendingCount == 1)
2084     {
2085         string = [NSMutableString stringWithFormat: NSLocalizedString( @"%d encode pending in the queue", @"" ), fPendingCount];
2086     }
2087     else
2088     {
2089         string = [NSMutableString stringWithFormat: NSLocalizedString( @"%d encode(s) pending in the queue", @"" ), fPendingCount];
2090     }
2091     [fQueueStatus setStringValue:string];
2092 }
2093
2094 /* This method will set any item marked as encoding back to pending
2095  * currently used right after a queue reload
2096  */
2097 - (void) setQueueEncodingItemsAsPending
2098 {
2099     NSEnumerator *enumerator = [QueueFileArray objectEnumerator];
2100         id tempObject;
2101     NSMutableArray *tempArray;
2102     tempArray = [NSMutableArray array];
2103     /* we look here to see if the preset is we move on to the next one */
2104     while ( tempObject = [enumerator nextObject] )  
2105     {
2106         /* If the queue item is marked as "encoding" (1)
2107          * then change its status back to pending (2) which effectively
2108          * puts it back into the queue to be encoded
2109          */
2110         if ([[tempObject objectForKey:@"Status"] intValue] == 1)
2111         {
2112             [tempObject setObject:[NSNumber numberWithInt: 2] forKey:@"Status"];
2113         }
2114         [tempArray addObject:tempObject];
2115     }
2116     
2117     [QueueFileArray setArray:tempArray];
2118     [self saveQueueFileItem];
2119 }
2120
2121
2122 /* This method will clear the queue of any encodes that are not still pending
2123  * this includes both successfully completed encodes as well as cancelled encodes */
2124 - (void) clearQueueEncodedItems
2125 {
2126     NSEnumerator *enumerator = [QueueFileArray objectEnumerator];
2127         id tempObject;
2128     NSMutableArray *tempArray;
2129     tempArray = [NSMutableArray array];
2130     /* we look here to see if the preset is we move on to the next one */
2131     while ( tempObject = [enumerator nextObject] )  
2132     {
2133         /* If the queue item is either completed (0) or cancelled (3) from the
2134          * last session, then we put it in tempArray to be deleted from QueueFileArray.
2135          * NOTE: this means we retain pending (2) and also an item that is marked as
2136          * still encoding (1). If the queue has an item that is still marked as encoding
2137          * from a previous session, we can conlude that HB was either shutdown, or crashed
2138          * during the encodes so we keep it and tell the user in the "Load Queue Alert"
2139          */
2140         if ([[tempObject objectForKey:@"Status"] intValue] == 0 || [[tempObject objectForKey:@"Status"] intValue] == 3)
2141         {
2142             [tempArray addObject:tempObject];
2143         }
2144     }
2145     
2146     [QueueFileArray removeObjectsInArray:tempArray];
2147     [self saveQueueFileItem];
2148 }
2149
2150 /* This method will clear the queue of all encodes. effectively creating an empty queue */
2151 - (void) clearQueueAllItems
2152 {
2153     NSEnumerator *enumerator = [QueueFileArray objectEnumerator];
2154         id tempObject;
2155     NSMutableArray *tempArray;
2156     tempArray = [NSMutableArray array];
2157     /* we look here to see if the preset is we move on to the next one */
2158     while ( tempObject = [enumerator nextObject] )  
2159     {
2160         [tempArray addObject:tempObject];
2161     }
2162     
2163     [QueueFileArray removeObjectsInArray:tempArray];
2164     [self saveQueueFileItem];
2165 }
2166
2167 /* This method will duplicate prepareJob however into the
2168  * queue .plist instead of into the job structure so it can
2169  * be recalled later */
2170 - (NSDictionary *)createQueueFileItem
2171 {
2172     NSMutableDictionary *queueFileJob = [[NSMutableDictionary alloc] init];
2173     
2174        hb_list_t  * list  = hb_get_titles( fHandle );
2175     hb_title_t * title = (hb_title_t *) hb_list_item( list,
2176             [fSrcTitlePopUp indexOfSelectedItem] );
2177     hb_job_t * job = title->job;
2178     
2179     
2180     
2181     /* We use a number system to set the encode status of the queue item
2182      * 0 == already encoded
2183      * 1 == is being encoded
2184      * 2 == is yet to be encoded
2185      * 3 == cancelled
2186      */
2187     [queueFileJob setObject:[NSNumber numberWithInt:2] forKey:@"Status"];
2188     /* Source and Destination Information */
2189     
2190     [queueFileJob setObject:[NSString stringWithUTF8String: title->path] forKey:@"SourcePath"];
2191     [queueFileJob setObject:[fSrcDVD2Field stringValue] forKey:@"SourceName"];
2192     [queueFileJob setObject:[NSNumber numberWithInt:title->index] forKey:@"TitleNumber"];
2193     [queueFileJob setObject:[NSNumber numberWithInt:[fSrcAnglePopUp indexOfSelectedItem] + 1] forKey:@"TitleAngle"];
2194     
2195     /* Determine and set a variable to tell hb what start and stop times to use ... chapters vs seconds */
2196     if( [fEncodeStartStopPopUp indexOfSelectedItem] == 0 )
2197     {
2198         [queueFileJob setObject:[NSNumber numberWithInt:0] forKey:@"fEncodeStartStop"];    
2199     }
2200     else if ([fEncodeStartStopPopUp indexOfSelectedItem] == 1)
2201     {
2202         [queueFileJob setObject:[NSNumber numberWithInt:1] forKey:@"fEncodeStartStop"];   
2203     }
2204     else if ([fEncodeStartStopPopUp indexOfSelectedItem] == 2)
2205     {
2206         [queueFileJob setObject:[NSNumber numberWithInt:2] forKey:@"fEncodeStartStop"];
2207     }
2208     /* Chapter encode info */
2209     [queueFileJob setObject:[NSNumber numberWithInt:[fSrcChapterStartPopUp indexOfSelectedItem] + 1] forKey:@"ChapterStart"];
2210     [queueFileJob setObject:[NSNumber numberWithInt:[fSrcChapterEndPopUp indexOfSelectedItem] + 1] forKey:@"ChapterEnd"];
2211     /* Time (pts) encode info */
2212     [queueFileJob setObject:[NSNumber numberWithInt:[fSrcTimeStartEncodingField intValue]] forKey:@"StartSeconds"];
2213     [queueFileJob setObject:[NSNumber numberWithInt:[fSrcTimeEndEncodingField intValue] - [fSrcTimeStartEncodingField intValue]] forKey:@"StopSeconds"];
2214     /* Frame number encode info */
2215     [queueFileJob setObject:[NSNumber numberWithInt:[fSrcFrameStartEncodingField intValue]] forKey:@"StartFrame"];
2216     [queueFileJob setObject:[NSNumber numberWithInt:[fSrcFrameEndEncodingField intValue] - [fSrcFrameStartEncodingField intValue]] forKey:@"StopFrame"];
2217     
2218     
2219     /* The number of seek points equals the number of seconds announced in the title as that is our current granularity */
2220         int title_duration_seconds = (title->hours * 3600) + (title->minutes * 60) + (title->seconds);
2221     [queueFileJob setObject:[NSNumber numberWithInt:title_duration_seconds] forKey:@"SourceTotalSeconds"];
2222     
2223     [queueFileJob setObject:[fDstFile2Field stringValue] forKey:@"DestinationPath"];
2224     
2225     /* Lets get the preset info if there is any */
2226     [queueFileJob setObject:[fPresetSelectedDisplay stringValue] forKey:@"PresetName"];
2227     [queueFileJob setObject:[NSNumber numberWithInt:[fPresetsOutlineView selectedRow]] forKey:@"PresetIndexNum"];
2228     
2229     [queueFileJob setObject:[fDstFormatPopUp titleOfSelectedItem] forKey:@"FileFormat"];
2230     /* Chapter Markers*/
2231     /* If we have only one chapter or a title without chapters, set chapter markers to off */
2232     if ([fSrcChapterStartPopUp indexOfSelectedItem] ==  [fSrcChapterEndPopUp indexOfSelectedItem])
2233     {
2234         [queueFileJob setObject:[NSNumber numberWithInt:0] forKey:@"ChapterMarkers"];
2235     }
2236     else
2237     {
2238         [queueFileJob setObject:[NSNumber numberWithInt:[fCreateChapterMarkers state]] forKey:@"ChapterMarkers"];
2239     }
2240         
2241     /* We need to get the list of chapter names to put into an array and store 
2242      * in our queue, so they can be reapplied in prepareJob when this queue
2243      * item comes up if Chapter Markers is set to on.
2244      */
2245      int i;
2246      NSMutableArray *ChapterNamesArray = [[NSMutableArray alloc] init];
2247      int chaptercount = hb_list_count( fTitle->list_chapter );
2248      for( i = 0; i < chaptercount; i++ )
2249     {
2250         hb_chapter_t *chapter = (hb_chapter_t *) hb_list_item( fTitle->list_chapter, i );
2251         if( chapter != NULL )
2252         {
2253           [ChapterNamesArray addObject:[NSString stringWithCString:chapter->title encoding:NSUTF8StringEncoding]];
2254         }
2255     }
2256     [queueFileJob setObject:[NSMutableArray arrayWithArray: ChapterNamesArray] forKey:@"ChapterNames"];
2257     [ChapterNamesArray autorelease];
2258     
2259     /* Allow Mpeg4 64 bit formatting +4GB file sizes */
2260         [queueFileJob setObject:[NSNumber numberWithInt:[fDstMp4LargeFileCheck state]] forKey:@"Mp4LargeFile"];
2261     /* Mux mp4 with http optimization */
2262     [queueFileJob setObject:[NSNumber numberWithInt:[fDstMp4HttpOptFileCheck state]] forKey:@"Mp4HttpOptimize"];
2263     /* Add iPod uuid atom */
2264     [queueFileJob setObject:[NSNumber numberWithInt:[fDstMp4iPodFileCheck state]] forKey:@"Mp4iPodCompatible"];
2265     
2266     /* Codecs */
2267         /* Video encoder */
2268         [queueFileJob setObject:[fVidEncoderPopUp titleOfSelectedItem] forKey:@"VideoEncoder"];
2269         /* x264 Option String */
2270         [queueFileJob setObject:[fAdvancedOptions optionsString] forKey:@"x264Option"];
2271
2272         [queueFileJob setObject:[NSNumber numberWithInt:[fVidQualityMatrix selectedRow]] forKey:@"VideoQualityType"];
2273         [queueFileJob setObject:[fVidTargetSizeField stringValue] forKey:@"VideoTargetSize"];
2274         [queueFileJob setObject:[fVidBitrateField stringValue] forKey:@"VideoAvgBitrate"];
2275         [queueFileJob setObject:[NSNumber numberWithFloat:[fVidQualityRFField floatValue]] forKey:@"VideoQualitySlider"];
2276     /* Framerate */
2277     [queueFileJob setObject:[fVidRatePopUp titleOfSelectedItem] forKey:@"VideoFramerate"];
2278     
2279         /* 2 Pass Encoding */
2280         [queueFileJob setObject:[NSNumber numberWithInt:[fVidTwoPassCheck state]] forKey:@"VideoTwoPass"];
2281         /* Turbo 2 pass Encoding fVidTurboPassCheck*/
2282         [queueFileJob setObject:[NSNumber numberWithInt:[fVidTurboPassCheck state]] forKey:@"VideoTurboTwoPass"];
2283     
2284         /* Picture Sizing */
2285         /* Use Max Picture settings for whatever the dvd is.*/
2286         [queueFileJob setObject:[NSNumber numberWithInt:0] forKey:@"UsesMaxPictureSettings"];
2287         [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->width] forKey:@"PictureWidth"];
2288         [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->height] forKey:@"PictureHeight"];
2289         [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->keep_ratio] forKey:@"PictureKeepRatio"];
2290         [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->anamorphic.mode] forKey:@"PicturePAR"];
2291     /* if we are custom anamorphic, store the exact storage, par and display dims */
2292     if (fTitle->job->anamorphic.mode == 3)
2293     {
2294         [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->anamorphic.modulus] forKey:@"PicturePARModulus"];
2295         
2296         [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->width] forKey:@"PicturePARStorageWidth"];
2297         [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->height] forKey:@"PicturePARStorageHeight"];
2298         
2299         [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->anamorphic.par_width] forKey:@"PicturePARPixelWidth"];
2300         [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->anamorphic.par_height] forKey:@"PicturePARPixelHeight"];
2301         
2302         [queueFileJob setObject:[NSNumber numberWithFloat:fTitle->job->anamorphic.dar_width] forKey:@"PicturePARDisplayWidth"];
2303         [queueFileJob setObject:[NSNumber numberWithFloat:fTitle->job->anamorphic.dar_height] forKey:@"PicturePARDisplayHeight"];
2304
2305     }
2306     NSString * pictureSummary;
2307     pictureSummary = [fPictureSizeField stringValue];
2308     [queueFileJob setObject:pictureSummary forKey:@"PictureSizingSummary"];                 
2309     /* Set crop settings here */
2310         [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController autoCrop]] forKey:@"PictureAutoCrop"];
2311     [queueFileJob setObject:[NSNumber numberWithInt:job->crop[0]] forKey:@"PictureTopCrop"];
2312     [queueFileJob setObject:[NSNumber numberWithInt:job->crop[1]] forKey:@"PictureBottomCrop"];
2313         [queueFileJob setObject:[NSNumber numberWithInt:job->crop[2]] forKey:@"PictureLeftCrop"];
2314         [queueFileJob setObject:[NSNumber numberWithInt:job->crop[3]] forKey:@"PictureRightCrop"];
2315     
2316     /* Picture Filters */
2317     [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController detelecine]] forKey:@"PictureDetelecine"];
2318     [queueFileJob setObject:[fPictureController detelecineCustomString] forKey:@"PictureDetelecineCustom"];
2319     
2320     [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController useDecomb]] forKey:@"PictureDecombDeinterlace"];
2321     [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController decomb]] forKey:@"PictureDecomb"];
2322     [queueFileJob setObject:[fPictureController decombCustomString] forKey:@"PictureDecombCustom"];
2323     
2324     [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController deinterlace]] forKey:@"PictureDeinterlace"];
2325     [queueFileJob setObject:[fPictureController deinterlaceCustomString] forKey:@"PictureDeinterlaceCustom"];
2326     
2327     [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController denoise]] forKey:@"PictureDenoise"];
2328     [queueFileJob setObject:[fPictureController denoiseCustomString] forKey:@"PictureDenoiseCustom"];
2329     
2330     [queueFileJob setObject:[NSString stringWithFormat:@"%d",[fPictureController deblock]] forKey:@"PictureDeblock"];
2331     
2332     [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController grayscale]] forKey:@"VideoGrayScale"];
2333     
2334     /*Audio*/
2335     if ([fAudLang1PopUp indexOfSelectedItem] > 0)
2336     {
2337         [queueFileJob setObject:[NSNumber numberWithInt:[fAudLang1PopUp indexOfSelectedItem]] forKey:@"Audio1Track"];
2338         [queueFileJob setObject:[fAudLang1PopUp titleOfSelectedItem] forKey:@"Audio1TrackDescription"];
2339         [queueFileJob setObject:[fAudTrack1CodecPopUp titleOfSelectedItem] forKey:@"Audio1Encoder"];
2340         [queueFileJob setObject:[fAudTrack1MixPopUp titleOfSelectedItem] forKey:@"Audio1Mixdown"];
2341         [queueFileJob setObject:[fAudTrack1RatePopUp titleOfSelectedItem] forKey:@"Audio1Samplerate"];
2342         [queueFileJob setObject:[fAudTrack1BitratePopUp titleOfSelectedItem] forKey:@"Audio1Bitrate"];
2343         [queueFileJob setObject:[NSNumber numberWithFloat:[fAudTrack1DrcSlider floatValue]] forKey:@"Audio1TrackDRCSlider"];
2344     }
2345     if ([fAudLang2PopUp indexOfSelectedItem] > 0)
2346     {
2347         [queueFileJob setObject:[NSNumber numberWithInt:[fAudLang2PopUp indexOfSelectedItem]] forKey:@"Audio2Track"];
2348         [queueFileJob setObject:[fAudLang2PopUp titleOfSelectedItem] forKey:@"Audio2TrackDescription"];
2349         [queueFileJob setObject:[fAudTrack2CodecPopUp titleOfSelectedItem] forKey:@"Audio2Encoder"];
2350         [queueFileJob setObject:[fAudTrack2MixPopUp titleOfSelectedItem] forKey:@"Audio2Mixdown"];
2351         [queueFileJob setObject:[fAudTrack2RatePopUp titleOfSelectedItem] forKey:@"Audio2Samplerate"];
2352         [queueFileJob setObject:[fAudTrack2BitratePopUp titleOfSelectedItem] forKey:@"Audio2Bitrate"];
2353         [queueFileJob setObject:[NSNumber numberWithFloat:[fAudTrack2DrcSlider floatValue]] forKey:@"Audio2TrackDRCSlider"];
2354     }
2355     if ([fAudLang3PopUp indexOfSelectedItem] > 0)
2356     {
2357         [queueFileJob setObject:[NSNumber numberWithInt:[fAudLang3PopUp indexOfSelectedItem]] forKey:@"Audio3Track"];
2358         [queueFileJob setObject:[fAudLang3PopUp titleOfSelectedItem] forKey:@"Audio3TrackDescription"];
2359         [queueFileJob setObject:[fAudTrack3CodecPopUp titleOfSelectedItem] forKey:@"Audio3Encoder"];
2360         [queueFileJob setObject:[fAudTrack3MixPopUp titleOfSelectedItem] forKey:@"Audio3Mixdown"];
2361         [queueFileJob setObject:[fAudTrack3RatePopUp titleOfSelectedItem] forKey:@"Audio3Samplerate"];
2362         [queueFileJob setObject:[fAudTrack3BitratePopUp titleOfSelectedItem] forKey:@"Audio3Bitrate"];
2363         [queueFileJob setObject:[NSNumber numberWithFloat:[fAudTrack3DrcSlider floatValue]] forKey:@"Audio3TrackDRCSlider"];
2364     }
2365     if ([fAudLang4PopUp indexOfSelectedItem] > 0)
2366     {
2367         [queueFileJob setObject:[NSNumber numberWithInt:[fAudLang4PopUp indexOfSelectedItem]] forKey:@"Audio4Track"];
2368         [queueFileJob setObject:[fAudLang4PopUp titleOfSelectedItem] forKey:@"Audio4TrackDescription"];
2369         [queueFileJob setObject:[fAudTrack4CodecPopUp titleOfSelectedItem] forKey:@"Audio4Encoder"];
2370         [queueFileJob setObject:[fAudTrack4MixPopUp titleOfSelectedItem] forKey:@"Audio4Mixdown"];
2371         [queueFileJob setObject:[fAudTrack4RatePopUp titleOfSelectedItem] forKey:@"Audio4Samplerate"];
2372         [queueFileJob setObject:[fAudTrack4BitratePopUp titleOfSelectedItem] forKey:@"Audio4Bitrate"];
2373         [queueFileJob setObject:[NSNumber numberWithFloat:[fAudTrack4DrcSlider floatValue]] forKey:@"Audio4TrackDRCSlider"];
2374     }
2375     
2376         /* Subtitles*/
2377     NSMutableArray *subtitlesArray = [[NSMutableArray alloc] initWithArray:[fSubtitlesDelegate getSubtitleArray] copyItems:YES];
2378     [queueFileJob setObject:[NSArray arrayWithArray: subtitlesArray] forKey:@"SubtitleList"];
2379     [subtitlesArray autorelease];
2380
2381     /* Now we go ahead and set the "job->values in the plist for passing right to fQueueEncodeLibhb */
2382      
2383     [queueFileJob setObject:[NSNumber numberWithInt:[fSrcChapterStartPopUp indexOfSelectedItem] + 1] forKey:@"JobChapterStart"];
2384     
2385     [queueFileJob setObject:[NSNumber numberWithInt:[fSrcChapterEndPopUp indexOfSelectedItem] + 1] forKey:@"JobChapterEnd"];
2386     
2387     
2388     [queueFileJob setObject:[NSNumber numberWithInt:[[fDstFormatPopUp selectedItem] tag]] forKey:@"JobFileFormatMux"];
2389     
2390     /* Codecs */
2391         /* Video encoder */
2392         [queueFileJob setObject:[NSNumber numberWithInt:[[fVidEncoderPopUp selectedItem] tag]] forKey:@"JobVideoEncoderVcodec"];
2393         
2394     /* Framerate */
2395     [queueFileJob setObject:[NSNumber numberWithInt:[fVidRatePopUp indexOfSelectedItem]] forKey:@"JobIndexVideoFramerate"];
2396     [queueFileJob setObject:[NSNumber numberWithInt:title->rate] forKey:@"JobVrate"];
2397     [queueFileJob setObject:[NSNumber numberWithInt:title->rate_base] forKey:@"JobVrateBase"];
2398         
2399     /* Picture Sizing */
2400         /* Use Max Picture settings for whatever the dvd is.*/
2401         [queueFileJob setObject:[NSNumber numberWithInt:0] forKey:@"UsesMaxPictureSettings"];
2402         [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->width] forKey:@"PictureWidth"];
2403         [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->height] forKey:@"PictureHeight"];
2404         [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->keep_ratio] forKey:@"PictureKeepRatio"];
2405         [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->anamorphic.mode] forKey:@"PicturePAR"];
2406     
2407     /* Set crop settings here */
2408         [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController autoCrop]] forKey:@"PictureAutoCrop"];
2409     [queueFileJob setObject:[NSNumber numberWithInt:job->crop[0]] forKey:@"PictureTopCrop"];
2410     [queueFileJob setObject:[NSNumber numberWithInt:job->crop[1]] forKey:@"PictureBottomCrop"];
2411         [queueFileJob setObject:[NSNumber numberWithInt:job->crop[2]] forKey:@"PictureLeftCrop"];
2412         [queueFileJob setObject:[NSNumber numberWithInt:job->crop[3]] forKey:@"PictureRightCrop"];
2413     
2414     
2415     /*Audio*/
2416     if ([fAudLang1PopUp indexOfSelectedItem] > 0)
2417     {
2418         //[queueFileJob setObject:[fAudTrack1CodecPopUp indexOfSelectedItem] forKey:@"JobAudio1Encoder"];
2419         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack1CodecPopUp selectedItem] tag]] forKey:@"JobAudio1Encoder"];
2420         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack1MixPopUp selectedItem] tag]] forKey:@"JobAudio1Mixdown"];
2421         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack1RatePopUp selectedItem] tag]] forKey:@"JobAudio1Samplerate"];
2422         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack1BitratePopUp selectedItem] tag]] forKey:@"JobAudio1Bitrate"];
2423      }
2424     if ([fAudLang2PopUp indexOfSelectedItem] > 0)
2425     {
2426         //[queueFileJob setObject:[fAudTrack1CodecPopUp indexOfSelectedItem] forKey:@"JobAudio2Encoder"];
2427         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack2CodecPopUp selectedItem] tag]] forKey:@"JobAudio2Encoder"];
2428         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack2MixPopUp selectedItem] tag]] forKey:@"JobAudio2Mixdown"];
2429         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack2RatePopUp selectedItem] tag]] forKey:@"JobAudio2Samplerate"];
2430         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack2BitratePopUp selectedItem] tag]] forKey:@"JobAudio2Bitrate"];
2431     }
2432     if ([fAudLang3PopUp indexOfSelectedItem] > 0)
2433     {
2434         //[queueFileJob setObject:[fAudTrack1CodecPopUp indexOfSelectedItem] forKey:@"JobAudio3Encoder"];
2435         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack3CodecPopUp selectedItem] tag]] forKey:@"JobAudio3Encoder"];
2436         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack3MixPopUp selectedItem] tag]] forKey:@"JobAudio3Mixdown"];
2437         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack3RatePopUp selectedItem] tag]] forKey:@"JobAudio3Samplerate"];
2438         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack3BitratePopUp selectedItem] tag]] forKey:@"JobAudio3Bitrate"];
2439     }
2440     if ([fAudLang4PopUp indexOfSelectedItem] > 0)
2441     {
2442         //[queueFileJob setObject:[fAudTrack1CodecPopUp indexOfSelectedItem] forKey:@"JobAudio4Encoder"];
2443         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack4CodecPopUp selectedItem] tag]] forKey:@"JobAudio4Encoder"];
2444         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack4MixPopUp selectedItem] tag]] forKey:@"JobAudio4Mixdown"];
2445         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack4RatePopUp selectedItem] tag]] forKey:@"JobAudio4Samplerate"];
2446         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack4BitratePopUp selectedItem] tag]] forKey:@"JobAudio4Bitrate"];
2447     }
2448
2449  
2450     /* we need to auto relase the queueFileJob and return it */
2451     [queueFileJob autorelease];
2452     return queueFileJob;
2453
2454 }
2455
2456 /* this is actually called from the queue controller to modify the queue array and return it back to the queue controller */
2457 - (void)moveObjectsInQueueArray:(NSMutableArray *)array fromIndexes:(NSIndexSet *)indexSet toIndex:(NSUInteger)insertIndex
2458 {
2459     NSUInteger index = [indexSet lastIndex];
2460     NSUInteger aboveInsertIndexCount = 0;
2461     
2462     
2463     NSUInteger removeIndex;
2464         
2465     if (index >= insertIndex)
2466     {
2467         removeIndex = index + aboveInsertIndexCount;
2468         aboveInsertIndexCount++;
2469     }
2470     else
2471     {
2472         removeIndex = index;
2473         insertIndex--;
2474     }
2475
2476     id object = [[QueueFileArray objectAtIndex:removeIndex] retain];
2477     [QueueFileArray removeObjectAtIndex:removeIndex];
2478     [QueueFileArray insertObject:object atIndex:insertIndex];
2479     [object release];
2480         
2481     index = [indexSet indexLessThanIndex:index];
2482
2483    /* We save all of the Queue data here 
2484     * and it also gets sent back to the queue controller*/
2485     [self saveQueueFileItem]; 
2486     
2487 }
2488
2489
2490 #pragma mark -
2491 #pragma mark Queue Job Processing
2492
2493 - (void) incrementQueueItemDone:(int) queueItemDoneIndexNum
2494 {
2495     int i = currentQueueEncodeIndex;
2496     [[QueueFileArray objectAtIndex:i] setObject:[NSNumber numberWithInt:0] forKey:@"Status"];
2497         
2498     /* We save all of the Queue data here */
2499     [self saveQueueFileItem];
2500         /* We Reload the New Table data for presets */
2501     //[fPresetsOutlineView reloadData];
2502
2503     /* Since we have now marked a queue item as done
2504      * we can go ahead and increment currentQueueEncodeIndex 
2505      * so that if there is anything left in the queue we can
2506      * go ahead and move to the next item if we want to */
2507     currentQueueEncodeIndex++ ;
2508     [self writeToActivityLog: "incrementQueueItemDone currentQueueEncodeIndex is incremented to: %d", currentQueueEncodeIndex];
2509     int queueItems = [QueueFileArray count];
2510     /* If we still have more items in our queue, lets go to the next one */
2511     if (currentQueueEncodeIndex < queueItems)
2512     {
2513     [self writeToActivityLog: "incrementQueueItemDone currentQueueEncodeIndex is incremented to: %d", currentQueueEncodeIndex];
2514     [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]];
2515     }
2516     else
2517     {
2518         [self writeToActivityLog: "incrementQueueItemDone the %d item queue is complete", currentQueueEncodeIndex - 1];
2519     }
2520 }
2521
2522 /* Here we actually tell hb_scan to perform the source scan, using the path to source and title number*/
2523 - (void) performNewQueueScan:(NSString *) scanPath scanTitleNum: (int) scanTitleNum
2524 {
2525    /* Tell HB to output a new activity log file for this encode */
2526     [outputPanel startEncodeLog:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"DestinationPath"]];
2527     
2528     
2529      /* use a bool to determine whether or not we can decrypt using vlc */
2530     BOOL cancelScanDecrypt = 0;
2531     /* set the bool so that showNewScan knows to apply the appropriate queue
2532     * settings as this is a queue rescan
2533     */
2534     applyQueueToScan = YES;
2535     NSString *path = scanPath;
2536     HBDVDDetector *detector = [HBDVDDetector detectorForPath:path];
2537
2538         /*On Screen Notification*/
2539         //int status;
2540         //status = NSRunAlertPanel(@"HandBrake is now loading up a new queue item...",@"Would You Like to wait until you add another encode?", @"Cancel", @"Okay", nil);
2541         //[NSApp requestUserAttention:NSCriticalRequest];
2542
2543     if( [detector isVideoDVD] )
2544     {
2545         // The chosen path was actually on a DVD, so use the raw block
2546         // device path instead.
2547         path = [detector devicePath];
2548         [self writeToActivityLog: "trying to open a physical dvd at: %s", [scanPath UTF8String]];
2549
2550         /* lets check for vlc here to make sure we have a dylib available to use for decrypting */
2551         NSString *vlcPath = @"/Applications/VLC.app";
2552         NSFileManager * fileManager = [NSFileManager defaultManager];
2553             if ([fileManager fileExistsAtPath:vlcPath] == 0) 
2554             {
2555             /*vlc not found in /Applications so we set the bool to cancel scanning to 1 */
2556             cancelScanDecrypt = 1;
2557             [self writeToActivityLog: "VLC app not found for decrypting physical dvd"];
2558             int status;
2559             status = NSRunAlertPanel(@"HandBrake could not find VLC.",@"Please download and install VLC media player in your /Applications folder if you wish to read encrypted DVDs.", @"Get VLC", @"Cancel Scan", @"Attempt Scan Anyway");
2560             [NSApp requestUserAttention:NSCriticalRequest];
2561             
2562             if (status == NSAlertDefaultReturn)
2563             {
2564                 /* User chose to go download vlc (as they rightfully should) so we send them to the vlc site */
2565                 [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://www.videolan.org/"]];
2566             }
2567             else if (status == NSAlertAlternateReturn)
2568             {
2569             /* User chose to cancel the scan */
2570             [self writeToActivityLog: "cannot open physical dvd , scan cancelled"];
2571             }
2572             else
2573             {
2574             /* User chose to override our warning and scan the physical dvd anyway, at their own peril. on an encrypted dvd this produces massive log files and fails */
2575             cancelScanDecrypt = 0;
2576             [self writeToActivityLog: "user overrode vlc warning -trying to open physical dvd without decryption"];
2577             }
2578
2579         }
2580         else
2581         {
2582             /* VLC was found in /Applications so all is well, we can carry on using vlc's libdvdcss.dylib for decrypting if needed */
2583             [self writeToActivityLog: "VLC app found for decrypting physical dvd"];
2584         }
2585     }
2586
2587     if (cancelScanDecrypt == 0)
2588     {
2589         /* we actually pass the scan off to libhb here */
2590         /* If there is no title number passed to scan, we use "0"
2591          * which causes the default behavior of a full source scan
2592          */
2593         if (!scanTitleNum)
2594         {
2595             scanTitleNum = 0;
2596         }
2597         if (scanTitleNum > 0)
2598         {
2599             [self writeToActivityLog: "scanning specifically for title: %d", scanTitleNum];
2600         }
2601         
2602         [self writeToActivityLog: "performNewQueueScan currentQueueEncodeIndex is: %d", currentQueueEncodeIndex];
2603         /* We use our advance pref to determine how many previews to scan */
2604         int hb_num_previews = [[[NSUserDefaults standardUserDefaults] objectForKey:@"PreviewsNumber"] intValue];
2605         hb_scan( fQueueEncodeLibhb, [path UTF8String], scanTitleNum, hb_num_previews, 0 );
2606     }
2607 }
2608
2609 /* This method was originally used to load up a new queue item in the gui and
2610  * then start processing it. However we now have modified -prepareJob and use a second
2611  * instance of libhb to do our actual encoding, therefor right now it is not required. 
2612  * Nonetheless I want to leave this in here
2613  * because basically its everything we need to be able to actually modify a pending queue
2614  * item in the gui and resave it. At least for now - dynaflash
2615  */
2616
2617 - (IBAction)applyQueueSettings:(id)sender
2618 {
2619     NSMutableDictionary * queueToApply = [QueueFileArray objectAtIndex:currentQueueEncodeIndex];
2620     hb_job_t * job = fTitle->job;
2621     
2622     /* Set title number and chapters */
2623     /* since the queue only scans a single title, we really don't need to pick a title */
2624     //[fSrcTitlePopUp selectItemAtIndex: [[queueToApply objectForKey:@"TitleNumber"] intValue] - 1];
2625     
2626     [fSrcChapterStartPopUp selectItemAtIndex: [[queueToApply objectForKey:@"ChapterStart"] intValue] - 1];
2627     [fSrcChapterEndPopUp selectItemAtIndex: [[queueToApply objectForKey:@"ChapterEnd"] intValue] - 1];
2628     
2629     /* File Format */
2630     [fDstFormatPopUp selectItemWithTitle:[queueToApply objectForKey:@"FileFormat"]];
2631     [self formatPopUpChanged:nil];
2632     
2633     /* Chapter Markers*/
2634     [fCreateChapterMarkers setState:[[queueToApply objectForKey:@"ChapterMarkers"] intValue]];
2635     /* Allow Mpeg4 64 bit formatting +4GB file sizes */
2636     [fDstMp4LargeFileCheck setState:[[queueToApply objectForKey:@"Mp4LargeFile"] intValue]];
2637     /* Mux mp4 with http optimization */
2638     [fDstMp4HttpOptFileCheck setState:[[queueToApply objectForKey:@"Mp4HttpOptimize"] intValue]];
2639     
2640     /* Video encoder */
2641     /* We set the advanced opt string here if applicable*/
2642     [fVidEncoderPopUp selectItemWithTitle:[queueToApply objectForKey:@"VideoEncoder"]];
2643     [fAdvancedOptions setOptions:[queueToApply objectForKey:@"x264Option"]];
2644     
2645     /* Lets run through the following functions to get variables set there */
2646     [self videoEncoderPopUpChanged:nil];
2647     /* Set the state of ipod compatible with Mp4iPodCompatible. Only for x264*/
2648     [fDstMp4iPodFileCheck setState:[[queueToApply objectForKey:@"Mp4iPodCompatible"] intValue]];
2649     [self calculateBitrate:nil];
2650     
2651     /* Video quality */
2652     [fVidQualityMatrix selectCellAtRow:[[queueToApply objectForKey:@"VideoQualityType"] intValue] column:0];
2653     
2654     [fVidTargetSizeField setStringValue:[queueToApply objectForKey:@"VideoTargetSize"]];
2655     [fVidBitrateField setStringValue:[queueToApply objectForKey:@"VideoAvgBitrate"]];
2656     [fVidQualitySlider setFloatValue:[[queueToApply objectForKey:@"VideoQualitySlider"] floatValue]];
2657     
2658     [self videoMatrixChanged:nil];
2659     
2660     /* Video framerate */
2661     /* For video preset video framerate, we want to make sure that Same as source does not conflict with the
2662      detected framerate in the fVidRatePopUp so we use index 0*/
2663     if ([[queueToApply objectForKey:@"VideoFramerate"] isEqualToString:@"Same as source"])
2664     {
2665         [fVidRatePopUp selectItemAtIndex: 0];
2666     }
2667     else
2668     {
2669         [fVidRatePopUp selectItemWithTitle:[queueToApply objectForKey:@"VideoFramerate"]];
2670     }
2671     
2672     /* 2 Pass Encoding */
2673     [fVidTwoPassCheck setState:[[queueToApply objectForKey:@"VideoTwoPass"] intValue]];
2674     [self twoPassCheckboxChanged:nil];
2675     /* Turbo 1st pass for 2 Pass Encoding */
2676     [fVidTurboPassCheck setState:[[queueToApply objectForKey:@"VideoTurboTwoPass"] intValue]];
2677     
2678     /*Audio*/
2679     if ([queueToApply objectForKey:@"Audio1Track"] > 0)
2680     {
2681         if ([fAudLang1PopUp indexOfSelectedItem] == 0)
2682         {
2683             [fAudLang1PopUp selectItemAtIndex: 1];
2684         }
2685         [self audioTrackPopUpChanged: fAudLang1PopUp];
2686         [fAudTrack1CodecPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio1Encoder"]];
2687         [self audioTrackPopUpChanged: fAudTrack1CodecPopUp];
2688         [fAudTrack1MixPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio1Mixdown"]];
2689         /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
2690          * mixdown*/
2691         if  ([fAudTrack1MixPopUp selectedItem] == nil)
2692         {
2693             [self audioTrackPopUpChanged: fAudTrack1CodecPopUp];
2694         }
2695         [fAudTrack1RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Samplerate"]];
2696         /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
2697         if (![[queueToApply objectForKey:@"Audio1Encoder"] isEqualToString:@"AC3 Passthru"])
2698         {
2699             [fAudTrack1BitratePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio1Bitrate"]];
2700         }
2701         [fAudTrack1DrcSlider setFloatValue:[[queueToApply objectForKey:@"Audio1TrackDRCSlider"] floatValue]];
2702         [self audioDRCSliderChanged: fAudTrack1DrcSlider];
2703     }
2704     if ([queueToApply objectForKey:@"Audio2Track"] > 0)
2705     {
2706         if ([fAudLang2PopUp indexOfSelectedItem] == 0)
2707         {
2708             [fAudLang2PopUp selectItemAtIndex: 1];
2709         }
2710         [self audioTrackPopUpChanged: fAudLang2PopUp];
2711         [fAudTrack2CodecPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio2Encoder"]];
2712         [self audioTrackPopUpChanged: fAudTrack2CodecPopUp];
2713         [fAudTrack2MixPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio2Mixdown"]];
2714         /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
2715          * mixdown*/
2716         if  ([fAudTrack2MixPopUp selectedItem] == nil)
2717         {
2718             [self audioTrackPopUpChanged: fAudTrack2CodecPopUp];
2719         }
2720         [fAudTrack2RatePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio2Samplerate"]];
2721         /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
2722         if (![[queueToApply objectForKey:@"Audio2Encoder"] isEqualToString:@"AC3 Passthru"])
2723         {
2724             [fAudTrack2BitratePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio2Bitrate"]];
2725         }
2726         [fAudTrack2DrcSlider setFloatValue:[[queueToApply objectForKey:@"Audio2TrackDRCSlider"] floatValue]];
2727         [self audioDRCSliderChanged: fAudTrack2DrcSlider];
2728     }
2729     if ([queueToApply objectForKey:@"Audio3Track"] > 0)
2730     {
2731         if ([fAudLang3PopUp indexOfSelectedItem] == 0)
2732         {
2733             [fAudLang3PopUp selectItemAtIndex: 1];
2734         }
2735         [self audioTrackPopUpChanged: fAudLang3PopUp];
2736         [fAudTrack3CodecPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio3Encoder"]];
2737         [self audioTrackPopUpChanged: fAudTrack3CodecPopUp];
2738         [fAudTrack3MixPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio3Mixdown"]];
2739         /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
2740          * mixdown*/
2741         if  ([fAudTrack3MixPopUp selectedItem] == nil)
2742         {
2743             [self audioTrackPopUpChanged: fAudTrack3CodecPopUp];
2744         }
2745         [fAudTrack3RatePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio3Samplerate"]];
2746         /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
2747         if (![[queueToApply objectForKey:@"Audio3Encoder"] isEqualToString: @"AC3 Passthru"])
2748         {
2749             [fAudTrack3BitratePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio3Bitrate"]];
2750         }
2751         [fAudTrack3DrcSlider setFloatValue:[[queueToApply objectForKey:@"Audio3TrackDRCSlider"] floatValue]];
2752         [self audioDRCSliderChanged: fAudTrack3DrcSlider];
2753     }
2754     if ([queueToApply objectForKey:@"Audio4Track"] > 0)
2755     {
2756         if ([fAudLang4PopUp indexOfSelectedItem] == 0)
2757         {
2758             [fAudLang4PopUp selectItemAtIndex: 1];
2759         }
2760         [self audioTrackPopUpChanged: fAudLang4PopUp];
2761         [fAudTrack4CodecPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio4Encoder"]];
2762         [self audioTrackPopUpChanged: fAudTrack4CodecPopUp];
2763         [fAudTrack4MixPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio4Mixdown"]];
2764         /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
2765          * mixdown*/
2766         if  ([fAudTrack4MixPopUp selectedItem] == nil)
2767         {
2768             [self audioTrackPopUpChanged: fAudTrack4CodecPopUp];
2769         }
2770         [fAudTrack4RatePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio4Samplerate"]];
2771         /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
2772         if (![[chosenPreset objectForKey:@"Audio4Encoder"] isEqualToString:@"AC3 Passthru"])
2773         {
2774             [fAudTrack4BitratePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio4Bitrate"]];
2775         }
2776         [fAudTrack4DrcSlider setFloatValue:[[queueToApply objectForKey:@"Audio4TrackDRCSlider"] floatValue]];
2777         [self audioDRCSliderChanged: fAudTrack4DrcSlider];
2778     }
2779     
2780     
2781     /*Subtitles*/
2782     [fSubPopUp selectItemWithTitle:[queueToApply objectForKey:@"Subtitles"]];
2783     /* Forced Subtitles */
2784     [fSubForcedCheck setState:[[queueToApply objectForKey:@"SubtitlesForced"] intValue]];
2785     
2786     /* Picture Settings */
2787     /* we check to make sure the presets width/height does not exceed the sources width/height */
2788     if (fTitle->width < [[queueToApply objectForKey:@"PictureWidth"]  intValue] || fTitle->height < [[queueToApply objectForKey:@"PictureHeight"]  intValue])
2789     {
2790         /* if so, then we use the sources height and width to avoid scaling up */
2791         job->width = fTitle->width;
2792         job->height = fTitle->height;
2793     }
2794     else // source width/height is >= the preset height/width
2795     {
2796         /* we can go ahead and use the presets values for height and width */
2797         job->width = [[queueToApply objectForKey:@"PictureWidth"]  intValue];
2798         job->height = [[queueToApply objectForKey:@"PictureHeight"]  intValue];
2799     }
2800     job->keep_ratio = [[queueToApply objectForKey:@"PictureKeepRatio"]  intValue];
2801     if (job->keep_ratio == 1)
2802     {
2803         hb_fix_aspect( job, HB_KEEP_WIDTH );
2804         if( job->height > fTitle->height )
2805         {
2806             job->height = fTitle->height;
2807             hb_fix_aspect( job, HB_KEEP_HEIGHT );
2808         }
2809     }
2810     job->anamorphic.mode = [[queueToApply objectForKey:@"PicturePAR"]  intValue];
2811     
2812     
2813     /* If Cropping is set to custom, then recall all four crop values from
2814      when the preset was created and apply them */
2815     if ([[queueToApply objectForKey:@"PictureAutoCrop"]  intValue] == 0)
2816     {
2817         [fPictureController setAutoCrop:NO];
2818         
2819         /* Here we use the custom crop values saved at the time the preset was saved */
2820         job->crop[0] = [[queueToApply objectForKey:@"PictureTopCrop"]  intValue];
2821         job->crop[1] = [[queueToApply objectForKey:@"PictureBottomCrop"]  intValue];
2822         job->crop[2] = [[queueToApply objectForKey:@"PictureLeftCrop"]  intValue];
2823         job->crop[3] = [[queueToApply objectForKey:@"PictureRightCrop"]  intValue];
2824         
2825     }
2826     else /* if auto crop has been saved in preset, set to auto and use post scan auto crop */
2827     {
2828         [fPictureController setAutoCrop:YES];
2829         /* Here we use the auto crop values determined right after scan */
2830         job->crop[0] = AutoCropTop;
2831         job->crop[1] = AutoCropBottom;
2832         job->crop[2] = AutoCropLeft;
2833         job->crop[3] = AutoCropRight;
2834         
2835     }
2836     
2837     /* Filters */
2838     /* Deinterlace */
2839     [fPictureController setDeinterlace:[[queueToApply objectForKey:@"PictureDeinterlace"] intValue]];
2840     
2841     /* Detelecine */
2842     [fPictureController setDetelecine:[[queueToApply objectForKey:@"PictureDetelecine"] intValue]];
2843     /* Denoise */
2844     [fPictureController setDenoise:[[queueToApply objectForKey:@"PictureDenoise"] intValue]];
2845     /* Deblock */
2846     [fPictureController setDeblock:[[queueToApply objectForKey:@"PictureDeblock"] intValue]];
2847     /* Decomb */
2848     [fPictureController setDecomb:[[queueToApply objectForKey:@"PictureDecomb"] intValue]];
2849     /* Grayscale */
2850     [fPictureController setGrayscale:[[queueToApply objectForKey:@"VideoGrayScale"] intValue]];
2851     
2852     [self calculatePictureSizing:nil];
2853     
2854     
2855     /* somehow we need to figure out a way to tie the queue item to a preset if it used one */
2856     //[queueFileJob setObject:[fPresetSelectedDisplay stringValue] forKey:@"PresetName"];
2857     //    [queueFileJob setObject:[NSNumber numberWithInt:[fPresetsOutlineView selectedRow]] forKey:@"PresetIndexNum"];
2858     if ([queueToApply objectForKey:@"PresetIndexNum"]) // This item used a preset so insert that info
2859         {
2860                 /* Deselect the currently selected Preset if there is one*/
2861         //[fPresetsOutlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:[[queueToApply objectForKey:@"PresetIndexNum"] intValue]] byExtendingSelection:NO];
2862         //[self selectPreset:nil];
2863                 
2864         //[fPresetsOutlineView selectRow:[[queueToApply objectForKey:@"PresetIndexNum"] intValue]];
2865                 /* Change UI to show "Custom" settings are being used */
2866                 //[fPresetSelectedDisplay setStringValue: [[queueToApply objectForKey:@"PresetName"] stringValue]];
2867         
2868                 curUserPresetChosenNum = nil;
2869         }
2870     else
2871     {
2872         /* Deselect the currently selected Preset if there is one*/
2873                 [fPresetsOutlineView deselectRow:[fPresetsOutlineView selectedRow]];
2874                 /* Change UI to show "Custom" settings are being used */
2875                 [fPresetSelectedDisplay setStringValue: @"Custom"];
2876         
2877                 //curUserPresetChosenNum = nil;
2878     }
2879     
2880     /* We need to set this bool back to NO, in case the user wants to do a scan */
2881     //applyQueueToScan = NO;
2882     
2883     /* so now we go ahead and process the new settings */
2884     [self processNewQueueEncode];
2885 }
2886
2887
2888
2889 /* This assumes that we have re-scanned and loaded up a new queue item to send to libhb as fQueueEncodeLibhb */
2890 - (void) processNewQueueEncode
2891 {
2892     hb_list_t  * list  = hb_get_titles( fQueueEncodeLibhb );
2893     hb_title_t * title = (hb_title_t *) hb_list_item( list,0 ); // is always zero since now its a single title scan
2894     hb_job_t * job = title->job;
2895     
2896     if( !hb_list_count( list ) )
2897     {
2898         [self writeToActivityLog: "processNewQueueEncode WARNING nothing found in the title list"];
2899     }
2900     
2901     NSMutableDictionary * queueToApply = [QueueFileArray objectAtIndex:currentQueueEncodeIndex];
2902     [self writeToActivityLog: "Preset: %s", [[queueToApply objectForKey:@"PresetName"] UTF8String]];
2903     [self writeToActivityLog: "processNewQueueEncode number of passes expected is: %d", ([[queueToApply objectForKey:@"VideoTwoPass"] intValue] + 1)];
2904     job->file = [[queueToApply objectForKey:@"DestinationPath"] UTF8String];
2905     //[self writeToActivityLog: "processNewQueueEncode sending to prepareJob"];
2906     [self prepareJob];
2907     
2908     /*
2909      * If scanning we need to do some extra setup of the job.
2910      */
2911     if( job->indepth_scan == 1 )
2912     {
2913         char *x264opts_tmp;
2914         
2915         /*
2916          * When subtitle scan is enabled do a fast pre-scan job
2917          * which will determine which subtitles to enable, if any.
2918          */
2919         job->pass = -1;
2920         x264opts_tmp = job->x264opts;
2921         
2922         job->x264opts = NULL;
2923         
2924         job->indepth_scan = 1;  
2925
2926         
2927         /*
2928          * Add the pre-scan job
2929          */
2930         hb_add( fQueueEncodeLibhb, job );
2931         job->x264opts = x264opts_tmp;
2932     }
2933
2934     
2935     if( [[queueToApply objectForKey:@"VideoTwoPass"] intValue] == 1 )
2936     {
2937         job->indepth_scan = 0;
2938         
2939
2940         
2941         job->pass = 1;
2942         
2943         hb_add( fQueueEncodeLibhb, job );
2944         
2945         job->pass = 2;
2946         
2947         job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */  
2948         strcpy(job->x264opts, [[queueToApply objectForKey:@"x264Option"] UTF8String]);
2949         
2950         hb_add( fQueueEncodeLibhb, job );
2951         
2952     }
2953     else
2954     {
2955         job->indepth_scan = 0;
2956         job->pass = 0;
2957         
2958         hb_add( fQueueEncodeLibhb, job );
2959     }
2960         
2961     NSString *destinationDirectory = [[queueToApply objectForKey:@"DestinationPath"] stringByDeletingLastPathComponent];
2962         [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];
2963         /* Lets mark our new encode as 1 or "Encoding" */
2964     [queueToApply setObject:[NSNumber numberWithInt:1] forKey:@"Status"];
2965     [self saveQueueFileItem];
2966     
2967     /* we need to clean up the subtitle tracks after the job(s) have been set  */
2968     int num_subtitle_tracks = hb_list_count(job->list_subtitle);
2969     int ii;
2970     for(ii = 0; ii < num_subtitle_tracks; ii++)
2971     {
2972         hb_subtitle_t * subtitle;
2973         subtitle = (hb_subtitle_t *)hb_list_item(job->list_subtitle, 0);
2974         
2975
2976         hb_list_rem(job->list_subtitle, subtitle);
2977         free(subtitle);
2978     }
2979     
2980     
2981     /* We should be all setup so let 'er rip */   
2982     [self doRip];
2983 }
2984
2985 #pragma mark -
2986 #pragma mark Live Preview
2987 /* Note,this is much like prepareJob, but directly sets the job vars so Picture Preview
2988  * can encode to its temp preview directory and playback. This is *not* used for any actual user
2989  * encodes
2990  */
2991 - (void) prepareJobForPreview
2992 {
2993     hb_list_t  * list  = hb_get_titles( fHandle );
2994     hb_title_t * title = (hb_title_t *) hb_list_item( list,
2995             [fSrcTitlePopUp indexOfSelectedItem] );
2996     hb_job_t * job = title->job;
2997     hb_audio_config_t * audio;
2998     /* set job->angle for libdvdnav */
2999     job->angle = [fSrcAnglePopUp indexOfSelectedItem] + 1;
3000     /* Chapter selection */
3001     job->chapter_start = [fSrcChapterStartPopUp indexOfSelectedItem] + 1;
3002     job->chapter_end   = [fSrcChapterEndPopUp   indexOfSelectedItem] + 1;
3003         
3004     /* Format (Muxer) and Video Encoder */
3005     job->mux = [[fDstFormatPopUp selectedItem] tag];
3006     job->vcodec = [[fVidEncoderPopUp selectedItem] tag];
3007
3008     job->chapter_markers = 0;
3009     
3010         if( job->vcodec & HB_VCODEC_X264 )
3011     {
3012                 
3013                 /* Below Sends x264 options to the core library if x264 is selected*/
3014                 /* Lets use this as per Nyx, Thanks Nyx!*/
3015                 job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */
3016                 /* For previews we ignore the turbo option for the first pass of two since we only use 1 pass */
3017                 strcpy(job->x264opts, [[fAdvancedOptions optionsString] UTF8String]);
3018
3019         
3020     }
3021
3022     /* Video settings */
3023    /* Set vfr to 0 as it's only on if using same as source in the framerate popup
3024      * and detelecine is on, so we handle that in the logic below
3025      */
3026     job->vfr = 0;
3027     if( [fVidRatePopUp indexOfSelectedItem] > 0 )
3028     {
3029         /* a specific framerate has been chosen */
3030         job->vrate      = 27000000;
3031         job->vrate_base = hb_video_rates[[fVidRatePopUp indexOfSelectedItem]-1].rate;
3032         /* We are not same as source so we set job->cfr to 1 
3033          * to enable constant frame rate since user has specified
3034          * a specific framerate*/
3035         job->cfr = 1;
3036     }
3037     else
3038     {
3039         /* We are same as source (variable) */
3040         job->vrate      = title->rate;
3041         job->vrate_base = title->rate_base;
3042         /* We are same as source so we set job->cfr to 0 
3043          * to enable true same as source framerate */
3044         job->cfr = 0;
3045         /* If we are same as source and we have detelecine on, we need to turn on
3046          * job->vfr
3047          */
3048         if ([fPictureController detelecine] == 1)
3049         {
3050             job->vfr = 1;
3051         }
3052     }
3053
3054     switch( [fVidQualityMatrix selectedRow] )
3055     {
3056         case 0:
3057             /* Target size.
3058                Bitrate should already have been calculated and displayed
3059                in fVidBitrateField, so let's just use it */
3060         case 1:
3061             job->vquality = -1.0;
3062             job->vbitrate = [fVidBitrateField intValue];
3063             break;
3064         case 2:
3065             job->vquality = [fVidQualityRFField floatValue];
3066             job->vbitrate = 0;
3067             break;
3068     }
3069
3070     /* Subtitle settings */
3071     NSMutableArray *subtitlesArray = [[NSMutableArray alloc] initWithArray:[fSubtitlesDelegate getSubtitleArray] copyItems:YES];
3072     
3073     
3074 int subtitle = nil;
3075 int force;
3076 int burned;
3077 int def;
3078 bool one_burned = FALSE;
3079
3080     int i = 0;
3081     NSEnumerator *enumerator = [subtitlesArray objectEnumerator];
3082     id tempObject;
3083     while (tempObject = [enumerator nextObject])
3084     {
3085         
3086         subtitle = [[tempObject objectForKey:@"subtitleSourceTrackNum"] intValue];
3087         force = [[tempObject objectForKey:@"subtitleTrackForced"] intValue];
3088         burned = [[tempObject objectForKey:@"subtitleTrackBurned"] intValue];
3089         def = [[tempObject objectForKey:@"subtitleTrackDefault"] intValue];
3090         
3091         /* since the subtitleSourceTrackNum 0 is "None" in our array of the subtitle popups,
3092          * we want to ignore it for display as well as encoding.
3093          */
3094         if (subtitle > 0)
3095         {
3096             /* if i is 0, then we are in the first item of the subtitles which we need to 
3097              * check for the "Foreign Audio Search" which would be subtitleSourceTrackNum of 1
3098              * bearing in mind that for all tracks subtitleSourceTrackNum of 0 is None.
3099              */
3100             
3101             /* if we are on the first track and using "Foreign Audio Search" */ 
3102             if (i == 0 && subtitle == 1)
3103             {
3104                 /* NOTE: Currently foreign language search is borked for preview.
3105                  * Commented out but left in for initial commit. */
3106                 
3107                 
3108                 [self writeToActivityLog: "Foreign Language Search: %d", 1];
3109                 
3110                 job->indepth_scan = 1;
3111                 if (burned == 1 || job->mux != HB_MUX_MP4)
3112                 {
3113                     if (burned != 1 && job->mux == HB_MUX_MKV)
3114                     {
3115                         job->select_subtitle_config.dest = PASSTHRUSUB;
3116                     }
3117                     else
3118                     {
3119                         job->select_subtitle_config.dest = RENDERSUB;
3120                     }
3121                     
3122                     job->select_subtitle_config.force = force;
3123                     job->select_subtitle_config.default_track = def;
3124                     
3125                 }
3126                 
3127                 
3128             }
3129             else
3130             {
3131                 
3132                 /* for the actual source tracks, we must subtract the non source entries so 
3133                  * that the menu index matches the source subtitle_list index for convenience */
3134                 if (i == 0)
3135                 {
3136                     /* for the first track, the source tracks start at menu index 2 ( None is 0,
3137                      * Foreign Language Search is 1) so subtract 2 */
3138                     subtitle = subtitle - 2;
3139                 }
3140                 else
3141                 {
3142                     /* for all other tracks, the source tracks start at menu index 1 (None is 0)
3143                      * so subtract 1. */
3144                     
3145                     subtitle = subtitle - 1;
3146                 }
3147                 
3148                 /* We are setting a source subtitle so access the source subtitle info */  
3149                 hb_subtitle_t * subt;
3150                 
3151                 subt = (hb_subtitle_t *)hb_list_item(title->list_subtitle, subtitle);
3152                 
3153                 /* if we are getting the subtitles from an external srt file */
3154                 if ([[tempObject objectForKey:@"subtitleSourceTrackType"] isEqualToString:@"SRT"])
3155                 {
3156                     hb_subtitle_config_t sub_config;
3157                     
3158                     sub_config.offset = [[tempObject objectForKey:@"subtitleTrackSrtOffset"] intValue];
3159                     
3160                     /* we need to srncpy file path and char code */
3161                     strncpy(sub_config.src_filename, [[tempObject objectForKey:@"subtitleSourceSrtFilePath"] UTF8String], 128);
3162                     strncpy(sub_config.src_codeset, [[tempObject objectForKey:@"subtitleTrackSrtCharCode"] UTF8String], 40);
3163                     
3164                     sub_config.force = 0;
3165                     sub_config.dest = PASSTHRUSUB;
3166                     sub_config.default_track = def;
3167                     
3168                     hb_srt_add( job, &sub_config, [[tempObject objectForKey:@"subtitleTrackSrtLanguageIso3"] UTF8String]);
3169                 }
3170                 
3171                 if (subt != NULL)
3172                 {
3173                     [self writeToActivityLog: "Setting Subtitle: %s", subt];
3174
3175                     hb_subtitle_config_t sub_config = subt->config;
3176                     
3177                     if (!burned && job->mux == HB_MUX_MKV && 
3178                         subt->format == PICTURESUB)
3179                     {
3180                         sub_config.dest = PASSTHRUSUB;
3181                     }
3182                     else if (!burned && job->mux == HB_MUX_MP4 && 
3183                              subt->format == PICTURESUB)
3184                     {
3185                         // Skip any non-burned vobsubs when output is mp4
3186                         continue;
3187                     }
3188                     else if ( burned && subt->format == PICTURESUB )
3189                     {
3190                         // Only allow one subtitle to be burned into the video
3191                         if (one_burned)
3192                             continue;
3193                         one_burned = TRUE;
3194                     }
3195                     sub_config.force = force;
3196                     sub_config.default_track = def;
3197                     hb_subtitle_add( job, &sub_config, subtitle );
3198                 }   
3199                 
3200             }
3201         }
3202         i++;
3203     }
3204    
3205     
3206     
3207 [subtitlesArray autorelease];    
3208     
3209     
3210     /* Audio tracks and mixdowns */
3211     /* Lets make sure there arent any erroneous audio tracks in the job list, so lets make sure its empty*/
3212     int audiotrack_count = hb_list_count(job->list_audio);
3213     for( int i = 0; i < audiotrack_count;i++)
3214     {
3215         hb_audio_t * temp_audio = (hb_audio_t*) hb_list_item( job->list_audio, 0 );
3216         hb_list_rem(job->list_audio, temp_audio);
3217     }
3218     /* Now lets add our new tracks to the audio list here */
3219     if ([fAudLang1PopUp indexOfSelectedItem] > 0)
3220     {
3221         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3222         hb_audio_config_init(audio);
3223         audio->in.track = [fAudLang1PopUp indexOfSelectedItem] - 1;
3224         /* We go ahead and assign values to our audio->out.<properties> */
3225         audio->out.track = [fAudLang1PopUp indexOfSelectedItem] - 1;
3226         audio->out.codec = [[fAudTrack1CodecPopUp selectedItem] tag];
3227         audio->out.mixdown = [[fAudTrack1MixPopUp selectedItem] tag];
3228         audio->out.bitrate = [[fAudTrack1BitratePopUp selectedItem] tag];
3229         audio->out.samplerate = [[fAudTrack1RatePopUp selectedItem] tag];
3230         audio->out.dynamic_range_compression = [fAudTrack1DrcField floatValue];
3231         
3232         hb_audio_add( job, audio );
3233         free(audio);
3234     }  
3235     if ([fAudLang2PopUp indexOfSelectedItem] > 0)
3236     {
3237         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3238         hb_audio_config_init(audio);
3239         audio->in.track = [fAudLang2PopUp indexOfSelectedItem] - 1;
3240         /* We go ahead and assign values to our audio->out.<properties> */
3241         audio->out.track = [fAudLang2PopUp indexOfSelectedItem] - 1;
3242         audio->out.codec = [[fAudTrack2CodecPopUp selectedItem] tag];
3243         audio->out.mixdown = [[fAudTrack2MixPopUp selectedItem] tag];
3244         audio->out.bitrate = [[fAudTrack2BitratePopUp selectedItem] tag];
3245         audio->out.samplerate = [[fAudTrack2RatePopUp selectedItem] tag];
3246         audio->out.dynamic_range_compression = [fAudTrack2DrcField floatValue];
3247         
3248         hb_audio_add( job, audio );
3249         free(audio);
3250         
3251     }
3252     
3253     if ([fAudLang3PopUp indexOfSelectedItem] > 0)
3254     {
3255         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3256         hb_audio_config_init(audio);
3257         audio->in.track = [fAudLang3PopUp indexOfSelectedItem] - 1;
3258         /* We go ahead and assign values to our audio->out.<properties> */
3259         audio->out.track = [fAudLang3PopUp indexOfSelectedItem] - 1;
3260         audio->out.codec = [[fAudTrack3CodecPopUp selectedItem] tag];
3261         audio->out.mixdown = [[fAudTrack3MixPopUp selectedItem] tag];
3262         audio->out.bitrate = [[fAudTrack3BitratePopUp selectedItem] tag];
3263         audio->out.samplerate = [[fAudTrack3RatePopUp selectedItem] tag];
3264         audio->out.dynamic_range_compression = [fAudTrack3DrcField floatValue];
3265         
3266         hb_audio_add( job, audio );
3267         free(audio);
3268         
3269     }
3270
3271     if ([fAudLang4PopUp indexOfSelectedItem] > 0)
3272     {
3273         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3274         hb_audio_config_init(audio);
3275         audio->in.track = [fAudLang4PopUp indexOfSelectedItem] - 1;
3276         /* We go ahead and assign values to our audio->out.<properties> */
3277         audio->out.track = [fAudLang4PopUp indexOfSelectedItem] - 1;
3278         audio->out.codec = [[fAudTrack4CodecPopUp selectedItem] tag];
3279         audio->out.mixdown = [[fAudTrack4MixPopUp selectedItem] tag];
3280         audio->out.bitrate = [[fAudTrack4BitratePopUp selectedItem] tag];
3281         audio->out.samplerate = [[fAudTrack4RatePopUp selectedItem] tag];
3282         audio->out.dynamic_range_compression = [fAudTrack4DrcField floatValue];
3283         
3284         hb_audio_add( job, audio );
3285         free(audio);
3286         
3287     }
3288
3289     
3290     
3291     /* Filters */
3292     
3293     /* Though Grayscale is not really a filter, per se
3294      * we put it here since its in the filters panel
3295      */
3296      
3297     if ([fPictureController grayscale])
3298     {
3299         job->grayscale = 1;
3300     }
3301     else
3302     {
3303         job->grayscale = 0;
3304     }
3305     
3306     /* Initialize the filters list */
3307     job->filters = hb_list_init();
3308     
3309     /* Now lets call the filters if applicable.
3310     * The order of the filters is critical
3311     */
3312     
3313         /* Detelecine */
3314     if ([fPictureController detelecine] == 1)
3315     {
3316         /* use a custom detelecine string */
3317         hb_filter_detelecine.settings = (char *) [[fPictureController detelecineCustomString] UTF8String];
3318         hb_list_add( job->filters, &hb_filter_detelecine );
3319     }
3320     if ([fPictureController detelecine] == 2)
3321     {
3322         /* Default */
3323         hb_list_add( job->filters, &hb_filter_detelecine );
3324     }
3325     
3326     
3327     
3328     if ([fPictureController useDecomb] == 1)
3329     {
3330         /* Decomb */
3331         /* we add the custom string if present */
3332         if ([fPictureController decomb] == 1)
3333         {
3334             /* use a custom decomb string */
3335             hb_filter_decomb.settings = (char *) [[fPictureController decombCustomString] UTF8String];
3336             hb_list_add( job->filters, &hb_filter_decomb );
3337         }
3338         if ([fPictureController decomb] == 2)
3339         {
3340             /* Run old deinterlacer fd by default */
3341             //hb_filter_decomb.settings = (char *) [[fPicSettingDecomb stringValue] UTF8String];
3342             hb_list_add( job->filters, &hb_filter_decomb );
3343         }
3344     }
3345     else
3346     {
3347         
3348         /* Deinterlace */
3349         if ([fPictureController deinterlace] == 1)
3350         {
3351             /* we add the custom string if present */
3352             hb_filter_deinterlace.settings = (char *) [[fPictureController deinterlaceCustomString] UTF8String];
3353             hb_list_add( job->filters, &hb_filter_deinterlace );            
3354         }
3355         else if ([fPictureController deinterlace] == 2)
3356         {
3357             /* Run old deinterlacer fd by default */
3358             hb_filter_deinterlace.settings = "-1"; 
3359             hb_list_add( job->filters, &hb_filter_deinterlace );
3360         }
3361         else if ([fPictureController deinterlace] == 3)
3362         {
3363             /* Yadif mode 0 (without spatial deinterlacing.) */
3364             hb_filter_deinterlace.settings = "2"; 
3365             hb_list_add( job->filters, &hb_filter_deinterlace );            
3366         }
3367         else if ([fPictureController deinterlace] == 4)
3368         {
3369             /* Yadif (with spatial deinterlacing) */
3370             hb_filter_deinterlace.settings = "0"; 
3371             hb_list_add( job->filters, &hb_filter_deinterlace );            
3372         }
3373         
3374         }
3375     
3376     /* Denoise */
3377         if ([fPictureController denoise] == 1) // custom in popup
3378         {
3379                 /* we add the custom string if present */
3380         hb_filter_denoise.settings = (char *) [[fPictureController denoiseCustomString] UTF8String]; 
3381         hb_list_add( job->filters, &hb_filter_denoise );        
3382         }
3383     else if ([fPictureController denoise] == 2) // Weak in popup
3384         {
3385                 hb_filter_denoise.settings = "2:1:2:3"; 
3386         hb_list_add( job->filters, &hb_filter_denoise );        
3387         }
3388         else if ([fPictureController denoise] == 3) // Medium in popup
3389         {
3390                 hb_filter_denoise.settings = "3:2:2:3"; 
3391         hb_list_add( job->filters, &hb_filter_denoise );        
3392         }
3393         else if ([fPictureController denoise] == 4) // Strong in popup
3394         {
3395                 hb_filter_denoise.settings = "7:7:5:5"; 
3396         hb_list_add( job->filters, &hb_filter_denoise );        
3397         }
3398     
3399     
3400     /* Deblock  (uses pp7 default) */
3401     /* NOTE: even though there is a valid deblock setting of 0 for the filter, for 
3402      * the macgui's purposes a value of 0 actually means to not even use the filter
3403      * current hb_filter_deblock.settings valid ranges are from 5 - 15 
3404      */
3405     if ([fPictureController deblock] != 0)
3406     {
3407         NSString *deblockStringValue = [NSString stringWithFormat: @"%d",[fPictureController deblock]];
3408         hb_filter_deblock.settings = (char *) [deblockStringValue UTF8String];
3409         hb_list_add( job->filters, &hb_filter_deblock );
3410     }
3411
3412 }
3413
3414
3415 #pragma mark -
3416 #pragma mark Job Handling
3417
3418
3419 - (void) prepareJob
3420 {
3421     
3422     NSMutableDictionary * queueToApply = [QueueFileArray objectAtIndex:currentQueueEncodeIndex];
3423     hb_list_t  * list  = hb_get_titles( fQueueEncodeLibhb );
3424     hb_title_t * title = (hb_title_t *) hb_list_item( list,0 ); // is always zero since now its a single title scan
3425     hb_job_t * job = title->job;
3426     hb_audio_config_t * audio;
3427     /* Title Angle for dvdnav */
3428     job->angle = [[queueToApply objectForKey:@"TitleAngle"] intValue];
3429     
3430     if([[queueToApply objectForKey:@"fEncodeStartStop"] intValue] == 0)
3431     {
3432         /* Chapter selection */
3433         [self writeToActivityLog: "Start / Stop set to chapters"];
3434         job->chapter_start = [[queueToApply objectForKey:@"JobChapterStart"] intValue];
3435         job->chapter_end   = [[queueToApply objectForKey:@"JobChapterEnd"] intValue];
3436     }
3437     else if ([[queueToApply objectForKey:@"fEncodeStartStop"] intValue] == 1)
3438     {
3439         /* we are pts based start / stop */
3440         [self writeToActivityLog: "Start / Stop set to seconds ..."];
3441         
3442         /* Point A to Point B. Since we cannot get frame accurate start times, attempt to glean a semi-accurate start time based on a percentage of the
3443          * scanned title time as per live preview, while in some cases inaccurate its the best I can do with what I have barring a pre-scan index afaik.
3444          */
3445         /* Attempt to bastardize the live preview code to get a roughly 1 second accurate point a to point b encode ... */
3446         /* get the start seconds from the start seconds field */
3447         int start_seconds = [[queueToApply objectForKey:@"StartSeconds"] intValue];
3448         //job->start_at_preview = start_seconds;
3449         /* The number of seek points equals the number of seconds announced in the title as that is our current granularity */
3450         //job->seek_points = [[queueToApply objectForKey:@"SourceTotalSeconds"] intValue];
3451         job->pts_to_start = start_seconds * 90000LL;
3452         /* Stop seconds is actually the duration of encode, so subtract the end seconds from the start seconds */
3453         int stop_seconds = [[queueToApply objectForKey:@"StopSeconds"] intValue];
3454         job->pts_to_stop = stop_seconds * 90000LL;
3455
3456         /* A bunch of verbose activity log messages to check on what should be expected */
3457         [self writeToActivityLog: "point a to b should start at: %d seconds", start_seconds];
3458         [self writeToActivityLog: "point a to b should start at (hh:mm:ss): %d:%d:%d", start_seconds / 3600, ( start_seconds / 60 ) % 60,start_seconds % 60];
3459         [self writeToActivityLog: "point a to b duration: %d seconds", stop_seconds];
3460         [self writeToActivityLog: "point a to b duration (hh:mm:ss): %d:%d:%d", stop_seconds / 3600, ( stop_seconds / 60 ) % 60,stop_seconds % 60];
3461         [self writeToActivityLog: "point a to b should end at: %d seconds", start_seconds + stop_seconds];
3462         [self writeToActivityLog: "point a to b should end at (hh:mm:ss): %d:%d:%d", (start_seconds + stop_seconds) / 3600, ( (start_seconds + stop_seconds) / 60 ) % 60,(start_seconds + stop_seconds) % 60];
3463     }
3464     else if ([[queueToApply objectForKey:@"fEncodeStartStop"] intValue] == 2)
3465     {
3466         /* we are frame based start / stop */
3467         [self writeToActivityLog: "Start / Stop set to frames ..."];
3468         
3469         /* Point A to Point B. Since we cannot get frame accurate start times, attempt to glean a semi-accurate start time based on a percentage of the
3470          * scanned title time as per live preview, while in some cases inaccurate its the best I can do with what I have barring a pre-scan index afaik.
3471          */
3472         /* Attempt to bastardize the live preview code to get a roughly 1 second accurate point a to point b encode ... */
3473         /* get the start seconds from the start seconds field */
3474         int start_frame = [[queueToApply objectForKey:@"StartFrame"] intValue];
3475         //job->start_at_preview = start_seconds;
3476         /* The number of seek points equals the number of seconds announced in the title as that is our current granularity */
3477         //job->seek_points = [[queueToApply objectForKey:@"SourceTotalSeconds"] intValue];
3478         job->frame_to_start = start_frame;
3479         /* Stop seconds is actually the duration of encode, so subtract the end seconds from the start seconds */
3480         int stop_frame = [[queueToApply objectForKey:@"StopFrame"] intValue];
3481         job->frame_to_stop = stop_frame;
3482
3483         /* A bunch of verbose activity log messages to check on what should be expected */
3484         [self writeToActivityLog: "point a to b should start at frame %d", start_frame];
3485         [self writeToActivityLog: "point a to b duration: %d frames", stop_frame];
3486         [self writeToActivityLog: "point a to b should end at frame %d", start_frame + stop_frame];
3487     }
3488
3489         
3490         
3491     
3492     /* Format (Muxer) and Video Encoder */
3493     job->mux = [[queueToApply objectForKey:@"JobFileFormatMux"] intValue];
3494     job->vcodec = [[queueToApply objectForKey:@"JobVideoEncoderVcodec"] intValue];
3495     
3496     
3497     /* If mpeg-4, then set mpeg-4 specific options like chapters and > 4gb file sizes */
3498     if( [[queueToApply objectForKey:@"Mp4LargeFile"] intValue] == 1)
3499     {
3500         job->largeFileSize = 1;
3501     }
3502     else
3503     {
3504         job->largeFileSize = 0;
3505     }
3506     /* We set http optimized mp4 here */
3507     if( [[queueToApply objectForKey:@"Mp4HttpOptimize"] intValue] == 1 )
3508     {
3509         job->mp4_optimize = 1;
3510     }
3511     else
3512     {
3513         job->mp4_optimize = 0;
3514     }
3515
3516         
3517     /* We set the chapter marker extraction here based on the format being
3518      mpeg4 or mkv and the checkbox being checked */
3519     if ([[queueToApply objectForKey:@"ChapterMarkers"] intValue] == 1)
3520     {
3521         job->chapter_markers = 1;
3522         
3523         /* now lets get our saved chapter names out the array in the queue file
3524          * and insert them back into the title chapter list. We have it here,
3525          * because unless we are inserting chapter markers there is no need to
3526          * spend the overhead of iterating through the chapter names array imo
3527          * Also, note that if for some reason we don't apply chapter names, the
3528          * chapters just come out 001, 002, etc. etc.
3529          */
3530          
3531         NSMutableArray *ChapterNamesArray = [queueToApply objectForKey:@"ChapterNames"];
3532         int i = 0;
3533         NSEnumerator *enumerator = [ChapterNamesArray objectEnumerator];
3534         id tempObject;
3535         while (tempObject = [enumerator nextObject])
3536         {
3537             hb_chapter_t *chapter = (hb_chapter_t *) hb_list_item( title->list_chapter, i );
3538             if( chapter != NULL )
3539             {
3540                 strncpy( chapter->title, [tempObject UTF8String], 1023);
3541                 chapter->title[1023] = '\0';
3542             }
3543             i++;
3544         }
3545     }
3546     else
3547     {
3548         job->chapter_markers = 0;
3549     }
3550     
3551     if( job->vcodec & HB_VCODEC_X264 )
3552     {
3553                 if ([[queueToApply objectForKey:@"Mp4iPodCompatible"] intValue] == 1)
3554             {
3555             job->ipod_atom = 1;
3556                 }
3557         else
3558         {
3559             job->ipod_atom = 0;
3560         }
3561                 
3562                 
3563                 /* Below Sends x264 options to the core library if x264 is selected*/
3564                 /* Lets use this as per Nyx, Thanks Nyx!*/
3565                 job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */
3566                 /* Turbo first pass if two pass and Turbo First pass is selected */
3567                 if( [[queueToApply objectForKey:@"VideoTwoPass"] intValue] == 1 && [[queueToApply objectForKey:@"VideoTurboTwoPass"] intValue] == 1 )
3568                 {
3569                         /* pass the "Turbo" string to be appended to the existing x264 opts string into a variable for the first pass */
3570                         NSString *firstPassOptStringTurbo = @":ref=1:subme=2:me=dia:analyse=none:trellis=0:no-fast-pskip=0:8x8dct=0:weightb=0";
3571                         /* append the "Turbo" string variable to the existing opts string.
3572              Note: the "Turbo" string must be appended, not prepended to work properly*/
3573                         NSString *firstPassOptStringCombined = [[queueToApply objectForKey:@"x264Option"] stringByAppendingString:firstPassOptStringTurbo];
3574                         strcpy(job->x264opts, [firstPassOptStringCombined UTF8String]);
3575                 }
3576                 else
3577                 {
3578                         strcpy(job->x264opts, [[queueToApply objectForKey:@"x264Option"] UTF8String]);
3579                 }
3580         
3581     }
3582     
3583     
3584     /* Picture Size Settings */
3585     job->width = [[queueToApply objectForKey:@"PictureWidth"]  intValue];
3586     job->height = [[queueToApply objectForKey:@"PictureHeight"]  intValue];
3587     
3588     job->keep_ratio = [[queueToApply objectForKey:@"PictureKeepRatio"]  intValue];
3589     job->anamorphic.mode = [[queueToApply objectForKey:@"PicturePAR"]  intValue];
3590     if ([[queueToApply objectForKey:@"PicturePAR"]  intValue] == 3)
3591     {
3592         /* insert our custom values here for capuj */
3593         job->width = [[queueToApply objectForKey:@"PicturePARStorageWidth"]  intValue];
3594         job->height = [[queueToApply objectForKey:@"PicturePARStorageHeight"]  intValue];
3595         
3596         job->anamorphic.modulus = [[queueToApply objectForKey:@"PicturePARModulus"] intValue];
3597         
3598         job->anamorphic.par_width = [[queueToApply objectForKey:@"PicturePARPixelWidth"]  intValue];
3599         job->anamorphic.par_height = [[queueToApply objectForKey:@"PicturePARPixelHeight"]  intValue];
3600         
3601         job->anamorphic.dar_width = [[queueToApply objectForKey:@"PicturePARDisplayWidth"]  floatValue];
3602         job->anamorphic.dar_height = [[queueToApply objectForKey:@"PicturePARDisplayHeight"]  floatValue];
3603     }
3604     
3605     /* Here we use the crop values saved at the time the preset was saved */
3606     job->crop[0] = [[queueToApply objectForKey:@"PictureTopCrop"]  intValue];
3607     job->crop[1] = [[queueToApply objectForKey:@"PictureBottomCrop"]  intValue];
3608     job->crop[2] = [[queueToApply objectForKey:@"PictureLeftCrop"]  intValue];
3609     job->crop[3] = [[queueToApply objectForKey:@"PictureRightCrop"]  intValue];
3610     
3611     /* Video settings */
3612     /* Framerate */
3613     
3614     /* Set vfr to 0 as it's only on if using same as source in the framerate popup
3615      * and detelecine is on, so we handle that in the logic below
3616      */
3617     job->vfr = 0;
3618     if( [[queueToApply objectForKey:@"JobIndexVideoFramerate"] intValue] > 0 )
3619     {
3620         /* a specific framerate has been chosen */
3621         job->vrate      = 27000000;
3622         job->vrate_base = hb_video_rates[[[queueToApply objectForKey:@"JobIndexVideoFramerate"] intValue]-1].rate;
3623         /* We are not same as source so we set job->cfr to 1 
3624          * to enable constant frame rate since user has specified
3625          * a specific framerate*/
3626         job->cfr = 1;
3627     }
3628     else
3629     {
3630         /* We are same as source (variable) */
3631         job->vrate      = [[queueToApply objectForKey:@"JobVrate"] intValue];
3632         job->vrate_base = [[queueToApply objectForKey:@"JobVrateBase"] intValue];
3633         /* We are same as source so we set job->cfr to 0 
3634          * to enable true same as source framerate */
3635         job->cfr = 0;
3636         /* If we are same as source and we have detelecine on, we need to turn on
3637          * job->vfr
3638          */
3639         if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] == 1)
3640         {
3641             job->vfr = 1;
3642         }
3643     }
3644     
3645     if ( [[queueToApply objectForKey:@"VideoQualityType"] intValue] != 2 )
3646     {
3647         /* Target size.
3648          Bitrate should already have been calculated and displayed
3649          in fVidBitrateField, so let's just use it same as abr*/
3650         job->vquality = -1.0;
3651         job->vbitrate = [[queueToApply objectForKey:@"VideoAvgBitrate"] intValue];
3652     }
3653     if ( [[queueToApply objectForKey:@"VideoQualityType"] intValue] == 2 )
3654     {
3655         job->vquality = [[queueToApply objectForKey:@"VideoQualitySlider"] floatValue];
3656         job->vbitrate = 0;
3657         
3658     }
3659     
3660     job->grayscale = [[queueToApply objectForKey:@"VideoGrayScale"] intValue];
3661     
3662
3663
3664 #pragma mark -
3665 #pragma mark Process Subtitles to libhb
3666
3667 /* Map the settings in the dictionaries for the SubtitleList array to match title->list_subtitle
3668  * which means that we need to account for the offset of non source language settings in from
3669  * the NSPopUpCell menu. For all of the objects in the SubtitleList array this means 0 is "None"
3670  * from the popup menu, additionally the first track has "Foreign Audio Search" at 1. So we use
3671  * an int to offset the index number for the objectForKey:@"subtitleSourceTrackNum" to map that
3672  * to the source tracks position in title->list_subtitle.
3673  */
3674
3675 int subtitle = nil;
3676 int force;
3677 int burned;
3678 int def;
3679 bool one_burned = FALSE;
3680
3681     int i = 0;
3682     NSEnumerator *enumerator = [[queueToApply objectForKey:@"SubtitleList"] objectEnumerator];
3683     id tempObject;
3684     while (tempObject = [enumerator nextObject])
3685     {
3686         
3687         subtitle = [[tempObject objectForKey:@"subtitleSourceTrackNum"] intValue];
3688         force = [[tempObject objectForKey:@"subtitleTrackForced"] intValue];
3689         burned = [[tempObject objectForKey:@"subtitleTrackBurned"] intValue];
3690         def = [[tempObject objectForKey:@"subtitleTrackDefault"] intValue];
3691         
3692         /* since the subtitleSourceTrackNum 0 is "None" in our array of the subtitle popups,
3693          * we want to ignore it for display as well as encoding.
3694          */
3695         if (subtitle > 0)
3696         {
3697             /* if i is 0, then we are in the first item of the subtitles which we need to 
3698              * check for the "Foreign Audio Search" which would be subtitleSourceTrackNum of 1
3699              * bearing in mind that for all tracks subtitleSourceTrackNum of 0 is None.
3700              */
3701             
3702             /* if we are on the first track and using "Foreign Audio Search" */ 
3703             if (i == 0 && subtitle == 1)
3704             {
3705                 [self writeToActivityLog: "Foreign Language Search: %d", 1];
3706                 
3707                 job->indepth_scan = 1;
3708                 if (burned == 1 || job->mux != HB_MUX_MP4)
3709                 {
3710                     if (burned != 1 && job->mux == HB_MUX_MKV)
3711                     {
3712                         job->select_subtitle_config.dest = PASSTHRUSUB;
3713                     }
3714                     else
3715                     {
3716                         job->select_subtitle_config.dest = RENDERSUB;
3717                     }
3718                     
3719                     job->select_subtitle_config.force = force;
3720                     job->select_subtitle_config.default_track = def;
3721                 }
3722                 
3723                 
3724             }
3725             else
3726             {
3727                 
3728                 /* for the actual source tracks, we must subtract the non source entries so 
3729                  * that the menu index matches the source subtitle_list index for convenience */
3730                 if (i == 0)
3731                 {
3732                     /* for the first track, the source tracks start at menu index 2 ( None is 0,
3733                      * Foreign Language Search is 1) so subtract 2 */
3734                     subtitle = subtitle - 2;
3735                 }
3736                 else
3737                 {
3738                     /* for all other tracks, the source tracks start at menu index 1 (None is 0)
3739                      * so subtract 1. */
3740                     
3741                     subtitle = subtitle - 1;
3742                 }
3743                 
3744                 /* We are setting a source subtitle so access the source subtitle info */  
3745                 hb_subtitle_t * subt;
3746                 
3747                 subt = (hb_subtitle_t *)hb_list_item(title->list_subtitle, subtitle);
3748                 
3749                 /* if we are getting the subtitles from an external srt file */
3750                 if ([[tempObject objectForKey:@"subtitleSourceTrackType"] isEqualToString:@"SRT"])
3751                 {
3752                     hb_subtitle_config_t sub_config;
3753                     
3754                     sub_config.offset = [[tempObject objectForKey:@"subtitleTrackSrtOffset"] intValue];
3755                     
3756                     /* we need to srncpy file name and codeset */
3757                     //sub_config.src_filename = [[tempObject objectForKey:@"subtitleSourceSrtFilePath"] UTF8String];
3758                     strncpy(sub_config.src_filename, [[tempObject objectForKey:@"subtitleSourceSrtFilePath"] UTF8String], 128);
3759                     //sub_config.src_codeset = [[tempObject objectForKey:@"subtitleTrackSrtCharCode"] UTF8String];
3760                     strncpy(sub_config.src_codeset, [[tempObject objectForKey:@"subtitleTrackSrtCharCode"] UTF8String], 40);
3761                     
3762                     sub_config.force = 0;
3763                     sub_config.dest = PASSTHRUSUB;
3764                     sub_config.default_track = def;
3765                     
3766                     hb_srt_add( job, &sub_config, [[tempObject objectForKey:@"subtitleTrackSrtLanguageIso3"] UTF8String]);
3767                 }
3768                 
3769                 
3770                 if (subt != NULL)
3771                 {
3772                     hb_subtitle_config_t sub_config = subt->config;
3773                     
3774                     if (!burned && job->mux == HB_MUX_MKV && 
3775                         subt->format == PICTURESUB)
3776                     {
3777                         sub_config.dest = PASSTHRUSUB;
3778                     }
3779                     else if (!burned && job->mux == HB_MUX_MP4 && 
3780                              subt->format == PICTURESUB)
3781                     {
3782                         // Skip any non-burned vobsubs when output is mp4
3783                         continue;
3784                     }
3785                     else if ( burned && subt->format == PICTURESUB )
3786                     {
3787                         // Only allow one subtitle to be burned into the video
3788                         if (one_burned)
3789                             continue;
3790                         one_burned = TRUE;
3791                     }
3792                     sub_config.force = force;
3793                     sub_config.default_track = def;
3794                     hb_subtitle_add( job, &sub_config, subtitle );
3795                 }   
3796                 
3797             }
3798         }
3799         i++;
3800     }
3801
3802 #pragma mark -
3803
3804    
3805     /* Audio tracks and mixdowns */
3806     /* Lets make sure there arent any erroneous audio tracks in the job list, so lets make sure its empty*/
3807     int audiotrack_count = hb_list_count(job->list_audio);
3808     for( int i = 0; i < audiotrack_count;i++)
3809     {
3810         hb_audio_t * temp_audio = (hb_audio_t*) hb_list_item( job->list_audio, 0 );
3811         hb_list_rem(job->list_audio, temp_audio);
3812     }
3813     /* Now lets add our new tracks to the audio list here */
3814     if ([[queueToApply objectForKey:@"Audio1Track"] intValue] > 0)
3815     {
3816         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3817         hb_audio_config_init(audio);
3818         audio->in.track = [[queueToApply objectForKey:@"Audio1Track"] intValue] - 1;
3819         /* We go ahead and assign values to our audio->out.<properties> */
3820         audio->out.track = [[queueToApply objectForKey:@"Audio1Track"] intValue] - 1;
3821         audio->out.codec = [[queueToApply objectForKey:@"JobAudio1Encoder"] intValue];
3822         audio->out.mixdown = [[queueToApply objectForKey:@"JobAudio1Mixdown"] intValue];
3823         audio->out.bitrate = [[queueToApply objectForKey:@"JobAudio1Bitrate"] intValue];
3824         audio->out.samplerate = [[queueToApply objectForKey:@"JobAudio1Samplerate"] intValue];
3825         audio->out.dynamic_range_compression = [[queueToApply objectForKey:@"Audio1TrackDRCSlider"] floatValue];
3826         
3827         hb_audio_add( job, audio );
3828         free(audio);
3829     }  
3830     if ([[queueToApply objectForKey:@"Audio2Track"] intValue] > 0)
3831     {
3832         
3833         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3834         hb_audio_config_init(audio);
3835         audio->in.track = [[queueToApply objectForKey:@"Audio2Track"] intValue] - 1;
3836         [self writeToActivityLog: "prepareJob audiotrack 2 is: %d", audio->in.track];
3837         /* We go ahead and assign values to our audio->out.<properties> */
3838         audio->out.track = [[queueToApply objectForKey:@"Audio2Track"] intValue] - 1;
3839         audio->out.codec = [[queueToApply objectForKey:@"JobAudio2Encoder"] intValue];
3840         audio->out.mixdown = [[queueToApply objectForKey:@"JobAudio2Mixdown"] intValue];
3841         audio->out.bitrate = [[queueToApply objectForKey:@"JobAudio2Bitrate"] intValue];
3842         audio->out.samplerate = [[queueToApply objectForKey:@"JobAudio2Samplerate"] intValue];
3843         audio->out.dynamic_range_compression = [[queueToApply objectForKey:@"Audio2TrackDRCSlider"] floatValue];
3844         
3845         hb_audio_add( job, audio );
3846         free(audio);
3847     }
3848     
3849     if ([[queueToApply objectForKey:@"Audio3Track"] intValue] > 0)
3850     {
3851         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3852         hb_audio_config_init(audio);
3853         audio->in.track = [[queueToApply objectForKey:@"Audio3Track"] intValue] - 1;
3854         /* We go ahead and assign values to our audio->out.<properties> */
3855         audio->out.track = [[queueToApply objectForKey:@"Audio3Track"] intValue] - 1;
3856         audio->out.codec = [[queueToApply objectForKey:@"JobAudio3Encoder"] intValue];
3857         audio->out.mixdown = [[queueToApply objectForKey:@"JobAudio3Mixdown"] intValue];
3858         audio->out.bitrate = [[queueToApply objectForKey:@"JobAudio3Bitrate"] intValue];
3859         audio->out.samplerate = [[queueToApply objectForKey:@"JobAudio3Samplerate"] intValue];
3860         audio->out.dynamic_range_compression = [[queueToApply objectForKey:@"Audio3TrackDRCSlider"] floatValue];
3861         
3862         hb_audio_add( job, audio );
3863         free(audio);        
3864     }
3865     
3866     if ([[queueToApply objectForKey:@"Audio4Track"] intValue] > 0)
3867     {
3868         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3869         hb_audio_config_init(audio);
3870         audio->in.track = [[queueToApply objectForKey:@"Audio4Track"] intValue] - 1;
3871         /* We go ahead and assign values to our audio->out.<properties> */
3872         audio->out.track = [[queueToApply objectForKey:@"Audio4Track"] intValue] - 1;
3873         audio->out.codec = [[queueToApply objectForKey:@"JobAudio4Encoder"] intValue];
3874         audio->out.mixdown = [[queueToApply objectForKey:@"JobAudio4Mixdown"] intValue];
3875         audio->out.bitrate = [[queueToApply objectForKey:@"JobAudio4Bitrate"] intValue];
3876         audio->out.samplerate = [[queueToApply objectForKey:@"JobAudio4Samplerate"] intValue];
3877         audio->out.dynamic_range_compression = [[queueToApply objectForKey:@"Audio4TrackDRCSlider"] floatValue];
3878         
3879         hb_audio_add( job, audio );
3880         
3881
3882     }
3883     
3884     /* Filters */ 
3885     job->filters = hb_list_init();
3886     
3887     /* Now lets call the filters if applicable.
3888      * The order of the filters is critical
3889      */
3890     /* Detelecine */
3891     if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] == 1)
3892     {
3893         /* use a custom detelecine string */
3894         hb_filter_detelecine.settings = (char *) [[queueToApply objectForKey:@"PictureDetelecineCustom"] UTF8String];
3895         hb_list_add( job->filters, &hb_filter_detelecine );
3896     }
3897     if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] == 2)
3898     {
3899         /* Use libhb's default values */
3900         hb_list_add( job->filters, &hb_filter_detelecine );
3901     }
3902     
3903     if ([[queueToApply objectForKey:@"PictureDecombDeinterlace"] intValue] == 1)
3904     {
3905         /* Decomb */
3906         /* we add the custom string if present */
3907         if ([[queueToApply objectForKey:@"PictureDecomb"] intValue] == 1)
3908         {
3909             /* use a custom decomb string */
3910             hb_filter_decomb.settings = (char *) [[queueToApply objectForKey:@"PictureDecombCustom"] UTF8String];
3911             hb_list_add( job->filters, &hb_filter_decomb );
3912         }
3913         if ([[queueToApply objectForKey:@"PictureDecomb"] intValue] == 2)
3914         {
3915             /* Use libhb default */
3916             hb_list_add( job->filters, &hb_filter_decomb );
3917         }
3918         
3919     }
3920     else
3921     {
3922         
3923         /* Deinterlace */
3924         if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 1)
3925         {
3926             /* we add the custom string if present */
3927             hb_filter_deinterlace.settings = (char *) [[queueToApply objectForKey:@"PictureDeinterlaceCustom"] UTF8String];
3928             hb_list_add( job->filters, &hb_filter_deinterlace );            
3929         }
3930         else if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 2)
3931         {
3932             /* Run old deinterlacer fd by default */
3933             hb_filter_deinterlace.settings = "-1"; 
3934             hb_list_add( job->filters, &hb_filter_deinterlace );
3935         }
3936         else if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 3)
3937         {
3938             /* Yadif mode 0 (without spatial deinterlacing.) */
3939             hb_filter_deinterlace.settings = "2"; 
3940             hb_list_add( job->filters, &hb_filter_deinterlace );            
3941         }
3942         else if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 4)
3943         {
3944             /* Yadif (with spatial deinterlacing) */
3945             hb_filter_deinterlace.settings = "0"; 
3946             hb_list_add( job->filters, &hb_filter_deinterlace );            
3947         }
3948         
3949         
3950     }
3951     /* Denoise */
3952         if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 1) // Custom in popup
3953         {
3954                 /* we add the custom string if present */
3955         hb_filter_denoise.settings = (char *) [[queueToApply objectForKey:@"PictureDenoiseCustom"] UTF8String];
3956         hb_list_add( job->filters, &hb_filter_denoise );        
3957         }
3958     else if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 2) // Weak in popup
3959         {
3960                 hb_filter_denoise.settings = "2:1:2:3"; 
3961         hb_list_add( job->filters, &hb_filter_denoise );        
3962         }
3963         else if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 3) // Medium in popup
3964         {
3965                 hb_filter_denoise.settings = "3:2:2:3"; 
3966         hb_list_add( job->filters, &hb_filter_denoise );        
3967         }
3968         else if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 4) // Strong in popup
3969         {
3970                 hb_filter_denoise.settings = "7:7:5:5"; 
3971         hb_list_add( job->filters, &hb_filter_denoise );        
3972         }
3973     
3974     
3975     /* Deblock  (uses pp7 default) */
3976     /* NOTE: even though there is a valid deblock setting of 0 for the filter, for 
3977      * the macgui's purposes a value of 0 actually means to not even use the filter
3978      * current hb_filter_deblock.settings valid ranges are from 5 - 15 
3979      */
3980     if ([[queueToApply objectForKey:@"PictureDeblock"] intValue] != 0)
3981     {
3982         hb_filter_deblock.settings = (char *) [[queueToApply objectForKey:@"PictureDeblock"] UTF8String];
3983         hb_list_add( job->filters, &hb_filter_deblock );
3984     }
3985 [self writeToActivityLog: "prepareJob exiting"];    
3986 }
3987
3988
3989
3990 /* addToQueue: puts up an alert before ultimately calling doAddToQueue
3991 */
3992 - (IBAction) addToQueue: (id) sender
3993 {
3994         /* We get the destination directory from the destination field here */
3995         NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
3996         /* We check for a valid destination here */
3997         if ([[NSFileManager defaultManager] fileExistsAtPath:destinationDirectory] == 0) 
3998         {
3999                 NSRunAlertPanel(@"Warning!", @"This is not a valid destination directory!", @"OK", nil, nil);
4000         return;
4001         }
4002     
4003     BOOL fileExists;
4004     fileExists = NO;
4005     
4006     BOOL fileExistsInQueue;
4007     fileExistsInQueue = NO;
4008     
4009     /* We check for and existing file here */
4010     if([[NSFileManager defaultManager] fileExistsAtPath: [fDstFile2Field stringValue]])
4011     {
4012         fileExists = YES;
4013     }
4014     
4015     /* We now run through the queue and make sure we are not overwriting an exisiting queue item */
4016     int i = 0;
4017     NSEnumerator *enumerator = [QueueFileArray objectEnumerator];
4018         id tempObject;
4019         while (tempObject = [enumerator nextObject])
4020         {
4021                 NSDictionary *thisQueueDict = tempObject;
4022                 if ([[thisQueueDict objectForKey:@"DestinationPath"] isEqualToString: [fDstFile2Field stringValue]])
4023                 {
4024                         fileExistsInQueue = YES;        
4025                 }
4026         i++;
4027         }
4028     
4029     
4030         if(fileExists == YES)
4031     {
4032         NSBeginCriticalAlertSheet( NSLocalizedString( @"File already exists.", @"" ),
4033                                   NSLocalizedString( @"Cancel", @"" ), NSLocalizedString( @"Overwrite", @"" ), nil, fWindow, self,
4034                                   @selector( overwriteAddToQueueAlertDone:returnCode:contextInfo: ),
4035                                   NULL, NULL, [NSString stringWithFormat:
4036                                                NSLocalizedString( @"Do you want to overwrite %@?", @"" ),
4037                                                [fDstFile2Field stringValue]] );
4038     }
4039     else if (fileExistsInQueue == YES)
4040     {
4041     NSBeginCriticalAlertSheet( NSLocalizedString( @"There is already a queue item for this destination.", @"" ),
4042                                   NSLocalizedString( @"Cancel", @"" ), NSLocalizedString( @"Overwrite", @"" ), nil, fWindow, self,
4043                                   @selector( overwriteAddToQueueAlertDone:returnCode:contextInfo: ),
4044                                   NULL, NULL, [NSString stringWithFormat:
4045                                                NSLocalizedString( @"Do you want to overwrite %@?", @"" ),
4046                                                [fDstFile2Field stringValue]] );
4047     }
4048     else
4049     {
4050         [self doAddToQueue];
4051     }
4052 }
4053
4054 /* overwriteAddToQueueAlertDone: called from the alert posted by addToQueue that asks
4055    the user if they want to overwrite an exiting movie file.
4056 */
4057 - (void) overwriteAddToQueueAlertDone: (NSWindow *) sheet
4058     returnCode: (int) returnCode contextInfo: (void *) contextInfo
4059 {
4060     if( returnCode == NSAlertAlternateReturn )
4061         [self doAddToQueue];
4062 }
4063
4064 - (void) doAddToQueue
4065 {
4066     [self addQueueFileItem ];
4067 }
4068
4069
4070
4071 /* Rip: puts up an alert before ultimately calling doRip
4072 */
4073 - (IBAction) Rip: (id) sender
4074 {
4075     [self writeToActivityLog: "Rip: Pending queue count is %d", fPendingCount];
4076     /* Rip or Cancel ? */
4077     hb_state_t s;
4078     hb_get_state2( fQueueEncodeLibhb, &s );
4079     
4080     if(s.state == HB_STATE_WORKING || s.state == HB_STATE_PAUSED)
4081         {
4082         [self Cancel: sender];
4083         return;
4084     }
4085     
4086     /* We check to see if we need to warn the user that the computer will go to sleep
4087                  or shut down when encoding is finished */
4088                 [self remindUserOfSleepOrShutdown];
4089     
4090     // If there are pending jobs in the queue, then this is a rip the queue
4091     if (fPendingCount > 0)
4092     {
4093         /* here lets start the queue with the first pending item */
4094         [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]]; 
4095         
4096         return;
4097     }
4098     
4099     // Before adding jobs to the queue, check for a valid destination.
4100     
4101     NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
4102     if ([[NSFileManager defaultManager] fileExistsAtPath:destinationDirectory] == 0) 
4103     {
4104         NSRunAlertPanel(@"Warning!", @"This is not a valid destination directory!", @"OK", nil, nil);
4105         return;
4106     }
4107     
4108     /* We check for duplicate name here */
4109     if( [[NSFileManager defaultManager] fileExistsAtPath:[fDstFile2Field stringValue]] )
4110     {
4111         NSBeginCriticalAlertSheet( NSLocalizedString( @"File already exists", @"" ),
4112                                   NSLocalizedString( @"Cancel", "" ), NSLocalizedString( @"Overwrite", @"" ), nil, fWindow, self,
4113                                   @selector( overWriteAlertDone:returnCode:contextInfo: ),
4114                                   NULL, NULL, [NSString stringWithFormat:
4115                                                NSLocalizedString( @"Do you want to overwrite %@?", @"" ),
4116                                                [fDstFile2Field stringValue]] );
4117         
4118         // overWriteAlertDone: will be called when the alert is dismissed. It will call doRip.
4119     }
4120     else
4121     {
4122         /* if there are no pending jobs in the queue, then add this one to the queue and rip
4123          otherwise, just rip the queue */
4124         if(fPendingCount == 0)
4125         {
4126             [self doAddToQueue];
4127         }
4128         
4129         /* go right to processing the new queue encode */
4130         [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]]; 
4131         
4132     }
4133 }
4134
4135 /* overWriteAlertDone: called from the alert posted by Rip: that asks the user if they
4136    want to overwrite an exiting movie file.
4137 */
4138 - (void) overWriteAlertDone: (NSWindow *) sheet
4139     returnCode: (int) returnCode contextInfo: (void *) contextInfo
4140 {
4141     if( returnCode == NSAlertAlternateReturn )
4142     {
4143         /* if there are no jobs in the queue, then add this one to the queue and rip 
4144         otherwise, just rip the queue */
4145         if( fPendingCount == 0 )
4146         {
4147             [self doAddToQueue];
4148         }
4149
4150         NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
4151         [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];
4152         [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]]; 
4153       
4154     }
4155 }
4156
4157 - (void) remindUserOfSleepOrShutdown
4158 {
4159        if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Put Computer To Sleep"])
4160        {
4161                /*Warn that computer will sleep after encoding*/
4162                int reminduser;
4163                NSBeep();
4164                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);
4165                [NSApp requestUserAttention:NSCriticalRequest];
4166                if ( reminduser == NSAlertAlternateReturn )
4167                {
4168                        [self showPreferencesWindow:nil];
4169                }
4170        }
4171        else if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Shut Down Computer"])
4172        {
4173                /*Warn that computer will shut down after encoding*/
4174                int reminduser;
4175                NSBeep();
4176                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);
4177                [NSApp requestUserAttention:NSCriticalRequest];
4178                if ( reminduser == NSAlertAlternateReturn )
4179                {
4180                        [self showPreferencesWindow:nil];
4181                }
4182        }
4183
4184 }
4185
4186
4187 - (void) doRip
4188 {
4189     /* Let libhb do the job */
4190     hb_start( fQueueEncodeLibhb );
4191     /*set the fEncodeState State */
4192         fEncodeState = 1;
4193 }
4194
4195
4196 //------------------------------------------------------------------------------------
4197 // Displays an alert asking user if the want to cancel encoding of current job.
4198 // Cancel: returns immediately after posting the alert. Later, when the user
4199 // acknowledges the alert, doCancelCurrentJob is called.
4200 //------------------------------------------------------------------------------------
4201 - (IBAction)Cancel: (id)sender
4202 {
4203     if (!fQueueController) return;
4204     
4205   hb_pause( fQueueEncodeLibhb );
4206     NSString * alertTitle = [NSString stringWithFormat:NSLocalizedString(@"You are currently encoding. What would you like to do ?", nil)];
4207    
4208     // Which window to attach the sheet to?
4209     NSWindow * docWindow;
4210     if ([sender respondsToSelector: @selector(window)])
4211         docWindow = [sender window];
4212     else
4213         docWindow = fWindow;
4214         
4215     NSBeginCriticalAlertSheet(
4216             alertTitle,
4217             NSLocalizedString(@"Continue Encoding", nil),
4218             NSLocalizedString(@"Cancel Current and Stop", nil),
4219             NSLocalizedString(@"Cancel Current and Continue", nil),
4220             docWindow, self,
4221             nil, @selector(didDimissCancel:returnCode:contextInfo:), nil,
4222             NSLocalizedString(@"Your encode will be cancelled if you don't continue encoding.", nil));
4223     
4224     // didDimissCancelCurrentJob:returnCode:contextInfo: will be called when the dialog is dismissed
4225 }
4226
4227 - (void) didDimissCancel: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo
4228 {
4229    hb_resume( fQueueEncodeLibhb );
4230      if (returnCode == NSAlertOtherReturn)
4231     {
4232         [self doCancelCurrentJob];  // <- this also stops libhb
4233     }
4234     if (returnCode == NSAlertAlternateReturn)
4235     {
4236     [self doCancelCurrentJobAndStop];
4237     }
4238 }
4239
4240 //------------------------------------------------------------------------------------
4241 // Cancels and deletes the current job and stops libhb from processing the remaining
4242 // encodes.
4243 //------------------------------------------------------------------------------------
4244 - (void) doCancelCurrentJob
4245 {
4246     // Stop the current job. hb_stop will only cancel the current pass and then set
4247     // its state to HB_STATE_WORKDONE. It also does this asynchronously. So when we
4248     // see the state has changed to HB_STATE_WORKDONE (in updateUI), we'll delete the
4249     // remaining passes of the job and then start the queue back up if there are any
4250     // remaining jobs.
4251      
4252     
4253     hb_stop( fQueueEncodeLibhb );
4254     
4255     // Delete all remaining jobs since libhb doesn't do this on its own.
4256             hb_job_t * job;
4257             while( ( job = hb_job(fQueueEncodeLibhb, 0) ) )
4258                 hb_rem( fQueueEncodeLibhb, job );
4259                 
4260     fEncodeState = 2;   // don't alert at end of processing since this was a cancel
4261     
4262     // now that we've stopped the currently encoding job, lets mark it as cancelled
4263     [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] setObject:[NSNumber numberWithInt:3] forKey:@"Status"];
4264     // and as always, save it in the queue .plist...
4265     /* We save all of the Queue data here */
4266     [self saveQueueFileItem];
4267     // so now lets move to 
4268     currentQueueEncodeIndex++ ;
4269     // ... and see if there are more items left in our queue
4270     int queueItems = [QueueFileArray count];
4271     /* If we still have more items in our queue, lets go to the next one */
4272     if (currentQueueEncodeIndex < queueItems)
4273     {
4274     [self writeToActivityLog: "doCancelCurrentJob currentQueueEncodeIndex is incremented to: %d", currentQueueEncodeIndex];
4275     [self writeToActivityLog: "doCancelCurrentJob moving to the next job"];
4276     
4277     [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]];
4278     }
4279     else
4280     {
4281         [self writeToActivityLog: "doCancelCurrentJob the item queue is complete"];
4282     }
4283
4284 }
4285
4286 - (void) doCancelCurrentJobAndStop
4287 {
4288     hb_stop( fQueueEncodeLibhb );
4289     
4290     // Delete all remaining jobs since libhb doesn't do this on its own.
4291             hb_job_t * job;
4292             while( ( job = hb_job(fQueueEncodeLibhb, 0) ) )
4293                 hb_rem( fQueueEncodeLibhb, job );
4294                 
4295                 
4296     fEncodeState = 2;   // don't alert at end of processing since this was a cancel
4297     
4298     // now that we've stopped the currently encoding job, lets mark it as cancelled
4299     [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] setObject:[NSNumber numberWithInt:3] forKey:@"Status"];
4300     // and as always, save it in the queue .plist...
4301     /* We save all of the Queue data here */
4302     [self saveQueueFileItem];
4303     // so now lets move to 
4304     currentQueueEncodeIndex++ ;
4305     [self writeToActivityLog: "cancelling current job and stopping the queue"];
4306 }
4307 - (IBAction) Pause: (id) sender
4308 {
4309     hb_state_t s;
4310     hb_get_state2( fQueueEncodeLibhb, &s );
4311
4312     if( s.state == HB_STATE_PAUSED )
4313     {
4314         hb_resume( fQueueEncodeLibhb );
4315     }
4316     else
4317     {
4318         hb_pause( fQueueEncodeLibhb );
4319     }
4320 }
4321
4322 #pragma mark -
4323 #pragma mark GUI Controls Changed Methods
4324
4325 - (IBAction) titlePopUpChanged: (id) sender
4326 {
4327     hb_list_t  * list  = hb_get_titles( fHandle );
4328     hb_title_t * title = (hb_title_t*)
4329         hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
4330
4331     /* If we are a stream type, grok the output file name from title->name upon title change */
4332     if (title->type == HB_STREAM_TYPE)
4333     {
4334         /* we set the default name according to the new title->name */
4335         [fDstFile2Field setStringValue: [NSString stringWithFormat:
4336                                          @"%@/%@.%@", [[fDstFile2Field stringValue] stringByDeletingLastPathComponent],
4337                                          [NSString stringWithUTF8String: title->name],
4338                                          [[fDstFile2Field stringValue] pathExtension]]];
4339         /* If we have more than one title and are stream then we have a batch, change the source to read out the parent folder also */
4340         if ( hb_list_count( list ) > 1 )
4341         {                                  
4342             [fSrcDVD2Field setStringValue:[NSString stringWithFormat:@"%@/%@", browsedSourceDisplayName,[NSString stringWithUTF8String: title->name]]];
4343         }
4344     }
4345     
4346     /* For point a to point b pts encoding, set the start and end fields to 0 and the title duration in seconds respectively */
4347     int duration = (title->hours * 3600) + (title->minutes * 60) + (title->seconds);
4348     [fSrcTimeStartEncodingField setStringValue: [NSString stringWithFormat: @"%d", 0]];
4349     [fSrcTimeEndEncodingField setStringValue: [NSString stringWithFormat: @"%d", duration]];
4350     /* For point a to point b frame encoding, set the start and end fields to 0 and the title duration * announced fps in seconds respectively */
4351     [fSrcFrameStartEncodingField setStringValue: [NSString stringWithFormat: @"%d", 1]];
4352     //[fSrcFrameEndEncodingField setStringValue: [NSString stringWithFormat: @"%d", ((title->hours * 3600) + (title->minutes * 60) + (title->seconds)) * 24]];
4353     [fSrcFrameEndEncodingField setStringValue: [NSString stringWithFormat: @"%d", duration * (title->rate / title->rate_base)]];    
4354     
4355     /* If Auto Naming is on. We create an output filename of dvd name - title number */
4356     if( [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultAutoNaming"] > 0 && ( hb_list_count( list ) > 1 ) )
4357         {
4358                 [fDstFile2Field setStringValue: [NSString stringWithFormat:
4359                         @"%@/%@-%d.%@", [[fDstFile2Field stringValue] stringByDeletingLastPathComponent],
4360                         [browsedSourceDisplayName stringByDeletingPathExtension],
4361             title->index,
4362                         [[fDstFile2Field stringValue] pathExtension]]]; 
4363         }
4364     /* Update encode start / stop variables */
4365      
4366     
4367     
4368     /* Update chapter popups */
4369     [fSrcChapterStartPopUp removeAllItems];
4370     [fSrcChapterEndPopUp   removeAllItems];
4371     for( int i = 0; i < hb_list_count( title->list_chapter ); i++ )
4372     {
4373         [fSrcChapterStartPopUp addItemWithTitle: [NSString
4374             stringWithFormat: @"%d", i + 1]];
4375         [fSrcChapterEndPopUp addItemWithTitle: [NSString
4376             stringWithFormat: @"%d", i + 1]];
4377     }
4378
4379     [fSrcChapterStartPopUp selectItemAtIndex: 0];
4380     [fSrcChapterEndPopUp   selectItemAtIndex:
4381         hb_list_count( title->list_chapter ) - 1];
4382     [self chapterPopUpChanged:nil];
4383     
4384     /* if using dvd nav, show the angle widget */
4385     if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"UseDvdNav"] boolValue])
4386     {
4387         [fSrcAngleLabel setHidden:NO];
4388         [fSrcAnglePopUp setHidden:NO];
4389         
4390         [fSrcAnglePopUp removeAllItems];
4391         for( int i = 0; i < title->angle_count; i++ )
4392         {
4393             [fSrcAnglePopUp addItemWithTitle: [NSString stringWithFormat: @"%d", i + 1]];
4394         }
4395         [fSrcAnglePopUp selectItemAtIndex: 0];
4396     }
4397     else
4398     {
4399         [fSrcAngleLabel setHidden:YES];
4400         [fSrcAnglePopUp setHidden:YES];
4401     }
4402     
4403     /* Start Get and set the initial pic size for display */
4404         hb_job_t * job = title->job;
4405         fTitle = title;
4406     
4407     /* Set Auto Crop to on upon selecting a new title  */
4408     [fPictureController setAutoCrop:YES];
4409     
4410         /* We get the originial output picture width and height and put them
4411         in variables for use with some presets later on */
4412         PicOrigOutputWidth = job->width;
4413         PicOrigOutputHeight = job->height;
4414         AutoCropTop = job->crop[0];
4415         AutoCropBottom = job->crop[1];
4416         AutoCropLeft = job->crop[2];
4417         AutoCropRight = job->crop[3];
4418
4419         /* Reset the new title in fPictureController &&  fPreviewController*/
4420     [fPictureController SetTitle:title];
4421
4422         
4423     /* Update Subtitle Table */
4424     [fSubtitlesDelegate resetWithTitle:title];
4425     [fSubtitlesTable reloadData];
4426     
4427
4428     /* Update chapter table */
4429     [fChapterTitlesDelegate resetWithTitle:title];
4430     [fChapterTable reloadData];
4431
4432    /* Lets make sure there arent any erroneous audio tracks in the job list, so lets make sure its empty*/
4433     int audiotrack_count = hb_list_count(job->list_audio);
4434     for( int i = 0; i < audiotrack_count;i++)
4435     {
4436         hb_audio_t * temp_audio = (hb_audio_t*) hb_list_item( job->list_audio, 0 );
4437         hb_list_rem(job->list_audio, temp_audio);
4438     }
4439
4440     /* Update audio popups */
4441     [self addAllAudioTracksToPopUp: fAudLang1PopUp];
4442     [self addAllAudioTracksToPopUp: fAudLang2PopUp];
4443     [self addAllAudioTracksToPopUp: fAudLang3PopUp];
4444     [self addAllAudioTracksToPopUp: fAudLang4PopUp];
4445     /* search for the first instance of our prefs default language for track 1, and set track 2 to "none" */
4446         NSString * audioSearchPrefix = [[NSUserDefaults standardUserDefaults] stringForKey:@"DefaultLanguage"];
4447         [self selectAudioTrackInPopUp: fAudLang1PopUp searchPrefixString: audioSearchPrefix selectIndexIfNotFound: 1];
4448     [self selectAudioTrackInPopUp:fAudLang2PopUp searchPrefixString:nil selectIndexIfNotFound:0];
4449     [self selectAudioTrackInPopUp:fAudLang3PopUp searchPrefixString:nil selectIndexIfNotFound:0];
4450     [self selectAudioTrackInPopUp:fAudLang4PopUp searchPrefixString:nil selectIndexIfNotFound:0];
4451
4452         /* changing the title may have changed the audio channels on offer, */
4453         /* so call audioTrackPopUpChanged for both audio tracks to update the mixdown popups */
4454         [self audioTrackPopUpChanged: fAudLang1PopUp];
4455         [self audioTrackPopUpChanged: fAudLang2PopUp];
4456     [self audioTrackPopUpChanged: fAudLang3PopUp];
4457     [self audioTrackPopUpChanged: fAudLang4PopUp];
4458
4459     [fVidRatePopUp selectItemAtIndex: 0];
4460
4461     /* we run the picture size values through calculatePictureSizing to get all picture setting information*/
4462         [self calculatePictureSizing:nil];
4463
4464    /* lets call tableViewSelected to make sure that any preset we have selected is enforced after a title change */
4465     [self selectPreset:nil];
4466 }
4467
4468 - (IBAction) encodeStartStopPopUpChanged: (id) sender;
4469 {
4470     if( [fEncodeStartStopPopUp isEnabled] )
4471     {
4472         /* We are chapters */
4473         if( [fEncodeStartStopPopUp indexOfSelectedItem] == 0 )
4474         {
4475             [fSrcChapterStartPopUp  setHidden: NO];
4476             [fSrcChapterEndPopUp  setHidden: NO];
4477             
4478             [fSrcTimeStartEncodingField  setHidden: YES];
4479             [fSrcTimeEndEncodingField  setHidden: YES];
4480             
4481             [fSrcFrameStartEncodingField  setHidden: YES];
4482             [fSrcFrameEndEncodingField  setHidden: YES];
4483             
4484                [self chapterPopUpChanged:nil];   
4485         }
4486         /* We are time based (seconds) */
4487         else if ([fEncodeStartStopPopUp indexOfSelectedItem] == 1)
4488         {
4489             [fSrcChapterStartPopUp  setHidden: YES];
4490             [fSrcChapterEndPopUp  setHidden: YES];
4491             
4492             [fSrcTimeStartEncodingField  setHidden: NO];
4493             [fSrcTimeEndEncodingField  setHidden: NO];
4494             
4495             [fSrcFrameStartEncodingField  setHidden: YES];
4496             [fSrcFrameEndEncodingField  setHidden: YES];
4497             
4498             [self startEndSecValueChanged:nil];
4499         }
4500         /* We are frame based */
4501         else if ([fEncodeStartStopPopUp indexOfSelectedItem] == 2)
4502         {
4503             [fSrcChapterStartPopUp  setHidden: YES];
4504             [fSrcChapterEndPopUp  setHidden: YES];
4505             
4506             [fSrcTimeStartEncodingField  setHidden: YES];
4507             [fSrcTimeEndEncodingField  setHidden: YES];
4508             
4509             [fSrcFrameStartEncodingField  setHidden: NO];
4510             [fSrcFrameEndEncodingField  setHidden: NO];
4511             
4512             [self startEndFrameValueChanged:nil];
4513         }
4514     }
4515 }
4516
4517 - (IBAction) chapterPopUpChanged: (id) sender
4518 {
4519
4520         /* If start chapter popup is greater than end chapter popup,
4521         we set the end chapter popup to the same as start chapter popup */
4522         if ([fSrcChapterStartPopUp indexOfSelectedItem] > [fSrcChapterEndPopUp indexOfSelectedItem])
4523         {
4524                 [fSrcChapterEndPopUp selectItemAtIndex: [fSrcChapterStartPopUp indexOfSelectedItem]];
4525     }
4526
4527                 
4528         hb_list_t  * list  = hb_get_titles( fHandle );
4529     hb_title_t * title = (hb_title_t *)
4530         hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
4531
4532     hb_chapter_t * chapter;
4533     int64_t        duration = 0;
4534     for( int i = [fSrcChapterStartPopUp indexOfSelectedItem];
4535          i <= [fSrcChapterEndPopUp indexOfSelectedItem]; i++ )
4536     {
4537         chapter = (hb_chapter_t *) hb_list_item( title->list_chapter, i );
4538         duration += chapter->duration;
4539     }
4540     
4541     duration /= 90000; /* pts -> seconds */
4542     [fSrcDuration2Field setStringValue: [NSString stringWithFormat:
4543         @"%02lld:%02lld:%02lld", duration / 3600, ( duration / 60 ) % 60,
4544         duration % 60]];
4545     
4546     [self calculateBitrate: sender];
4547     
4548     if ( [fSrcChapterStartPopUp indexOfSelectedItem] ==  [fSrcChapterEndPopUp indexOfSelectedItem] )
4549     {
4550     /* Disable chapter markers for any source with less than two chapters as it makes no sense. */
4551     [fCreateChapterMarkers setEnabled: NO];
4552     [fCreateChapterMarkers setState: NSOffState];
4553     }
4554     else
4555     {
4556     [fCreateChapterMarkers setEnabled: YES];
4557     }
4558 }
4559
4560 - (IBAction) startEndSecValueChanged: (id) sender
4561 {
4562
4563         int duration = [fSrcTimeEndEncodingField intValue] - [fSrcTimeStartEncodingField intValue];
4564     [fSrcDuration2Field setStringValue: [NSString stringWithFormat:
4565         @"%02d:%02d:%02d", duration / 3600, ( duration / 60 ) % 60,
4566         duration % 60]];
4567     
4568     //[self calculateBitrate: sender];
4569     
4570 }
4571
4572 - (IBAction) startEndFrameValueChanged: (id) sender
4573 {
4574     hb_list_t  * list  = hb_get_titles( fHandle );
4575     hb_title_t * title = (hb_title_t*)
4576     hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
4577     
4578     int duration = ([fSrcFrameEndEncodingField intValue] - [fSrcFrameStartEncodingField intValue]) / (title->rate / title->rate_base);
4579     [fSrcDuration2Field setStringValue: [NSString stringWithFormat:
4580                                          @"%02d:%02d:%02d", duration / 3600, ( duration / 60 ) % 60,
4581                                          duration % 60]];
4582     
4583     //[self calculateBitrate: sender];
4584 }
4585
4586
4587 - (IBAction) formatPopUpChanged: (id) sender
4588 {
4589     NSString * string = [fDstFile2Field stringValue];
4590     int format = [fDstFormatPopUp indexOfSelectedItem];
4591     char * ext = NULL;
4592         /* Initially set the large file (64 bit formatting) output checkbox to hidden */
4593     [fDstMp4LargeFileCheck setHidden: YES];
4594     [fDstMp4HttpOptFileCheck setHidden: YES];
4595     [fDstMp4iPodFileCheck setHidden: YES];
4596     
4597     /* Update the Video Codec PopUp */
4598     /* lets get the tag of the currently selected item first so we might reset it later */
4599     int selectedVidEncoderTag;
4600     selectedVidEncoderTag = [[fVidEncoderPopUp selectedItem] tag];
4601     
4602     /* Note: we now store the video encoder int values from common.c in the tags of each popup for easy retrieval later */
4603     [fVidEncoderPopUp removeAllItems];
4604     NSMenuItem *menuItem;
4605     /* These video encoders are available to all of our current muxers, so lets list them once here */
4606     menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"MPEG-4 (FFmpeg)" action: NULL keyEquivalent: @""];
4607     [menuItem setTag: HB_VCODEC_FFMPEG];
4608     
4609     switch( format )
4610     {
4611         case 0:
4612                         /*Get Default MP4 File Extension*/
4613                         if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultMpegName"] > 0)
4614                         {
4615                                 ext = "m4v";
4616                         }
4617                         else
4618                         {
4619                                 ext = "mp4";
4620                         }
4621             /* Add additional video encoders here */
4622             menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"H.264 (x264)" action: NULL keyEquivalent: @""];
4623             [menuItem setTag: HB_VCODEC_X264];
4624             /* We show the mp4 option checkboxes here since we are mp4 */
4625             [fCreateChapterMarkers setEnabled: YES];
4626                         [fDstMp4LargeFileCheck setHidden: NO];
4627                         [fDstMp4HttpOptFileCheck setHidden: NO];
4628             [fDstMp4iPodFileCheck setHidden: NO];
4629             break;
4630             
4631             case 1:
4632             ext = "mkv";
4633             /* Add additional video encoders here */
4634             menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"H.264 (x264)" action: NULL keyEquivalent: @""];
4635             [menuItem setTag: HB_VCODEC_X264];
4636             menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"VP3 (Theora)" action: NULL keyEquivalent: @""];
4637             [menuItem setTag: HB_VCODEC_THEORA];
4638             /* We enable the create chapters checkbox here */
4639                         [fCreateChapterMarkers setEnabled: YES];
4640                         break;
4641             
4642
4643     }
4644     /* tell fSubtitlesDelegate we have a new video container */
4645     
4646     [fSubtitlesDelegate containerChanged:[[fDstFormatPopUp selectedItem] tag]];
4647     [fSubtitlesTable reloadData];
4648     /* if we have a previously selected vid encoder tag, then try to select it */
4649     if (selectedVidEncoderTag)
4650     {
4651         [fVidEncoderPopUp selectItemWithTag: selectedVidEncoderTag];
4652     }
4653     else
4654     {
4655         [fVidEncoderPopUp selectItemAtIndex: 0];
4656     }
4657
4658     [self audioAddAudioTrackCodecs: fAudTrack1CodecPopUp];
4659     [self audioAddAudioTrackCodecs: fAudTrack2CodecPopUp];
4660     [self audioAddAudioTrackCodecs: fAudTrack3CodecPopUp];
4661     [self audioAddAudioTrackCodecs: fAudTrack4CodecPopUp];
4662
4663     if( format == 0 )
4664         [self autoSetM4vExtension: sender];
4665     else
4666         [fDstFile2Field setStringValue: [NSString stringWithFormat:@"%@.%s", [string stringByDeletingPathExtension], ext]];
4667
4668     if( SuccessfulScan )
4669     {
4670         /* Add/replace to the correct extension */
4671         [self audioTrackPopUpChanged: fAudLang1PopUp];
4672         [self audioTrackPopUpChanged: fAudLang2PopUp];
4673         [self audioTrackPopUpChanged: fAudLang3PopUp];
4674         [self audioTrackPopUpChanged: fAudLang4PopUp];
4675
4676         if( [fVidEncoderPopUp selectedItem] == nil )
4677         {
4678
4679             [fVidEncoderPopUp selectItemAtIndex:0];
4680             [self videoEncoderPopUpChanged:nil];
4681
4682             /* changing the format may mean that we can / can't offer mono or 6ch, */
4683             /* so call audioTrackPopUpChanged for both audio tracks to update the mixdown popups */
4684
4685             /* We call the method to properly enable/disable turbo 2 pass */
4686             [self twoPassCheckboxChanged: sender];
4687             /* We call method method to change UI to reflect whether a preset is used or not*/
4688         }
4689     }
4690         [self customSettingUsed: sender];
4691 }
4692
4693 - (IBAction) autoSetM4vExtension: (id) sender
4694 {
4695     if ( [fDstFormatPopUp indexOfSelectedItem] )
4696         return;
4697
4698     NSString * extension = @"mp4";
4699
4700     if( [[fAudTrack1CodecPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack2CodecPopUp selectedItem] tag] == HB_ACODEC_AC3 ||
4701                                                         [[fAudTrack3CodecPopUp selectedItem] tag] == HB_ACODEC_AC3 ||
4702                                                         [[fAudTrack4CodecPopUp selectedItem] tag] == HB_ACODEC_AC3 ||
4703                                                         [fCreateChapterMarkers state] == NSOnState ||
4704                                                         [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultMpegName"] > 0 )
4705     {
4706         extension = @"m4v";
4707     }
4708
4709     if( [extension isEqualTo: [[fDstFile2Field stringValue] pathExtension]] )
4710         return;
4711     else
4712         [fDstFile2Field setStringValue: [NSString stringWithFormat:@"%@.%@",
4713                                     [[fDstFile2Field stringValue] stringByDeletingPathExtension], extension]];
4714 }
4715
4716 /* Method to determine if we should change the UI
4717 To reflect whether or not a Preset is being used or if
4718 the user is using "Custom" settings by determining the sender*/
4719 - (IBAction) customSettingUsed: (id) sender
4720 {
4721         if ([sender stringValue])
4722         {
4723                 /* Deselect the currently selected Preset if there is one*/
4724                 [fPresetsOutlineView deselectRow:[fPresetsOutlineView selectedRow]];
4725                 /* Change UI to show "Custom" settings are being used */
4726                 [fPresetSelectedDisplay setStringValue: @"Custom"];
4727
4728                 curUserPresetChosenNum = nil;
4729         }
4730 [self calculateBitrate:nil];
4731 }
4732
4733
4734 #pragma mark -
4735 #pragma mark - Video
4736
4737 - (IBAction) videoEncoderPopUpChanged: (id) sender
4738 {
4739     hb_job_t * job = fTitle->job;
4740     int videoEncoder = [[fVidEncoderPopUp selectedItem] tag];
4741     
4742     [fAdvancedOptions setHidden:YES];
4743     /* If we are using x264 then show the x264 advanced panel*/
4744     if (videoEncoder == HB_VCODEC_X264)
4745     {
4746         [fAdvancedOptions setHidden:NO];
4747         [self autoSetM4vExtension: sender];
4748     }
4749
4750     if (videoEncoder == HB_VCODEC_FFMPEG)
4751     {
4752         /* We set the iPod atom checkbox to disabled and uncheck it as its only for x264 in the mp4
4753          container. Format is taken care of in formatPopUpChanged method by hiding and unchecking
4754          anything other than MP4.
4755          */ 
4756         [fDstMp4iPodFileCheck setEnabled: NO];
4757         [fDstMp4iPodFileCheck setState: NSOffState];
4758     }
4759     else
4760     {
4761         [fDstMp4iPodFileCheck setEnabled: YES];
4762     }
4763     [self setupQualitySlider];
4764         [self calculatePictureSizing: sender];
4765         [self twoPassCheckboxChanged: sender];
4766 }
4767
4768
4769 - (IBAction) twoPassCheckboxChanged: (id) sender
4770 {
4771         /* check to see if x264 is chosen */
4772         if([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_X264)
4773     {
4774                 if( [fVidTwoPassCheck state] == NSOnState)
4775                 {
4776                         [fVidTurboPassCheck setHidden: NO];
4777                 }
4778                 else
4779                 {
4780                         [fVidTurboPassCheck setHidden: YES];
4781                         [fVidTurboPassCheck setState: NSOffState];
4782                 }
4783                 /* Make sure Two Pass is checked if Turbo is checked */
4784                 if( [fVidTurboPassCheck state] == NSOnState)
4785                 {
4786                         [fVidTwoPassCheck setState: NSOnState];
4787                 }
4788         }
4789         else
4790         {
4791                 [fVidTurboPassCheck setHidden: YES];
4792                 [fVidTurboPassCheck setState: NSOffState];
4793         }
4794         
4795         /* We call method method to change UI to reflect whether a preset is used or not*/
4796         [self customSettingUsed: sender];
4797 }
4798
4799 - (IBAction ) videoFrameRateChanged: (id) sender
4800 {
4801     /* We call method method to calculatePictureSizing to error check detelecine*/
4802     [self calculatePictureSizing: sender];
4803
4804     /* We call method method to change UI to reflect whether a preset is used or not*/
4805         [self customSettingUsed: sender];
4806 }
4807 - (IBAction) videoMatrixChanged: (id) sender;
4808 {
4809     bool target, bitrate, quality;
4810
4811     target = bitrate = quality = false;
4812     if( [fVidQualityMatrix isEnabled] )
4813     {
4814         switch( [fVidQualityMatrix selectedRow] )
4815         {
4816             case 0:
4817                 target = true;
4818                 break;
4819             case 1:
4820                 bitrate = true;
4821                 break;
4822             case 2:
4823                 quality = true;
4824                 break;
4825         }
4826     }
4827     [fVidTargetSizeField  setEnabled: target];
4828     [fVidBitrateField     setEnabled: bitrate];
4829     [fVidQualitySlider    setEnabled: quality];
4830     [fVidQualityRFField   setEnabled: quality];
4831     [fVidQualityRFLabel    setEnabled: quality];
4832     [fVidTwoPassCheck     setEnabled: !quality &&
4833         [fVidQualityMatrix isEnabled]];
4834     if( quality )
4835     {
4836         [fVidTwoPassCheck setState: NSOffState];
4837                 [fVidTurboPassCheck setHidden: YES];
4838                 [fVidTurboPassCheck setState: NSOffState];
4839     }
4840
4841     [self qualitySliderChanged: sender];
4842     [self calculateBitrate: sender];
4843         [self customSettingUsed: sender];
4844 }
4845
4846 /* Use this method to setup the quality slider for cq/rf values depending on
4847  * the video encoder selected.
4848  */
4849 - (void) setupQualitySlider
4850 {
4851     /* Get the current slider maxValue to check for a change in slider scale later
4852      * so that we can choose a new similar value on the new slider scale */
4853     float previousMaxValue = [fVidQualitySlider maxValue];
4854     float previousPercentOfSliderScale = [fVidQualitySlider floatValue] / ([fVidQualitySlider maxValue] - [fVidQualitySlider minValue] + 1);
4855     NSString * qpRFLabelString = @"QP:";
4856     /* x264 0-51 */
4857     if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_X264)
4858     {
4859         [fVidQualitySlider setMinValue:0.0];
4860         [fVidQualitySlider setMaxValue:51.0];
4861         /* As x264 allows for qp/rf values that are fractional, we get the value from the preferences */
4862         int fractionalGranularity = 1 / [[NSUserDefaults standardUserDefaults] floatForKey:@"x264CqSliderFractional"];
4863         [fVidQualitySlider setNumberOfTickMarks:(([fVidQualitySlider maxValue] - [fVidQualitySlider minValue]) * fractionalGranularity) + 1];
4864         qpRFLabelString = @"RF:";
4865     }
4866     /* ffmpeg  1-31 */
4867     if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_FFMPEG )
4868     {
4869         [fVidQualitySlider setMinValue:1.0];
4870         [fVidQualitySlider setMaxValue:31.0];
4871         [fVidQualitySlider setNumberOfTickMarks:31];
4872     }
4873     /* Theora 0-63 */
4874     if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_THEORA)
4875     {
4876         [fVidQualitySlider setMinValue:0.0];
4877         [fVidQualitySlider setMaxValue:63.0];
4878         [fVidQualitySlider setNumberOfTickMarks:64];
4879     }
4880     [fVidQualityRFLabel setStringValue:qpRFLabelString];
4881     
4882     /* check to see if we have changed slider scales */
4883     if (previousMaxValue != [fVidQualitySlider maxValue])
4884     {
4885         /* if so, convert the old setting to the new scale as close as possible based on percentages */
4886         float rf =  ([fVidQualitySlider maxValue] - [fVidQualitySlider minValue] + 1) * previousPercentOfSliderScale;
4887         [fVidQualitySlider setFloatValue:rf];
4888     }
4889     
4890     [self qualitySliderChanged:nil];
4891 }
4892
4893 - (IBAction) qualitySliderChanged: (id) sender
4894 {
4895     /* Our constant quality slider is in a range based
4896      * on each encoders qp/rf values. The range depends
4897      * on the encoder. Also, the range is inverse of quality
4898      * for all of the encoders *except* for theora
4899      * (ie. as the "quality" goes up, the cq or rf value
4900      * actually goes down). Since the IB sliders always set
4901      * their max value at the right end of the slider, we
4902      * will calculate the inverse, so as the slider floatValue
4903      * goes up, we will show the inverse in the rf field
4904      * so, the floatValue at the right for x264 would be 51
4905      * and our rf field needs to show 0 and vice versa.
4906      */
4907     
4908     float sliderRfInverse = ([fVidQualitySlider maxValue] - [fVidQualitySlider floatValue]) + [fVidQualitySlider minValue];
4909     /* If the encoder is theora, use the float, otherwise use the inverse float*/
4910     float sliderRfToPercent;
4911     if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_THEORA)
4912     {
4913         [fVidQualityRFField setStringValue: [NSString stringWithFormat: @"%.2f", [fVidQualitySlider floatValue]]];
4914         sliderRfToPercent = [fVidQualityRFField floatValue] / ([fVidQualitySlider maxValue] - [fVidQualitySlider minValue]);   
4915     }
4916     else
4917     {
4918         [fVidQualityRFField setStringValue: [NSString stringWithFormat: @"%.2f", sliderRfInverse]];
4919         sliderRfToPercent = ( ([fVidQualitySlider maxValue] - [fVidQualitySlider minValue])  - ([fVidQualityRFField floatValue] - [fVidQualitySlider minValue])) / ([fVidQualitySlider maxValue] - [fVidQualitySlider minValue]);
4920     }
4921     [fVidConstantCell setTitle: [NSString stringWithFormat:
4922                                  NSLocalizedString( @"Constant quality: %.2f %%", @"" ), 100 * sliderRfToPercent]];
4923     
4924     [self customSettingUsed: sender];
4925 }
4926
4927 - (void) controlTextDidChange: (NSNotification *) notification
4928 {
4929     [self calculateBitrate:nil];
4930 }
4931
4932 - (IBAction) calculateBitrate: (id) sender
4933 {
4934     if( !fHandle || [fVidQualityMatrix selectedRow] != 0 || !SuccessfulScan )
4935     {
4936         return;
4937     }
4938
4939     hb_list_t  * list  = hb_get_titles( fHandle );
4940     hb_title_t * title = (hb_title_t *) hb_list_item( list,
4941             [fSrcTitlePopUp indexOfSelectedItem] );
4942     hb_job_t * job = title->job;
4943     hb_audio_config_t * audio;
4944     /* For  hb_calc_bitrate in addition to the Target Size in MB out of the
4945      * Target Size Field, we also need the job info for the Muxer, the Chapters
4946      * as well as all of the audio track info.
4947      * This used to be accomplished by simply calling prepareJob here, however
4948      * since the resilient queue sets the queue array values instead of the job
4949      * values directly, we duplicate the old prepareJob code here for the variables
4950      * needed
4951      */
4952     job->chapter_start = [fSrcChapterStartPopUp indexOfSelectedItem] + 1;
4953     job->chapter_end = [fSrcChapterEndPopUp indexOfSelectedItem] + 1; 
4954     job->mux = [[fDstFormatPopUp selectedItem] tag];
4955     
4956     /* Audio goes here */
4957     int audiotrack_count = hb_list_count(job->list_audio);
4958     for( int i = 0; i < audiotrack_count;i++)
4959     {
4960         hb_audio_t * temp_audio = (hb_audio_t*) hb_list_item( job->list_audio, 0 );
4961         hb_list_rem(job->list_audio, temp_audio);
4962     }
4963     /* Now we need our audio info here for each track if applicable */
4964     if ([fAudLang1PopUp indexOfSelectedItem] > 0)
4965     {
4966         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
4967         hb_audio_config_init(audio);
4968         audio->in.track = [fAudLang1PopUp indexOfSelectedItem] - 1;
4969         /* We go ahead and assign values to our audio->out.<properties> */
4970         audio->out.track = [fAudLang1PopUp indexOfSelectedItem] - 1;
4971         audio->out.codec = [[fAudTrack1CodecPopUp selectedItem] tag];
4972         audio->out.mixdown = [[fAudTrack1MixPopUp selectedItem] tag];
4973         audio->out.bitrate = [[fAudTrack1BitratePopUp selectedItem] tag];
4974         audio->out.samplerate = [[fAudTrack1RatePopUp selectedItem] tag];
4975         audio->out.dynamic_range_compression = [fAudTrack1DrcField floatValue];
4976         
4977         hb_audio_add( job, audio );
4978         free(audio);
4979     }  
4980     if ([fAudLang2PopUp indexOfSelectedItem] > 0)
4981     {
4982         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
4983         hb_audio_config_init(audio);
4984         audio->in.track = [fAudLang2PopUp indexOfSelectedItem] - 1;
4985         /* We go ahead and assign values to our audio->out.<properties> */
4986         audio->out.track = [fAudLang2PopUp indexOfSelectedItem] - 1;
4987         audio->out.codec = [[fAudTrack2CodecPopUp selectedItem] tag];
4988         audio->out.mixdown = [[fAudTrack2MixPopUp selectedItem] tag];
4989         audio->out.bitrate = [[fAudTrack2BitratePopUp selectedItem] tag];
4990         audio->out.samplerate = [[fAudTrack2RatePopUp selectedItem] tag];
4991         audio->out.dynamic_range_compression = [fAudTrack2DrcField floatValue];
4992         
4993         hb_audio_add( job, audio );
4994         free(audio);
4995         
4996     }
4997     
4998     if ([fAudLang3PopUp indexOfSelectedItem] > 0)
4999     {
5000         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
5001         hb_audio_config_init(audio);
5002         audio->in.track = [fAudLang3PopUp indexOfSelectedItem] - 1;
5003         /* We go ahead and assign values to our audio->out.<properties> */
5004         audio->out.track = [fAudLang3PopUp indexOfSelectedItem] - 1;
5005         audio->out.codec = [[fAudTrack3CodecPopUp selectedItem] tag];
5006         audio->out.mixdown = [[fAudTrack3MixPopUp selectedItem] tag];
5007         audio->out.bitrate = [[fAudTrack3BitratePopUp selectedItem] tag];
5008         audio->out.samplerate = [[fAudTrack3RatePopUp selectedItem] tag];
5009         audio->out.dynamic_range_compression = [fAudTrack3DrcField floatValue];
5010         
5011         hb_audio_add( job, audio );
5012         free(audio);
5013         
5014     }
5015
5016     if ([fAudLang4PopUp indexOfSelectedItem] > 0)
5017     {
5018         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
5019         hb_audio_config_init(audio);
5020         audio->in.track = [fAudLang4PopUp indexOfSelectedItem] - 1;
5021         /* We go ahead and assign values to our audio->out.<properties> */
5022         audio->out.track = [fAudLang4PopUp indexOfSelectedItem] - 1;
5023         audio->out.codec = [[fAudTrack4CodecPopUp selectedItem] tag];
5024         audio->out.mixdown = [[fAudTrack4MixPopUp selectedItem] tag];
5025         audio->out.bitrate = [[fAudTrack4BitratePopUp selectedItem] tag];
5026         audio->out.samplerate = [[fAudTrack4RatePopUp selectedItem] tag];
5027         audio->out.dynamic_range_compression = [fAudTrack4DrcField floatValue];
5028         
5029         hb_audio_add( job, audio );
5030         free(audio);
5031         
5032     }
5033        
5034 [fVidBitrateField setIntValue: hb_calc_bitrate( job, [fVidTargetSizeField intValue] )];
5035 }
5036
5037 #pragma mark -
5038 #pragma mark - Picture
5039
5040 /* lets set the picture size back to the max from right after title scan
5041    Lets use an IBAction here as down the road we could always use a checkbox
5042    in the gui to easily take the user back to max. Remember, the compiler
5043    resolves IBActions down to -(void) during compile anyway */
5044 - (IBAction) revertPictureSizeToMax: (id) sender
5045 {
5046         hb_job_t * job = fTitle->job;
5047         /* Here we apply the title source and height */
5048     job->width = fTitle->width;
5049     job->height = fTitle->height;
5050     
5051     [self calculatePictureSizing: sender];
5052     /* We call method to change UI to reflect whether a preset is used or not*/    
5053     [self customSettingUsed: sender];
5054 }
5055
5056 /**
5057  * Registers changes made in the Picture Settings Window.
5058  */
5059
5060 - (void)pictureSettingsDidChange 
5061 {
5062         [self calculatePictureSizing:nil];
5063 }
5064
5065 /* Get and Display Current Pic Settings in main window */
5066 - (IBAction) calculatePictureSizing: (id) sender
5067 {
5068         if (fTitle->job->anamorphic.mode > 0)
5069         {
5070         fTitle->job->keep_ratio = 0;
5071         }
5072     
5073     [fPictureSizeField setStringValue: [NSString stringWithFormat:@"Picture Size: %@", [fPictureController getPictureSizeInfoString]]];
5074     
5075     NSString *picCropping;
5076     /* Set the display field for crop as per boolean */
5077         if (![fPictureController autoCrop])
5078         {
5079         picCropping =  @"Custom";
5080         }
5081         else
5082         {
5083                 picCropping =  @"Auto";
5084         }
5085     picCropping = [picCropping stringByAppendingString:[NSString stringWithFormat:@" %d/%d/%d/%d",fTitle->job->crop[0],fTitle->job->crop[1],fTitle->job->crop[2],fTitle->job->crop[3]]];
5086     
5087     [fPictureCroppingField setStringValue: [NSString stringWithFormat:@"Picture Cropping: %@",picCropping]];
5088     
5089     NSString *videoFilters;
5090     videoFilters = @"";
5091     /* Detelecine */
5092     if ([fPictureController detelecine] == 2) 
5093     {
5094         videoFilters = [videoFilters stringByAppendingString:@" - Detelecine (Default)"];
5095     }
5096     else if ([fPictureController detelecine] == 1) 
5097     {
5098         videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Detelecine (%@)",[fPictureController detelecineCustomString]]];
5099     }
5100     
5101     
5102     if ([fPictureController useDecomb] == 1)
5103     {
5104         /* Decomb */
5105         if ([fPictureController decomb] == 2)
5106         {
5107             videoFilters = [videoFilters stringByAppendingString:@" - Decomb (Default)"];
5108         }
5109         else if ([fPictureController decomb] == 1)
5110         {
5111             videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Decomb (%@)",[fPictureController decombCustomString]]];
5112         }
5113     }
5114     else
5115     {
5116         /* Deinterlace */
5117         if ([fPictureController deinterlace] > 0)
5118         {
5119             fTitle->job->deinterlace  = 1;
5120         }
5121         else
5122         {
5123             fTitle->job->deinterlace  = 0;
5124         }
5125         
5126         if ([fPictureController deinterlace] == 2)
5127         {
5128             videoFilters = [videoFilters stringByAppendingString:@" - Deinterlace (Fast)"];
5129         }
5130         else if ([fPictureController deinterlace] == 3)
5131         {
5132             videoFilters = [videoFilters stringByAppendingString:@" - Deinterlace (Slow)"];
5133         }
5134         else if ([fPictureController deinterlace] == 4)
5135         {
5136             videoFilters = [videoFilters stringByAppendingString:@" - Deinterlace (Slower)"];
5137         }
5138         else if ([fPictureController deinterlace] == 1)
5139         {
5140             videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Deinterlace (%@)",[fPictureController deinterlaceCustomString]]];
5141         }
5142         }
5143     
5144     
5145     /* Denoise */
5146         if ([fPictureController denoise] == 2)
5147         {
5148                 videoFilters = [videoFilters stringByAppendingString:@" - Denoise (Weak)"];
5149     }
5150         else if ([fPictureController denoise] == 3)
5151         {
5152                 videoFilters = [videoFilters stringByAppendingString:@" - Denoise (Medium)"];
5153     }
5154         else if ([fPictureController denoise] == 4)
5155         {
5156                 videoFilters = [videoFilters stringByAppendingString:@" - Denoise (Strong)"];
5157         }
5158     else if ([fPictureController denoise] == 1)
5159         {
5160                 videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Denoise (%@)",[fPictureController denoiseCustomString]]];
5161         }
5162     
5163     /* Deblock */
5164     if ([fPictureController deblock] > 0) 
5165     {
5166         videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Deblock (%d)",[fPictureController deblock]]];
5167     }
5168         
5169     /* Grayscale */
5170     if ([fPictureController grayscale]) 
5171     {
5172         videoFilters = [videoFilters stringByAppendingString:@" - Grayscale"];
5173     }
5174     [fVideoFiltersField setStringValue: [NSString stringWithFormat:@"Video Filters: %@", videoFilters]];
5175     
5176     //[fPictureController reloadStillPreview]; 
5177 }
5178
5179
5180 #pragma mark -
5181 #pragma mark - Audio and Subtitles
5182 - (IBAction) audioCodecsPopUpChanged: (id) sender
5183 {
5184     
5185     NSPopUpButton * audiotrackPopUp;
5186     NSPopUpButton * sampleratePopUp;
5187     NSPopUpButton * bitratePopUp;
5188     NSPopUpButton * audiocodecPopUp;
5189     if (sender == fAudTrack1CodecPopUp)
5190     {
5191         audiotrackPopUp = fAudLang1PopUp;
5192         audiocodecPopUp = fAudTrack1CodecPopUp;
5193         sampleratePopUp = fAudTrack1RatePopUp;
5194         bitratePopUp = fAudTrack1BitratePopUp;
5195     }
5196     else if (sender == fAudTrack2CodecPopUp)
5197     {
5198         audiotrackPopUp = fAudLang2PopUp;
5199         audiocodecPopUp = fAudTrack2CodecPopUp;
5200         sampleratePopUp = fAudTrack2RatePopUp;
5201         bitratePopUp = fAudTrack2BitratePopUp;
5202     }
5203     else if (sender == fAudTrack3CodecPopUp)
5204     {
5205         audiotrackPopUp = fAudLang3PopUp;
5206         audiocodecPopUp = fAudTrack3CodecPopUp;
5207         sampleratePopUp = fAudTrack3RatePopUp;
5208         bitratePopUp = fAudTrack3BitratePopUp;
5209     }
5210     else
5211     {
5212         audiotrackPopUp = fAudLang4PopUp;
5213         audiocodecPopUp = fAudTrack4CodecPopUp;
5214         sampleratePopUp = fAudTrack4RatePopUp;
5215         bitratePopUp = fAudTrack4BitratePopUp;
5216     }
5217         
5218     /* changing the codecs on offer may mean that we can / can't offer mono or 6ch, */
5219         /* so call audioTrackPopUpChanged for both audio tracks to update the mixdown popups */
5220     [self audioTrackPopUpChanged: audiotrackPopUp];
5221     
5222 }
5223
5224 - (IBAction) setEnabledStateOfAudioMixdownControls: (id) sender
5225 {
5226     /* We will be setting the enabled/disabled state of each tracks audio controls based on
5227      * the settings of the source audio for that track. We leave the samplerate and bitrate
5228      * to audiotrackMixdownChanged
5229      */
5230     
5231     /* We will first verify that a lower track number has been selected before enabling each track
5232      * for example, make sure a track is selected for track 1 before enabling track 2, etc.
5233      */
5234     if ([fAudLang1PopUp indexOfSelectedItem] == 0)
5235     {
5236         [fAudLang2PopUp setEnabled: NO];
5237         [fAudLang2PopUp selectItemAtIndex: 0];
5238     }
5239     else
5240     {
5241         [fAudLang2PopUp setEnabled: YES];
5242     }
5243     
5244     if ([fAudLang2PopUp indexOfSelectedItem] == 0)
5245     {
5246         [fAudLang3PopUp setEnabled: NO];
5247         [fAudLang3PopUp selectItemAtIndex: 0];
5248     }
5249     else
5250     {
5251         [fAudLang3PopUp setEnabled: YES];
5252     }
5253     if ([fAudLang3PopUp indexOfSelectedItem] == 0)
5254     {
5255         [fAudLang4PopUp setEnabled: NO];
5256         [fAudLang4PopUp selectItemAtIndex: 0];
5257     }
5258     else
5259     {
5260         [fAudLang4PopUp setEnabled: YES];
5261     }
5262     /* enable/disable the mixdown text and popupbutton for audio track 1 */
5263     [fAudTrack1CodecPopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
5264     [fAudTrack1MixPopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
5265     [fAudTrack1RatePopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
5266     [fAudTrack1BitratePopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
5267     [fAudTrack1DrcSlider setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
5268     [fAudTrack1DrcField setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
5269     if ([fAudLang1PopUp indexOfSelectedItem] == 0)
5270     {
5271         [fAudTrack1CodecPopUp removeAllItems];
5272         [fAudTrack1MixPopUp removeAllItems];
5273         [fAudTrack1RatePopUp removeAllItems];
5274         [fAudTrack1BitratePopUp removeAllItems];
5275         [fAudTrack1DrcSlider setFloatValue: 0.00];
5276         [self audioDRCSliderChanged: fAudTrack1DrcSlider];
5277     }
5278     else if ([[fAudTrack1MixPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack1MixPopUp selectedItem] tag] == HB_ACODEC_DCA)
5279     {
5280         [fAudTrack1RatePopUp setEnabled: NO];
5281         [fAudTrack1BitratePopUp setEnabled: NO];
5282         [fAudTrack1DrcSlider setEnabled: NO];
5283         [fAudTrack1DrcField setEnabled: NO];
5284     }
5285     
5286     /* enable/disable the mixdown text and popupbutton for audio track 2 */
5287     [fAudTrack2CodecPopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
5288     [fAudTrack2MixPopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
5289     [fAudTrack2RatePopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
5290     [fAudTrack2BitratePopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
5291     [fAudTrack2DrcSlider setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
5292     [fAudTrack2DrcField setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
5293     if ([fAudLang2PopUp indexOfSelectedItem] == 0)
5294     {
5295         [fAudTrack2CodecPopUp removeAllItems];
5296         [fAudTrack2MixPopUp removeAllItems];
5297         [fAudTrack2RatePopUp removeAllItems];
5298         [fAudTrack2BitratePopUp removeAllItems];
5299         [fAudTrack2DrcSlider setFloatValue: 0.00];
5300         [self audioDRCSliderChanged: fAudTrack2DrcSlider];
5301     }
5302     else if ([[fAudTrack2MixPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack2MixPopUp selectedItem] tag] == HB_ACODEC_DCA)
5303     {
5304         [fAudTrack2RatePopUp setEnabled: NO];
5305         [fAudTrack2BitratePopUp setEnabled: NO];
5306         [fAudTrack2DrcSlider setEnabled: NO];
5307         [fAudTrack2DrcField setEnabled: NO];
5308     }
5309     
5310     /* enable/disable the mixdown text and popupbutton for audio track 3 */
5311     [fAudTrack3CodecPopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
5312     [fAudTrack3MixPopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
5313     [fAudTrack3RatePopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
5314     [fAudTrack3BitratePopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
5315     [fAudTrack3DrcSlider setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
5316     [fAudTrack3DrcField setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
5317     if ([fAudLang3PopUp indexOfSelectedItem] == 0)
5318     {
5319         [fAudTrack3CodecPopUp removeAllItems];
5320         [fAudTrack3MixPopUp removeAllItems];
5321         [fAudTrack3RatePopUp removeAllItems];
5322         [fAudTrack3BitratePopUp removeAllItems];
5323         [fAudTrack3DrcSlider setFloatValue: 0.00];
5324         [self audioDRCSliderChanged: fAudTrack3DrcSlider];
5325     }
5326     else if ([[fAudTrack3MixPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack3MixPopUp selectedItem] tag] == HB_ACODEC_DCA)
5327     {
5328         [fAudTrack3RatePopUp setEnabled: NO];
5329         [fAudTrack3BitratePopUp setEnabled: NO];
5330         [fAudTrack3DrcSlider setEnabled: NO];
5331         [fAudTrack3DrcField setEnabled: NO];
5332     }
5333     
5334     /* enable/disable the mixdown text and popupbutton for audio track 4 */
5335     [fAudTrack4CodecPopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
5336     [fAudTrack4MixPopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
5337     [fAudTrack4RatePopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
5338     [fAudTrack4BitratePopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
5339     [fAudTrack4DrcSlider setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
5340     [fAudTrack4DrcField setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
5341     if ([fAudLang4PopUp indexOfSelectedItem] == 0)
5342     {
5343         [fAudTrack4CodecPopUp removeAllItems];
5344         [fAudTrack4MixPopUp removeAllItems];
5345         [fAudTrack4RatePopUp removeAllItems];
5346         [fAudTrack4BitratePopUp removeAllItems];
5347         [fAudTrack4DrcSlider setFloatValue: 0.00];
5348         [self audioDRCSliderChanged: fAudTrack4DrcSlider];
5349     }
5350     else if ([[fAudTrack4MixPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack4MixPopUp selectedItem] tag] == HB_ACODEC_DCA)
5351     {
5352         [fAudTrack4RatePopUp setEnabled: NO];
5353         [fAudTrack4BitratePopUp setEnabled: NO];
5354         [fAudTrack4DrcSlider setEnabled: NO];
5355         [fAudTrack4DrcField setEnabled: NO];
5356     }
5357     
5358 }
5359
5360 - (IBAction) addAllAudioTracksToPopUp: (id) sender
5361 {
5362
5363     hb_list_t  * list  = hb_get_titles( fHandle );
5364     hb_title_t * title = (hb_title_t*)
5365         hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
5366
5367         hb_audio_config_t * audio;
5368
5369     [sender removeAllItems];
5370     [sender addItemWithTitle: NSLocalizedString( @"None", @"" )];
5371     for( int i = 0; i < hb_list_count( title->list_audio ); i++ )
5372     {
5373         audio = (hb_audio_config_t *) hb_list_audio_config_item( title->list_audio, i );
5374         [[sender menu] addItemWithTitle:
5375             [NSString stringWithUTF8String: audio->lang.description]
5376             action: NULL keyEquivalent: @""];
5377     }
5378     [sender selectItemAtIndex: 0];
5379
5380 }
5381
5382 - (IBAction) selectAudioTrackInPopUp: (id) sender searchPrefixString: (NSString *) searchPrefixString selectIndexIfNotFound: (int) selectIndexIfNotFound
5383 {
5384
5385     /* this method can be used to find a language, or a language-and-source-format combination, by passing in the appropriate string */
5386     /* e.g. to find the first French track, pass in an NSString * of "Francais" */
5387     /* e.g. to find the first English 5.1 AC3 track, pass in an NSString * of "English (AC3) (5.1 ch)" */
5388     /* if no matching track is found, then selectIndexIfNotFound is used to choose which track to select instead */
5389
5390         if (searchPrefixString)
5391         {
5392
5393         for( int i = 0; i < [sender numberOfItems]; i++ )
5394         {
5395             /* Try to find the desired search string */
5396             if ([[[sender itemAtIndex: i] title] hasPrefix:searchPrefixString])
5397             {
5398                 [sender selectItemAtIndex: i];
5399                 return;
5400             }
5401         }
5402         /* couldn't find the string, so select the requested "search string not found" item */
5403         /* index of 0 means select the "none" item */
5404         /* index of 1 means select the first audio track */
5405         [sender selectItemAtIndex: selectIndexIfNotFound];
5406         }
5407     else
5408     {
5409         /* if no search string is provided, then select the selectIndexIfNotFound item */
5410         [sender selectItemAtIndex: selectIndexIfNotFound];
5411     }
5412
5413 }
5414 - (IBAction) audioAddAudioTrackCodecs: (id)sender
5415 {
5416     int format = [fDstFormatPopUp indexOfSelectedItem];
5417     
5418     /* setup pointers to the appropriate popups for the correct track */
5419     NSPopUpButton * audiocodecPopUp;
5420     NSPopUpButton * audiotrackPopUp;
5421     if (sender == fAudTrack1CodecPopUp)
5422     {
5423         audiotrackPopUp = fAudLang1PopUp;
5424         audiocodecPopUp = fAudTrack1CodecPopUp;
5425     }
5426     else if (sender == fAudTrack2CodecPopUp)
5427     {
5428         audiotrackPopUp = fAudLang2PopUp;
5429         audiocodecPopUp = fAudTrack2CodecPopUp;
5430     }
5431     else if (sender == fAudTrack3CodecPopUp)
5432     {
5433         audiotrackPopUp = fAudLang3PopUp;
5434         audiocodecPopUp = fAudTrack3CodecPopUp;
5435     }
5436     else
5437     {
5438         audiotrackPopUp = fAudLang4PopUp;
5439         audiocodecPopUp = fAudTrack4CodecPopUp;
5440     }
5441     
5442     [audiocodecPopUp removeAllItems];
5443     /* Make sure "None" isnt selected in the source track */
5444     if ([audiotrackPopUp indexOfSelectedItem] > 0)
5445     {
5446         [audiocodecPopUp setEnabled:YES];
5447         NSMenuItem *menuItem;
5448         /* We setup our appropriate popups for codecs and put the int value in the popup tag for easy retrieval */
5449         switch( format )
5450         {
5451             case 0:
5452                 /* MP4 */
5453                 // FAAC
5454                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (faac)" action: NULL keyEquivalent: @""];
5455                 [menuItem setTag: HB_ACODEC_FAAC];
5456
5457                 // CA_AAC
5458                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (CoreAudio)" action: NULL keyEquivalent: @""];
5459                 [menuItem setTag: HB_ACODEC_CA_AAC];
5460
5461                 // AC3 Passthru
5462                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AC3 Passthru" action: NULL keyEquivalent: @""];
5463                 [menuItem setTag: HB_ACODEC_AC3];
5464                 break;
5465                 
5466             case 1:
5467                 /* MKV */
5468                 // FAAC
5469                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (faac)" action: NULL keyEquivalent: @""];
5470                 [menuItem setTag: HB_ACODEC_FAAC];
5471                 // CA_AAC
5472                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (CoreAudio)" action: NULL keyEquivalent: @""];
5473                 [menuItem setTag: HB_ACODEC_CA_AAC];
5474                 // AC3 Passthru
5475                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AC3 Passthru" action: NULL keyEquivalent: @""];
5476                 [menuItem setTag: HB_ACODEC_AC3];
5477                 // DTS Passthru
5478                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"DTS Passthru" action: NULL keyEquivalent: @""];
5479                 [menuItem setTag: HB_ACODEC_DCA];
5480                 // MP3
5481                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"MP3 (lame)" action: NULL keyEquivalent: @""];
5482                 [menuItem setTag: HB_ACODEC_LAME];
5483                 // Vorbis
5484                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"Vorbis (vorbis)" action: NULL keyEquivalent: @""];
5485                 [menuItem setTag: HB_ACODEC_VORBIS];
5486                 break;
5487                 
5488             case 2: 
5489                 /* AVI */
5490                 // MP3
5491                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"MP3 (lame)" action: NULL keyEquivalent: @""];
5492                 [menuItem setTag: HB_ACODEC_LAME];
5493                 // AC3 Passthru
5494                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AC3 Passthru" action: NULL keyEquivalent: @""];
5495                 [menuItem setTag: HB_ACODEC_AC3];
5496                 break;
5497                 
5498             case 3:
5499                 /* OGM */
5500                 // Vorbis
5501                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"Vorbis (vorbis)" action: NULL keyEquivalent: @""];
5502                 [menuItem setTag: HB_ACODEC_VORBIS];
5503                 // MP3
5504                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"MP3 (lame)" action: NULL keyEquivalent: @""];
5505                 [menuItem setTag: HB_ACODEC_LAME];
5506                 break;
5507         }
5508         [audiocodecPopUp selectItemAtIndex:0];
5509     }
5510     else
5511     {
5512         [audiocodecPopUp setEnabled:NO];
5513     }
5514 }
5515
5516 - (IBAction) audioTrackPopUpChanged: (id) sender
5517 {
5518     /* utility function to call audioTrackPopUpChanged without passing in a mixdown-to-use */
5519     [self audioTrackPopUpChanged: sender mixdownToUse: 0];
5520 }
5521
5522 - (IBAction) audioTrackPopUpChanged: (id) sender mixdownToUse: (int) mixdownToUse
5523 {
5524     
5525     /* make sure we have a selected title before continuing */
5526     if (fTitle == NULL) return;
5527     /* if the sender is the lanaguage popup and there is nothing in the codec popup, lets call
5528     * audioAddAudioTrackCodecs on the codec popup to populate it properly before moving on
5529     */
5530     if (sender == fAudLang1PopUp && [[fAudTrack1CodecPopUp menu] numberOfItems] == 0)
5531     {
5532         [self audioAddAudioTrackCodecs: fAudTrack1CodecPopUp];
5533     }
5534     if (sender == fAudLang2PopUp && [[fAudTrack2CodecPopUp menu] numberOfItems] == 0)
5535     {
5536         [self audioAddAudioTrackCodecs: fAudTrack2CodecPopUp];
5537     }
5538     if (sender == fAudLang3PopUp && [[fAudTrack3CodecPopUp menu] numberOfItems] == 0)
5539     {
5540         [self audioAddAudioTrackCodecs: fAudTrack3CodecPopUp];
5541     }
5542     if (sender == fAudLang4PopUp && [[fAudTrack4CodecPopUp menu] numberOfItems] == 0)
5543     {
5544         [self audioAddAudioTrackCodecs: fAudTrack4CodecPopUp];
5545     }
5546     
5547     /* Now lets make the sender the appropriate Audio Track popup from this point on */
5548     if (sender == fAudTrack1CodecPopUp || sender == fAudTrack1MixPopUp)
5549     {
5550         sender = fAudLang1PopUp;
5551     }
5552     if (sender == fAudTrack2CodecPopUp || sender == fAudTrack2MixPopUp)
5553     {
5554         sender = fAudLang2PopUp;
5555     }
5556     if (sender == fAudTrack3CodecPopUp || sender == fAudTrack3MixPopUp)
5557     {
5558         sender = fAudLang3PopUp;
5559     }
5560     if (sender == fAudTrack4CodecPopUp || sender == fAudTrack4MixPopUp)
5561     {
5562         sender = fAudLang4PopUp;
5563     }
5564     
5565     /* pointer to this track's mixdown, codec, sample rate and bitrate NSPopUpButton's */
5566     NSPopUpButton * mixdownPopUp;
5567     NSPopUpButton * audiocodecPopUp;
5568     NSPopUpButton * sampleratePopUp;
5569     NSPopUpButton * bitratePopUp;
5570     if (sender == fAudLang1PopUp)
5571     {
5572         mixdownPopUp = fAudTrack1MixPopUp;
5573         audiocodecPopUp = fAudTrack1CodecPopUp;
5574         sampleratePopUp = fAudTrack1RatePopUp;
5575         bitratePopUp = fAudTrack1BitratePopUp;
5576     }
5577     else if (sender == fAudLang2PopUp)
5578     {
5579         mixdownPopUp = fAudTrack2MixPopUp;
5580         audiocodecPopUp = fAudTrack2CodecPopUp;
5581         sampleratePopUp = fAudTrack2RatePopUp;
5582         bitratePopUp = fAudTrack2BitratePopUp;
5583     }
5584     else if (sender == fAudLang3PopUp)
5585     {
5586         mixdownPopUp = fAudTrack3MixPopUp;
5587         audiocodecPopUp = fAudTrack3CodecPopUp;
5588         sampleratePopUp = fAudTrack3RatePopUp;
5589         bitratePopUp = fAudTrack3BitratePopUp;
5590     }
5591     else
5592     {
5593         mixdownPopUp = fAudTrack4MixPopUp;
5594         audiocodecPopUp = fAudTrack4CodecPopUp;
5595         sampleratePopUp = fAudTrack4RatePopUp;
5596         bitratePopUp = fAudTrack4BitratePopUp;
5597     }
5598
5599     /* get the index of the selected audio Track*/
5600     int thisAudioIndex = [sender indexOfSelectedItem] - 1;
5601
5602     /* pointer for the hb_audio_s struct we will use later on */
5603     hb_audio_config_t * audio;
5604
5605     int acodec;
5606     /* check if the audio mixdown controls need their enabled state changing */
5607     [self setEnabledStateOfAudioMixdownControls:nil];
5608
5609     if (thisAudioIndex != -1)
5610     {
5611
5612         /* get the audio */
5613         audio = (hb_audio_config_t *) hb_list_audio_config_item( fTitle->list_audio, thisAudioIndex );// Should "fTitle" be title and be setup ?
5614
5615         /* actually manipulate the proper mixdowns here */
5616         /* delete the previous audio mixdown options */
5617         [mixdownPopUp removeAllItems];
5618
5619         acodec = [[audiocodecPopUp selectedItem] tag];
5620
5621         if (audio != NULL)
5622         {
5623
5624             /* find out if our selected output audio codec supports mono and / or 6ch */
5625             /* we also check for an input codec of AC3 or DCA,
5626              as they are the only libraries able to do the mixdown to mono / conversion to 6-ch */
5627             /* audioCodecsSupportMono and audioCodecsSupport6Ch are the same for now,
5628              but this may change in the future, so they are separated for flexibility */
5629             int audioCodecsSupportMono =
5630                     (audio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)) &&
5631                     (acodec != HB_ACODEC_LAME);
5632             int audioCodecsSupport6Ch =
5633                     (audio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)) &&
5634                     (acodec != HB_ACODEC_LAME);
5635             
5636             /* check for AC-3 passthru */
5637             if (audio->in.codec == HB_ACODEC_AC3 && acodec == HB_ACODEC_AC3)
5638             {
5639                 
5640             NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5641                  [NSString stringWithUTF8String: "AC3 Passthru"]
5642                                                action: NULL keyEquivalent: @""];
5643              [menuItem setTag: HB_ACODEC_AC3];   
5644             }
5645             else if (audio->in.codec == HB_ACODEC_DCA && acodec == HB_ACODEC_DCA)
5646             {
5647             NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5648                  [NSString stringWithUTF8String: "DTS Passthru"]
5649                                                action: NULL keyEquivalent: @""];
5650              [menuItem setTag: HB_ACODEC_DCA]; 
5651             }
5652             else
5653             {
5654                 
5655                 /* add the appropriate audio mixdown menuitems to the popupbutton */
5656                 /* in each case, we set the new menuitem's tag to be the amixdown value for that mixdown,
5657                  so that we can reference the mixdown later */
5658                 
5659                 /* keep a track of the min and max mixdowns we used, so we can select the best match later */
5660                 int minMixdownUsed = 0;
5661                 int maxMixdownUsed = 0;
5662                 
5663                 /* get the input channel layout without any lfe channels */
5664                 int layout = audio->in.channel_layout & HB_INPUT_CH_LAYOUT_DISCRETE_NO_LFE_MASK;
5665                 
5666                 /* do we want to add a mono option? */
5667                 if (audioCodecsSupportMono == 1)
5668                 {
5669                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5670                                             [NSString stringWithUTF8String: hb_audio_mixdowns[0].human_readable_name]
5671                                                                           action: NULL keyEquivalent: @""];
5672                     [menuItem setTag: hb_audio_mixdowns[0].amixdown];
5673                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[0].amixdown;
5674                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[0].amixdown);
5675                 }
5676                 
5677                 /* do we want to add a stereo option? */
5678                 /* offer stereo if we have a mono source and non-mono-supporting codecs, as otherwise we won't have a mixdown at all */
5679                 /* also offer stereo if we have a stereo-or-better source */
5680                 if ((layout == HB_INPUT_CH_LAYOUT_MONO && audioCodecsSupportMono == 0) || layout >= HB_INPUT_CH_LAYOUT_STEREO)
5681                 {
5682                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5683                                             [NSString stringWithUTF8String: hb_audio_mixdowns[1].human_readable_name]
5684                                                                           action: NULL keyEquivalent: @""];
5685                     [menuItem setTag: hb_audio_mixdowns[1].amixdown];
5686                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[1].amixdown;
5687                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[1].amixdown);
5688                 }
5689                 
5690                 /* do we want to add a dolby surround (DPL1) option? */
5691                 if (layout == HB_INPUT_CH_LAYOUT_3F1R || layout == HB_INPUT_CH_LAYOUT_3F2R || layout == HB_INPUT_CH_LAYOUT_DOLBY)
5692                 {
5693                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5694                                             [NSString stringWithUTF8String: hb_audio_mixdowns[2].human_readable_name]
5695                                                                           action: NULL keyEquivalent: @""];
5696                     [menuItem setTag: hb_audio_mixdowns[2].amixdown];
5697                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[2].amixdown;
5698                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[2].amixdown);
5699                 }
5700                 
5701                 /* do we want to add a dolby pro logic 2 (DPL2) option? */
5702                 if (layout == HB_INPUT_CH_LAYOUT_3F2R)
5703                 {
5704                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5705                                             [NSString stringWithUTF8String: hb_audio_mixdowns[3].human_readable_name]
5706                                                                           action: NULL keyEquivalent: @""];
5707                     [menuItem setTag: hb_audio_mixdowns[3].amixdown];
5708                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[3].amixdown;
5709                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[3].amixdown);
5710                 }
5711                 
5712                 /* do we want to add a 6-channel discrete option? */
5713                 if (audioCodecsSupport6Ch == 1 && layout == HB_INPUT_CH_LAYOUT_3F2R && (audio->in.channel_layout & HB_INPUT_CH_LAYOUT_HAS_LFE))
5714                 {
5715                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5716                                             [NSString stringWithUTF8String: hb_audio_mixdowns[4].human_readable_name]
5717                                                                           action: NULL keyEquivalent: @""];
5718                     [menuItem setTag: hb_audio_mixdowns[4].amixdown];
5719                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[4].amixdown;
5720                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[4].amixdown);
5721                 }
5722                 
5723                 /* do we want to add an AC-3 passthrough option? */
5724                 if (audio->in.codec == HB_ACODEC_AC3 && acodec == HB_ACODEC_AC3) 
5725                 {
5726                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5727                                             [NSString stringWithUTF8String: hb_audio_mixdowns[5].human_readable_name]
5728                                                                           action: NULL keyEquivalent: @""];
5729                     [menuItem setTag: HB_ACODEC_AC3];
5730                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[5].amixdown;
5731                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[5].amixdown);
5732                 }
5733                 
5734                 /* do we want to add a DTS Passthru option ? HB_ACODEC_DCA*/
5735                 if (audio->in.codec == HB_ACODEC_DCA && acodec == HB_ACODEC_DCA) 
5736                 {
5737                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5738                                             [NSString stringWithUTF8String: hb_audio_mixdowns[5].human_readable_name]
5739                                                                           action: NULL keyEquivalent: @""];
5740                     [menuItem setTag: HB_ACODEC_DCA];
5741                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[5].amixdown;
5742                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[5].amixdown);
5743                 }
5744                 
5745                 /* auto-select the best mixdown based on our saved mixdown preference */
5746                 
5747                 /* for now, this is hard-coded to a "best" mixdown of HB_AMIXDOWN_DOLBYPLII */
5748                 /* ultimately this should be a prefs option */
5749                 int useMixdown;
5750                 
5751                 /* if we passed in a mixdown to use - in order to load a preset - then try and use it */
5752                 if (mixdownToUse > 0)
5753                 {
5754                     useMixdown = mixdownToUse;
5755                 }
5756                 else
5757                 {
5758                     useMixdown = HB_AMIXDOWN_DOLBYPLII;
5759                 }
5760                 
5761                 /* if useMixdown > maxMixdownUsed, then use maxMixdownUsed */
5762                 if (useMixdown > maxMixdownUsed)
5763                 { 
5764                     useMixdown = maxMixdownUsed;
5765                 }
5766                 
5767                 /* if useMixdown < minMixdownUsed, then use minMixdownUsed */
5768                 if (useMixdown < minMixdownUsed)
5769                 { 
5770                     useMixdown = minMixdownUsed;
5771                 }
5772                 
5773                 /* select the (possibly-amended) preferred mixdown */
5774                 [mixdownPopUp selectItemWithTag: useMixdown];
5775
5776             }
5777             /* In the case of a source track that is not AC3 and the user tries to use AC3 Passthru (which does not work)
5778              * we force the Audio Codec choice back to a workable codec. We use MP3 for avi and aac for all
5779              * other containers.
5780              */
5781             if (audio->in.codec != HB_ACODEC_AC3 && [[audiocodecPopUp selectedItem] tag] == HB_ACODEC_AC3)
5782             {
5783                 /* If we are using the avi container, we select MP3 as there is no aac available*/
5784                 if ([[fDstFormatPopUp selectedItem] tag] == HB_MUX_AVI)
5785                 {
5786                     [audiocodecPopUp selectItemWithTag: HB_ACODEC_LAME];
5787                 }
5788                 else
5789                 {
5790                     [audiocodecPopUp selectItemWithTag: HB_ACODEC_FAAC];
5791                 }
5792             }
5793             
5794             /* In the case of a source track that is not DTS and the user tries to use DTS Passthru (which does not work)
5795              * we force the Audio Codec choice back to a workable codec. We use MP3 for avi and aac for all
5796              * other containers.
5797              */
5798             if (audio->in.codec != HB_ACODEC_DCA && [[audiocodecPopUp selectedItem] tag] == HB_ACODEC_DCA)
5799             {
5800                 /* If we are using the avi container, we select MP3 as there is no aac available*/
5801                 if ([[fDstFormatPopUp selectedItem] tag] == HB_MUX_AVI)
5802                 {
5803                     [audiocodecPopUp selectItemWithTag: HB_ACODEC_LAME];
5804                 }
5805                 else
5806                 {
5807                     [audiocodecPopUp selectItemWithTag: HB_ACODEC_FAAC];
5808                 }
5809             }
5810             
5811             /* Setup our samplerate and bitrate popups we will need based on mixdown */
5812             [self audioTrackMixdownChanged: mixdownPopUp];             
5813         }
5814     
5815     }
5816     if( [fDstFormatPopUp indexOfSelectedItem] == 0 )
5817     {
5818         [self autoSetM4vExtension: sender];
5819     }
5820 }
5821
5822 - (IBAction) audioTrackMixdownChanged: (id) sender
5823 {
5824     
5825     int acodec;
5826     /* setup pointers to all of the other audio track controls
5827     * we will need later
5828     */
5829     NSPopUpButton * mixdownPopUp;
5830     NSPopUpButton * sampleratePopUp;
5831     NSPopUpButton * bitratePopUp;
5832     NSPopUpButton * audiocodecPopUp;
5833     NSPopUpButton * audiotrackPopUp;
5834     NSSlider * drcSlider;
5835     NSTextField * drcField;
5836     if (sender == fAudTrack1MixPopUp)
5837     {
5838         audiotrackPopUp = fAudLang1PopUp;
5839         audiocodecPopUp = fAudTrack1CodecPopUp;
5840         mixdownPopUp = fAudTrack1MixPopUp;
5841         sampleratePopUp = fAudTrack1RatePopUp;
5842         bitratePopUp = fAudTrack1BitratePopUp;
5843         drcSlider = fAudTrack1DrcSlider;
5844         drcField = fAudTrack1DrcField;
5845     }
5846     else if (sender == fAudTrack2MixPopUp)
5847     {
5848         audiotrackPopUp = fAudLang2PopUp;
5849         audiocodecPopUp = fAudTrack2CodecPopUp;
5850         mixdownPopUp = fAudTrack2MixPopUp;
5851         sampleratePopUp = fAudTrack2RatePopUp;
5852         bitratePopUp = fAudTrack2BitratePopUp;
5853         drcSlider = fAudTrack2DrcSlider;
5854         drcField = fAudTrack2DrcField;
5855     }
5856     else if (sender == fAudTrack3MixPopUp)
5857     {
5858         audiotrackPopUp = fAudLang3PopUp;
5859         audiocodecPopUp = fAudTrack3CodecPopUp;
5860         mixdownPopUp = fAudTrack3MixPopUp;
5861         sampleratePopUp = fAudTrack3RatePopUp;
5862         bitratePopUp = fAudTrack3BitratePopUp;
5863         drcSlider = fAudTrack3DrcSlider;
5864         drcField = fAudTrack3DrcField;
5865     }
5866     else
5867     {
5868         audiotrackPopUp = fAudLang4PopUp;
5869         audiocodecPopUp = fAudTrack4CodecPopUp;
5870         mixdownPopUp = fAudTrack4MixPopUp;
5871         sampleratePopUp = fAudTrack4RatePopUp;
5872         bitratePopUp = fAudTrack4BitratePopUp;
5873         drcSlider = fAudTrack4DrcSlider;
5874         drcField = fAudTrack4DrcField;
5875     }
5876     acodec = [[audiocodecPopUp selectedItem] tag];
5877     /* storage variable for the min and max bitrate allowed for this codec */
5878     int minbitrate;
5879     int maxbitrate;
5880     
5881     switch( acodec )
5882     {
5883         case HB_ACODEC_FAAC:
5884             /* check if we have a 6ch discrete conversion in either audio track */
5885             if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH)
5886             {
5887                 /* FAAC has a minimum of 192 kbps for 6-channel discrete */
5888                 minbitrate = 192;
5889                 /* If either mixdown popup includes 6-channel discrete, then allow up to 448 kbps */
5890                 maxbitrate = 448;
5891                 break;
5892             }
5893             else
5894             {
5895                 /* FAAC is happy using our min bitrate of 32 kbps for stereo or mono */
5896                 minbitrate = 32;
5897                 /* FAAC won't honour anything more than 160 for stereo, so let's not offer it */
5898                 /* note: haven't dealt with mono separately here, FAAC will just use the max it can */
5899                 maxbitrate = 160;
5900                 break;
5901             }
5902
5903         case HB_ACODEC_CA_AAC:
5904             /* check if we have a 6ch discrete conversion in either audio track */
5905             if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH)
5906             {
5907                 minbitrate = 128;
5908                 maxbitrate = 768;
5909                 break;
5910             }
5911             else
5912             {
5913                 minbitrate = 64;
5914                 maxbitrate = 320;
5915                 break;
5916             }
5917
5918             case HB_ACODEC_LAME:
5919             /* Lame is happy using our min bitrate of 32 kbps */
5920             minbitrate = 32;
5921             /* Lame won't encode if the bitrate is higher than 320 kbps */
5922             maxbitrate = 320;
5923             break;
5924             
5925             case HB_ACODEC_VORBIS:
5926             if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH)
5927             {
5928                 /* Vorbis causes a crash if we use a bitrate below 192 kbps with 6 channel */
5929                 minbitrate = 192;
5930                 /* If either mixdown popup includes 6-channel discrete, then allow up to 384 kbps */
5931                 maxbitrate = 384;
5932                 break;
5933             }
5934             else
5935             {
5936                 /* Vorbis causes a crash if we use a bitrate below 48 kbps */
5937                 minbitrate = 48;
5938                 /* Vorbis can cope with 384 kbps quite happily, even for stereo */
5939                 maxbitrate = 384;
5940                 break;
5941             }
5942             
5943             default:
5944             /* AC3 passthru disables the bitrate dropdown anyway, so we might as well just use the min and max bitrate */
5945             minbitrate = 32;
5946             maxbitrate = 384;
5947             
5948     }
5949     
5950     /* make sure we have a selected title before continuing */
5951     if (fTitle == NULL) return;
5952     /* get the audio so we can find out what input rates are*/
5953     hb_audio_config_t * audio;
5954     audio = (hb_audio_config_t *) hb_list_audio_config_item( fTitle->list_audio, [audiotrackPopUp indexOfSelectedItem] - 1 );
5955     int inputbitrate = audio->in.bitrate / 1000;
5956     int inputsamplerate = audio->in.samplerate;
5957     
5958     if ([[mixdownPopUp selectedItem] tag] != HB_ACODEC_AC3 && [[mixdownPopUp selectedItem] tag] != HB_ACODEC_DCA)
5959     {
5960         [bitratePopUp removeAllItems];
5961         
5962         for( int i = 0; i < hb_audio_bitrates_count; i++ )
5963         {
5964             if (hb_audio_bitrates[i].rate >= minbitrate && hb_audio_bitrates[i].rate <= maxbitrate)
5965             {
5966                 /* add a new menuitem for this bitrate */
5967                 NSMenuItem *menuItem = [[bitratePopUp menu] addItemWithTitle:
5968                                         [NSString stringWithUTF8String: hb_audio_bitrates[i].string]
5969                                                                       action: NULL keyEquivalent: @""];
5970                 /* set its tag to be the actual bitrate as an integer, so we can retrieve it later */
5971                 [menuItem setTag: hb_audio_bitrates[i].rate];
5972             }
5973         }
5974         
5975         /* select the default bitrate (but use 384 for 6-ch AAC) */
5976         if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH)
5977         {
5978             [bitratePopUp selectItemWithTag: 384];
5979         }
5980         else
5981         {
5982             [bitratePopUp selectItemWithTag: hb_audio_bitrates[hb_audio_bitrates_default].rate];
5983         }
5984     }
5985     /* populate and set the sample rate popup */
5986     /* Audio samplerate */
5987     [sampleratePopUp removeAllItems];
5988     /* we create a same as source selection (Auto) so that we can choose to use the input sample rate */
5989     NSMenuItem *menuItem = [[sampleratePopUp menu] addItemWithTitle: @"Auto" action: NULL keyEquivalent: @""];
5990     [menuItem setTag: inputsamplerate];
5991     
5992     for( int i = 0; i < hb_audio_rates_count; i++ )
5993     {
5994         NSMenuItem *menuItem = [[sampleratePopUp menu] addItemWithTitle:
5995                                 [NSString stringWithUTF8String: hb_audio_rates[i].string]
5996                                                                  action: NULL keyEquivalent: @""];
5997         [menuItem setTag: hb_audio_rates[i].rate];
5998     }
5999     /* We use the input sample rate as the default sample rate as downsampling just makes audio worse
6000     * and there is no compelling reason to use anything else as default, though the users default
6001     * preset will likely override any setting chosen here.
6002     */
6003     [sampleratePopUp selectItemWithTag: inputsamplerate];
6004     
6005     
6006     /* Since AC3 Pass Thru and DTS Pass Thru uses the input bitrate and sample rate, we get the input tracks
6007     * bitrate and display it in the bitrate popup even though libhb happily ignores any bitrate input from
6008     * the gui. We do this for better user feedback in the audio tab as well as the queue for the most part
6009     */
6010     if ([[mixdownPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[mixdownPopUp selectedItem] tag] == HB_ACODEC_DCA)
6011     {
6012         
6013         /* lets also set the bitrate popup to the input bitrate as thats what passthru will use */
6014         [bitratePopUp removeAllItems];
6015         NSMenuItem *menuItem = [[bitratePopUp menu] addItemWithTitle:
6016                                 [NSString stringWithFormat:@"%d", inputbitrate]
6017                                                               action: NULL keyEquivalent: @""];
6018         [menuItem setTag: inputbitrate];
6019         /* For ac3 passthru we disable the sample rate and bitrate popups as well as the drc slider*/
6020         [bitratePopUp setEnabled: NO];
6021         [sampleratePopUp setEnabled: NO];
6022         
6023         [drcSlider setFloatValue: 0.00];
6024         [self audioDRCSliderChanged: drcSlider];
6025         [drcSlider setEnabled: NO];
6026         [drcField setEnabled: NO];
6027     }
6028     else
6029     {
6030         [sampleratePopUp setEnabled: YES];
6031         [bitratePopUp setEnabled: YES];
6032         [drcSlider setEnabled: YES];
6033         [drcField setEnabled: YES];
6034     }
6035 [self calculateBitrate:nil];    
6036 }
6037
6038 - (IBAction) audioDRCSliderChanged: (id) sender
6039 {
6040     NSSlider * drcSlider;
6041     NSTextField * drcField;
6042     if (sender == fAudTrack1DrcSlider)
6043     {
6044         drcSlider = fAudTrack1DrcSlider;
6045         drcField = fAudTrack1DrcField;
6046     }
6047     else if (sender == fAudTrack2DrcSlider)
6048     {
6049         drcSlider = fAudTrack2DrcSlider;
6050         drcField = fAudTrack2DrcField;
6051     }
6052     else if (sender == fAudTrack3DrcSlider)
6053     {
6054         drcSlider = fAudTrack3DrcSlider;
6055         drcField = fAudTrack3DrcField;
6056     }
6057     else
6058     {
6059         drcSlider = fAudTrack4DrcSlider;
6060         drcField = fAudTrack4DrcField;
6061     }
6062     
6063     /* If we are between 0.0 and 1.0 on the slider, snap it to 1.0 */
6064     if ([drcSlider floatValue] > 0.0 && [drcSlider floatValue] < 1.0)
6065     {
6066         [drcSlider setFloatValue:1.0];
6067     }
6068     
6069     
6070     [drcField setStringValue: [NSString stringWithFormat: @"%.2f", [drcSlider floatValue]]];
6071     /* For now, do not call this until we have an intelligent way to determine audio track selections
6072     * compared to presets
6073     */
6074     //[self customSettingUsed: sender];
6075 }
6076
6077 #pragma mark -
6078
6079 - (IBAction) browseImportSrtFile: (id) sender
6080 {
6081
6082     NSOpenPanel * panel;
6083         
6084     panel = [NSOpenPanel openPanel];
6085     [panel setAllowsMultipleSelection: NO];
6086     [panel setCanChooseFiles: YES];
6087     [panel setCanChooseDirectories: NO ];
6088     NSString * sourceDirectory;
6089         if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastSrtImportDirectory"])
6090         {
6091                 sourceDirectory = [[NSUserDefaults standardUserDefaults] stringForKey:@"LastSrtImportDirectory"];
6092         }
6093         else
6094         {
6095                 sourceDirectory = @"~/Desktop";
6096                 sourceDirectory = [sourceDirectory stringByExpandingTildeInPath];
6097         }
6098     /* we open up the browse srt sheet here and call for browseImportSrtFileDone after the sheet is closed */
6099     NSArray *fileTypes = [NSArray arrayWithObjects:@"plist", @"srt", nil];
6100     [panel beginSheetForDirectory: sourceDirectory file: nil types: fileTypes
6101                    modalForWindow: fWindow modalDelegate: self
6102                    didEndSelector: @selector( browseImportSrtFileDone:returnCode:contextInfo: )
6103                       contextInfo: sender];
6104 }
6105
6106 - (void) browseImportSrtFileDone: (NSSavePanel *) sheet
6107                      returnCode: (int) returnCode contextInfo: (void *) contextInfo
6108 {
6109     if( returnCode == NSOKButton )
6110     {
6111         NSString *importSrtDirectory = [[sheet filename] stringByDeletingLastPathComponent];
6112         NSString *importSrtFilePath = [sheet filename];
6113         [[NSUserDefaults standardUserDefaults] setObject:importSrtDirectory forKey:@"LastSrtImportDirectory"];
6114         
6115         /* now pass the string off to fSubtitlesDelegate to add the srt file to the dropdown */
6116         [fSubtitlesDelegate createSubtitleSrtTrack:importSrtFilePath];
6117         
6118         [fSubtitlesTable reloadData];
6119         
6120     }
6121 }                                           
6122
6123 #pragma mark -
6124 #pragma mark Open New Windows
6125
6126 - (IBAction) openHomepage: (id) sender
6127 {
6128     [[NSWorkspace sharedWorkspace] openURL: [NSURL
6129         URLWithString:@"http://handbrake.fr/"]];
6130 }
6131
6132 - (IBAction) openForums: (id) sender
6133 {
6134     [[NSWorkspace sharedWorkspace] openURL: [NSURL
6135         URLWithString:@"http://handbrake.fr/forum/"]];
6136 }
6137 - (IBAction) openUserGuide: (id) sender
6138 {
6139     [[NSWorkspace sharedWorkspace] openURL: [NSURL
6140         URLWithString:@"http://handbrake.fr/trac/wiki/HandBrakeGuide"]];
6141 }
6142
6143 /**
6144  * Shows debug output window.
6145  */
6146 - (IBAction)showDebugOutputPanel:(id)sender
6147 {
6148     [outputPanel showOutputPanel:sender];
6149 }
6150
6151 /**
6152  * Shows preferences window.
6153  */
6154 - (IBAction) showPreferencesWindow: (id) sender
6155 {
6156     NSWindow * window = [fPreferencesController window];
6157     if (![window isVisible])
6158         [window center];
6159
6160     [window makeKeyAndOrderFront: nil];
6161 }
6162
6163 /**
6164  * Shows queue window.
6165  */
6166 - (IBAction) showQueueWindow:(id)sender
6167 {
6168     [fQueueController showQueueWindow:sender];
6169 }
6170
6171
6172 - (IBAction) toggleDrawer:(id)sender {
6173     [fPresetDrawer toggle:self];
6174 }
6175
6176 /**
6177  * Shows Picture Settings Window.
6178  */
6179
6180 - (IBAction) showPicturePanel: (id) sender
6181 {
6182         [fPictureController showPictureWindow:sender];
6183 }
6184
6185 - (void) picturePanelFullScreen
6186 {
6187         [fPictureController setToFullScreenMode];
6188 }
6189
6190 - (void) picturePanelWindowed
6191 {
6192         [fPictureController setToWindowedMode];
6193 }
6194
6195 - (IBAction) showPreviewWindow: (id) sender
6196 {
6197         [fPictureController showPreviewWindow:sender];
6198 }
6199
6200 #pragma mark -
6201 #pragma mark Preset Outline View Methods
6202 #pragma mark - Required
6203 /* These are required by the NSOutlineView Datasource Delegate */
6204
6205
6206 /* used to specify the number of levels to show for each item */
6207 - (int)outlineView:(NSOutlineView *)fPresetsOutlineView numberOfChildrenOfItem:(id)item
6208 {
6209     /* currently use no levels to test outline view viability */
6210     if (item == nil) // for an outline view the root level of the hierarchy is always nil
6211     {
6212         return [UserPresets count];
6213     }
6214     else
6215     {
6216         /* we need to return the count of the array in ChildrenArray for this folder */
6217         NSArray *children = nil;
6218         children = [item objectForKey:@"ChildrenArray"];
6219         if ([children count] > 0)
6220         {
6221             return [children count];
6222         }
6223         else
6224         {
6225             return 0;
6226         }
6227     }
6228 }
6229
6230 /* We use this to deterimine children of an item */
6231 - (id)outlineView:(NSOutlineView *)fPresetsOutlineView child:(NSInteger)index ofItem:(id)item
6232 {
6233     
6234     /* we need to return the count of the array in ChildrenArray for this folder */
6235     NSArray *children = nil;
6236     if (item == nil)
6237     {
6238         children = UserPresets;
6239     }
6240     else
6241     {
6242         if ([item objectForKey:@"ChildrenArray"])
6243         {
6244             children = [item objectForKey:@"ChildrenArray"];
6245         }
6246     }   
6247     if ((children == nil) || ( [children count] <= (NSUInteger) index))
6248     {
6249         return nil;
6250     }
6251     else
6252     {
6253         return [children objectAtIndex:index];
6254     }
6255     
6256     
6257     // We are only one level deep, so we can't be asked about children
6258     //NSAssert (NO, @"Presets View outlineView:child:ofItem: currently can't handle nested items.");
6259     //return nil;
6260 }
6261
6262 /* We use this to determine if an item should be expandable */
6263 - (BOOL)outlineView:(NSOutlineView *)fPresetsOutlineView isItemExpandable:(id)item
6264 {
6265     
6266     /* we need to return the count of the array in ChildrenArray for this folder */
6267     NSArray *children= nil;
6268     if (item == nil)
6269     {
6270         children = UserPresets;
6271     }
6272     else
6273     {
6274         if ([item objectForKey:@"ChildrenArray"])
6275         {
6276             children = [item objectForKey:@"ChildrenArray"];
6277         }
6278     }   
6279     
6280     /* To deterimine if an item should show a disclosure triangle
6281      * we could do it by the children count as so:
6282      * if ([children count] < 1)
6283      * However, lets leave the triangle show even if there are no
6284      * children to help indicate a folder, just like folder in the
6285      * finder can show a disclosure triangle even when empty
6286      */
6287     
6288     /* We need to determine if the item is a folder */
6289    if ([[item objectForKey:@"Folder"] intValue] == 1)
6290    {
6291         return YES;
6292     }
6293     else
6294     {
6295         return NO;
6296     }
6297     
6298 }
6299
6300 - (BOOL)outlineView:(NSOutlineView *)outlineView shouldExpandItem:(id)item
6301 {
6302     // Our outline view has no levels, but we can still expand every item. Doing so
6303     // just makes the row taller. See heightOfRowByItem below.
6304 //return ![(HBQueueOutlineView*)outlineView isDragging];
6305
6306 return YES;
6307 }
6308
6309
6310 /* Used to tell the outline view which information is to be displayed per item */
6311 - (id)outlineView:(NSOutlineView *)fPresetsOutlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
6312 {
6313         /* We have two columns right now, icon and PresetName */
6314         
6315     if ([[tableColumn identifier] isEqualToString:@"PresetName"])
6316     {
6317         return [item objectForKey:@"PresetName"];
6318     }
6319     else
6320     {
6321         //return @"";
6322         return nil;
6323     }
6324 }
6325
6326 - (id)outlineView:(NSOutlineView *)outlineView itemForPersistentObject:(id)object
6327 {
6328     return [NSKeyedUnarchiver unarchiveObjectWithData:object];
6329 }
6330 - (id)outlineView:(NSOutlineView *)outlineView persistentObjectForItem:(id)item
6331 {
6332     return [NSKeyedArchiver archivedDataWithRootObject:item];
6333 }
6334
6335 #pragma mark - Added Functionality (optional)
6336 /* Use to customize the font and display characteristics of the title cell */
6337 - (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
6338 {
6339     if ([[tableColumn identifier] isEqualToString:@"PresetName"])
6340     {
6341         NSFont *txtFont;
6342         NSColor *fontColor;
6343         NSColor *shadowColor;
6344         txtFont = [NSFont systemFontOfSize: [NSFont smallSystemFontSize]];
6345         /*check to see if its a selected row */
6346         if ([fPresetsOutlineView selectedRow] == [fPresetsOutlineView rowForItem:item])
6347         {
6348             
6349             fontColor = [NSColor blackColor];
6350             shadowColor = [NSColor colorWithDeviceRed:(127.0/255.0) green:(140.0/255.0) blue:(160.0/255.0) alpha:1.0];
6351         }
6352         else
6353         {
6354             if ([[item objectForKey:@"Type"] intValue] == 0)
6355             {
6356                 fontColor = [NSColor blueColor];
6357             }
6358             else // User created preset, use a black font
6359             {
6360                 fontColor = [NSColor blackColor];
6361             }
6362             /* check to see if its a folder */
6363             //if ([[item objectForKey:@"Folder"] intValue] == 1)
6364             //{
6365             //fontColor = [NSColor greenColor];
6366             //}
6367             
6368             
6369         }
6370         /* We use Bold Text for the HB Default */
6371         if ([[item objectForKey:@"Default"] intValue] == 1)// 1 is HB default
6372         {
6373             txtFont = [NSFont boldSystemFontOfSize: [NSFont smallSystemFontSize]];
6374         }
6375         /* We use Bold Text for the User Specified Default */
6376         if ([[item objectForKey:@"Default"] intValue] == 2)// 2 is User default
6377         {
6378             txtFont = [NSFont boldSystemFontOfSize: [NSFont smallSystemFontSize]];
6379         }
6380         
6381         
6382         [cell setTextColor:fontColor];
6383         [cell setFont:txtFont];
6384         
6385     }
6386 }
6387
6388 /* We use this to edit the name field in the outline view */
6389 - (void)outlineView:(NSOutlineView *)outlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
6390 {
6391     if ([[tableColumn identifier] isEqualToString:@"PresetName"])
6392     {
6393         id theRecord;
6394         
6395         theRecord = item;
6396         [theRecord setObject:object forKey:@"PresetName"];
6397         
6398         [self sortPresets];
6399         
6400         [fPresetsOutlineView reloadData];
6401         /* We save all of the preset data here */
6402         [self savePreset];
6403     }
6404 }
6405 /* We use this to provide tooltips for the items in the presets outline view */
6406 - (NSString *)outlineView:(NSOutlineView *)fPresetsOutlineView toolTipForCell:(NSCell *)cell rect:(NSRectPointer)rect tableColumn:(NSTableColumn *)tc item:(id)item mouseLocation:(NSPoint)mouseLocation
6407 {
6408     //if ([[tc identifier] isEqualToString:@"PresetName"])
6409     //{
6410         /* initialize the tooltip contents variable */
6411         NSString *loc_tip;
6412         /* if there is a description for the preset, we show it in the tooltip */
6413         if ([item objectForKey:@"PresetDescription"])
6414         {
6415             loc_tip = [item objectForKey:@"PresetDescription"];
6416             return (loc_tip);
6417         }
6418         else
6419         {
6420             loc_tip = @"No description available";
6421         }
6422         return (loc_tip);
6423     //}
6424 }
6425
6426 #pragma mark -
6427 #pragma mark Preset Outline View Methods (dragging related)
6428
6429
6430 - (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard
6431 {
6432         // Dragging is only allowed for custom presets.
6433     //[[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Default"] intValue] != 1
6434         if ([[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Type"] intValue] == 0) // 0 is built in preset
6435     {
6436         return NO;
6437     }
6438     // Don't retain since this is just holding temporaral drag information, and it is
6439     //only used during a drag!  We could put this in the pboard actually.
6440     fDraggedNodes = items;
6441     // Provide data for our custom type, and simple NSStrings.
6442     [pboard declareTypes:[NSArray arrayWithObjects: DragDropSimplePboardType, nil] owner:self];
6443     
6444     // the actual data doesn't matter since DragDropSimplePboardType drags aren't recognized by anyone but us!.
6445     [pboard setData:[NSData data] forType:DragDropSimplePboardType]; 
6446     
6447     return YES;
6448 }
6449
6450 - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
6451 {
6452         
6453         // Don't allow dropping ONTO an item since they can't really contain any children.
6454     
6455     BOOL isOnDropTypeProposal = index == NSOutlineViewDropOnItemIndex;
6456     if (isOnDropTypeProposal)
6457         return NSDragOperationNone;
6458     
6459     // Don't allow dropping INTO an item since they can't really contain any children as of yet.
6460         if (item != nil)
6461         {
6462                 index = [fPresetsOutlineView rowForItem: item] + 1;
6463                 item = nil;
6464         }
6465     
6466     // Don't allow dropping into the Built In Presets.
6467     if (index < presetCurrentBuiltInCount)
6468     {
6469         return NSDragOperationNone;
6470         index = MAX (index, presetCurrentBuiltInCount);
6471         }    
6472         
6473     [outlineView setDropItem:item dropChildIndex:index];
6474     return NSDragOperationGeneric;
6475 }
6476
6477
6478
6479 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index
6480 {
6481     /* first, lets see if we are dropping into a folder */
6482     if ([[fPresetsOutlineView itemAtRow:index] objectForKey:@"Folder"] && [[[fPresetsOutlineView itemAtRow:index] objectForKey:@"Folder"] intValue] == 1) // if its a folder
6483         {
6484     NSMutableArray *childrenArray = [[NSMutableArray alloc] init];
6485     childrenArray = [[fPresetsOutlineView itemAtRow:index] objectForKey:@"ChildrenArray"];
6486     [childrenArray addObject:item];
6487     [[fPresetsOutlineView itemAtRow:index] setObject:[NSMutableArray arrayWithArray: childrenArray] forKey:@"ChildrenArray"];
6488     [childrenArray autorelease];
6489     }
6490     else // We are not, so we just move the preset into the existing array 
6491     {
6492         NSMutableIndexSet *moveItems = [NSMutableIndexSet indexSet];
6493         id obj;
6494         NSEnumerator *enumerator = [fDraggedNodes objectEnumerator];
6495         while (obj = [enumerator nextObject])
6496         {
6497             [moveItems addIndex:[UserPresets indexOfObject:obj]];
6498         }
6499         // Successful drop, lets rearrange the view and save it all
6500         [self moveObjectsInPresetsArray:UserPresets fromIndexes:moveItems toIndex: index];
6501     }
6502     [fPresetsOutlineView reloadData];
6503     [self savePreset];
6504     return YES;
6505 }
6506
6507 - (void)moveObjectsInPresetsArray:(NSMutableArray *)array fromIndexes:(NSIndexSet *)indexSet toIndex:(NSUInteger)insertIndex
6508 {
6509     NSUInteger index = [indexSet lastIndex];
6510     NSUInteger aboveInsertIndexCount = 0;
6511     
6512     NSUInteger removeIndex;
6513
6514     if (index >= insertIndex)
6515     {
6516         removeIndex = index + aboveInsertIndexCount;
6517         aboveInsertIndexCount++;
6518     }
6519     else
6520     {
6521         removeIndex = index;
6522         insertIndex--;
6523     }
6524
6525     id object = [[array objectAtIndex:removeIndex] retain];
6526     [array removeObjectAtIndex:removeIndex];
6527     [array insertObject:object atIndex:insertIndex];
6528     [object release];
6529
6530     index = [indexSet indexLessThanIndex:index];
6531 }
6532
6533
6534
6535 #pragma mark - Functional Preset NSOutlineView Methods
6536
6537 - (IBAction)selectPreset:(id)sender
6538 {
6539     
6540     if ([fPresetsOutlineView selectedRow] >= 0 && [[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Folder"] intValue] != 1)
6541     {
6542         chosenPreset = [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]];
6543         [fPresetSelectedDisplay setStringValue:[chosenPreset objectForKey:@"PresetName"]];
6544         
6545         if ([[chosenPreset objectForKey:@"Default"] intValue] == 1)
6546         {
6547             [fPresetSelectedDisplay setStringValue:[NSString stringWithFormat:@"%@ (Default)", [chosenPreset objectForKey:@"PresetName"]]];
6548         }
6549         else
6550         {
6551             [fPresetSelectedDisplay setStringValue:[chosenPreset objectForKey:@"PresetName"]];
6552         }
6553         
6554         /* File Format */
6555         [fDstFormatPopUp selectItemWithTitle:[chosenPreset objectForKey:@"FileFormat"]];
6556         [self formatPopUpChanged:nil];
6557         
6558         /* Chapter Markers*/
6559         [fCreateChapterMarkers setState:[[chosenPreset objectForKey:@"ChapterMarkers"] intValue]];
6560         /* check to see if we have only one chapter */
6561         [self chapterPopUpChanged:nil];
6562         
6563         /* Allow Mpeg4 64 bit formatting +4GB file sizes */
6564         [fDstMp4LargeFileCheck setState:[[chosenPreset objectForKey:@"Mp4LargeFile"] intValue]];
6565         /* Mux mp4 with http optimization */
6566         [fDstMp4HttpOptFileCheck setState:[[chosenPreset objectForKey:@"Mp4HttpOptimize"] intValue]];
6567         
6568         /* Video encoder */
6569         [fVidEncoderPopUp selectItemWithTitle:[chosenPreset objectForKey:@"VideoEncoder"]];
6570         /* We set the advanced opt string here if applicable*/
6571         [fAdvancedOptions setOptions:[chosenPreset objectForKey:@"x264Option"]];
6572         
6573         /* Lets run through the following functions to get variables set there */
6574         [self videoEncoderPopUpChanged:nil];
6575         /* Set the state of ipod compatible with Mp4iPodCompatible. Only for x264*/
6576         [fDstMp4iPodFileCheck setState:[[chosenPreset objectForKey:@"Mp4iPodCompatible"] intValue]];
6577         [self calculateBitrate:nil];
6578         
6579         /* Video quality */
6580         [fVidQualityMatrix selectCellAtRow:[[chosenPreset objectForKey:@"VideoQualityType"] intValue] column:0];
6581         
6582         [fVidTargetSizeField setStringValue:[chosenPreset objectForKey:@"VideoTargetSize"]];
6583         [fVidBitrateField setStringValue:[chosenPreset objectForKey:@"VideoAvgBitrate"]];
6584         
6585         /* Since we are now using RF Values for the slider, we detect if the preset uses an old quality float.
6586          * So, check to see if the quality value is less than 1.0 which should indicate the old ".062" type
6587          * quality preset. Caveat: in the case of x264, where the RF scale starts at 0, it would misinterpret
6588          * a preset that uses 0.0 - 0.99 for RF as an old style preset. Not sure how to get around that one yet,
6589          * though it should be a corner case since it would pretty much be a preset for lossless encoding. */
6590         if ([[chosenPreset objectForKey:@"VideoQualitySlider"] floatValue] < 1.0)
6591         {
6592             /* For the quality slider we need to convert the old percent's to the new rf scales */
6593             float rf =  (([fVidQualitySlider maxValue] - [fVidQualitySlider minValue]) * [[chosenPreset objectForKey:@"VideoQualitySlider"] floatValue]);
6594             [fVidQualitySlider setFloatValue:rf];
6595             
6596         }
6597         else
6598         {
6599             /* Since theora's qp value goes up from left to right, we can just set the slider float value */
6600             if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_THEORA)
6601             {
6602                 [fVidQualitySlider setFloatValue:[[chosenPreset objectForKey:@"VideoQualitySlider"] floatValue]];
6603             }
6604             else
6605             {
6606                 /* since ffmpeg and x264 use an "inverted" slider (lower qp/rf values indicate a higher quality) we invert the value on the slider */
6607                 [fVidQualitySlider setFloatValue:([fVidQualitySlider maxValue] + [fVidQualitySlider minValue]) - [[chosenPreset objectForKey:@"VideoQualitySlider"] floatValue]];
6608             }
6609         }
6610         
6611         [self videoMatrixChanged:nil];
6612         
6613         /* Video framerate */
6614         /* For video preset video framerate, we want to make sure that Same as source does not conflict with the
6615          detected framerate in the fVidRatePopUp so we use index 0*/
6616         if ([[chosenPreset objectForKey:@"VideoFramerate"] isEqualToString:@"Same as source"])
6617         {
6618             [fVidRatePopUp selectItemAtIndex: 0];
6619         }
6620         else
6621         {
6622             [fVidRatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"VideoFramerate"]];
6623         }
6624         
6625         
6626         /* 2 Pass Encoding */
6627         [fVidTwoPassCheck setState:[[chosenPreset objectForKey:@"VideoTwoPass"] intValue]];
6628         [self twoPassCheckboxChanged:nil];
6629         
6630         /* Turbo 1st pass for 2 Pass Encoding */
6631         [fVidTurboPassCheck setState:[[chosenPreset objectForKey:@"VideoTurboTwoPass"] intValue]];
6632         
6633         /*Audio*/
6634         /* First we check to see if we are using the current audio track layout based on AudioList array */
6635         if ([chosenPreset objectForKey:@"AudioList"])
6636         {
6637             
6638             /* pointer to this track's mixdown, codec, sample rate and bitrate NSPopUpButton's */
6639             NSPopUpButton * trackLangPopUp = nil;
6640             NSPopUpButton * mixdownPopUp = nil;
6641             NSPopUpButton * audiocodecPopUp = nil;
6642             NSPopUpButton * sampleratePopUp = nil;
6643             NSPopUpButton * bitratePopUp = nil;
6644             NSSlider      * drcSlider = nil;
6645             
6646             
6647             /* Populate the audio widgets based on the contents of the AudioList array */
6648             int i = 0;
6649             NSEnumerator *enumerator = [[chosenPreset objectForKey:@"AudioList"] objectEnumerator];
6650             id tempObject;
6651             while (tempObject = [enumerator nextObject])
6652             {
6653                 i++;
6654                 if( i == 1 )
6655                 {
6656                     trackLangPopUp = fAudLang1PopUp;
6657                     mixdownPopUp = fAudTrack1MixPopUp;
6658                     audiocodecPopUp = fAudTrack1CodecPopUp;
6659                     sampleratePopUp = fAudTrack1RatePopUp;
6660                     bitratePopUp = fAudTrack1BitratePopUp;
6661                     drcSlider = fAudTrack1DrcSlider;
6662                 }
6663                 if( i == 2 )
6664                 {
6665                     trackLangPopUp = fAudLang2PopUp;
6666                     mixdownPopUp = fAudTrack2MixPopUp;
6667                     audiocodecPopUp = fAudTrack2CodecPopUp;
6668                     sampleratePopUp = fAudTrack2RatePopUp;
6669                     bitratePopUp = fAudTrack2BitratePopUp;
6670                     drcSlider = fAudTrack2DrcSlider;
6671                 }
6672                 if( i == 3 )
6673                 {
6674                     trackLangPopUp = fAudLang3PopUp;
6675                     mixdownPopUp = fAudTrack3MixPopUp;
6676                     audiocodecPopUp = fAudTrack3CodecPopUp;
6677                     sampleratePopUp = fAudTrack3RatePopUp;
6678                     bitratePopUp = fAudTrack3BitratePopUp;
6679                     drcSlider = fAudTrack3DrcSlider;
6680                 }
6681                 if( i == 4 )
6682                 {
6683                     trackLangPopUp = fAudLang4PopUp;
6684                     mixdownPopUp = fAudTrack4MixPopUp;
6685                     audiocodecPopUp = fAudTrack4CodecPopUp;
6686                     sampleratePopUp = fAudTrack4RatePopUp;
6687                     bitratePopUp = fAudTrack4BitratePopUp;
6688                     drcSlider = fAudTrack4DrcSlider;
6689                 }
6690                 
6691                 
6692                 if ([trackLangPopUp indexOfSelectedItem] == 0)
6693                 {
6694                     [trackLangPopUp selectItemAtIndex: 1];
6695                 }
6696                 [self audioTrackPopUpChanged: trackLangPopUp];
6697                 [audiocodecPopUp selectItemWithTitle:[tempObject objectForKey:@"AudioEncoder"]];
6698                 /* check our pref for core audio and use it in place of faac if applicable */
6699                 if ([[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES && 
6700                     [[tempObject objectForKey:@"AudioEncoder"] isEqualToString: @"AAC (faac)"])
6701                 {
6702                     [audiocodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"];
6703                 }                    
6704                 
6705                 [self audioTrackPopUpChanged: audiocodecPopUp];
6706                 [mixdownPopUp selectItemWithTitle:[tempObject objectForKey:@"AudioMixdown"]];
6707                 /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
6708                  * mixdown*/
6709                 if  ([mixdownPopUp selectedItem] == nil)
6710                 {
6711                     [self audioTrackPopUpChanged: audiocodecPopUp];
6712                 }
6713                 [sampleratePopUp selectItemWithTitle:[tempObject objectForKey:@"AudioSamplerate"]];
6714                 /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
6715                 if (![[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"AC3 Passthru"])
6716                 {
6717                     [bitratePopUp selectItemWithTitle:[tempObject objectForKey:@"AudioBitrate"]];
6718                 }
6719                 [drcSlider setFloatValue:[[tempObject objectForKey:@"AudioTrackDRCSlider"] floatValue]];
6720                 [self audioDRCSliderChanged: drcSlider];
6721                 
6722                 
6723                 /* If we are any track greater than 1 check to make sure we have a matching source codec is using ac3 passthru or dts passthru,
6724                  * if not we will set the track to "None". Track 1 is allowed to mixdown to a suitable DPL2 mix if we cannot passthru */
6725                 
6726                 if( i > 1 )
6727                 {
6728                     /* Check to see if the preset asks for a passhthru track (AC3 or DTS) and verify there is a matching source track if not, set the track to "None". */
6729                     if (([[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"AC3 Passthru"] || [[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"DTS Passthru"])  && [trackLangPopUp indexOfSelectedItem] != 0)
6730                     {
6731                         hb_audio_config_t * audio;
6732                         /* get the audio source audio codec */
6733                         audio = (hb_audio_config_t *) hb_list_audio_config_item( fTitle->list_audio, [trackLangPopUp indexOfSelectedItem] - 1 );
6734                         if (audio != NULL && [[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"AC3 Passthru"] && audio->in.codec != HB_ACODEC_AC3 ||
6735                             [[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"DTS Passthru"] && audio->in.codec != HB_ACODEC_DCA )
6736                         {
6737                             /* We have a preset using ac3 passthru but no ac3 source audio, so set the track to "None" and bail */
6738                             if ([[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"AC3 Passthru"])
6739                             {
6740                                 [self writeToActivityLog: "Preset calls for AC3 Pass thru ..."];
6741                             }
6742                             if ([[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"DTS Passthru"])
6743                             {
6744                                 [self writeToActivityLog: "Preset calls for DTS Pass thru ..."];
6745                             }
6746                             [self writeToActivityLog: "No matching source codec, setting track  %d to None", i];
6747                             [trackLangPopUp selectItemAtIndex: 0];
6748                             [self audioTrackPopUpChanged: trackLangPopUp]; 
6749                         }   
6750                     }
6751                 }
6752             }
6753             
6754             /* We now cleanup any extra audio tracks that may have been previously set if we need to */
6755             
6756             if (i < 4)
6757             {
6758                 [fAudLang4PopUp selectItemAtIndex: 0];
6759                 [self audioTrackPopUpChanged: fAudLang4PopUp];
6760                 
6761                 if (i < 3)
6762                 {
6763                     [fAudLang3PopUp selectItemAtIndex: 0];
6764                     [self audioTrackPopUpChanged: fAudLang3PopUp];
6765                     
6766                     if (i < 2)
6767                     {
6768                         [fAudLang2PopUp selectItemAtIndex: 0];
6769                         [self audioTrackPopUpChanged: fAudLang2PopUp];
6770                     }
6771                 }
6772             }
6773             
6774         }
6775         else
6776         {
6777             if ([chosenPreset objectForKey:@"Audio1Track"] > 0)
6778             {
6779                 if ([fAudLang1PopUp indexOfSelectedItem] == 0)
6780                 {
6781                     [fAudLang1PopUp selectItemAtIndex: 1];
6782                 }
6783                 [self audioTrackPopUpChanged: fAudLang1PopUp];
6784                 [fAudTrack1CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Encoder"]];
6785                 /* check our pref for core audio and use it in place of faac if applicable */
6786                 if ([[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES && 
6787                     [[chosenPreset objectForKey:@"Audio1Encoder"] isEqualToString: @"AAC (faac)"])
6788                 {
6789                     [fAudTrack1CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"];
6790                 }
6791                 [self audioTrackPopUpChanged: fAudTrack1CodecPopUp];
6792                 [fAudTrack1MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Mixdown"]];
6793                 /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
6794                  * mixdown*/
6795                 if  ([fAudTrack1MixPopUp selectedItem] == nil)
6796                 {
6797                     [self audioTrackPopUpChanged: fAudTrack1CodecPopUp];
6798                 }
6799                 [fAudTrack1RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Samplerate"]];
6800                 /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
6801                 if (![[chosenPreset objectForKey:@"Audio1Encoder"] isEqualToString:@"AC3 Passthru"])
6802                 {
6803                     [fAudTrack1BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Bitrate"]];
6804                 }
6805                 [fAudTrack1DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio1TrackDRCSlider"] floatValue]];
6806                 [self audioDRCSliderChanged: fAudTrack1DrcSlider];
6807             }
6808             
6809             if ([chosenPreset objectForKey:@"Audio2Track"] > 0)
6810             {
6811                 if ([fAudLang2PopUp indexOfSelectedItem] == 0)
6812                 {
6813                     [fAudLang2PopUp selectItemAtIndex: 1];
6814                 }
6815                 [self audioTrackPopUpChanged: fAudLang2PopUp];
6816                 [fAudTrack2CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Encoder"]];
6817                 /* check our pref for core audio and use it in place of faac if applicable */
6818                 if ([[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES && 
6819                     [[chosenPreset objectForKey:@"Audio2Encoder"] isEqualToString: @"AAC (faac)"])
6820                 {
6821                     [fAudTrack2CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"];
6822                 }
6823                 [self audioTrackPopUpChanged: fAudTrack2CodecPopUp];
6824                 [fAudTrack2MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Mixdown"]];
6825                 /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
6826                  * mixdown*/
6827                 if  ([fAudTrack2MixPopUp selectedItem] == nil)
6828                 {
6829                     [self audioTrackPopUpChanged: fAudTrack2CodecPopUp];
6830                 }
6831                 [fAudTrack2RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Samplerate"]];
6832                 /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
6833                 if (![[chosenPreset objectForKey:@"Audio2Encoder"] isEqualToString:@"AC3 Passthru"])
6834                 {
6835                     [fAudTrack2BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Bitrate"]];
6836                 }
6837                 [fAudTrack2DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio2TrackDRCSlider"] floatValue]];
6838                 [self audioDRCSliderChanged: fAudTrack2DrcSlider];
6839             }
6840             if ([chosenPreset objectForKey:@"Audio3Track"] > 0)
6841             {
6842                 if ([fAudLang3PopUp indexOfSelectedItem] == 0)
6843                 {
6844                     [fAudLang3PopUp selectItemAtIndex: 1];
6845                 }
6846                 [self audioTrackPopUpChanged: fAudLang3PopUp];
6847                 [fAudTrack3CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Encoder"]];
6848                 /* check our pref for core audio and use it in place of faac if applicable */
6849                 if ([[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES && 
6850                     [[chosenPreset objectForKey:@"Audio3Encoder"] isEqualToString: @"AAC (faac)"])
6851                 {
6852                     [fAudTrack3CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"];
6853                 }
6854                 [self audioTrackPopUpChanged: fAudTrack3CodecPopUp];
6855                 [fAudTrack3MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Mixdown"]];
6856                 /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
6857                  * mixdown*/
6858                 if  ([fAudTrack3MixPopUp selectedItem] == nil)
6859                 {
6860                     [self audioTrackPopUpChanged: fAudTrack3CodecPopUp];
6861                 }
6862                 [fAudTrack3RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Samplerate"]];
6863                 /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
6864                 if (![[chosenPreset objectForKey:@"Audio3Encoder"] isEqualToString: @"AC3 Passthru"])
6865                 {
6866                     [fAudTrack3BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Bitrate"]];
6867                 }
6868                 [fAudTrack3DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio3TrackDRCSlider"] floatValue]];
6869                 [self audioDRCSliderChanged: fAudTrack3DrcSlider];
6870             }
6871             if ([chosenPreset objectForKey:@"Audio4Track"] > 0)
6872             {
6873                 if ([fAudLang4PopUp indexOfSelectedItem] == 0)
6874                 {
6875                     [fAudLang4PopUp selectItemAtIndex: 1];
6876                 }
6877                 [self audioTrackPopUpChanged: fAudLang4PopUp];
6878                 [fAudTrack4CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Encoder"]];
6879                 /* check our pref for core audio and use it in place of faac if applicable */
6880                 if ([[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES && 
6881                     [[chosenPreset objectForKey:@"Audio4Encoder"] isEqualToString: @"AAC (faac)"])
6882                 {
6883                     [fAudTrack4CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"];
6884                 }
6885                 [self audioTrackPopUpChanged: fAudTrack4CodecPopUp];
6886                 [fAudTrack4MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Mixdown"]];
6887                 /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
6888                  * mixdown*/
6889                 if  ([fAudTrack4MixPopUp selectedItem] == nil)
6890                 {
6891                     [self audioTrackPopUpChanged: fAudTrack4CodecPopUp];
6892                 }
6893                 [fAudTrack4RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Samplerate"]];
6894                 /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
6895                 if (![[chosenPreset objectForKey:@"Audio4Encoder"] isEqualToString:@"AC3 Passthru"])
6896                 {
6897                     [fAudTrack4BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Bitrate"]];
6898                 }
6899                 [fAudTrack4DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio4TrackDRCSlider"] floatValue]];
6900                 [self audioDRCSliderChanged: fAudTrack4DrcSlider];
6901             }
6902             
6903             /* We now cleanup any extra audio tracks that may have been previously set if we need to */
6904             
6905             if (![chosenPreset objectForKey:@"Audio2Track"] || [chosenPreset objectForKey:@"Audio2Track"] == 0)
6906             {
6907                 [fAudLang2PopUp selectItemAtIndex: 0];
6908                 [self audioTrackPopUpChanged: fAudLang2PopUp];
6909             }
6910             if (![chosenPreset objectForKey:@"Audio3Track"] || [chosenPreset objectForKey:@"Audio3Track"] > 0)
6911             {
6912                 [fAudLang3PopUp selectItemAtIndex: 0];
6913                 [self audioTrackPopUpChanged: fAudLang3PopUp];
6914             }
6915             if (![chosenPreset objectForKey:@"Audio4Track"] || [chosenPreset objectForKey:@"Audio4Track"] > 0)
6916             {
6917                 [fAudLang4PopUp selectItemAtIndex: 0];
6918                 [self audioTrackPopUpChanged: fAudLang4PopUp];
6919             }
6920         }
6921         
6922         /*Subtitles*/
6923         [fSubPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Subtitles"]];
6924         /* Forced Subtitles */
6925         [fSubForcedCheck setState:[[chosenPreset objectForKey:@"SubtitlesForced"] intValue]];
6926         
6927         /* Picture Settings */
6928         /* Note: objectForKey:@"UsesPictureSettings" refers to picture size, which encompasses:
6929          * height, width, keep ar, anamorphic and crop settings.
6930          * picture filters are handled separately below.
6931          */
6932         /* Check to see if the objectForKey:@"UsesPictureSettings is greater than 0, as 0 means use picture sizing "None" 
6933          * ( 2 is use max for source and 1 is use exact size when the preset was created ) and the 
6934          * preset completely ignores any picture sizing values in the preset.
6935          */
6936         if ([[chosenPreset objectForKey:@"UsesPictureSettings"]  intValue] > 0)
6937         {
6938             hb_job_t * job = fTitle->job;
6939             
6940             /* If Cropping is set to custom, then recall all four crop values from
6941              when the preset was created and apply them */
6942             if ([[chosenPreset objectForKey:@"PictureAutoCrop"]  intValue] == 0)
6943             {
6944                 [fPictureController setAutoCrop:NO];
6945                 
6946                 /* Here we use the custom crop values saved at the time the preset was saved */
6947                 job->crop[0] = [[chosenPreset objectForKey:@"PictureTopCrop"]  intValue];
6948                 job->crop[1] = [[chosenPreset objectForKey:@"PictureBottomCrop"]  intValue];
6949                 job->crop[2] = [[chosenPreset objectForKey:@"PictureLeftCrop"]  intValue];
6950                 job->crop[3] = [[chosenPreset objectForKey:@"PictureRightCrop"]  intValue];
6951                 
6952             }
6953             else /* if auto crop has been saved in preset, set to auto and use post scan auto crop */
6954             {
6955                 [fPictureController setAutoCrop:YES];
6956                 /* Here we use the auto crop values determined right after scan */
6957                 job->crop[0] = AutoCropTop;
6958                 job->crop[1] = AutoCropBottom;
6959                 job->crop[2] = AutoCropLeft;
6960                 job->crop[3] = AutoCropRight;
6961                 
6962             }
6963             
6964             
6965             /* Check to see if the objectForKey:@"UsesPictureSettings is 2 which is "Use Max for the source */
6966             if ([[chosenPreset objectForKey:@"UsesPictureSettings"]  intValue] == 2 || [[chosenPreset objectForKey:@"UsesMaxPictureSettings"]  intValue] == 1)
6967             {
6968                 /* Use Max Picture settings for whatever the dvd is.*/
6969                 [self revertPictureSizeToMax:nil];
6970                 job->keep_ratio = [[chosenPreset objectForKey:@"PictureKeepRatio"]  intValue];
6971                 if (job->keep_ratio == 1)
6972                 {
6973                     hb_fix_aspect( job, HB_KEEP_WIDTH );
6974                     if( job->height > fTitle->height )
6975                     {
6976                         job->height = fTitle->height;
6977                         hb_fix_aspect( job, HB_KEEP_HEIGHT );
6978                     }
6979                 }
6980                 job->anamorphic.mode = [[chosenPreset objectForKey:@"PicturePAR"]  intValue];
6981             }
6982             else // /* If not 0 or 2 we assume objectForKey:@"UsesPictureSettings is 1 which is "Use picture sizing from when the preset was set" */
6983             {
6984                 /* we check to make sure the presets width/height does not exceed the sources width/height */
6985                 if (fTitle->width < [[chosenPreset objectForKey:@"PictureWidth"]  intValue] || fTitle->height < [[chosenPreset objectForKey:@"PictureHeight"]  intValue])
6986                 {
6987                     /* if so, then we use the sources height and width to avoid scaling up */
6988                     //job->width = fTitle->width;
6989                     //job->height = fTitle->height;
6990                     [self revertPictureSizeToMax:nil];
6991                 }
6992                 else // source width/height is >= the preset height/width
6993                 {
6994                     /* we can go ahead and use the presets values for height and width */
6995                     job->width = [[chosenPreset objectForKey:@"PictureWidth"]  intValue];
6996                     job->height = [[chosenPreset objectForKey:@"PictureHeight"]  intValue];
6997                 }
6998                 job->keep_ratio = [[chosenPreset objectForKey:@"PictureKeepRatio"]  intValue];
6999                 if (job->keep_ratio == 1)
7000                 {
7001                     hb_fix_aspect( job, HB_KEEP_WIDTH );
7002                     if( job->height > fTitle->height )
7003                     {
7004                         job->height = fTitle->height;
7005                         hb_fix_aspect( job, HB_KEEP_HEIGHT );
7006                     }
7007                 }
7008                 job->anamorphic.mode = [[chosenPreset objectForKey:@"PicturePAR"]  intValue];
7009                 
7010             }
7011             
7012             
7013         }
7014         /* If the preset has an objectForKey:@"UsesPictureFilters", and handle the filters here */
7015         if ([chosenPreset objectForKey:@"UsesPictureFilters"] && [[chosenPreset objectForKey:@"UsesPictureFilters"]  intValue] > 0)
7016         {
7017             /* Filters */
7018             
7019             /* We only allow *either* Decomb or Deinterlace. So check for the PictureDecombDeinterlace key.
7020              * also, older presets may not have this key, in which case we also check to see if that preset had  PictureDecomb
7021              * specified, in which case we use decomb and ignore any possible Deinterlace settings as using both was less than
7022              * sane.
7023              */
7024             [fPictureController setUseDecomb:1];
7025             [fPictureController setDecomb:0];
7026             [fPictureController setDeinterlace:0];
7027             if ([[chosenPreset objectForKey:@"PictureDecombDeinterlace"] intValue] == 1 || [[chosenPreset objectForKey:@"PictureDecomb"] intValue] > 0)
7028             {
7029                 /* we are using decomb */
7030                 /* Decomb */
7031                 if ([[chosenPreset objectForKey:@"PictureDecomb"] intValue] > 0)
7032                 {
7033                     [fPictureController setDecomb:[[chosenPreset objectForKey:@"PictureDecomb"] intValue]];
7034                     
7035                     /* if we are using "Custom" in the decomb setting, also set the custom string*/
7036                     if ([[chosenPreset objectForKey:@"PictureDecomb"] intValue] == 1)
7037                     {
7038                         [fPictureController setDecombCustomString:[chosenPreset objectForKey:@"PictureDecombCustom"]];    
7039                     }
7040                 }
7041              }
7042             else
7043             {
7044                 /* We are using Deinterlace */
7045                 /* Deinterlace */
7046                 if ([[chosenPreset objectForKey:@"PictureDeinterlace"] intValue] > 0)
7047                 {
7048                     [fPictureController setUseDecomb:0];
7049                     [fPictureController setDeinterlace:[[chosenPreset objectForKey:@"PictureDeinterlace"] intValue]];
7050                     /* if we are using "Custom" in the deinterlace setting, also set the custom string*/
7051                     if ([[chosenPreset objectForKey:@"PictureDeinterlace"] intValue] == 1)
7052                     {
7053                         [fPictureController setDeinterlaceCustomString:[chosenPreset objectForKey:@"PictureDeinterlaceCustom"]];    
7054                     }
7055                 }
7056             }
7057             
7058             
7059             /* Detelecine */
7060             if ([[chosenPreset objectForKey:@"PictureDetelecine"] intValue] > 0)
7061             {
7062                 [fPictureController setDetelecine:[[chosenPreset objectForKey:@"PictureDetelecine"] intValue]];
7063                 /* if we are using "Custom" in the detelecine setting, also set the custom string*/
7064                 if ([[chosenPreset objectForKey:@"PictureDetelecine"] intValue] == 1)
7065                 {
7066                     [fPictureController setDetelecineCustomString:[chosenPreset objectForKey:@"PictureDetelecineCustom"]];    
7067                 }
7068             }
7069             else
7070             {
7071                 [fPictureController setDetelecine:0];
7072             }
7073             
7074             /* Denoise */
7075             if ([[chosenPreset objectForKey:@"PictureDenoise"] intValue] > 0)
7076             {
7077                 [fPictureController setDenoise:[[chosenPreset objectForKey:@"PictureDenoise"] intValue]];
7078                 /* if we are using "Custom" in the denoise setting, also set the custom string*/
7079                 if ([[chosenPreset objectForKey:@"PictureDenoise"] intValue] == 1)
7080                 {
7081                     [fPictureController setDenoiseCustomString:[chosenPreset objectForKey:@"PictureDenoiseCustom"]];    
7082                 }
7083             }
7084             else
7085             {
7086                 [fPictureController setDenoise:0];
7087             }   
7088             
7089             /* Deblock */
7090             if ([[chosenPreset objectForKey:@"PictureDeblock"] intValue] == 1)
7091             {
7092                 /* if its a one, then its the old on/off deblock, set on to 5*/
7093                 [fPictureController setDeblock:5];
7094             }
7095             else
7096             {
7097                 /* use the settings intValue */
7098                 [fPictureController setDeblock:[[chosenPreset objectForKey:@"PictureDeblock"] intValue]];
7099             }
7100             
7101             if ([[chosenPreset objectForKey:@"VideoGrayScale"] intValue] == 1)
7102             {
7103                 [fPictureController setGrayscale:1];
7104             }
7105             else
7106             {
7107                 [fPictureController setGrayscale:0];
7108             }
7109         }
7110         /* we call SetTitle: in fPictureController so we get an instant update in the Picture Settings window */
7111         [fPictureController SetTitle:fTitle];
7112         [fPictureController SetTitle:fTitle];
7113         [self calculatePictureSizing:nil];
7114     }
7115 }
7116
7117
7118 #pragma mark -
7119 #pragma mark Manage Presets
7120
7121 - (void) loadPresets {
7122         /* We declare the default NSFileManager into fileManager */
7123         NSFileManager * fileManager = [NSFileManager defaultManager];
7124         /*We define the location of the user presets file */
7125     UserPresetsFile = @"~/Library/Application Support/HandBrake/UserPresets.plist";
7126         UserPresetsFile = [[UserPresetsFile stringByExpandingTildeInPath]retain];
7127     /* We check for the presets.plist */
7128         if ([fileManager fileExistsAtPath:UserPresetsFile] == 0)
7129         {
7130                 [fileManager createFileAtPath:UserPresetsFile contents:nil attributes:nil];
7131         }
7132
7133         UserPresets = [[NSMutableArray alloc] initWithContentsOfFile:UserPresetsFile];
7134         if (nil == UserPresets)
7135         {
7136                 UserPresets = [[NSMutableArray alloc] init];
7137                 [self addFactoryPresets:nil];
7138         }
7139         [fPresetsOutlineView reloadData];
7140     
7141     [self checkBuiltInsForUpdates];
7142 }
7143
7144 - (void) checkBuiltInsForUpdates {
7145     
7146         BOOL updateBuiltInPresets = NO;
7147     int i = 0;
7148     NSEnumerator *enumerator = [UserPresets objectEnumerator];
7149     id tempObject;
7150     while (tempObject = [enumerator nextObject])
7151     {
7152         /* iterate through the built in presets to see if any have an old build number */
7153         NSMutableDictionary *thisPresetDict = tempObject;
7154         /*Key Type == 0 is built in, and key PresetBuildNumber is the build number it was created with */
7155         if ([[thisPresetDict objectForKey:@"Type"] intValue] == 0)              
7156         {
7157                         if (![thisPresetDict objectForKey:@"PresetBuildNumber"] || [[thisPresetDict objectForKey:@"PresetBuildNumber"] intValue] < [[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] intValue])
7158             {
7159                 updateBuiltInPresets = YES;
7160             }   
7161                 }
7162         i++;
7163     }
7164     /* if we have built in presets to update, then do so AlertBuiltInPresetUpdate*/
7165     if ( updateBuiltInPresets == YES)
7166     {
7167         if( [[NSUserDefaults standardUserDefaults] boolForKey:@"AlertBuiltInPresetUpdate"] == YES)
7168         {
7169             /* Show an alert window that built in presets will be updated */
7170             /*On Screen Notification*/
7171             int status;
7172             NSBeep();
7173             status = NSRunAlertPanel(@"HandBrake has determined your built in presets are out of date...",@"HandBrake will now update your built-in presets.", @"OK", nil, nil);
7174             [NSApp requestUserAttention:NSCriticalRequest];
7175         }
7176         /* when alert is dismissed, go ahead and update the built in presets */
7177         [self addFactoryPresets:nil];
7178     }
7179     
7180 }
7181
7182
7183 - (IBAction) showAddPresetPanel: (id) sender
7184 {
7185     /* Deselect the currently selected Preset if there is one*/
7186     [fPresetsOutlineView deselectRow:[fPresetsOutlineView selectedRow]];
7187
7188     /* Populate the preset picture settings popup here */
7189     [fPresetNewPicSettingsPopUp removeAllItems];
7190     [fPresetNewPicSettingsPopUp addItemWithTitle:@"None"];
7191     [fPresetNewPicSettingsPopUp addItemWithTitle:@"Current"];
7192     [fPresetNewPicSettingsPopUp addItemWithTitle:@"Source Maximum (post source scan)"];
7193     [fPresetNewPicSettingsPopUp selectItemAtIndex: 0];  
7194     /* Uncheck the preset use filters checkbox */
7195     [fPresetNewPicFiltersCheck setState:NSOffState];
7196     // fPresetNewFolderCheck
7197     [fPresetNewFolderCheck setState:NSOffState];
7198     /* Erase info from the input fields*/
7199         [fPresetNewName setStringValue: @""];
7200         [fPresetNewDesc setStringValue: @""];
7201         /* Show the panel */
7202         [NSApp beginSheet:fAddPresetPanel modalForWindow:fWindow modalDelegate:nil didEndSelector:NULL contextInfo:NULL];
7203 }
7204
7205 - (IBAction) closeAddPresetPanel: (id) sender
7206 {
7207     [NSApp endSheet: fAddPresetPanel];
7208     [fAddPresetPanel orderOut: self];
7209 }
7210
7211 - (IBAction)addUserPreset:(id)sender
7212 {
7213     if (![[fPresetNewName stringValue] length])
7214             NSRunAlertPanel(@"Warning!", @"You need to insert a name for the preset.", @"OK", nil , nil);
7215     else
7216     {
7217         /* Here we create a custom user preset */
7218         [UserPresets addObject:[self createPreset]];
7219         [self addPreset];
7220
7221         [self closeAddPresetPanel:nil];
7222     }
7223 }
7224 - (void)addPreset
7225 {
7226
7227         
7228         /* We Reload the New Table data for presets */
7229     [fPresetsOutlineView reloadData];
7230    /* We save all of the preset data here */
7231     [self savePreset];
7232 }
7233
7234 - (void)sortPresets
7235 {
7236
7237         
7238         /* We Sort the Presets By Factory or Custom */
7239         NSSortDescriptor * presetTypeDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"Type" 
7240                                                     ascending:YES] autorelease];
7241         /* We Sort the Presets Alphabetically by name  We do not use this now as we have drag and drop*/
7242         /*
7243     NSSortDescriptor * presetNameDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"PresetName" 
7244                                                     ascending:YES selector:@selector(caseInsensitiveCompare:)] autorelease];
7245         //NSArray *sortDescriptors=[NSArray arrayWithObjects:presetTypeDescriptor,presetNameDescriptor,nil];
7246     
7247     */
7248     /* Since we can drag and drop our custom presets, lets just sort by type and not name */
7249     NSArray *sortDescriptors=[NSArray arrayWithObjects:presetTypeDescriptor,nil];
7250         NSArray *sortedArray=[UserPresets sortedArrayUsingDescriptors:sortDescriptors];
7251         [UserPresets setArray:sortedArray];
7252         
7253
7254 }
7255
7256 - (IBAction)insertPreset:(id)sender
7257 {
7258     int index = [fPresetsOutlineView selectedRow];
7259     [UserPresets insertObject:[self createPreset] atIndex:index];
7260     [fPresetsOutlineView reloadData];
7261     [self savePreset];
7262 }
7263
7264 - (NSDictionary *)createPreset
7265 {
7266     NSMutableDictionary *preset = [[NSMutableDictionary alloc] init];
7267     /* Preset build number */
7268     [preset setObject:[NSString stringWithFormat: @"%d", [[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] intValue]] forKey:@"PresetBuildNumber"];
7269     [preset setObject:[fPresetNewName stringValue] forKey:@"PresetName"];
7270         /* Get the New Preset Name from the field in the AddPresetPanel */
7271     [preset setObject:[fPresetNewName stringValue] forKey:@"PresetName"];
7272     /* Set whether or not this is to be a folder fPresetNewFolderCheck*/
7273     [preset setObject:[NSNumber numberWithBool:[fPresetNewFolderCheck state]] forKey:@"Folder"];
7274         /*Set whether or not this is a user preset or factory 0 is factory, 1 is user*/
7275         [preset setObject:[NSNumber numberWithInt:1] forKey:@"Type"];
7276         /*Set whether or not this is default, at creation set to 0*/
7277         [preset setObject:[NSNumber numberWithInt:0] forKey:@"Default"];
7278     if ([fPresetNewFolderCheck state] == YES)
7279     {
7280         /* initialize and set an empty array for children here since we are a new folder */
7281         NSMutableArray *childrenArray = [[NSMutableArray alloc] init];
7282         [preset setObject:[NSMutableArray arrayWithArray: childrenArray] forKey:@"ChildrenArray"];
7283         [childrenArray autorelease];
7284     }
7285     else // we are not creating a preset folder, so we go ahead with the rest of the preset info
7286     {
7287         /*Get the whether or not to apply pic Size and Cropping (includes Anamorphic)*/
7288         [preset setObject:[NSNumber numberWithInt:[fPresetNewPicSettingsPopUp indexOfSelectedItem]] forKey:@"UsesPictureSettings"];
7289         /* Get whether or not to use the current Picture Filter settings for the preset */
7290         [preset setObject:[NSNumber numberWithInt:[fPresetNewPicFiltersCheck state]] forKey:@"UsesPictureFilters"];
7291         
7292         /* Get New Preset Description from the field in the AddPresetPanel*/
7293         [preset setObject:[fPresetNewDesc stringValue] forKey:@"PresetDescription"];
7294         /* File Format */
7295         [preset setObject:[fDstFormatPopUp titleOfSelectedItem] forKey:@"FileFormat"];
7296         /* Chapter Markers fCreateChapterMarkers*/
7297         [preset setObject:[NSNumber numberWithInt:[fCreateChapterMarkers state]] forKey:@"ChapterMarkers"];
7298         /* Allow Mpeg4 64 bit formatting +4GB file sizes */
7299         [preset setObject:[NSNumber numberWithInt:[fDstMp4LargeFileCheck state]] forKey:@"Mp4LargeFile"];
7300         /* Mux mp4 with http optimization */
7301         [preset setObject:[NSNumber numberWithInt:[fDstMp4HttpOptFileCheck state]] forKey:@"Mp4HttpOptimize"];
7302         /* Add iPod uuid atom */
7303         [preset setObject:[NSNumber numberWithInt:[fDstMp4iPodFileCheck state]] forKey:@"Mp4iPodCompatible"];
7304         
7305         /* Codecs */
7306         /* Video encoder */
7307         [preset setObject:[fVidEncoderPopUp titleOfSelectedItem] forKey:@"VideoEncoder"];
7308         /* x264 Option String */
7309         [preset setObject:[fAdvancedOptions optionsString] forKey:@"x264Option"];
7310         
7311         [preset setObject:[NSNumber numberWithInt:[fVidQualityMatrix selectedRow]] forKey:@"VideoQualityType"];
7312         [preset setObject:[fVidTargetSizeField stringValue] forKey:@"VideoTargetSize"];
7313         [preset setObject:[fVidBitrateField stringValue] forKey:@"VideoAvgBitrate"];
7314         [preset setObject:[NSNumber numberWithFloat:[fVidQualityRFField floatValue]] forKey:@"VideoQualitySlider"];
7315         
7316         /* Video framerate */
7317         if ([fVidRatePopUp indexOfSelectedItem] == 0) // Same as source is selected
7318         {
7319             [preset setObject:@"Same as source" forKey:@"VideoFramerate"];
7320         }
7321         else // we can record the actual titleOfSelectedItem
7322         {
7323             [preset setObject:[fVidRatePopUp titleOfSelectedItem] forKey:@"VideoFramerate"];
7324         }
7325         
7326         /* 2 Pass Encoding */
7327         [preset setObject:[NSNumber numberWithInt:[fVidTwoPassCheck state]] forKey:@"VideoTwoPass"];
7328         /* Turbo 2 pass Encoding fVidTurboPassCheck*/
7329         [preset setObject:[NSNumber numberWithInt:[fVidTurboPassCheck state]] forKey:@"VideoTurboTwoPass"];
7330         /*Picture Settings*/
7331         hb_job_t * job = fTitle->job;
7332         /* Picture Sizing */
7333         /* Use Max Picture settings for whatever the dvd is.*/
7334         [preset setObject:[NSNumber numberWithInt:0] forKey:@"UsesMaxPictureSettings"];
7335         [preset setObject:[NSNumber numberWithInt:fTitle->job->width] forKey:@"PictureWidth"];
7336         [preset setObject:[NSNumber numberWithInt:fTitle->job->height] forKey:@"PictureHeight"];
7337         [preset setObject:[NSNumber numberWithInt:fTitle->job->keep_ratio] forKey:@"PictureKeepRatio"];
7338         [preset setObject:[NSNumber numberWithInt:fTitle->job->anamorphic.mode] forKey:@"PicturePAR"];
7339         
7340         /* Set crop settings here */
7341         [preset setObject:[NSNumber numberWithInt:[fPictureController autoCrop]] forKey:@"PictureAutoCrop"];
7342         [preset setObject:[NSNumber numberWithInt:job->crop[0]] forKey:@"PictureTopCrop"];
7343         [preset setObject:[NSNumber numberWithInt:job->crop[1]] forKey:@"PictureBottomCrop"];
7344         [preset setObject:[NSNumber numberWithInt:job->crop[2]] forKey:@"PictureLeftCrop"];
7345         [preset setObject:[NSNumber numberWithInt:job->crop[3]] forKey:@"PictureRightCrop"];
7346         
7347         /* Picture Filters */
7348         [preset setObject:[NSNumber numberWithInt:[fPictureController useDecomb]] forKey:@"PictureDecombDeinterlace"];
7349         [preset setObject:[NSNumber numberWithInt:[fPictureController deinterlace]] forKey:@"PictureDeinterlace"];
7350         [preset setObject:[fPictureController deinterlaceCustomString] forKey:@"PictureDeinterlaceCustom"];
7351         [preset setObject:[NSNumber numberWithInt:[fPictureController detelecine]] forKey:@"PictureDetelecine"];
7352         [preset setObject:[fPictureController detelecineCustomString] forKey:@"PictureDetelecineCustom"];
7353         [preset setObject:[NSNumber numberWithInt:[fPictureController denoise]] forKey:@"PictureDenoise"];
7354         [preset setObject:[fPictureController denoiseCustomString] forKey:@"PictureDenoiseCustom"];
7355         [preset setObject:[NSNumber numberWithInt:[fPictureController deblock]] forKey:@"PictureDeblock"]; 
7356         [preset setObject:[NSNumber numberWithInt:[fPictureController decomb]] forKey:@"PictureDecomb"];
7357         [preset setObject:[fPictureController decombCustomString] forKey:@"PictureDecombCustom"];
7358         [preset setObject:[NSNumber numberWithInt:[fPictureController grayscale]] forKey:@"VideoGrayScale"];
7359         
7360         /*Audio*/
7361         NSMutableArray *audioListArray = [[NSMutableArray alloc] init];
7362         /* we actually call the methods for the nests here */
7363         if ([fAudLang1PopUp indexOfSelectedItem] > 0)
7364         {
7365             NSMutableDictionary *audioTrack1Array = [[NSMutableDictionary alloc] init];
7366             [audioTrack1Array setObject:[NSNumber numberWithInt:[fAudLang1PopUp indexOfSelectedItem]] forKey:@"AudioTrack"];
7367             [audioTrack1Array setObject:[fAudLang1PopUp titleOfSelectedItem] forKey:@"AudioTrackDescription"];
7368             [audioTrack1Array setObject:[fAudTrack1CodecPopUp titleOfSelectedItem] forKey:@"AudioEncoder"];
7369             [audioTrack1Array setObject:[fAudTrack1MixPopUp titleOfSelectedItem] forKey:@"AudioMixdown"];
7370             [audioTrack1Array setObject:[fAudTrack1RatePopUp titleOfSelectedItem] forKey:@"AudioSamplerate"];
7371             [audioTrack1Array setObject:[fAudTrack1BitratePopUp titleOfSelectedItem] forKey:@"AudioBitrate"];
7372             [audioTrack1Array setObject:[NSNumber numberWithFloat:[fAudTrack1DrcSlider floatValue]] forKey:@"AudioTrackDRCSlider"];
7373             [audioTrack1Array autorelease];
7374             [audioListArray addObject:audioTrack1Array];
7375         }
7376         
7377         if ([fAudLang2PopUp indexOfSelectedItem] > 0)
7378         {
7379             NSMutableDictionary *audioTrack2Array = [[NSMutableDictionary alloc] init];
7380             [audioTrack2Array setObject:[NSNumber numberWithInt:[fAudLang2PopUp indexOfSelectedItem]] forKey:@"AudioTrack"];
7381             [audioTrack2Array setObject:[fAudLang2PopUp titleOfSelectedItem] forKey:@"AudioTrackDescription"];
7382             [audioTrack2Array setObject:[fAudTrack2CodecPopUp titleOfSelectedItem] forKey:@"AudioEncoder"];
7383             [audioTrack2Array setObject:[fAudTrack2MixPopUp titleOfSelectedItem] forKey:@"AudioMixdown"];
7384             [audioTrack2Array setObject:[fAudTrack2RatePopUp titleOfSelectedItem] forKey:@"AudioSamplerate"];
7385             [audioTrack2Array setObject:[fAudTrack2BitratePopUp titleOfSelectedItem] forKey:@"AudioBitrate"];
7386             [audioTrack2Array setObject:[NSNumber numberWithFloat:[fAudTrack2DrcSlider floatValue]] forKey:@"AudioTrackDRCSlider"];
7387             [audioTrack2Array autorelease];
7388             [audioListArray addObject:audioTrack2Array];
7389         }
7390         
7391         if ([fAudLang3PopUp indexOfSelectedItem] > 0)
7392         {
7393             NSMutableDictionary *audioTrack3Array = [[NSMutableDictionary alloc] init];
7394             [audioTrack3Array setObject:[NSNumber numberWithInt:[fAudLang3PopUp indexOfSelectedItem]] forKey:@"AudioTrack"];
7395             [audioTrack3Array setObject:[fAudLang3PopUp titleOfSelectedItem] forKey:@"AudioTrackDescription"];
7396             [audioTrack3Array setObject:[fAudTrack3CodecPopUp titleOfSelectedItem] forKey:@"AudioEncoder"];
7397             [audioTrack3Array setObject:[fAudTrack3MixPopUp titleOfSelectedItem] forKey:@"AudioMixdown"];
7398             [audioTrack3Array setObject:[fAudTrack3RatePopUp titleOfSelectedItem] forKey:@"AudioSamplerate"];
7399             [audioTrack3Array setObject:[fAudTrack3BitratePopUp titleOfSelectedItem] forKey:@"AudioBitrate"];
7400             [audioTrack3Array setObject:[NSNumber numberWithFloat:[fAudTrack3DrcSlider floatValue]] forKey:@"AudioTrackDRCSlider"];
7401             [audioTrack3Array autorelease];
7402             [audioListArray addObject:audioTrack3Array];
7403         }
7404         
7405         if ([fAudLang4PopUp indexOfSelectedItem] > 0)
7406         {
7407             NSMutableDictionary *audioTrack4Array = [[NSMutableDictionary alloc] init];
7408             [audioTrack4Array setObject:[NSNumber numberWithInt:[fAudLang4PopUp indexOfSelectedItem]] forKey:@"AudioTrack"];
7409             [audioTrack4Array setObject:[fAudLang4PopUp titleOfSelectedItem] forKey:@"AudioTrackDescription"];
7410             [audioTrack4Array setObject:[fAudTrack4CodecPopUp titleOfSelectedItem] forKey:@"AudioEncoder"];
7411             [audioTrack4Array setObject:[fAudTrack4MixPopUp titleOfSelectedItem] forKey:@"AudioMixdown"];
7412             [audioTrack4Array setObject:[fAudTrack4RatePopUp titleOfSelectedItem] forKey:@"AudioSamplerate"];
7413             [audioTrack4Array setObject:[fAudTrack4BitratePopUp titleOfSelectedItem] forKey:@"AudioBitrate"];
7414             [audioTrack4Array setObject:[NSNumber numberWithFloat:[fAudTrack4DrcSlider floatValue]] forKey:@"AudioTrackDRCSlider"];
7415             [audioTrack4Array autorelease];
7416             [audioListArray addObject:audioTrack4Array];
7417         }
7418         
7419         
7420         [preset setObject:[NSMutableArray arrayWithArray: audioListArray] forKey:@"AudioList"];
7421
7422         
7423         /* Temporarily remove subtitles from creating a new preset as it has to be converted over to use the new
7424          * subititle array code. */
7425         /* Subtitles*/
7426         //[preset setObject:[fSubPopUp titleOfSelectedItem] forKey:@"Subtitles"];
7427         /* Forced Subtitles */
7428         //[preset setObject:[NSNumber numberWithInt:[fSubForcedCheck state]] forKey:@"SubtitlesForced"];
7429     }
7430     [preset autorelease];
7431     return preset;
7432     
7433 }
7434
7435 - (void)savePreset
7436 {
7437     [UserPresets writeToFile:UserPresetsFile atomically:YES];
7438         /* We get the default preset in case it changed */
7439         [self getDefaultPresets:nil];
7440
7441 }
7442
7443 - (IBAction)deletePreset:(id)sender
7444 {
7445     
7446     
7447     if ( [fPresetsOutlineView numberOfSelectedRows] == 0 )
7448     {
7449         return;
7450     }
7451     /* Alert user before deleting preset */
7452         int status;
7453     status = NSRunAlertPanel(@"Warning!", @"Are you sure that you want to delete the selected preset?", @"OK", @"Cancel", nil);
7454     
7455     if ( status == NSAlertDefaultReturn ) 
7456     {
7457         int presetToModLevel = [fPresetsOutlineView levelForItem: [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]]];
7458         NSDictionary *presetToMod = [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]];
7459         NSDictionary *presetToModParent = [fPresetsOutlineView parentForItem: presetToMod];
7460         
7461         NSEnumerator *enumerator;
7462         NSMutableArray *presetsArrayToMod;
7463         NSMutableArray *tempArray;
7464         id tempObject;
7465         /* If we are a root level preset, we are modding the UserPresets array */
7466         if (presetToModLevel == 0)
7467         {
7468             presetsArrayToMod = UserPresets;
7469         }
7470         else // We have a parent preset, so we modify the chidren array object for key
7471         {
7472             presetsArrayToMod = [presetToModParent objectForKey:@"ChildrenArray"]; 
7473         }
7474         
7475         enumerator = [presetsArrayToMod objectEnumerator];
7476         tempArray = [NSMutableArray array];
7477         
7478         while (tempObject = [enumerator nextObject]) 
7479         {
7480             NSDictionary *thisPresetDict = tempObject;
7481             if (thisPresetDict == presetToMod)
7482             {
7483                 [tempArray addObject:tempObject];
7484             }
7485         }
7486         
7487         [presetsArrayToMod removeObjectsInArray:tempArray];
7488         [fPresetsOutlineView reloadData];
7489         [self savePreset];   
7490     }
7491 }
7492
7493
7494 #pragma mark -
7495 #pragma mark Import Export Preset(s)
7496
7497 - (IBAction) browseExportPresetFile: (id) sender
7498 {
7499     /* Open a panel to let the user choose where and how to save the export file */
7500     NSSavePanel * panel = [NSSavePanel savePanel];
7501         /* We get the current file name and path from the destination field here */
7502     NSString *defaultExportDirectory = [NSString stringWithFormat: @"%@/Desktop/", NSHomeDirectory()];
7503
7504         [panel beginSheetForDirectory: defaultExportDirectory file: @"HB_Export.plist"
7505                                    modalForWindow: fWindow modalDelegate: self
7506                                    didEndSelector: @selector( browseExportPresetFileDone:returnCode:contextInfo: )
7507                                           contextInfo: NULL];
7508 }
7509
7510 - (void) browseExportPresetFileDone: (NSSavePanel *) sheet
7511                    returnCode: (int) returnCode contextInfo: (void *) contextInfo
7512 {
7513     if( returnCode == NSOKButton )
7514     {
7515         NSString *presetExportDirectory = [[sheet filename] stringByDeletingLastPathComponent];
7516         NSString *exportPresetsFile = [sheet filename];
7517         [[NSUserDefaults standardUserDefaults] setObject:presetExportDirectory forKey:@"LastPresetExportDirectory"];
7518         /* We check for the presets.plist */
7519         if ([[NSFileManager defaultManager] fileExistsAtPath:exportPresetsFile] == 0)
7520         {
7521             [[NSFileManager defaultManager] createFileAtPath:exportPresetsFile contents:nil attributes:nil];
7522         }
7523         NSMutableArray * presetsToExport = [[NSMutableArray alloc] initWithContentsOfFile:exportPresetsFile];
7524         if (nil == presetsToExport)
7525         {
7526             presetsToExport = [[NSMutableArray alloc] init];
7527             
7528             /* now get and add selected presets to export */
7529             
7530         }
7531         if ([fPresetsOutlineView selectedRow] >= 0 && [[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Folder"] intValue] != 1)
7532         {
7533             [presetsToExport addObject:[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]]];
7534             [presetsToExport writeToFile:exportPresetsFile atomically:YES];
7535             
7536         }
7537         
7538     }
7539 }
7540
7541
7542 - (IBAction) browseImportPresetFile: (id) sender
7543 {
7544
7545     NSOpenPanel * panel;
7546         
7547     panel = [NSOpenPanel openPanel];
7548     [panel setAllowsMultipleSelection: NO];
7549     [panel setCanChooseFiles: YES];
7550     [panel setCanChooseDirectories: NO ];
7551     NSString * sourceDirectory;
7552         if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastPresetImportDirectory"])
7553         {
7554                 sourceDirectory = [[NSUserDefaults standardUserDefaults] stringForKey:@"LastPresetImportDirectory"];
7555         }
7556         else
7557         {
7558                 sourceDirectory = @"~/Desktop";
7559                 sourceDirectory = [sourceDirectory stringByExpandingTildeInPath];
7560         }
7561     /* we open up the browse sources sheet here and call for browseSourcesDone after the sheet is closed
7562         * to evaluate whether we want to specify a title, we pass the sender in the contextInfo variable
7563         */
7564     /* set this for allowed file types, not sure if we should allow xml or not */
7565     NSArray *fileTypes = [NSArray arrayWithObjects:@"plist", @"xml", nil];
7566     [panel beginSheetForDirectory: sourceDirectory file: nil types: fileTypes
7567                    modalForWindow: fWindow modalDelegate: self
7568                    didEndSelector: @selector( browseImportPresetDone:returnCode:contextInfo: )
7569                       contextInfo: sender];
7570 }
7571
7572 - (void) browseImportPresetDone: (NSSavePanel *) sheet
7573                      returnCode: (int) returnCode contextInfo: (void *) contextInfo
7574 {
7575     if( returnCode == NSOKButton )
7576     {
7577         NSString *importPresetsDirectory = [[sheet filename] stringByDeletingLastPathComponent];
7578         NSString *importPresetsFile = [sheet filename];
7579         [[NSUserDefaults standardUserDefaults] setObject:importPresetsDirectory forKey:@"LastPresetImportDirectory"];
7580         /* NOTE: here we need to do some sanity checking to verify we do not hose up our presets file   */
7581         NSMutableArray * presetsToImport = [[NSMutableArray alloc] initWithContentsOfFile:importPresetsFile];
7582         /* iterate though the new array of presets to import and add them to our presets array */
7583         int i = 0;
7584         NSEnumerator *enumerator = [presetsToImport objectEnumerator];
7585         id tempObject;
7586         while (tempObject = [enumerator nextObject])
7587         {
7588             /* make any changes to the incoming preset we see fit */
7589             /* make sure the incoming preset is not tagged as default */
7590             [tempObject setObject:[NSNumber numberWithInt:0] forKey:@"Default"];
7591             /* prepend "(imported) to the name of the incoming preset for clarification since it can be changed */
7592             NSString * prependedName = [@"(import) " stringByAppendingString:[tempObject objectForKey:@"PresetName"]] ;
7593             [tempObject setObject:prependedName forKey:@"PresetName"];
7594             
7595             /* actually add the new preset to our presets array */
7596             [UserPresets addObject:tempObject];
7597             i++;
7598         }
7599         [presetsToImport autorelease];
7600         [self sortPresets];
7601         [self addPreset];
7602         
7603     }
7604 }
7605
7606 #pragma mark -
7607 #pragma mark Manage Default Preset
7608
7609 - (IBAction)getDefaultPresets:(id)sender
7610 {
7611         presetHbDefault = nil;
7612     presetUserDefault = nil;
7613     presetUserDefaultParent = nil;
7614     presetUserDefaultParentParent = nil;
7615     NSMutableDictionary *presetHbDefaultParent = nil;
7616     NSMutableDictionary *presetHbDefaultParentParent = nil;
7617     
7618     int i = 0;
7619     BOOL userDefaultFound = NO;
7620     presetCurrentBuiltInCount = 0;
7621     /* First we iterate through the root UserPresets array to check for defaults */
7622     NSEnumerator *enumerator = [UserPresets objectEnumerator];
7623         id tempObject;
7624         while (tempObject = [enumerator nextObject])
7625         {
7626                 NSMutableDictionary *thisPresetDict = tempObject;
7627                 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 1) // 1 is HB default
7628                 {
7629                         presetHbDefault = thisPresetDict;       
7630                 }
7631                 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 2) // 2 is User specified default
7632                 {
7633                         presetUserDefault = thisPresetDict;
7634             userDefaultFound = YES;
7635         }
7636         if ([[thisPresetDict objectForKey:@"Type"] intValue] == 0) // Type 0 is a built in preset               
7637         {
7638                         presetCurrentBuiltInCount++; // <--increment the current number of built in presets     
7639                 }
7640                 i++;
7641         
7642         /* if we run into a folder, go to level 1 and iterate through the children arrays for the default */
7643         if ([thisPresetDict objectForKey:@"ChildrenArray"])
7644         {
7645             NSMutableDictionary *thisPresetDictParent = thisPresetDict;
7646             NSEnumerator *enumerator = [[thisPresetDict objectForKey:@"ChildrenArray"] objectEnumerator];
7647             id tempObject;
7648             while (tempObject = [enumerator nextObject])
7649             {
7650                 NSMutableDictionary *thisPresetDict = tempObject;
7651                 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 1) // 1 is HB default
7652                 {
7653                     presetHbDefault = thisPresetDict;
7654                     presetHbDefaultParent = thisPresetDictParent;
7655                 }
7656                 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 2) // 2 is User specified default
7657                 {
7658                     presetUserDefault = thisPresetDict;
7659                     presetUserDefaultParent = thisPresetDictParent;
7660                     userDefaultFound = YES;
7661                 }
7662                 
7663                 /* if we run into a folder, go to level 2 and iterate through the children arrays for the default */
7664                 if ([thisPresetDict objectForKey:@"ChildrenArray"])
7665                 {
7666                     NSMutableDictionary *thisPresetDictParentParent = thisPresetDict;
7667                     NSEnumerator *enumerator = [[thisPresetDict objectForKey:@"ChildrenArray"] objectEnumerator];
7668                     id tempObject;
7669                     while (tempObject = [enumerator nextObject])
7670                     {
7671                         NSMutableDictionary *thisPresetDict = tempObject;
7672                         if ([[thisPresetDict objectForKey:@"Default"] intValue] == 1) // 1 is HB default
7673                         {
7674                             presetHbDefault = thisPresetDict;
7675                             presetHbDefaultParent = thisPresetDictParent;
7676                             presetHbDefaultParentParent = thisPresetDictParentParent;   
7677                         }
7678                         if ([[thisPresetDict objectForKey:@"Default"] intValue] == 2) // 2 is User specified default
7679                         {
7680                             presetUserDefault = thisPresetDict;
7681                             presetUserDefaultParent = thisPresetDictParent;
7682                             presetUserDefaultParentParent = thisPresetDictParentParent;
7683                             userDefaultFound = YES;     
7684                         }
7685                         
7686                     }
7687                 }
7688             }
7689         }
7690         
7691         }
7692     /* check to see if a user specified preset was found, if not then assign the parents for
7693      * the presetHbDefault so that we can open the parents for the nested presets
7694      */
7695     if (userDefaultFound == NO)
7696     {
7697         presetUserDefaultParent = presetHbDefaultParent;
7698         presetUserDefaultParentParent = presetHbDefaultParentParent;
7699     }
7700 }
7701
7702 - (IBAction)setDefaultPreset:(id)sender
7703 {
7704 /* We need to determine if the item is a folder */
7705    if ([[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Folder"] intValue] == 1)
7706    {
7707    return;
7708    }
7709
7710     int i = 0;
7711     NSEnumerator *enumerator = [UserPresets objectEnumerator];
7712         id tempObject;
7713         /* First make sure the old user specified default preset is removed */
7714     while (tempObject = [enumerator nextObject])
7715         {
7716                 NSMutableDictionary *thisPresetDict = tempObject;
7717                 if ([[tempObject objectForKey:@"Default"] intValue] != 1) // if not the default HB Preset, set to 0
7718                 {
7719                         [[UserPresets objectAtIndex:i] setObject:[NSNumber numberWithInt:0] forKey:@"Default"]; 
7720                 }
7721                 
7722                 /* if we run into a folder, go to level 1 and iterate through the children arrays for the default */
7723         if ([thisPresetDict objectForKey:@"ChildrenArray"])
7724         {
7725             NSEnumerator *enumerator = [[thisPresetDict objectForKey:@"ChildrenArray"] objectEnumerator];
7726             id tempObject;
7727             int ii = 0;
7728             while (tempObject = [enumerator nextObject])
7729             {
7730                 NSMutableDictionary *thisPresetDict1 = tempObject;
7731                 if ([[tempObject objectForKey:@"Default"] intValue] != 1) // if not the default HB Preset, set to 0
7732                 {
7733                     [[[thisPresetDict objectForKey:@"ChildrenArray"] objectAtIndex:ii] setObject:[NSNumber numberWithInt:0] forKey:@"Default"]; 
7734                 }
7735                 /* if we run into a folder, go to level 2 and iterate through the children arrays for the default */
7736                 if ([thisPresetDict1 objectForKey:@"ChildrenArray"])
7737                 {
7738                     NSEnumerator *enumerator = [[thisPresetDict1 objectForKey:@"ChildrenArray"] objectEnumerator];
7739                     id tempObject;
7740                     int iii = 0;
7741                     while (tempObject = [enumerator nextObject])
7742                     {
7743                         if ([[tempObject objectForKey:@"Default"] intValue] != 1) // if not the default HB Preset, set to 0
7744                         {
7745                             [[[thisPresetDict1 objectForKey:@"ChildrenArray"] objectAtIndex:iii] setObject:[NSNumber numberWithInt:0] forKey:@"Default"];       
7746                         }
7747                         iii++;
7748                     }
7749                 }
7750                 ii++;
7751             }
7752             
7753         }
7754         i++; 
7755         }
7756     
7757     
7758     int presetToModLevel = [fPresetsOutlineView levelForItem: [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]]];
7759     NSDictionary *presetToMod = [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]];
7760     NSDictionary *presetToModParent = [fPresetsOutlineView parentForItem: presetToMod];
7761     
7762     
7763     NSMutableArray *presetsArrayToMod;
7764     NSMutableArray *tempArray;
7765     
7766     /* If we are a root level preset, we are modding the UserPresets array */
7767     if (presetToModLevel == 0)
7768     {
7769         presetsArrayToMod = UserPresets;
7770     }
7771     else // We have a parent preset, so we modify the chidren array object for key
7772     {
7773         presetsArrayToMod = [presetToModParent objectForKey:@"ChildrenArray"]; 
7774     }
7775     
7776     enumerator = [presetsArrayToMod objectEnumerator];
7777     tempArray = [NSMutableArray array];
7778     int iiii = 0;
7779     while (tempObject = [enumerator nextObject]) 
7780     {
7781         NSDictionary *thisPresetDict = tempObject;
7782         if (thisPresetDict == presetToMod)
7783         {
7784             if ([[tempObject objectForKey:@"Default"] intValue] != 1) // if not the default HB Preset, set to 2
7785             {
7786                 [[presetsArrayToMod objectAtIndex:iiii] setObject:[NSNumber numberWithInt:2] forKey:@"Default"];        
7787             }
7788         }
7789      iiii++;
7790      }
7791     
7792     
7793     /* We save all of the preset data here */
7794     [self savePreset];
7795     /* We Reload the New Table data for presets */
7796     [fPresetsOutlineView reloadData];
7797 }
7798
7799 - (IBAction)selectDefaultPreset:(id)sender
7800 {
7801         NSMutableDictionary *presetToMod;
7802     /* if there is a user specified default, we use it */
7803         if (presetUserDefault)
7804         {
7805         presetToMod = presetUserDefault;
7806     }
7807         else if (presetHbDefault) //else we use the built in default presetHbDefault
7808         {
7809         presetToMod = presetHbDefault;
7810         }
7811     else
7812     {
7813     return;
7814     }
7815     
7816     if (presetUserDefaultParent != nil)
7817     {
7818         [fPresetsOutlineView expandItem:presetUserDefaultParent];
7819         
7820     }
7821     if (presetUserDefaultParentParent != nil)
7822     {
7823         [fPresetsOutlineView expandItem:presetUserDefaultParentParent];
7824         
7825     }
7826     
7827     [fPresetsOutlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:[fPresetsOutlineView rowForItem: presetToMod]] byExtendingSelection:NO];
7828         [self selectPreset:nil];
7829 }
7830
7831
7832 #pragma mark -
7833 #pragma mark Manage Built In Presets
7834
7835
7836 - (IBAction)deleteFactoryPresets:(id)sender
7837 {
7838     //int status;
7839     NSEnumerator *enumerator = [UserPresets objectEnumerator];
7840         id tempObject;
7841     
7842         //NSNumber *index;
7843     NSMutableArray *tempArray;
7844
7845
7846         tempArray = [NSMutableArray array];
7847         /* we look here to see if the preset is we move on to the next one */
7848         while ( tempObject = [enumerator nextObject] )  
7849                 {
7850                         /* if the preset is "Factory" then we put it in the array of
7851                         presets to delete */
7852                         if ([[tempObject objectForKey:@"Type"] intValue] == 0)
7853                         {
7854                                 [tempArray addObject:tempObject];
7855                         }
7856         }
7857         
7858         [UserPresets removeObjectsInArray:tempArray];
7859         [fPresetsOutlineView reloadData];
7860         [self savePreset];   
7861
7862 }
7863
7864    /* We use this method to recreate new, updated factory presets */
7865 - (IBAction)addFactoryPresets:(id)sender
7866 {
7867     
7868     /* First, we delete any existing built in presets */
7869     [self deleteFactoryPresets: sender];
7870     /* Then we generate new built in presets programmatically with fPresetsBuiltin
7871      * which is all setup in HBPresets.h and  HBPresets.m*/
7872     [fPresetsBuiltin generateBuiltinPresets:UserPresets];
7873     /* update build number for built in presets */
7874     /* iterate though the new array of presets to import and add them to our presets array */
7875     int i = 0;
7876     NSEnumerator *enumerator = [UserPresets objectEnumerator];
7877     id tempObject;
7878     while (tempObject = [enumerator nextObject])
7879     {
7880         /* Record the apps current build number in the PresetBuildNumber key */
7881         if ([[tempObject objectForKey:@"Type"] intValue] == 0) // Type 0 is a built in preset           
7882         {
7883             /* Preset build number */
7884             [[UserPresets objectAtIndex:i] setObject:[NSNumber numberWithInt:[[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] intValue]] forKey:@"PresetBuildNumber"];
7885         }
7886         i++;
7887     }
7888     /* report the built in preset updating to the activity log */
7889     [self writeToActivityLog: "built in presets updated to build number: %d", [[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] intValue]];
7890     
7891     [self sortPresets];
7892     [self addPreset];
7893     
7894 }
7895
7896
7897 @end
7898
7899 /*******************************
7900  * Subclass of the HBPresetsOutlineView *
7901  *******************************/
7902
7903 @implementation HBPresetsOutlineView
7904 - (NSImage *)dragImageForRowsWithIndexes:(NSIndexSet *)dragRows tableColumns:(NSArray *)tableColumns event:(NSEvent*)dragEvent offset:(NSPointPointer)dragImageOffset
7905 {
7906     fIsDragging = YES;
7907
7908     // By default, NSTableView only drags an image of the first column. Change this to
7909     // drag an image of the queue's icon and PresetName columns.
7910     NSArray * cols = [NSArray arrayWithObjects: [self tableColumnWithIdentifier:@"PresetName"], nil];
7911     return [super dragImageForRowsWithIndexes:dragRows tableColumns:cols event:dragEvent offset:dragImageOffset];
7912 }
7913
7914
7915
7916 - (void) mouseDown:(NSEvent *)theEvent
7917 {
7918     [super mouseDown:theEvent];
7919         fIsDragging = NO;
7920 }
7921
7922
7923
7924 - (BOOL) isDragging;
7925 {
7926     return fIsDragging;
7927 }
7928 @end
7929
7930
7931