OSDN Git Service

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