OSDN Git Service

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