OSDN Git Service

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