OSDN Git Service

- Some DVB programs have a lot of streams (20 for BBC HD) so up max from 16 to 32
[handbrake-jp/handbrake-jp-git.git] / macosx / Controller.mm
1 /* $Id: Controller.mm,v 1.79 2005/11/04 19:41:32 titer Exp $
2
3    This file is part of the HandBrake source code.
4    Homepage: <http://handbrake.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 = hb_subtitle_config_s::PASSTHRUSUB;
2890                     }
2891                     else
2892                     {
2893                         job->select_subtitle_config.dest = hb_subtitle_config_s::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 (subt != NULL)
2928                 {
2929                     [self writeToActivityLog: "Setting Subtitle: %s", subt];
2930
2931                     hb_subtitle_config_t sub_config = subt->config;
2932                     
2933                     if (!burned && job->mux == HB_MUX_MKV && 
2934                         subt->format == hb_subtitle_s::PICTURESUB)
2935                     {
2936                         sub_config.dest = hb_subtitle_config_s::PASSTHRUSUB;
2937                     }
2938                     else if (!burned && job->mux == HB_MUX_MP4 && 
2939                              subt->format == hb_subtitle_s::PICTURESUB)
2940                     {
2941                         // Skip any non-burned vobsubs when output is mp4
2942                         continue;
2943                     }
2944                     else if ( burned && subt->format == hb_subtitle_s::PICTURESUB )
2945                     {
2946                         // Only allow one subtitle to be burned into the video
2947                         if (one_burned)
2948                             continue;
2949                         one_burned = TRUE;
2950                     }
2951                     sub_config.force = force;
2952                     sub_config.default_track = def;
2953                     hb_subtitle_add( job, &sub_config, subtitle );
2954                 }   
2955                 
2956             }
2957         }
2958         i++;
2959     }
2960    
2961     
2962     
2963 [subtitlesArray autorelease];    
2964     
2965     
2966     /* Audio tracks and mixdowns */
2967     /* Lets make sure there arent any erroneous audio tracks in the job list, so lets make sure its empty*/
2968     int audiotrack_count = hb_list_count(job->list_audio);
2969     for( int i = 0; i < audiotrack_count;i++)
2970     {
2971         hb_audio_t * temp_audio = (hb_audio_t*) hb_list_item( job->list_audio, 0 );
2972         hb_list_rem(job->list_audio, temp_audio);
2973     }
2974     /* Now lets add our new tracks to the audio list here */
2975     if ([fAudLang1PopUp indexOfSelectedItem] > 0)
2976     {
2977         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
2978         hb_audio_config_init(audio);
2979         audio->in.track = [fAudLang1PopUp indexOfSelectedItem] - 1;
2980         /* We go ahead and assign values to our audio->out.<properties> */
2981         audio->out.track = [fAudLang1PopUp indexOfSelectedItem] - 1;
2982         audio->out.codec = [[fAudTrack1CodecPopUp selectedItem] tag];
2983         audio->out.mixdown = [[fAudTrack1MixPopUp selectedItem] tag];
2984         audio->out.bitrate = [[fAudTrack1BitratePopUp selectedItem] tag];
2985         audio->out.samplerate = [[fAudTrack1RatePopUp selectedItem] tag];
2986         audio->out.dynamic_range_compression = [fAudTrack1DrcField floatValue];
2987         
2988         hb_audio_add( job, audio );
2989         free(audio);
2990     }  
2991     if ([fAudLang2PopUp indexOfSelectedItem] > 0)
2992     {
2993         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
2994         hb_audio_config_init(audio);
2995         audio->in.track = [fAudLang2PopUp indexOfSelectedItem] - 1;
2996         /* We go ahead and assign values to our audio->out.<properties> */
2997         audio->out.track = [fAudLang2PopUp indexOfSelectedItem] - 1;
2998         audio->out.codec = [[fAudTrack2CodecPopUp selectedItem] tag];
2999         audio->out.mixdown = [[fAudTrack2MixPopUp selectedItem] tag];
3000         audio->out.bitrate = [[fAudTrack2BitratePopUp selectedItem] tag];
3001         audio->out.samplerate = [[fAudTrack2RatePopUp selectedItem] tag];
3002         audio->out.dynamic_range_compression = [fAudTrack2DrcField floatValue];
3003         
3004         hb_audio_add( job, audio );
3005         free(audio);
3006         
3007     }
3008     
3009     if ([fAudLang3PopUp indexOfSelectedItem] > 0)
3010     {
3011         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3012         hb_audio_config_init(audio);
3013         audio->in.track = [fAudLang3PopUp indexOfSelectedItem] - 1;
3014         /* We go ahead and assign values to our audio->out.<properties> */
3015         audio->out.track = [fAudLang3PopUp indexOfSelectedItem] - 1;
3016         audio->out.codec = [[fAudTrack3CodecPopUp selectedItem] tag];
3017         audio->out.mixdown = [[fAudTrack3MixPopUp selectedItem] tag];
3018         audio->out.bitrate = [[fAudTrack3BitratePopUp selectedItem] tag];
3019         audio->out.samplerate = [[fAudTrack3RatePopUp selectedItem] tag];
3020         audio->out.dynamic_range_compression = [fAudTrack3DrcField floatValue];
3021         
3022         hb_audio_add( job, audio );
3023         free(audio);
3024         
3025     }
3026
3027     if ([fAudLang4PopUp indexOfSelectedItem] > 0)
3028     {
3029         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3030         hb_audio_config_init(audio);
3031         audio->in.track = [fAudLang4PopUp indexOfSelectedItem] - 1;
3032         /* We go ahead and assign values to our audio->out.<properties> */
3033         audio->out.track = [fAudLang4PopUp indexOfSelectedItem] - 1;
3034         audio->out.codec = [[fAudTrack4CodecPopUp selectedItem] tag];
3035         audio->out.mixdown = [[fAudTrack4MixPopUp selectedItem] tag];
3036         audio->out.bitrate = [[fAudTrack4BitratePopUp selectedItem] tag];
3037         audio->out.samplerate = [[fAudTrack4RatePopUp selectedItem] tag];
3038         audio->out.dynamic_range_compression = [fAudTrack4DrcField floatValue];
3039         
3040         hb_audio_add( job, audio );
3041         free(audio);
3042         
3043     }
3044
3045     
3046     
3047     /* Filters */
3048     
3049     /* Though Grayscale is not really a filter, per se
3050      * we put it here since its in the filters panel
3051      */
3052      
3053     if ([fPictureController grayscale])
3054     {
3055         job->grayscale = 1;
3056     }
3057     else
3058     {
3059         job->grayscale = 0;
3060     }
3061     
3062     /* Initialize the filters list */
3063     job->filters = hb_list_init();
3064     
3065     /* Now lets call the filters if applicable.
3066     * The order of the filters is critical
3067     */
3068     
3069         /* Detelecine */
3070     if ([fPictureController detelecine] == 1)
3071     {
3072         hb_list_add( job->filters, &hb_filter_detelecine );
3073     }
3074     if ([fPictureController detelecine] == 2)
3075     {
3076         /* use a custom detelecine string */
3077         hb_filter_detelecine.settings = (char *) [[fPictureController detelecineCustomString] UTF8String];
3078         hb_list_add( job->filters, &hb_filter_detelecine );
3079     }
3080     if ([fPictureController useDecomb] == 1)
3081     {
3082         /* Decomb */
3083         if ([fPictureController decomb] == 1)
3084         {
3085             /* Run old deinterlacer fd by default */
3086             //hb_filter_decomb.settings = (char *) [[fPicSettingDecomb stringValue] UTF8String];
3087             hb_list_add( job->filters, &hb_filter_decomb );
3088         }
3089         /* we add the custom string if present */
3090         if ([fPictureController decomb] == 2)
3091         {
3092             /* use a custom decomb string */
3093             hb_filter_decomb.settings = (char *) [[fPictureController decombCustomString] UTF8String];
3094             hb_list_add( job->filters, &hb_filter_decomb );
3095         }
3096     }
3097     else
3098     {
3099         
3100         /* Deinterlace */
3101         if ([fPictureController deinterlace] == 1)
3102         {
3103             /* Run old deinterlacer fd by default */
3104             hb_filter_deinterlace.settings = "-1"; 
3105             hb_list_add( job->filters, &hb_filter_deinterlace );
3106         }
3107         else if ([fPictureController deinterlace] == 2)
3108         {
3109             /* Yadif mode 0 (without spatial deinterlacing.) */
3110             hb_filter_deinterlace.settings = "2"; 
3111             hb_list_add( job->filters, &hb_filter_deinterlace );            
3112         }
3113         else if ([fPictureController deinterlace] == 3)
3114         {
3115             /* Yadif (with spatial deinterlacing) */
3116             hb_filter_deinterlace.settings = "0"; 
3117             hb_list_add( job->filters, &hb_filter_deinterlace );            
3118         }
3119         else if ([fPictureController deinterlace] == 4)
3120         {
3121             /* we add the custom string if present */
3122             hb_filter_deinterlace.settings = (char *) [[fPictureController deinterlaceCustomString] UTF8String];
3123             hb_list_add( job->filters, &hb_filter_deinterlace );            
3124         }
3125         }
3126     
3127     /* Denoise */
3128         if ([fPictureController denoise] == 1) // Weak in popup
3129         {
3130                 hb_filter_denoise.settings = "2:1:2:3"; 
3131         hb_list_add( job->filters, &hb_filter_denoise );        
3132         }
3133         else if ([fPictureController denoise] == 2) // Medium in popup
3134         {
3135                 hb_filter_denoise.settings = "3:2:2:3"; 
3136         hb_list_add( job->filters, &hb_filter_denoise );        
3137         }
3138         else if ([fPictureController denoise] == 3) // Strong in popup
3139         {
3140                 hb_filter_denoise.settings = "7:7:5:5"; 
3141         hb_list_add( job->filters, &hb_filter_denoise );        
3142         }
3143     else if ([fPictureController denoise] == 4) // custom in popup
3144         {
3145                 /* we add the custom string if present */
3146         hb_filter_denoise.settings = (char *) [[fPictureController denoiseCustomString] UTF8String]; 
3147         hb_list_add( job->filters, &hb_filter_denoise );        
3148         }
3149     
3150     /* Deblock  (uses pp7 default) */
3151     /* NOTE: even though there is a valid deblock setting of 0 for the filter, for 
3152      * the macgui's purposes a value of 0 actually means to not even use the filter
3153      * current hb_filter_deblock.settings valid ranges are from 5 - 15 
3154      */
3155     if ([fPictureController deblock] != 0)
3156     {
3157         NSString *deblockStringValue = [NSString stringWithFormat: @"%d",[fPictureController deblock]];
3158         hb_filter_deblock.settings = (char *) [deblockStringValue UTF8String];
3159         hb_list_add( job->filters, &hb_filter_deblock );
3160     }
3161
3162 }
3163
3164
3165 #pragma mark -
3166 #pragma mark Job Handling
3167
3168
3169 - (void) prepareJob
3170 {
3171     
3172     NSMutableDictionary * queueToApply = [QueueFileArray objectAtIndex:currentQueueEncodeIndex];
3173     hb_list_t  * list  = hb_get_titles( fQueueEncodeLibhb );
3174     hb_title_t * title = (hb_title_t *) hb_list_item( list,0 ); // is always zero since now its a single title scan
3175     hb_job_t * job = title->job;
3176     hb_audio_config_t * audio;
3177     /* Title Angle for dvdnav */
3178     job->angle = [[queueToApply objectForKey:@"TitleAngle"] intValue];
3179     /* Chapter selection */
3180     job->chapter_start = [[queueToApply objectForKey:@"JobChapterStart"] intValue];
3181     job->chapter_end   = [[queueToApply objectForKey:@"JobChapterEnd"] intValue];
3182         
3183     /* Format (Muxer) and Video Encoder */
3184     job->mux = [[queueToApply objectForKey:@"JobFileFormatMux"] intValue];
3185     job->vcodec = [[queueToApply objectForKey:@"JobVideoEncoderVcodec"] intValue];
3186     
3187     
3188     /* If mpeg-4, then set mpeg-4 specific options like chapters and > 4gb file sizes */
3189     if( [[queueToApply objectForKey:@"Mp4LargeFile"] intValue] == 1)
3190     {
3191         job->largeFileSize = 1;
3192     }
3193     else
3194     {
3195         job->largeFileSize = 0;
3196     }
3197     /* We set http optimized mp4 here */
3198     if( [[queueToApply objectForKey:@"Mp4HttpOptimize"] intValue] == 1 )
3199     {
3200         job->mp4_optimize = 1;
3201     }
3202     else
3203     {
3204         job->mp4_optimize = 0;
3205     }
3206
3207         
3208     /* We set the chapter marker extraction here based on the format being
3209      mpeg4 or mkv and the checkbox being checked */
3210     if ([[queueToApply objectForKey:@"ChapterMarkers"] intValue] == 1)
3211     {
3212         job->chapter_markers = 1;
3213         
3214         /* now lets get our saved chapter names out the array in the queue file
3215          * and insert them back into the title chapter list. We have it here,
3216          * because unless we are inserting chapter markers there is no need to
3217          * spend the overhead of iterating through the chapter names array imo
3218          * Also, note that if for some reason we don't apply chapter names, the
3219          * chapters just come out 001, 002, etc. etc.
3220          */
3221          
3222         NSMutableArray *ChapterNamesArray = [queueToApply objectForKey:@"ChapterNames"];
3223         int i = 0;
3224         NSEnumerator *enumerator = [ChapterNamesArray objectEnumerator];
3225         id tempObject;
3226         while (tempObject = [enumerator nextObject])
3227         {
3228             hb_chapter_t *chapter = (hb_chapter_t *) hb_list_item( title->list_chapter, i );
3229             if( chapter != NULL )
3230             {
3231                 strncpy( chapter->title, [tempObject UTF8String], 1023);
3232                 chapter->title[1023] = '\0';
3233             }
3234             i++;
3235         }
3236     }
3237     else
3238     {
3239         job->chapter_markers = 0;
3240     }
3241     
3242     if( job->vcodec & HB_VCODEC_X264 )
3243     {
3244                 if ([[queueToApply objectForKey:@"Mp4iPodCompatible"] intValue] == 1)
3245             {
3246             job->ipod_atom = 1;
3247                 }
3248         else
3249         {
3250             job->ipod_atom = 0;
3251         }
3252                 
3253                 /* Set this flag to switch from Constant Quantizer(default) to Constant Rate Factor Thanks jbrjake
3254          Currently only used with Constant Quality setting*/
3255                 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultCrf"] > 0 && [[queueToApply objectForKey:@"VideoQualityType"] intValue] == 2)
3256                 {
3257                 job->crf = 1;
3258                 }
3259                 /* Below Sends x264 options to the core library if x264 is selected*/
3260                 /* Lets use this as per Nyx, Thanks Nyx!*/
3261                 job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */
3262                 /* Turbo first pass if two pass and Turbo First pass is selected */
3263                 if( [[queueToApply objectForKey:@"VideoTwoPass"] intValue] == 1 && [[queueToApply objectForKey:@"VideoTurboTwoPass"] intValue] == 1 )
3264                 {
3265                         /* pass the "Turbo" string to be appended to the existing x264 opts string into a variable for the first pass */
3266                         NSString *firstPassOptStringTurbo = @":ref=1:subme=1:me=dia:analyse=none:trellis=0:no-fast-pskip=0:8x8dct=0:weightb=0";
3267                         /* append the "Turbo" string variable to the existing opts string.
3268              Note: the "Turbo" string must be appended, not prepended to work properly*/
3269                         NSString *firstPassOptStringCombined = [[queueToApply objectForKey:@"x264Option"] stringByAppendingString:firstPassOptStringTurbo];
3270                         strcpy(job->x264opts, [firstPassOptStringCombined UTF8String]);
3271                 }
3272                 else
3273                 {
3274                         strcpy(job->x264opts, [[queueToApply objectForKey:@"x264Option"] UTF8String]);
3275                 }
3276         
3277     }
3278     
3279     
3280     /* Picture Size Settings */
3281     job->width = [[queueToApply objectForKey:@"PictureWidth"]  intValue];
3282     job->height = [[queueToApply objectForKey:@"PictureHeight"]  intValue];
3283     
3284     job->keep_ratio = [[queueToApply objectForKey:@"PictureKeepRatio"]  intValue];
3285     job->anamorphic.mode = [[queueToApply objectForKey:@"PicturePAR"]  intValue];
3286     if ([[queueToApply objectForKey:@"PicturePAR"]  intValue] == 3)
3287     {
3288         /* insert our custom values here for capuj */
3289         job->width = [[queueToApply objectForKey:@"PicturePARStorageWidth"]  intValue];
3290         job->height = [[queueToApply objectForKey:@"PicturePARStorageHeight"]  intValue];
3291         
3292         job->anamorphic.par_width = [[queueToApply objectForKey:@"PicturePARPixelWidth"]  intValue];
3293         job->anamorphic.par_height = [[queueToApply objectForKey:@"PicturePARPixelHeight"]  intValue];
3294         
3295         job->anamorphic.dar_width = [[queueToApply objectForKey:@"PicturePARDisplayWidth"]  floatValue];
3296         job->anamorphic.dar_height = [[queueToApply objectForKey:@"PicturePARDisplayHeight"]  floatValue];
3297     }
3298     
3299     /* Here we use the crop values saved at the time the preset was saved */
3300     job->crop[0] = [[queueToApply objectForKey:@"PictureTopCrop"]  intValue];
3301     job->crop[1] = [[queueToApply objectForKey:@"PictureBottomCrop"]  intValue];
3302     job->crop[2] = [[queueToApply objectForKey:@"PictureLeftCrop"]  intValue];
3303     job->crop[3] = [[queueToApply objectForKey:@"PictureRightCrop"]  intValue];
3304     
3305     /* Video settings */
3306     /* Framerate */
3307     
3308     /* Set vfr to 0 as it's only on if using same as source in the framerate popup
3309      * and detelecine is on, so we handle that in the logic below
3310      */
3311     job->vfr = 0;
3312     if( [[queueToApply objectForKey:@"JobIndexVideoFramerate"] intValue] > 0 )
3313     {
3314         /* a specific framerate has been chosen */
3315         job->vrate      = 27000000;
3316         job->vrate_base = hb_video_rates[[[queueToApply objectForKey:@"JobIndexVideoFramerate"] intValue]-1].rate;
3317         /* We are not same as source so we set job->cfr to 1 
3318          * to enable constant frame rate since user has specified
3319          * a specific framerate*/
3320         job->cfr = 1;
3321     }
3322     else
3323     {
3324         /* We are same as source (variable) */
3325         job->vrate      = [[queueToApply objectForKey:@"JobVrate"] intValue];
3326         job->vrate_base = [[queueToApply objectForKey:@"JobVrateBase"] intValue];
3327         /* We are same as source so we set job->cfr to 0 
3328          * to enable true same as source framerate */
3329         job->cfr = 0;
3330         /* If we are same as source and we have detelecine on, we need to turn on
3331          * job->vfr
3332          */
3333         if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] == 1)
3334         {
3335             job->vfr = 1;
3336         }
3337     }
3338     
3339     if ( [[queueToApply objectForKey:@"VideoQualityType"] intValue] != 2 )
3340     {
3341         /* Target size.
3342          Bitrate should already have been calculated and displayed
3343          in fVidBitrateField, so let's just use it same as abr*/
3344         job->vquality = -1.0;
3345         job->vbitrate = [[queueToApply objectForKey:@"VideoAvgBitrate"] intValue];
3346     }
3347     if ( [[queueToApply objectForKey:@"VideoQualityType"] intValue] == 2 )
3348     {
3349         job->vquality = [[queueToApply objectForKey:@"VideoQualitySlider"] floatValue];
3350         job->vbitrate = 0;
3351         
3352     }
3353     
3354     job->grayscale = [[queueToApply objectForKey:@"VideoGrayScale"] intValue];
3355     
3356
3357
3358 #pragma mark -
3359 #pragma mark Process Subtitles to libhb
3360
3361 /* Map the settings in the dictionaries for the SubtitleList array to match title->list_subtitle
3362  * which means that we need to account for the offset of non source language settings in from
3363  * the NSPopUpCell menu. For all of the objects in the SubtitleList array this means 0 is "None"
3364  * from the popup menu, additionally the first track has "Foreign Audio Search" at 1. So we use
3365  * an int to offset the index number for the objectForKey:@"subtitleSourceTrackNum" to map that
3366  * to the source tracks position in title->list_subtitle.
3367  */
3368
3369 int subtitle = nil;
3370 int force;
3371 int burned;
3372 int def;
3373 bool one_burned = FALSE;
3374
3375     int i = 0;
3376     NSEnumerator *enumerator = [[queueToApply objectForKey:@"SubtitleList"] objectEnumerator];
3377     id tempObject;
3378     while (tempObject = [enumerator nextObject])
3379     {
3380         
3381         subtitle = [[tempObject objectForKey:@"subtitleSourceTrackNum"] intValue];
3382         force = [[tempObject objectForKey:@"subtitleTrackForced"] intValue];
3383         burned = [[tempObject objectForKey:@"subtitleTrackBurned"] intValue];
3384         def = [[tempObject objectForKey:@"subtitleTrackDefault"] intValue];
3385         
3386         /* since the subtitleSourceTrackNum 0 is "None" in our array of the subtitle popups,
3387          * we want to ignore it for display as well as encoding.
3388          */
3389         if (subtitle > 0)
3390         {
3391             /* if i is 0, then we are in the first item of the subtitles which we need to 
3392              * check for the "Foreign Audio Search" which would be subtitleSourceTrackNum of 1
3393              * bearing in mind that for all tracks subtitleSourceTrackNum of 0 is None.
3394              */
3395             
3396             /* if we are on the first track and using "Foreign Audio Search" */ 
3397             if (i == 0 && subtitle == 1)
3398             {
3399                 [self writeToActivityLog: "Foreign Language Search: %d", 1];
3400                 
3401                 job->indepth_scan = 1;
3402                 if (burned == 1 || job->mux != HB_MUX_MP4)
3403                 {
3404                     if (burned != 1 && job->mux == HB_MUX_MKV)
3405                     {
3406                         job->select_subtitle_config.dest = hb_subtitle_config_s::PASSTHRUSUB;
3407                     }
3408                     else
3409                     {
3410                         job->select_subtitle_config.dest = hb_subtitle_config_s::RENDERSUB;
3411                     }
3412                     
3413                     job->select_subtitle_config.force = force;
3414                     job->select_subtitle_config.default_track = def;
3415                 }
3416                 
3417                 
3418             }
3419             else
3420             {
3421                 
3422                 /* for the actual source tracks, we must subtract the non source entries so 
3423                  * that the menu index matches the source subtitle_list index for convenience */
3424                 if (i == 0)
3425                 {
3426                     /* for the first track, the source tracks start at menu index 2 ( None is 0,
3427                      * Foreign Language Search is 1) so subtract 2 */
3428                     subtitle = subtitle - 2;
3429                 }
3430                 else
3431                 {
3432                     /* for all other tracks, the source tracks start at menu index 1 (None is 0)
3433                      * so subtract 1. */
3434                     
3435                     subtitle = subtitle - 1;
3436                 }
3437                 
3438                 /* We are setting a source subtitle so access the source subtitle info */  
3439                 hb_subtitle_t * subt;
3440                 
3441                 subt = (hb_subtitle_t *)hb_list_item(title->list_subtitle, subtitle);
3442                 
3443                 if (subt != NULL)
3444                 {
3445                     [self writeToActivityLog: "Setting Subtitle: %s", subt];
3446
3447                     hb_subtitle_config_t sub_config = subt->config;
3448                     
3449                     if (!burned && job->mux == HB_MUX_MKV && 
3450                         subt->format == hb_subtitle_s::PICTURESUB)
3451                     {
3452                         sub_config.dest = hb_subtitle_config_s::PASSTHRUSUB;
3453                     }
3454                     else if (!burned && job->mux == HB_MUX_MP4 && 
3455                              subt->format == hb_subtitle_s::PICTURESUB)
3456                     {
3457                         // Skip any non-burned vobsubs when output is mp4
3458                         continue;
3459                     }
3460                     else if ( burned && subt->format == hb_subtitle_s::PICTURESUB )
3461                     {
3462                         // Only allow one subtitle to be burned into the video
3463                         if (one_burned)
3464                             continue;
3465                         one_burned = TRUE;
3466                     }
3467                     sub_config.force = force;
3468                     sub_config.default_track = def;
3469                     hb_subtitle_add( job, &sub_config, subtitle );
3470                 }   
3471                 
3472             }
3473         }
3474         i++;
3475     }
3476
3477 #pragma mark -
3478
3479    
3480     /* Audio tracks and mixdowns */
3481     /* Lets make sure there arent any erroneous audio tracks in the job list, so lets make sure its empty*/
3482     int audiotrack_count = hb_list_count(job->list_audio);
3483     for( int i = 0; i < audiotrack_count;i++)
3484     {
3485         hb_audio_t * temp_audio = (hb_audio_t*) hb_list_item( job->list_audio, 0 );
3486         hb_list_rem(job->list_audio, temp_audio);
3487     }
3488     /* Now lets add our new tracks to the audio list here */
3489     if ([[queueToApply objectForKey:@"Audio1Track"] intValue] > 0)
3490     {
3491         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3492         hb_audio_config_init(audio);
3493         audio->in.track = [[queueToApply objectForKey:@"Audio1Track"] intValue] - 1;
3494         /* We go ahead and assign values to our audio->out.<properties> */
3495         audio->out.track = [[queueToApply objectForKey:@"Audio1Track"] intValue] - 1;
3496         audio->out.codec = [[queueToApply objectForKey:@"JobAudio1Encoder"] intValue];
3497         audio->out.mixdown = [[queueToApply objectForKey:@"JobAudio1Mixdown"] intValue];
3498         audio->out.bitrate = [[queueToApply objectForKey:@"JobAudio1Bitrate"] intValue];
3499         audio->out.samplerate = [[queueToApply objectForKey:@"JobAudio1Samplerate"] intValue];
3500         audio->out.dynamic_range_compression = [[queueToApply objectForKey:@"Audio1TrackDRCSlider"] floatValue];
3501         
3502         hb_audio_add( job, audio );
3503         free(audio);
3504     }  
3505     if ([[queueToApply objectForKey:@"Audio2Track"] intValue] > 0)
3506     {
3507         
3508         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3509         hb_audio_config_init(audio);
3510         audio->in.track = [[queueToApply objectForKey:@"Audio2Track"] intValue] - 1;
3511         [self writeToActivityLog: "prepareJob audiotrack 2 is: %d", audio->in.track];
3512         /* We go ahead and assign values to our audio->out.<properties> */
3513         audio->out.track = [[queueToApply objectForKey:@"Audio2Track"] intValue] - 1;
3514         audio->out.codec = [[queueToApply objectForKey:@"JobAudio2Encoder"] intValue];
3515         audio->out.mixdown = [[queueToApply objectForKey:@"JobAudio2Mixdown"] intValue];
3516         audio->out.bitrate = [[queueToApply objectForKey:@"JobAudio2Bitrate"] intValue];
3517         audio->out.samplerate = [[queueToApply objectForKey:@"JobAudio2Samplerate"] intValue];
3518         audio->out.dynamic_range_compression = [[queueToApply objectForKey:@"Audio2TrackDRCSlider"] floatValue];
3519         
3520         hb_audio_add( job, audio );
3521         free(audio);
3522     }
3523     
3524     if ([[queueToApply objectForKey:@"Audio3Track"] intValue] > 0)
3525     {
3526         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3527         hb_audio_config_init(audio);
3528         audio->in.track = [[queueToApply objectForKey:@"Audio3Track"] intValue] - 1;
3529         /* We go ahead and assign values to our audio->out.<properties> */
3530         audio->out.track = [[queueToApply objectForKey:@"Audio3Track"] intValue] - 1;
3531         audio->out.codec = [[queueToApply objectForKey:@"JobAudio3Encoder"] intValue];
3532         audio->out.mixdown = [[queueToApply objectForKey:@"JobAudio3Mixdown"] intValue];
3533         audio->out.bitrate = [[queueToApply objectForKey:@"JobAudio3Bitrate"] intValue];
3534         audio->out.samplerate = [[queueToApply objectForKey:@"JobAudio3Samplerate"] intValue];
3535         audio->out.dynamic_range_compression = [[queueToApply objectForKey:@"Audio3TrackDRCSlider"] floatValue];
3536         
3537         hb_audio_add( job, audio );
3538         free(audio);        
3539     }
3540     
3541     if ([[queueToApply objectForKey:@"Audio4Track"] intValue] > 0)
3542     {
3543         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3544         hb_audio_config_init(audio);
3545         audio->in.track = [[queueToApply objectForKey:@"Audio4Track"] intValue] - 1;
3546         /* We go ahead and assign values to our audio->out.<properties> */
3547         audio->out.track = [[queueToApply objectForKey:@"Audio4Track"] intValue] - 1;
3548         audio->out.codec = [[queueToApply objectForKey:@"JobAudio4Encoder"] intValue];
3549         audio->out.mixdown = [[queueToApply objectForKey:@"JobAudio4Mixdown"] intValue];
3550         audio->out.bitrate = [[queueToApply objectForKey:@"JobAudio4Bitrate"] intValue];
3551         audio->out.samplerate = [[queueToApply objectForKey:@"JobAudio4Samplerate"] intValue];
3552         audio->out.dynamic_range_compression = [[queueToApply objectForKey:@"Audio4TrackDRCSlider"] floatValue];
3553         
3554         hb_audio_add( job, audio );
3555         
3556
3557     }
3558     
3559     /* Filters */ 
3560     job->filters = hb_list_init();
3561     
3562     /* Now lets call the filters if applicable.
3563      * The order of the filters is critical
3564      */
3565     /* Detelecine */
3566     if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] == 1)
3567     {
3568         //if ([queueToApply objectForKey:@"PictureDetelecineCustom"])
3569         hb_list_add( job->filters, &hb_filter_detelecine );
3570     }
3571     if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] == 2)
3572     {
3573         /* use a custom detelecine string */
3574         hb_filter_detelecine.settings = (char *) [[queueToApply objectForKey:@"PictureDetelecineCustom"] UTF8String];
3575         hb_list_add( job->filters, &hb_filter_detelecine );
3576     }
3577     
3578     if ([[queueToApply objectForKey:@"PictureDecombDeinterlace"] intValue] == 1)
3579     {
3580         /* Decomb */
3581         if ([[queueToApply objectForKey:@"PictureDecomb"] intValue] == 1)
3582         {
3583             /* Run old deinterlacer fd by default */
3584             hb_list_add( job->filters, &hb_filter_decomb );
3585         }
3586         /* we add the custom string if present */
3587         if ([[queueToApply objectForKey:@"PictureDecomb"] intValue] == 2)
3588         {
3589             /* use a custom decomb string */
3590             hb_filter_decomb.settings = (char *) [[queueToApply objectForKey:@"PictureDecombCustom"] UTF8String];
3591             hb_list_add( job->filters, &hb_filter_decomb );
3592         }
3593         
3594     }
3595     else
3596     {
3597         
3598         /* Deinterlace */
3599         if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 1)
3600         {
3601             /* Run old deinterlacer fd by default */
3602             hb_filter_deinterlace.settings = "-1"; 
3603             hb_list_add( job->filters, &hb_filter_deinterlace );
3604         }
3605         else if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 2)
3606         {
3607             /* Yadif mode 0 (without spatial deinterlacing.) */
3608             hb_filter_deinterlace.settings = "2"; 
3609             hb_list_add( job->filters, &hb_filter_deinterlace );            
3610         }
3611         else if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 3)
3612         {
3613             /* Yadif (with spatial deinterlacing) */
3614             hb_filter_deinterlace.settings = "0"; 
3615             hb_list_add( job->filters, &hb_filter_deinterlace );            
3616         }
3617         else if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 4)
3618         {
3619             /* we add the custom string if present */
3620             hb_filter_deinterlace.settings = (char *) [[queueToApply objectForKey:@"PictureDeinterlaceCustom"] UTF8String];
3621             hb_list_add( job->filters, &hb_filter_deinterlace );            
3622         }
3623         
3624         
3625     }
3626     /* Denoise */
3627         if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 1) // Weak in popup
3628         {
3629                 hb_filter_denoise.settings = "2:1:2:3"; 
3630         hb_list_add( job->filters, &hb_filter_denoise );        
3631         }
3632         else if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 2) // Medium in popup
3633         {
3634                 hb_filter_denoise.settings = "3:2:2:3"; 
3635         hb_list_add( job->filters, &hb_filter_denoise );        
3636         }
3637         else if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 3) // Strong in popup
3638         {
3639                 hb_filter_denoise.settings = "7:7:5:5"; 
3640         hb_list_add( job->filters, &hb_filter_denoise );        
3641         }
3642     else if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 4) // Custom in popup
3643         {
3644                 /* we add the custom string if present */
3645         hb_filter_denoise.settings = (char *) [[queueToApply objectForKey:@"PictureDenoiseCustom"] UTF8String];
3646         hb_list_add( job->filters, &hb_filter_denoise );        
3647         }
3648     
3649     /* Deblock  (uses pp7 default) */
3650     /* NOTE: even though there is a valid deblock setting of 0 for the filter, for 
3651      * the macgui's purposes a value of 0 actually means to not even use the filter
3652      * current hb_filter_deblock.settings valid ranges are from 5 - 15 
3653      */
3654     if ([[queueToApply objectForKey:@"PictureDeblock"] intValue] != 0)
3655     {
3656         hb_filter_deblock.settings = (char *) [[queueToApply objectForKey:@"PictureDeblock"] UTF8String];
3657         hb_list_add( job->filters, &hb_filter_deblock );
3658     }
3659 [self writeToActivityLog: "prepareJob exiting"];    
3660 }
3661
3662
3663
3664 /* addToQueue: puts up an alert before ultimately calling doAddToQueue
3665 */
3666 - (IBAction) addToQueue: (id) sender
3667 {
3668         /* We get the destination directory from the destination field here */
3669         NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
3670         /* We check for a valid destination here */
3671         if ([[NSFileManager defaultManager] fileExistsAtPath:destinationDirectory] == 0) 
3672         {
3673                 NSRunAlertPanel(@"Warning!", @"This is not a valid destination directory!", @"OK", nil, nil);
3674         return;
3675         }
3676     
3677     BOOL fileExists;
3678     fileExists = NO;
3679     
3680     BOOL fileExistsInQueue;
3681     fileExistsInQueue = NO;
3682     
3683     /* We check for and existing file here */
3684     if([[NSFileManager defaultManager] fileExistsAtPath: [fDstFile2Field stringValue]])
3685     {
3686         fileExists = YES;
3687     }
3688     
3689     /* We now run through the queue and make sure we are not overwriting an exisiting queue item */
3690     int i = 0;
3691     NSEnumerator *enumerator = [QueueFileArray objectEnumerator];
3692         id tempObject;
3693         while (tempObject = [enumerator nextObject])
3694         {
3695                 NSDictionary *thisQueueDict = tempObject;
3696                 if ([[thisQueueDict objectForKey:@"DestinationPath"] isEqualToString: [fDstFile2Field stringValue]])
3697                 {
3698                         fileExistsInQueue = YES;        
3699                 }
3700         i++;
3701         }
3702     
3703     
3704         if(fileExists == YES)
3705     {
3706         NSBeginCriticalAlertSheet( NSLocalizedString( @"File already exists.", @"" ),
3707                                   NSLocalizedString( @"Cancel", @"" ), NSLocalizedString( @"Overwrite", @"" ), nil, fWindow, self,
3708                                   @selector( overwriteAddToQueueAlertDone:returnCode:contextInfo: ),
3709                                   NULL, NULL, [NSString stringWithFormat:
3710                                                NSLocalizedString( @"Do you want to overwrite %@?", @"" ),
3711                                                [fDstFile2Field stringValue]] );
3712     }
3713     else if (fileExistsInQueue == YES)
3714     {
3715     NSBeginCriticalAlertSheet( NSLocalizedString( @"There is already a queue item for this destination.", @"" ),
3716                                   NSLocalizedString( @"Cancel", @"" ), NSLocalizedString( @"Overwrite", @"" ), nil, fWindow, self,
3717                                   @selector( overwriteAddToQueueAlertDone:returnCode:contextInfo: ),
3718                                   NULL, NULL, [NSString stringWithFormat:
3719                                                NSLocalizedString( @"Do you want to overwrite %@?", @"" ),
3720                                                [fDstFile2Field stringValue]] );
3721     }
3722     else
3723     {
3724         [self doAddToQueue];
3725     }
3726 }
3727
3728 /* overwriteAddToQueueAlertDone: called from the alert posted by addToQueue that asks
3729    the user if they want to overwrite an exiting movie file.
3730 */
3731 - (void) overwriteAddToQueueAlertDone: (NSWindow *) sheet
3732     returnCode: (int) returnCode contextInfo: (void *) contextInfo
3733 {
3734     if( returnCode == NSAlertAlternateReturn )
3735         [self doAddToQueue];
3736 }
3737
3738 - (void) doAddToQueue
3739 {
3740     [self addQueueFileItem ];
3741 }
3742
3743
3744
3745 /* Rip: puts up an alert before ultimately calling doRip
3746 */
3747 - (IBAction) Rip: (id) sender
3748 {
3749     [self writeToActivityLog: "Rip: Pending queue count is %d", fPendingCount];
3750     /* Rip or Cancel ? */
3751     hb_state_t s;
3752     hb_get_state2( fQueueEncodeLibhb, &s );
3753     
3754     if(s.state == HB_STATE_WORKING || s.state == HB_STATE_PAUSED)
3755         {
3756         [self Cancel: sender];
3757         return;
3758     }
3759     
3760     /* We check to see if we need to warn the user that the computer will go to sleep
3761                  or shut down when encoding is finished */
3762                 [self remindUserOfSleepOrShutdown];
3763     
3764     // If there are pending jobs in the queue, then this is a rip the queue
3765     if (fPendingCount > 0)
3766     {
3767         /* here lets start the queue with the first pending item */
3768         [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]]; 
3769         
3770         return;
3771     }
3772     
3773     // Before adding jobs to the queue, check for a valid destination.
3774     
3775     NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
3776     if ([[NSFileManager defaultManager] fileExistsAtPath:destinationDirectory] == 0) 
3777     {
3778         NSRunAlertPanel(@"Warning!", @"This is not a valid destination directory!", @"OK", nil, nil);
3779         return;
3780     }
3781     
3782     /* We check for duplicate name here */
3783     if( [[NSFileManager defaultManager] fileExistsAtPath:[fDstFile2Field stringValue]] )
3784     {
3785         NSBeginCriticalAlertSheet( NSLocalizedString( @"File already exists", @"" ),
3786                                   NSLocalizedString( @"Cancel", "" ), NSLocalizedString( @"Overwrite", @"" ), nil, fWindow, self,
3787                                   @selector( overWriteAlertDone:returnCode:contextInfo: ),
3788                                   NULL, NULL, [NSString stringWithFormat:
3789                                                NSLocalizedString( @"Do you want to overwrite %@?", @"" ),
3790                                                [fDstFile2Field stringValue]] );
3791         
3792         // overWriteAlertDone: will be called when the alert is dismissed. It will call doRip.
3793     }
3794     else
3795     {
3796         /* if there are no pending jobs in the queue, then add this one to the queue and rip
3797          otherwise, just rip the queue */
3798         if(fPendingCount == 0)
3799         {
3800             [self writeToActivityLog: "Rip: No pending jobs, so sending this one to doAddToQueue"];
3801             [self doAddToQueue];
3802         }
3803         
3804         /* go right to processing the new queue encode */
3805         [self writeToActivityLog: "Rip: Going right to performNewQueueScan"];
3806         [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]]; 
3807         
3808     }
3809 }
3810
3811 /* overWriteAlertDone: called from the alert posted by Rip: that asks the user if they
3812    want to overwrite an exiting movie file.
3813 */
3814 - (void) overWriteAlertDone: (NSWindow *) sheet
3815     returnCode: (int) returnCode contextInfo: (void *) contextInfo
3816 {
3817     if( returnCode == NSAlertAlternateReturn )
3818     {
3819         /* if there are no jobs in the queue, then add this one to the queue and rip 
3820         otherwise, just rip the queue */
3821         if( fPendingCount == 0 )
3822         {
3823             [self doAddToQueue];
3824         }
3825
3826         NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
3827         [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];
3828         [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]]; 
3829       
3830     }
3831 }
3832
3833 - (void) remindUserOfSleepOrShutdown
3834 {
3835        if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Put Computer To Sleep"])
3836        {
3837                /*Warn that computer will sleep after encoding*/
3838                int reminduser;
3839                NSBeep();
3840                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);
3841                [NSApp requestUserAttention:NSCriticalRequest];
3842                if ( reminduser == NSAlertAlternateReturn )
3843                {
3844                        [self showPreferencesWindow:nil];
3845                }
3846        }
3847        else if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Shut Down Computer"])
3848        {
3849                /*Warn that computer will shut down after encoding*/
3850                int reminduser;
3851                NSBeep();
3852                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);
3853                [NSApp requestUserAttention:NSCriticalRequest];
3854                if ( reminduser == NSAlertAlternateReturn )
3855                {
3856                        [self showPreferencesWindow:nil];
3857                }
3858        }
3859
3860 }
3861
3862
3863 - (void) doRip
3864 {
3865     /* Let libhb do the job */
3866     hb_start( fQueueEncodeLibhb );
3867     /*set the fEncodeState State */
3868         fEncodeState = 1;
3869 }
3870
3871
3872 //------------------------------------------------------------------------------------
3873 // Displays an alert asking user if the want to cancel encoding of current job.
3874 // Cancel: returns immediately after posting the alert. Later, when the user
3875 // acknowledges the alert, doCancelCurrentJob is called.
3876 //------------------------------------------------------------------------------------
3877 - (IBAction)Cancel: (id)sender
3878 {
3879     if (!fQueueController) return;
3880     
3881   hb_pause( fQueueEncodeLibhb );
3882     NSString * alertTitle = [NSString stringWithFormat:NSLocalizedString(@"You are currently encoding. What would you like to do ?", nil)];
3883    
3884     // Which window to attach the sheet to?
3885     NSWindow * docWindow;
3886     if ([sender respondsToSelector: @selector(window)])
3887         docWindow = [sender window];
3888     else
3889         docWindow = fWindow;
3890         
3891     NSBeginCriticalAlertSheet(
3892             alertTitle,
3893             NSLocalizedString(@"Continue Encoding", nil),
3894             NSLocalizedString(@"Cancel Current and Stop", nil),
3895             NSLocalizedString(@"Cancel Current and Continue", nil),
3896             docWindow, self,
3897             nil, @selector(didDimissCancel:returnCode:contextInfo:), nil,
3898             NSLocalizedString(@"Your encode will be cancelled if you don't continue encoding.", nil));
3899     
3900     // didDimissCancelCurrentJob:returnCode:contextInfo: will be called when the dialog is dismissed
3901 }
3902
3903 - (void) didDimissCancel: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo
3904 {
3905    hb_resume( fQueueEncodeLibhb );
3906      if (returnCode == NSAlertOtherReturn)
3907     {
3908         [self doCancelCurrentJob];  // <- this also stops libhb
3909     }
3910     if (returnCode == NSAlertAlternateReturn)
3911     {
3912     [self doCancelCurrentJobAndStop];
3913     }
3914 }
3915
3916 //------------------------------------------------------------------------------------
3917 // Cancels and deletes the current job and stops libhb from processing the remaining
3918 // encodes.
3919 //------------------------------------------------------------------------------------
3920 - (void) doCancelCurrentJob
3921 {
3922     // Stop the current job. hb_stop will only cancel the current pass and then set
3923     // its state to HB_STATE_WORKDONE. It also does this asynchronously. So when we
3924     // see the state has changed to HB_STATE_WORKDONE (in updateUI), we'll delete the
3925     // remaining passes of the job and then start the queue back up if there are any
3926     // remaining jobs.
3927      
3928     
3929     hb_stop( fQueueEncodeLibhb );
3930     
3931     // Delete all remaining jobs since libhb doesn't do this on its own.
3932             hb_job_t * job;
3933             while( ( job = hb_job(fQueueEncodeLibhb, 0) ) )
3934                 hb_rem( fQueueEncodeLibhb, job );
3935                 
3936     fEncodeState = 2;   // don't alert at end of processing since this was a cancel
3937     
3938     // now that we've stopped the currently encoding job, lets mark it as cancelled
3939     [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] setObject:[NSNumber numberWithInt:3] forKey:@"Status"];
3940     // and as always, save it in the queue .plist...
3941     /* We save all of the Queue data here */
3942     [self saveQueueFileItem];
3943     // so now lets move to 
3944     currentQueueEncodeIndex++ ;
3945     // ... and see if there are more items left in our queue
3946     int queueItems = [QueueFileArray count];
3947     /* If we still have more items in our queue, lets go to the next one */
3948     if (currentQueueEncodeIndex < queueItems)
3949     {
3950     [self writeToActivityLog: "doCancelCurrentJob currentQueueEncodeIndex is incremented to: %d", currentQueueEncodeIndex];
3951     [self writeToActivityLog: "doCancelCurrentJob moving to the next job"];
3952     
3953     [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]];
3954     }
3955     else
3956     {
3957         [self writeToActivityLog: "doCancelCurrentJob the item queue is complete"];
3958     }
3959
3960 }
3961
3962 - (void) doCancelCurrentJobAndStop
3963 {
3964     hb_stop( fQueueEncodeLibhb );
3965     
3966     // Delete all remaining jobs since libhb doesn't do this on its own.
3967             hb_job_t * job;
3968             while( ( job = hb_job(fQueueEncodeLibhb, 0) ) )
3969                 hb_rem( fQueueEncodeLibhb, job );
3970                 
3971                 
3972     fEncodeState = 2;   // don't alert at end of processing since this was a cancel
3973     
3974     // now that we've stopped the currently encoding job, lets mark it as cancelled
3975     [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] setObject:[NSNumber numberWithInt:3] forKey:@"Status"];
3976     // and as always, save it in the queue .plist...
3977     /* We save all of the Queue data here */
3978     [self saveQueueFileItem];
3979     // so now lets move to 
3980     currentQueueEncodeIndex++ ;
3981     [self writeToActivityLog: "cancelling current job and stopping the queue"];
3982 }
3983 - (IBAction) Pause: (id) sender
3984 {
3985     hb_state_t s;
3986     hb_get_state2( fQueueEncodeLibhb, &s );
3987
3988     if( s.state == HB_STATE_PAUSED )
3989     {
3990         hb_resume( fQueueEncodeLibhb );
3991     }
3992     else
3993     {
3994         hb_pause( fQueueEncodeLibhb );
3995     }
3996 }
3997
3998 #pragma mark -
3999 #pragma mark GUI Controls Changed Methods
4000
4001 - (IBAction) titlePopUpChanged: (id) sender
4002 {
4003     hb_list_t  * list  = hb_get_titles( fHandle );
4004     hb_title_t * title = (hb_title_t*)
4005         hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
4006
4007     /* If Auto Naming is on. We create an output filename of dvd name - title number */
4008     if( [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultAutoNaming"] > 0 && ( hb_list_count( list ) > 1 ) )
4009         {
4010                 [fDstFile2Field setStringValue: [NSString stringWithFormat:
4011                         @"%@/%@-%d.%@", [[fDstFile2Field stringValue] stringByDeletingLastPathComponent],
4012                         [browsedSourceDisplayName stringByDeletingPathExtension],
4013             title->index,
4014                         [[fDstFile2Field stringValue] pathExtension]]]; 
4015         }
4016
4017     /* Update chapter popups */
4018     [fSrcChapterStartPopUp removeAllItems];
4019     [fSrcChapterEndPopUp   removeAllItems];
4020     for( int i = 0; i < hb_list_count( title->list_chapter ); i++ )
4021     {
4022         [fSrcChapterStartPopUp addItemWithTitle: [NSString
4023             stringWithFormat: @"%d", i + 1]];
4024         [fSrcChapterEndPopUp addItemWithTitle: [NSString
4025             stringWithFormat: @"%d", i + 1]];
4026     }
4027
4028     [fSrcChapterStartPopUp selectItemAtIndex: 0];
4029     [fSrcChapterEndPopUp   selectItemAtIndex:
4030         hb_list_count( title->list_chapter ) - 1];
4031     [self chapterPopUpChanged:nil];
4032     
4033     /* if using dvd nav, show the angle widget */
4034     if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"UseDvdNav"] boolValue])
4035     {
4036         [fSrcAngleLabel setHidden:NO];
4037         [fSrcAnglePopUp setHidden:NO];
4038         
4039         [fSrcAnglePopUp removeAllItems];
4040         for( int i = 0; i < title->angle_count; i++ )
4041         {
4042             [fSrcAnglePopUp addItemWithTitle: [NSString stringWithFormat: @"%d", i + 1]];
4043         }
4044         [fSrcAnglePopUp selectItemAtIndex: 0];
4045     }
4046     else
4047     {
4048         [fSrcAngleLabel setHidden:YES];
4049         [fSrcAnglePopUp setHidden:YES];
4050     }
4051     
4052     /* Start Get and set the initial pic size for display */
4053         hb_job_t * job = title->job;
4054         fTitle = title;
4055     
4056     /* Set Auto Crop to on upon selecting a new title  */
4057     [fPictureController setAutoCrop:YES];
4058     
4059         /* We get the originial output picture width and height and put them
4060         in variables for use with some presets later on */
4061         PicOrigOutputWidth = job->width;
4062         PicOrigOutputHeight = job->height;
4063         AutoCropTop = job->crop[0];
4064         AutoCropBottom = job->crop[1];
4065         AutoCropLeft = job->crop[2];
4066         AutoCropRight = job->crop[3];
4067
4068         /* Reset the new title in fPictureController &&  fPreviewController*/
4069     [fPictureController SetTitle:title];
4070
4071         
4072     /* Update Subtitle Table */
4073     [fSubtitlesDelegate resetWithTitle:title];
4074     [fSubtitlesTable reloadData];
4075     
4076
4077     /* Update chapter table */
4078     [fChapterTitlesDelegate resetWithTitle:title];
4079     [fChapterTable reloadData];
4080
4081    /* Lets make sure there arent any erroneous audio tracks in the job list, so lets make sure its empty*/
4082     int audiotrack_count = hb_list_count(job->list_audio);
4083     for( int i = 0; i < audiotrack_count;i++)
4084     {
4085         hb_audio_t * temp_audio = (hb_audio_t*) hb_list_item( job->list_audio, 0 );
4086         hb_list_rem(job->list_audio, temp_audio);
4087     }
4088
4089     /* Update audio popups */
4090     [self addAllAudioTracksToPopUp: fAudLang1PopUp];
4091     [self addAllAudioTracksToPopUp: fAudLang2PopUp];
4092     [self addAllAudioTracksToPopUp: fAudLang3PopUp];
4093     [self addAllAudioTracksToPopUp: fAudLang4PopUp];
4094     /* search for the first instance of our prefs default language for track 1, and set track 2 to "none" */
4095         NSString * audioSearchPrefix = [[NSUserDefaults standardUserDefaults] stringForKey:@"DefaultLanguage"];
4096         [self selectAudioTrackInPopUp: fAudLang1PopUp searchPrefixString: audioSearchPrefix selectIndexIfNotFound: 1];
4097     [self selectAudioTrackInPopUp:fAudLang2PopUp searchPrefixString:nil selectIndexIfNotFound:0];
4098     [self selectAudioTrackInPopUp:fAudLang3PopUp searchPrefixString:nil selectIndexIfNotFound:0];
4099     [self selectAudioTrackInPopUp:fAudLang4PopUp searchPrefixString:nil selectIndexIfNotFound:0];
4100
4101         /* changing the title may have changed the audio channels on offer, */
4102         /* so call audioTrackPopUpChanged for both audio tracks to update the mixdown popups */
4103         [self audioTrackPopUpChanged: fAudLang1PopUp];
4104         [self audioTrackPopUpChanged: fAudLang2PopUp];
4105     [self audioTrackPopUpChanged: fAudLang3PopUp];
4106     [self audioTrackPopUpChanged: fAudLang4PopUp];
4107
4108     [fVidRatePopUp selectItemAtIndex: 0];
4109
4110     /* we run the picture size values through calculatePictureSizing to get all picture setting information*/
4111         [self calculatePictureSizing:nil];
4112
4113    /* lets call tableViewSelected to make sure that any preset we have selected is enforced after a title change */
4114     [self selectPreset:nil];
4115 }
4116
4117 - (IBAction) chapterPopUpChanged: (id) sender
4118 {
4119
4120         /* If start chapter popup is greater than end chapter popup,
4121         we set the end chapter popup to the same as start chapter popup */
4122         if ([fSrcChapterStartPopUp indexOfSelectedItem] > [fSrcChapterEndPopUp indexOfSelectedItem])
4123         {
4124                 [fSrcChapterEndPopUp selectItemAtIndex: [fSrcChapterStartPopUp indexOfSelectedItem]];
4125     }
4126
4127                 
4128         hb_list_t  * list  = hb_get_titles( fHandle );
4129     hb_title_t * title = (hb_title_t *)
4130         hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
4131
4132     hb_chapter_t * chapter;
4133     int64_t        duration = 0;
4134     for( int i = [fSrcChapterStartPopUp indexOfSelectedItem];
4135          i <= [fSrcChapterEndPopUp indexOfSelectedItem]; i++ )
4136     {
4137         chapter = (hb_chapter_t *) hb_list_item( title->list_chapter, i );
4138         duration += chapter->duration;
4139     }
4140     
4141     duration /= 90000; /* pts -> seconds */
4142     [fSrcDuration2Field setStringValue: [NSString stringWithFormat:
4143         @"%02lld:%02lld:%02lld", duration / 3600, ( duration / 60 ) % 60,
4144         duration % 60]];
4145
4146     [self calculateBitrate: sender];
4147     
4148     if ( [fSrcChapterStartPopUp indexOfSelectedItem] ==  [fSrcChapterEndPopUp indexOfSelectedItem] )
4149     {
4150     /* Disable chapter markers for any source with less than two chapters as it makes no sense. */
4151     [fCreateChapterMarkers setEnabled: NO];
4152     [fCreateChapterMarkers setState: NSOffState];
4153     }
4154     else
4155     {
4156     [fCreateChapterMarkers setEnabled: YES];
4157     }
4158 }
4159
4160 - (IBAction) formatPopUpChanged: (id) sender
4161 {
4162     NSString * string = [fDstFile2Field stringValue];
4163     int format = [fDstFormatPopUp indexOfSelectedItem];
4164     char * ext = NULL;
4165         /* Initially set the large file (64 bit formatting) output checkbox to hidden */
4166     [fDstMp4LargeFileCheck setHidden: YES];
4167     [fDstMp4HttpOptFileCheck setHidden: YES];
4168     [fDstMp4iPodFileCheck setHidden: YES];
4169     
4170     /* Update the Video Codec PopUp */
4171     /* lets get the tag of the currently selected item first so we might reset it later */
4172     int selectedVidEncoderTag;
4173     selectedVidEncoderTag = [[fVidEncoderPopUp selectedItem] tag];
4174     
4175     /* Note: we now store the video encoder int values from common.c in the tags of each popup for easy retrieval later */
4176     [fVidEncoderPopUp removeAllItems];
4177     NSMenuItem *menuItem;
4178     /* These video encoders are available to all of our current muxers, so lets list them once here */
4179     menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"MPEG-4 (FFmpeg)" action: NULL keyEquivalent: @""];
4180     [menuItem setTag: HB_VCODEC_FFMPEG];
4181     
4182     switch( format )
4183     {
4184         case 0:
4185                         /*Get Default MP4 File Extension*/
4186                         if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultMpegName"] > 0)
4187                         {
4188                                 ext = "m4v";
4189                         }
4190                         else
4191                         {
4192                                 ext = "mp4";
4193                         }
4194             /* Add additional video encoders here */
4195             menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"H.264 (x264)" action: NULL keyEquivalent: @""];
4196             [menuItem setTag: HB_VCODEC_X264];
4197             /* We show the mp4 option checkboxes here since we are mp4 */
4198             [fCreateChapterMarkers setEnabled: YES];
4199                         [fDstMp4LargeFileCheck setHidden: NO];
4200                         [fDstMp4HttpOptFileCheck setHidden: NO];
4201             [fDstMp4iPodFileCheck setHidden: NO];
4202             break;
4203             
4204             case 1:
4205             ext = "mkv";
4206             /* Add additional video encoders here */
4207             menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"H.264 (x264)" action: NULL keyEquivalent: @""];
4208             [menuItem setTag: HB_VCODEC_X264];
4209             menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"VP3 (Theora)" action: NULL keyEquivalent: @""];
4210             [menuItem setTag: HB_VCODEC_THEORA];
4211             /* We enable the create chapters checkbox here */
4212                         [fCreateChapterMarkers setEnabled: YES];
4213                         break;
4214             
4215
4216     }
4217     /* tell fSubtitlesDelegate we have a new video container */
4218     
4219     [fSubtitlesDelegate containerChanged:[[fDstFormatPopUp selectedItem] tag]];
4220     [fSubtitlesTable reloadData];
4221     /* if we have a previously selected vid encoder tag, then try to select it */
4222     if (selectedVidEncoderTag)
4223     {
4224         [fVidEncoderPopUp selectItemWithTag: selectedVidEncoderTag];
4225     }
4226     else
4227     {
4228         [fVidEncoderPopUp selectItemAtIndex: 0];
4229     }
4230
4231     [self audioAddAudioTrackCodecs: fAudTrack1CodecPopUp];
4232     [self audioAddAudioTrackCodecs: fAudTrack2CodecPopUp];
4233     [self audioAddAudioTrackCodecs: fAudTrack3CodecPopUp];
4234     [self audioAddAudioTrackCodecs: fAudTrack4CodecPopUp];
4235
4236     if( format == 0 )
4237         [self autoSetM4vExtension: sender];
4238     else
4239         [fDstFile2Field setStringValue: [NSString stringWithFormat:@"%@.%s", [string stringByDeletingPathExtension], ext]];
4240
4241     if( SuccessfulScan )
4242     {
4243         /* Add/replace to the correct extension */
4244         [self audioTrackPopUpChanged: fAudLang1PopUp];
4245         [self audioTrackPopUpChanged: fAudLang2PopUp];
4246         [self audioTrackPopUpChanged: fAudLang3PopUp];
4247         [self audioTrackPopUpChanged: fAudLang4PopUp];
4248
4249         if( [fVidEncoderPopUp selectedItem] == nil )
4250         {
4251
4252             [fVidEncoderPopUp selectItemAtIndex:0];
4253             [self videoEncoderPopUpChanged:nil];
4254
4255             /* changing the format may mean that we can / can't offer mono or 6ch, */
4256             /* so call audioTrackPopUpChanged for both audio tracks to update the mixdown popups */
4257
4258             /* We call the method to properly enable/disable turbo 2 pass */
4259             [self twoPassCheckboxChanged: sender];
4260             /* We call method method to change UI to reflect whether a preset is used or not*/
4261         }
4262     }
4263         [self customSettingUsed: sender];
4264 }
4265
4266 - (IBAction) autoSetM4vExtension: (id) sender
4267 {
4268     if ( [fDstFormatPopUp indexOfSelectedItem] )
4269         return;
4270
4271     NSString * extension = @"mp4";
4272
4273     if( [[fAudTrack1CodecPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack2CodecPopUp selectedItem] tag] == HB_ACODEC_AC3 ||
4274                                                         [[fAudTrack3CodecPopUp selectedItem] tag] == HB_ACODEC_AC3 ||
4275                                                         [[fAudTrack4CodecPopUp selectedItem] tag] == HB_ACODEC_AC3 ||
4276                                                         [fCreateChapterMarkers state] == NSOnState ||
4277                                                         [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultMpegName"] > 0 )
4278     {
4279         extension = @"m4v";
4280     }
4281
4282     if( [extension isEqualTo: [[fDstFile2Field stringValue] pathExtension]] )
4283         return;
4284     else
4285         [fDstFile2Field setStringValue: [NSString stringWithFormat:@"%@.%@",
4286                                     [[fDstFile2Field stringValue] stringByDeletingPathExtension], extension]];
4287 }
4288
4289 /* Method to determine if we should change the UI
4290 To reflect whether or not a Preset is being used or if
4291 the user is using "Custom" settings by determining the sender*/
4292 - (IBAction) customSettingUsed: (id) sender
4293 {
4294         if ([sender stringValue])
4295         {
4296                 /* Deselect the currently selected Preset if there is one*/
4297                 [fPresetsOutlineView deselectRow:[fPresetsOutlineView selectedRow]];
4298                 /* Change UI to show "Custom" settings are being used */
4299                 [fPresetSelectedDisplay setStringValue: @"Custom"];
4300
4301                 curUserPresetChosenNum = nil;
4302         }
4303 [self calculateBitrate:nil];
4304 }
4305
4306
4307 #pragma mark -
4308 #pragma mark - Video
4309
4310 - (IBAction) videoEncoderPopUpChanged: (id) sender
4311 {
4312     hb_job_t * job = fTitle->job;
4313     int videoEncoder = [[fVidEncoderPopUp selectedItem] tag];
4314     
4315     [fAdvancedOptions setHidden:YES];
4316     /* If we are using x264 then show the x264 advanced panel*/
4317     if (videoEncoder == HB_VCODEC_X264)
4318     {
4319         [fAdvancedOptions setHidden:NO];
4320         [self autoSetM4vExtension: sender];
4321     }
4322     
4323     /* We need to set loose anamorphic as available depending on whether or not the ffmpeg encoder
4324     is being used as it borks up loose anamorphic .
4325     For convenience lets use the titleOfSelected index. Probably should revisit whether or not we want
4326     to use the index itself but this is easier */
4327     if (videoEncoder == HB_VCODEC_FFMPEG)
4328     {
4329         if (job->anamorphic.mode == 2)
4330         {
4331             job->anamorphic.mode = 0;
4332         }
4333         [fPictureController setAllowLooseAnamorphic:NO];
4334         /* We set the iPod atom checkbox to disabled and uncheck it as its only for x264 in the mp4
4335          container. Format is taken care of in formatPopUpChanged method by hiding and unchecking
4336          anything other than MP4.
4337          */ 
4338         [fDstMp4iPodFileCheck setEnabled: NO];
4339         [fDstMp4iPodFileCheck setState: NSOffState];
4340     }
4341     else
4342     {
4343         [fPictureController setAllowLooseAnamorphic:YES];
4344         [fDstMp4iPodFileCheck setEnabled: YES];
4345     }
4346     [self setupQualitySlider];
4347         [self calculatePictureSizing: sender];
4348         [self twoPassCheckboxChanged: sender];
4349 }
4350
4351
4352 - (IBAction) twoPassCheckboxChanged: (id) sender
4353 {
4354         /* check to see if x264 is chosen */
4355         if([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_X264)
4356     {
4357                 if( [fVidTwoPassCheck state] == NSOnState)
4358                 {
4359                         [fVidTurboPassCheck setHidden: NO];
4360                 }
4361                 else
4362                 {
4363                         [fVidTurboPassCheck setHidden: YES];
4364                         [fVidTurboPassCheck setState: NSOffState];
4365                 }
4366                 /* Make sure Two Pass is checked if Turbo is checked */
4367                 if( [fVidTurboPassCheck state] == NSOnState)
4368                 {
4369                         [fVidTwoPassCheck setState: NSOnState];
4370                 }
4371         }
4372         else
4373         {
4374                 [fVidTurboPassCheck setHidden: YES];
4375                 [fVidTurboPassCheck setState: NSOffState];
4376         }
4377         
4378         /* We call method method to change UI to reflect whether a preset is used or not*/
4379         [self customSettingUsed: sender];
4380 }
4381
4382 - (IBAction ) videoFrameRateChanged: (id) sender
4383 {
4384     /* We call method method to calculatePictureSizing to error check detelecine*/
4385     [self calculatePictureSizing: sender];
4386
4387     /* We call method method to change UI to reflect whether a preset is used or not*/
4388         [self customSettingUsed: sender];
4389 }
4390 - (IBAction) videoMatrixChanged: (id) sender;
4391 {
4392     bool target, bitrate, quality;
4393
4394     target = bitrate = quality = false;
4395     if( [fVidQualityMatrix isEnabled] )
4396     {
4397         switch( [fVidQualityMatrix selectedRow] )
4398         {
4399             case 0:
4400                 target = true;
4401                 break;
4402             case 1:
4403                 bitrate = true;
4404                 break;
4405             case 2:
4406                 quality = true;
4407                 break;
4408         }
4409     }
4410     [fVidTargetSizeField  setEnabled: target];
4411     [fVidBitrateField     setEnabled: bitrate];
4412     [fVidQualitySlider    setEnabled: quality];
4413     [fVidQualityRFField   setEnabled: quality];
4414     [fVidQualityRFLabel    setEnabled: quality];
4415     [fVidTwoPassCheck     setEnabled: !quality &&
4416         [fVidQualityMatrix isEnabled]];
4417     if( quality )
4418     {
4419         [fVidTwoPassCheck setState: NSOffState];
4420                 [fVidTurboPassCheck setHidden: YES];
4421                 [fVidTurboPassCheck setState: NSOffState];
4422     }
4423
4424     [self qualitySliderChanged: sender];
4425     [self calculateBitrate: sender];
4426         [self customSettingUsed: sender];
4427 }
4428
4429 /* Use this method to setup the quality slider for cq/rf values depending on
4430  * the video encoder selected.
4431  */
4432 - (void) setupQualitySlider
4433 {
4434     /* Get the current slider maxValue to check for a change in slider scale later
4435      * so that we can choose a new similar value on the new slider scale */
4436     float previousMaxValue = [fVidQualitySlider maxValue];
4437     float previousPercentOfSliderScale = [fVidQualitySlider floatValue] / ([fVidQualitySlider maxValue] - [fVidQualitySlider minValue] + 1);
4438     NSString * qpRFLabelString = @"QP:";
4439     /* x264 0-51 */
4440     if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_X264)
4441     {
4442         [fVidQualitySlider setMinValue:0.0];
4443         [fVidQualitySlider setMaxValue:51.0];
4444         /* As x264 allows for qp/rf values that are fractional, we get the value from the preferences */
4445         int fractionalGranularity = 1 / [[NSUserDefaults standardUserDefaults] floatForKey:@"x264CqSliderFractional"];
4446         [fVidQualitySlider setNumberOfTickMarks:(([fVidQualitySlider maxValue] - [fVidQualitySlider minValue]) * fractionalGranularity) + 1];
4447         if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultCrf"] > 0)
4448         {
4449             qpRFLabelString = @"RF:";
4450         }
4451     }
4452     /* ffmpeg  1-31 */
4453     if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_FFMPEG )
4454     {
4455         [fVidQualitySlider setMinValue:1.0];
4456         [fVidQualitySlider setMaxValue:31.0];
4457         [fVidQualitySlider setNumberOfTickMarks:31];
4458     }
4459     /* Theora 0-63 */
4460     if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_THEORA)
4461     {
4462         [fVidQualitySlider setMinValue:0.0];
4463         [fVidQualitySlider setMaxValue:63.0];
4464         [fVidQualitySlider setNumberOfTickMarks:64];
4465     }
4466     [fVidQualityRFLabel setStringValue:qpRFLabelString];
4467     
4468     /* check to see if we have changed slider scales */
4469     if (previousMaxValue != [fVidQualitySlider maxValue])
4470     {
4471         /* if so, convert the old setting to the new scale as close as possible based on percentages */
4472         float rf =  ([fVidQualitySlider maxValue] - [fVidQualitySlider minValue] + 1) * previousPercentOfSliderScale;
4473         [fVidQualitySlider setFloatValue:rf];
4474     }
4475     
4476     [self qualitySliderChanged:nil];
4477 }
4478
4479 - (IBAction) qualitySliderChanged: (id) sender
4480 {
4481     /* Our constant quality slider is in a range based
4482      * on each encoders qp/rf values. The range depends
4483      * on the encoder. Also, the range is inverse of quality
4484      * for all of the encoders *except* for theora
4485      * (ie. as the "quality" goes up, the cq or rf value
4486      * actually goes down). Since the IB sliders always set
4487      * their max value at the right end of the slider, we
4488      * will calculate the inverse, so as the slider floatValue
4489      * goes up, we will show the inverse in the rf field
4490      * so, the floatValue at the right for x264 would be 51
4491      * and our rf field needs to show 0 and vice versa.
4492      */
4493     
4494     float sliderRfInverse = ([fVidQualitySlider maxValue] - [fVidQualitySlider floatValue]) + [fVidQualitySlider minValue];
4495     /* If the encoder is theora, use the float, otherwise use the inverse float*/
4496     float sliderRfToPercent;
4497     if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_THEORA)
4498     {
4499         [fVidQualityRFField setStringValue: [NSString stringWithFormat: @"%.2f", [fVidQualitySlider floatValue]]];
4500         sliderRfToPercent = [fVidQualityRFField floatValue] / ([fVidQualitySlider maxValue] - [fVidQualitySlider minValue]);   
4501     }
4502     else
4503     {
4504         [fVidQualityRFField setStringValue: [NSString stringWithFormat: @"%.2f", sliderRfInverse]];
4505         sliderRfToPercent = ( ([fVidQualitySlider maxValue] - [fVidQualitySlider minValue])  - ([fVidQualityRFField floatValue] - [fVidQualitySlider minValue])) / ([fVidQualitySlider maxValue] - [fVidQualitySlider minValue]);
4506     }
4507     [fVidConstantCell setTitle: [NSString stringWithFormat:
4508                                  NSLocalizedString( @"Constant quality: %.2f %%", @"" ), 100 * sliderRfToPercent]];
4509     
4510     [self customSettingUsed: sender];
4511 }
4512
4513 - (void) controlTextDidChange: (NSNotification *) notification
4514 {
4515     [self calculateBitrate:nil];
4516 }
4517
4518 - (IBAction) calculateBitrate: (id) sender
4519 {
4520     if( !fHandle || [fVidQualityMatrix selectedRow] != 0 || !SuccessfulScan )
4521     {
4522         return;
4523     }
4524
4525     hb_list_t  * list  = hb_get_titles( fHandle );
4526     hb_title_t * title = (hb_title_t *) hb_list_item( list,
4527             [fSrcTitlePopUp indexOfSelectedItem] );
4528     hb_job_t * job = title->job;
4529     hb_audio_config_t * audio;
4530     /* For  hb_calc_bitrate in addition to the Target Size in MB out of the
4531      * Target Size Field, we also need the job info for the Muxer, the Chapters
4532      * as well as all of the audio track info.
4533      * This used to be accomplished by simply calling prepareJob here, however
4534      * since the resilient queue sets the queue array values instead of the job
4535      * values directly, we duplicate the old prepareJob code here for the variables
4536      * needed
4537      */
4538     job->chapter_start = [fSrcChapterStartPopUp indexOfSelectedItem] + 1;
4539     job->chapter_end = [fSrcChapterEndPopUp indexOfSelectedItem] + 1; 
4540     job->mux = [[fDstFormatPopUp selectedItem] tag];
4541     
4542     /* Audio goes here */
4543     int audiotrack_count = hb_list_count(job->list_audio);
4544     for( int i = 0; i < audiotrack_count;i++)
4545     {
4546         hb_audio_t * temp_audio = (hb_audio_t*) hb_list_item( job->list_audio, 0 );
4547         hb_list_rem(job->list_audio, temp_audio);
4548     }
4549     /* Now we need our audio info here for each track if applicable */
4550     if ([fAudLang1PopUp indexOfSelectedItem] > 0)
4551     {
4552         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
4553         hb_audio_config_init(audio);
4554         audio->in.track = [fAudLang1PopUp indexOfSelectedItem] - 1;
4555         /* We go ahead and assign values to our audio->out.<properties> */
4556         audio->out.track = [fAudLang1PopUp indexOfSelectedItem] - 1;
4557         audio->out.codec = [[fAudTrack1CodecPopUp selectedItem] tag];
4558         audio->out.mixdown = [[fAudTrack1MixPopUp selectedItem] tag];
4559         audio->out.bitrate = [[fAudTrack1BitratePopUp selectedItem] tag];
4560         audio->out.samplerate = [[fAudTrack1RatePopUp selectedItem] tag];
4561         audio->out.dynamic_range_compression = [fAudTrack1DrcField floatValue];
4562         
4563         hb_audio_add( job, audio );
4564         free(audio);
4565     }  
4566     if ([fAudLang2PopUp indexOfSelectedItem] > 0)
4567     {
4568         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
4569         hb_audio_config_init(audio);
4570         audio->in.track = [fAudLang2PopUp indexOfSelectedItem] - 1;
4571         /* We go ahead and assign values to our audio->out.<properties> */
4572         audio->out.track = [fAudLang2PopUp indexOfSelectedItem] - 1;
4573         audio->out.codec = [[fAudTrack2CodecPopUp selectedItem] tag];
4574         audio->out.mixdown = [[fAudTrack2MixPopUp selectedItem] tag];
4575         audio->out.bitrate = [[fAudTrack2BitratePopUp selectedItem] tag];
4576         audio->out.samplerate = [[fAudTrack2RatePopUp selectedItem] tag];
4577         audio->out.dynamic_range_compression = [fAudTrack2DrcField floatValue];
4578         
4579         hb_audio_add( job, audio );
4580         free(audio);
4581         
4582     }
4583     
4584     if ([fAudLang3PopUp indexOfSelectedItem] > 0)
4585     {
4586         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
4587         hb_audio_config_init(audio);
4588         audio->in.track = [fAudLang3PopUp indexOfSelectedItem] - 1;
4589         /* We go ahead and assign values to our audio->out.<properties> */
4590         audio->out.track = [fAudLang3PopUp indexOfSelectedItem] - 1;
4591         audio->out.codec = [[fAudTrack3CodecPopUp selectedItem] tag];
4592         audio->out.mixdown = [[fAudTrack3MixPopUp selectedItem] tag];
4593         audio->out.bitrate = [[fAudTrack3BitratePopUp selectedItem] tag];
4594         audio->out.samplerate = [[fAudTrack3RatePopUp selectedItem] tag];
4595         audio->out.dynamic_range_compression = [fAudTrack3DrcField floatValue];
4596         
4597         hb_audio_add( job, audio );
4598         free(audio);
4599         
4600     }
4601
4602     if ([fAudLang4PopUp indexOfSelectedItem] > 0)
4603     {
4604         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
4605         hb_audio_config_init(audio);
4606         audio->in.track = [fAudLang4PopUp indexOfSelectedItem] - 1;
4607         /* We go ahead and assign values to our audio->out.<properties> */
4608         audio->out.track = [fAudLang4PopUp indexOfSelectedItem] - 1;
4609         audio->out.codec = [[fAudTrack4CodecPopUp selectedItem] tag];
4610         audio->out.mixdown = [[fAudTrack4MixPopUp selectedItem] tag];
4611         audio->out.bitrate = [[fAudTrack4BitratePopUp selectedItem] tag];
4612         audio->out.samplerate = [[fAudTrack4RatePopUp selectedItem] tag];
4613         audio->out.dynamic_range_compression = [fAudTrack4DrcField floatValue];
4614         
4615         hb_audio_add( job, audio );
4616         free(audio);
4617         
4618     }
4619        
4620 [fVidBitrateField setIntValue: hb_calc_bitrate( job, [fVidTargetSizeField intValue] )];
4621 }
4622
4623 #pragma mark -
4624 #pragma mark - Picture
4625
4626 /* lets set the picture size back to the max from right after title scan
4627    Lets use an IBAction here as down the road we could always use a checkbox
4628    in the gui to easily take the user back to max. Remember, the compiler
4629    resolves IBActions down to -(void) during compile anyway */
4630 - (IBAction) revertPictureSizeToMax: (id) sender
4631 {
4632         hb_job_t * job = fTitle->job;
4633         /* Here we apply the title source and height */
4634     job->width = fTitle->width;
4635     job->height = fTitle->height;
4636     
4637     [self calculatePictureSizing: sender];
4638     /* We call method to change UI to reflect whether a preset is used or not*/    
4639     [self customSettingUsed: sender];
4640 }
4641
4642 /**
4643  * Registers changes made in the Picture Settings Window.
4644  */
4645
4646 - (void)pictureSettingsDidChange 
4647 {
4648         [self calculatePictureSizing:nil];
4649 }
4650
4651 /* Get and Display Current Pic Settings in main window */
4652 - (IBAction) calculatePictureSizing: (id) sender
4653 {
4654         if (fTitle->job->anamorphic.mode > 0)
4655         {
4656         fTitle->job->keep_ratio = 0;
4657         }
4658     
4659     [fPictureSizeField setStringValue: [NSString stringWithFormat:@"Picture Size: %@", [fPictureController getPictureSizeInfoString]]];
4660     
4661     NSString *picCropping;
4662     /* Set the display field for crop as per boolean */
4663         if (![fPictureController autoCrop])
4664         {
4665         picCropping =  @"Custom";
4666         }
4667         else
4668         {
4669                 picCropping =  @"Auto";
4670         }
4671     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]]];
4672     
4673     [fPictureCroppingField setStringValue: [NSString stringWithFormat:@"Picture Cropping: %@",picCropping]];
4674     
4675     NSString *videoFilters;
4676     videoFilters = @"";
4677     /* Detelecine */
4678     if ([fPictureController detelecine] == 1) 
4679     {
4680         videoFilters = [videoFilters stringByAppendingString:@" - Detelecine (Default)"];
4681     }
4682     else if ([fPictureController detelecine] == 2) 
4683     {
4684         videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Detelecine (%@)",[fPictureController detelecineCustomString]]];
4685     }
4686     
4687     
4688     if ([fPictureController useDecomb] == 1)
4689     {
4690         /* Decomb */
4691         if ([fPictureController decomb] == 1)
4692         {
4693             videoFilters = [videoFilters stringByAppendingString:@" - Decomb (Default)"];
4694         }
4695         else if ([fPictureController decomb] == 2)
4696         {
4697             videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Decomb (%@)",[fPictureController decombCustomString]]];
4698         }
4699     }
4700     else
4701     {
4702         /* Deinterlace */
4703         if ([fPictureController deinterlace] > 0)
4704         {
4705             fTitle->job->deinterlace  = 1;
4706         }
4707         else
4708         {
4709             fTitle->job->deinterlace  = 0;
4710         }
4711         
4712         if ([fPictureController deinterlace] == 1)
4713         {
4714             videoFilters = [videoFilters stringByAppendingString:@" - Deinterlace (Fast)"];
4715         }
4716         else if ([fPictureController deinterlace] == 2)
4717         {
4718             videoFilters = [videoFilters stringByAppendingString:@" - Deinterlace (Slow)"];
4719         }
4720         else if ([fPictureController deinterlace] == 3)
4721         {
4722             videoFilters = [videoFilters stringByAppendingString:@" - Deinterlace (Slower)"];
4723         }
4724         else if ([fPictureController deinterlace] == 4)
4725         {
4726             videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Deinterlace (%@)",[fPictureController deinterlaceCustomString]]];
4727         }
4728         }
4729     
4730     
4731     /* Denoise */
4732         if ([fPictureController denoise] == 1)
4733         {
4734                 videoFilters = [videoFilters stringByAppendingString:@" - Denoise (Weak)"];
4735     }
4736         else if ([fPictureController denoise] == 2)
4737         {
4738                 videoFilters = [videoFilters stringByAppendingString:@" - Denoise (Medium)"];
4739     }
4740         else if ([fPictureController denoise] == 3)
4741         {
4742                 videoFilters = [videoFilters stringByAppendingString:@" - Denoise (Strong)"];
4743         }
4744     else if ([fPictureController denoise] == 4)
4745         {
4746                 videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Denoise (%@)",[fPictureController denoiseCustomString]]];
4747         }
4748     
4749     /* Deblock */
4750     if ([fPictureController deblock] > 0) 
4751     {
4752         videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Deblock (%d)",[fPictureController deblock]]];
4753     }
4754         
4755     /* Grayscale */
4756     if ([fPictureController grayscale]) 
4757     {
4758         videoFilters = [videoFilters stringByAppendingString:@" - Grayscale"];
4759     }
4760     [fVideoFiltersField setStringValue: [NSString stringWithFormat:@"Video Filters: %@", videoFilters]];
4761     
4762     //[fPictureController reloadStillPreview]; 
4763 }
4764
4765
4766 #pragma mark -
4767 #pragma mark - Audio and Subtitles
4768 - (IBAction) audioCodecsPopUpChanged: (id) sender
4769 {
4770     
4771     NSPopUpButton * audiotrackPopUp;
4772     NSPopUpButton * sampleratePopUp;
4773     NSPopUpButton * bitratePopUp;
4774     NSPopUpButton * audiocodecPopUp;
4775     if (sender == fAudTrack1CodecPopUp)
4776     {
4777         audiotrackPopUp = fAudLang1PopUp;
4778         audiocodecPopUp = fAudTrack1CodecPopUp;
4779         sampleratePopUp = fAudTrack1RatePopUp;
4780         bitratePopUp = fAudTrack1BitratePopUp;
4781     }
4782     else if (sender == fAudTrack2CodecPopUp)
4783     {
4784         audiotrackPopUp = fAudLang2PopUp;
4785         audiocodecPopUp = fAudTrack2CodecPopUp;
4786         sampleratePopUp = fAudTrack2RatePopUp;
4787         bitratePopUp = fAudTrack2BitratePopUp;
4788     }
4789     else if (sender == fAudTrack3CodecPopUp)
4790     {
4791         audiotrackPopUp = fAudLang3PopUp;
4792         audiocodecPopUp = fAudTrack3CodecPopUp;
4793         sampleratePopUp = fAudTrack3RatePopUp;
4794         bitratePopUp = fAudTrack3BitratePopUp;
4795     }
4796     else
4797     {
4798         audiotrackPopUp = fAudLang4PopUp;
4799         audiocodecPopUp = fAudTrack4CodecPopUp;
4800         sampleratePopUp = fAudTrack4RatePopUp;
4801         bitratePopUp = fAudTrack4BitratePopUp;
4802     }
4803         
4804     /* changing the codecs on offer may mean that we can / can't offer mono or 6ch, */
4805         /* so call audioTrackPopUpChanged for both audio tracks to update the mixdown popups */
4806     [self audioTrackPopUpChanged: audiotrackPopUp];
4807     
4808 }
4809
4810 - (IBAction) setEnabledStateOfAudioMixdownControls: (id) sender
4811 {
4812     /* We will be setting the enabled/disabled state of each tracks audio controls based on
4813      * the settings of the source audio for that track. We leave the samplerate and bitrate
4814      * to audiotrackMixdownChanged
4815      */
4816     
4817     /* We will first verify that a lower track number has been selected before enabling each track
4818      * for example, make sure a track is selected for track 1 before enabling track 2, etc.
4819      */
4820     if ([fAudLang1PopUp indexOfSelectedItem] == 0)
4821     {
4822         [fAudLang2PopUp setEnabled: NO];
4823         [fAudLang2PopUp selectItemAtIndex: 0];
4824     }
4825     else
4826     {
4827         [fAudLang2PopUp setEnabled: YES];
4828     }
4829     
4830     if ([fAudLang2PopUp indexOfSelectedItem] == 0)
4831     {
4832         [fAudLang3PopUp setEnabled: NO];
4833         [fAudLang3PopUp selectItemAtIndex: 0];
4834     }
4835     else
4836     {
4837         [fAudLang3PopUp setEnabled: YES];
4838     }
4839     if ([fAudLang3PopUp indexOfSelectedItem] == 0)
4840     {
4841         [fAudLang4PopUp setEnabled: NO];
4842         [fAudLang4PopUp selectItemAtIndex: 0];
4843     }
4844     else
4845     {
4846         [fAudLang4PopUp setEnabled: YES];
4847     }
4848     /* enable/disable the mixdown text and popupbutton for audio track 1 */
4849     [fAudTrack1CodecPopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
4850     [fAudTrack1MixPopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
4851     [fAudTrack1RatePopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
4852     [fAudTrack1BitratePopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
4853     [fAudTrack1DrcSlider setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
4854     [fAudTrack1DrcField setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
4855     if ([fAudLang1PopUp indexOfSelectedItem] == 0)
4856     {
4857         [fAudTrack1CodecPopUp removeAllItems];
4858         [fAudTrack1MixPopUp removeAllItems];
4859         [fAudTrack1RatePopUp removeAllItems];
4860         [fAudTrack1BitratePopUp removeAllItems];
4861         [fAudTrack1DrcSlider setFloatValue: 1.00];
4862         [self audioDRCSliderChanged: fAudTrack1DrcSlider];
4863     }
4864     else if ([[fAudTrack1MixPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack1MixPopUp selectedItem] tag] == HB_ACODEC_DCA)
4865     {
4866         [fAudTrack1RatePopUp setEnabled: NO];
4867         [fAudTrack1BitratePopUp setEnabled: NO];
4868         [fAudTrack1DrcSlider setEnabled: NO];
4869         [fAudTrack1DrcField setEnabled: NO];
4870     }
4871     
4872     /* enable/disable the mixdown text and popupbutton for audio track 2 */
4873     [fAudTrack2CodecPopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
4874     [fAudTrack2MixPopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
4875     [fAudTrack2RatePopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
4876     [fAudTrack2BitratePopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
4877     [fAudTrack2DrcSlider setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
4878     [fAudTrack2DrcField setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
4879     if ([fAudLang2PopUp indexOfSelectedItem] == 0)
4880     {
4881         [fAudTrack2CodecPopUp removeAllItems];
4882         [fAudTrack2MixPopUp removeAllItems];
4883         [fAudTrack2RatePopUp removeAllItems];
4884         [fAudTrack2BitratePopUp removeAllItems];
4885         [fAudTrack2DrcSlider setFloatValue: 1.00];
4886         [self audioDRCSliderChanged: fAudTrack2DrcSlider];
4887     }
4888     else if ([[fAudTrack2MixPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack2MixPopUp selectedItem] tag] == HB_ACODEC_DCA)
4889     {
4890         [fAudTrack2RatePopUp setEnabled: NO];
4891         [fAudTrack2BitratePopUp setEnabled: NO];
4892         [fAudTrack2DrcSlider setEnabled: NO];
4893         [fAudTrack2DrcField setEnabled: NO];
4894     }
4895     
4896     /* enable/disable the mixdown text and popupbutton for audio track 3 */
4897     [fAudTrack3CodecPopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
4898     [fAudTrack3MixPopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
4899     [fAudTrack3RatePopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
4900     [fAudTrack3BitratePopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
4901     [fAudTrack3DrcSlider setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
4902     [fAudTrack3DrcField setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
4903     if ([fAudLang3PopUp indexOfSelectedItem] == 0)
4904     {
4905         [fAudTrack3CodecPopUp removeAllItems];
4906         [fAudTrack3MixPopUp removeAllItems];
4907         [fAudTrack3RatePopUp removeAllItems];
4908         [fAudTrack3BitratePopUp removeAllItems];
4909         [fAudTrack3DrcSlider setFloatValue: 1.00];
4910         [self audioDRCSliderChanged: fAudTrack3DrcSlider];
4911     }
4912     else if ([[fAudTrack3MixPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack3MixPopUp selectedItem] tag] == HB_ACODEC_DCA)
4913     {
4914         [fAudTrack3RatePopUp setEnabled: NO];
4915         [fAudTrack3BitratePopUp setEnabled: NO];
4916         [fAudTrack3DrcSlider setEnabled: NO];
4917         [fAudTrack3DrcField setEnabled: NO];
4918     }
4919     
4920     /* enable/disable the mixdown text and popupbutton for audio track 4 */
4921     [fAudTrack4CodecPopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
4922     [fAudTrack4MixPopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
4923     [fAudTrack4RatePopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
4924     [fAudTrack4BitratePopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
4925     [fAudTrack4DrcSlider setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
4926     [fAudTrack4DrcField setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
4927     if ([fAudLang4PopUp indexOfSelectedItem] == 0)
4928     {
4929         [fAudTrack4CodecPopUp removeAllItems];
4930         [fAudTrack4MixPopUp removeAllItems];
4931         [fAudTrack4RatePopUp removeAllItems];
4932         [fAudTrack4BitratePopUp removeAllItems];
4933         [fAudTrack4DrcSlider setFloatValue: 1.00];
4934         [self audioDRCSliderChanged: fAudTrack4DrcSlider];
4935     }
4936     else if ([[fAudTrack4MixPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack4MixPopUp selectedItem] tag] == HB_ACODEC_DCA)
4937     {
4938         [fAudTrack4RatePopUp setEnabled: NO];
4939         [fAudTrack4BitratePopUp setEnabled: NO];
4940         [fAudTrack4DrcSlider setEnabled: NO];
4941         [fAudTrack4DrcField setEnabled: NO];
4942     }
4943     
4944 }
4945
4946 - (IBAction) addAllAudioTracksToPopUp: (id) sender
4947 {
4948
4949     hb_list_t  * list  = hb_get_titles( fHandle );
4950     hb_title_t * title = (hb_title_t*)
4951         hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
4952
4953         hb_audio_config_t * audio;
4954
4955     [sender removeAllItems];
4956     [sender addItemWithTitle: NSLocalizedString( @"None", @"" )];
4957     for( int i = 0; i < hb_list_count( title->list_audio ); i++ )
4958     {
4959         audio = (hb_audio_config_t *) hb_list_audio_config_item( title->list_audio, i );
4960         [[sender menu] addItemWithTitle:
4961             [NSString stringWithCString: audio->lang.description]
4962             action: NULL keyEquivalent: @""];
4963     }
4964     [sender selectItemAtIndex: 0];
4965
4966 }
4967
4968 - (IBAction) selectAudioTrackInPopUp: (id) sender searchPrefixString: (NSString *) searchPrefixString selectIndexIfNotFound: (int) selectIndexIfNotFound
4969 {
4970
4971     /* this method can be used to find a language, or a language-and-source-format combination, by passing in the appropriate string */
4972     /* e.g. to find the first French track, pass in an NSString * of "Francais" */
4973     /* e.g. to find the first English 5.1 AC3 track, pass in an NSString * of "English (AC3) (5.1 ch)" */
4974     /* if no matching track is found, then selectIndexIfNotFound is used to choose which track to select instead */
4975
4976         if (searchPrefixString)
4977         {
4978
4979         for( int i = 0; i < [sender numberOfItems]; i++ )
4980         {
4981             /* Try to find the desired search string */
4982             if ([[[sender itemAtIndex: i] title] hasPrefix:searchPrefixString])
4983             {
4984                 [sender selectItemAtIndex: i];
4985                 return;
4986             }
4987         }
4988         /* couldn't find the string, so select the requested "search string not found" item */
4989         /* index of 0 means select the "none" item */
4990         /* index of 1 means select the first audio track */
4991         [sender selectItemAtIndex: selectIndexIfNotFound];
4992         }
4993     else
4994     {
4995         /* if no search string is provided, then select the selectIndexIfNotFound item */
4996         [sender selectItemAtIndex: selectIndexIfNotFound];
4997     }
4998
4999 }
5000 - (IBAction) audioAddAudioTrackCodecs: (id)sender
5001 {
5002     int format = [fDstFormatPopUp indexOfSelectedItem];
5003     
5004     /* setup pointers to the appropriate popups for the correct track */
5005     NSPopUpButton * audiocodecPopUp;
5006     NSPopUpButton * audiotrackPopUp;
5007     if (sender == fAudTrack1CodecPopUp)
5008     {
5009         audiotrackPopUp = fAudLang1PopUp;
5010         audiocodecPopUp = fAudTrack1CodecPopUp;
5011     }
5012     else if (sender == fAudTrack2CodecPopUp)
5013     {
5014         audiotrackPopUp = fAudLang2PopUp;
5015         audiocodecPopUp = fAudTrack2CodecPopUp;
5016     }
5017     else if (sender == fAudTrack3CodecPopUp)
5018     {
5019         audiotrackPopUp = fAudLang3PopUp;
5020         audiocodecPopUp = fAudTrack3CodecPopUp;
5021     }
5022     else
5023     {
5024         audiotrackPopUp = fAudLang4PopUp;
5025         audiocodecPopUp = fAudTrack4CodecPopUp;
5026     }
5027     
5028     [audiocodecPopUp removeAllItems];
5029     /* Make sure "None" isnt selected in the source track */
5030     if ([audiotrackPopUp indexOfSelectedItem] > 0)
5031     {
5032         [audiocodecPopUp setEnabled:YES];
5033         NSMenuItem *menuItem;
5034         /* We setup our appropriate popups for codecs and put the int value in the popup tag for easy retrieval */
5035         switch( format )
5036         {
5037             case 0:
5038                 /* MP4 */
5039                 // FAAC
5040                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (faac)" action: NULL keyEquivalent: @""];
5041                 [menuItem setTag: HB_ACODEC_FAAC];
5042
5043                 // CA_AAC
5044                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (CoreAudio)" action: NULL keyEquivalent: @""];
5045                 [menuItem setTag: HB_ACODEC_CA_AAC];
5046
5047                 // AC3 Passthru
5048                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AC3 Passthru" action: NULL keyEquivalent: @""];
5049                 [menuItem setTag: HB_ACODEC_AC3];
5050                 break;
5051                 
5052             case 1:
5053                 /* MKV */
5054                 // FAAC
5055                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (faac)" action: NULL keyEquivalent: @""];
5056                 [menuItem setTag: HB_ACODEC_FAAC];
5057                 // CA_AAC
5058                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (CoreAudio)" action: NULL keyEquivalent: @""];
5059                 [menuItem setTag: HB_ACODEC_CA_AAC];
5060                 // AC3 Passthru
5061                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AC3 Passthru" action: NULL keyEquivalent: @""];
5062                 [menuItem setTag: HB_ACODEC_AC3];
5063                 // DTS Passthru
5064                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"DTS Passthru" action: NULL keyEquivalent: @""];
5065                 [menuItem setTag: HB_ACODEC_DCA];
5066                 // MP3
5067                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"MP3 (lame)" action: NULL keyEquivalent: @""];
5068                 [menuItem setTag: HB_ACODEC_LAME];
5069                 // Vorbis
5070                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"Vorbis (vorbis)" action: NULL keyEquivalent: @""];
5071                 [menuItem setTag: HB_ACODEC_VORBIS];
5072                 break;
5073                 
5074             case 2: 
5075                 /* AVI */
5076                 // MP3
5077                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"MP3 (lame)" action: NULL keyEquivalent: @""];
5078                 [menuItem setTag: HB_ACODEC_LAME];
5079                 // AC3 Passthru
5080                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AC3 Passthru" action: NULL keyEquivalent: @""];
5081                 [menuItem setTag: HB_ACODEC_AC3];
5082                 break;
5083                 
5084             case 3:
5085                 /* OGM */
5086                 // Vorbis
5087                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"Vorbis (vorbis)" action: NULL keyEquivalent: @""];
5088                 [menuItem setTag: HB_ACODEC_VORBIS];
5089                 // MP3
5090                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"MP3 (lame)" action: NULL keyEquivalent: @""];
5091                 [menuItem setTag: HB_ACODEC_LAME];
5092                 break;
5093         }
5094         [audiocodecPopUp selectItemAtIndex:0];
5095     }
5096     else
5097     {
5098         [audiocodecPopUp setEnabled:NO];
5099     }
5100 }
5101
5102 - (IBAction) audioTrackPopUpChanged: (id) sender
5103 {
5104     /* utility function to call audioTrackPopUpChanged without passing in a mixdown-to-use */
5105     [self audioTrackPopUpChanged: sender mixdownToUse: 0];
5106 }
5107
5108 - (IBAction) audioTrackPopUpChanged: (id) sender mixdownToUse: (int) mixdownToUse
5109 {
5110     
5111     /* make sure we have a selected title before continuing */
5112     if (fTitle == NULL) return;
5113     /* if the sender is the lanaguage popup and there is nothing in the codec popup, lets call
5114     * audioAddAudioTrackCodecs on the codec popup to populate it properly before moving on
5115     */
5116     if (sender == fAudLang1PopUp && [[fAudTrack1CodecPopUp menu] numberOfItems] == 0)
5117     {
5118         [self audioAddAudioTrackCodecs: fAudTrack1CodecPopUp];
5119     }
5120     if (sender == fAudLang2PopUp && [[fAudTrack2CodecPopUp menu] numberOfItems] == 0)
5121     {
5122         [self audioAddAudioTrackCodecs: fAudTrack2CodecPopUp];
5123     }
5124     if (sender == fAudLang3PopUp && [[fAudTrack3CodecPopUp menu] numberOfItems] == 0)
5125     {
5126         [self audioAddAudioTrackCodecs: fAudTrack3CodecPopUp];
5127     }
5128     if (sender == fAudLang4PopUp && [[fAudTrack4CodecPopUp menu] numberOfItems] == 0)
5129     {
5130         [self audioAddAudioTrackCodecs: fAudTrack4CodecPopUp];
5131     }
5132     
5133     /* Now lets make the sender the appropriate Audio Track popup from this point on */
5134     if (sender == fAudTrack1CodecPopUp || sender == fAudTrack1MixPopUp)
5135     {
5136         sender = fAudLang1PopUp;
5137     }
5138     if (sender == fAudTrack2CodecPopUp || sender == fAudTrack2MixPopUp)
5139     {
5140         sender = fAudLang2PopUp;
5141     }
5142     if (sender == fAudTrack3CodecPopUp || sender == fAudTrack3MixPopUp)
5143     {
5144         sender = fAudLang3PopUp;
5145     }
5146     if (sender == fAudTrack4CodecPopUp || sender == fAudTrack4MixPopUp)
5147     {
5148         sender = fAudLang4PopUp;
5149     }
5150     
5151     /* pointer to this track's mixdown, codec, sample rate and bitrate NSPopUpButton's */
5152     NSPopUpButton * mixdownPopUp;
5153     NSPopUpButton * audiocodecPopUp;
5154     NSPopUpButton * sampleratePopUp;
5155     NSPopUpButton * bitratePopUp;
5156     if (sender == fAudLang1PopUp)
5157     {
5158         mixdownPopUp = fAudTrack1MixPopUp;
5159         audiocodecPopUp = fAudTrack1CodecPopUp;
5160         sampleratePopUp = fAudTrack1RatePopUp;
5161         bitratePopUp = fAudTrack1BitratePopUp;
5162     }
5163     else if (sender == fAudLang2PopUp)
5164     {
5165         mixdownPopUp = fAudTrack2MixPopUp;
5166         audiocodecPopUp = fAudTrack2CodecPopUp;
5167         sampleratePopUp = fAudTrack2RatePopUp;
5168         bitratePopUp = fAudTrack2BitratePopUp;
5169     }
5170     else if (sender == fAudLang3PopUp)
5171     {
5172         mixdownPopUp = fAudTrack3MixPopUp;
5173         audiocodecPopUp = fAudTrack3CodecPopUp;
5174         sampleratePopUp = fAudTrack3RatePopUp;
5175         bitratePopUp = fAudTrack3BitratePopUp;
5176     }
5177     else
5178     {
5179         mixdownPopUp = fAudTrack4MixPopUp;
5180         audiocodecPopUp = fAudTrack4CodecPopUp;
5181         sampleratePopUp = fAudTrack4RatePopUp;
5182         bitratePopUp = fAudTrack4BitratePopUp;
5183     }
5184
5185     /* get the index of the selected audio Track*/
5186     int thisAudioIndex = [sender indexOfSelectedItem] - 1;
5187
5188     /* pointer for the hb_audio_s struct we will use later on */
5189     hb_audio_config_t * audio;
5190
5191     int acodec;
5192     /* check if the audio mixdown controls need their enabled state changing */
5193     [self setEnabledStateOfAudioMixdownControls:nil];
5194
5195     if (thisAudioIndex != -1)
5196     {
5197
5198         /* get the audio */
5199         audio = (hb_audio_config_t *) hb_list_audio_config_item( fTitle->list_audio, thisAudioIndex );// Should "fTitle" be title and be setup ?
5200
5201         /* actually manipulate the proper mixdowns here */
5202         /* delete the previous audio mixdown options */
5203         [mixdownPopUp removeAllItems];
5204
5205         acodec = [[audiocodecPopUp selectedItem] tag];
5206
5207         if (audio != NULL)
5208         {
5209
5210             /* find out if our selected output audio codec supports mono and / or 6ch */
5211             /* we also check for an input codec of AC3 or DCA,
5212              as they are the only libraries able to do the mixdown to mono / conversion to 6-ch */
5213             /* audioCodecsSupportMono and audioCodecsSupport6Ch are the same for now,
5214              but this may change in the future, so they are separated for flexibility */
5215             int audioCodecsSupportMono =
5216                     (audio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)) &&
5217                     (acodec != HB_ACODEC_LAME);
5218             int audioCodecsSupport6Ch =
5219                     (audio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)) &&
5220                     (acodec != HB_ACODEC_LAME);
5221             
5222             /* check for AC-3 passthru */
5223             if (audio->in.codec == HB_ACODEC_AC3 && acodec == HB_ACODEC_AC3)
5224             {
5225                 
5226             NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5227                  [NSString stringWithCString: "AC3 Passthru"]
5228                                                action: NULL keyEquivalent: @""];
5229              [menuItem setTag: HB_ACODEC_AC3];   
5230             }
5231             else if (audio->in.codec == HB_ACODEC_DCA && acodec == HB_ACODEC_DCA)
5232             {
5233             NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5234                  [NSString stringWithCString: "DTS Passthru"]
5235                                                action: NULL keyEquivalent: @""];
5236              [menuItem setTag: HB_ACODEC_DCA]; 
5237             }
5238             else
5239             {
5240                 
5241                 /* add the appropriate audio mixdown menuitems to the popupbutton */
5242                 /* in each case, we set the new menuitem's tag to be the amixdown value for that mixdown,
5243                  so that we can reference the mixdown later */
5244                 
5245                 /* keep a track of the min and max mixdowns we used, so we can select the best match later */
5246                 int minMixdownUsed = 0;
5247                 int maxMixdownUsed = 0;
5248                 
5249                 /* get the input channel layout without any lfe channels */
5250                 int layout = audio->in.channel_layout & HB_INPUT_CH_LAYOUT_DISCRETE_NO_LFE_MASK;
5251                 
5252                 /* do we want to add a mono option? */
5253                 if (audioCodecsSupportMono == 1)
5254                 {
5255                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5256                                             [NSString stringWithCString: hb_audio_mixdowns[0].human_readable_name]
5257                                                                           action: NULL keyEquivalent: @""];
5258                     [menuItem setTag: hb_audio_mixdowns[0].amixdown];
5259                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[0].amixdown;
5260                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[0].amixdown);
5261                 }
5262                 
5263                 /* do we want to add a stereo option? */
5264                 /* offer stereo if we have a mono source and non-mono-supporting codecs, as otherwise we won't have a mixdown at all */
5265                 /* also offer stereo if we have a stereo-or-better source */
5266                 if ((layout == HB_INPUT_CH_LAYOUT_MONO && audioCodecsSupportMono == 0) || layout >= HB_INPUT_CH_LAYOUT_STEREO)
5267                 {
5268                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5269                                             [NSString stringWithCString: hb_audio_mixdowns[1].human_readable_name]
5270                                                                           action: NULL keyEquivalent: @""];
5271                     [menuItem setTag: hb_audio_mixdowns[1].amixdown];
5272                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[1].amixdown;
5273                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[1].amixdown);
5274                 }
5275                 
5276                 /* do we want to add a dolby surround (DPL1) option? */
5277                 if (layout == HB_INPUT_CH_LAYOUT_3F1R || layout == HB_INPUT_CH_LAYOUT_3F2R || layout == HB_INPUT_CH_LAYOUT_DOLBY)
5278                 {
5279                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5280                                             [NSString stringWithCString: hb_audio_mixdowns[2].human_readable_name]
5281                                                                           action: NULL keyEquivalent: @""];
5282                     [menuItem setTag: hb_audio_mixdowns[2].amixdown];
5283                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[2].amixdown;
5284                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[2].amixdown);
5285                 }
5286                 
5287                 /* do we want to add a dolby pro logic 2 (DPL2) option? */
5288                 if (layout == HB_INPUT_CH_LAYOUT_3F2R)
5289                 {
5290                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5291                                             [NSString stringWithCString: hb_audio_mixdowns[3].human_readable_name]
5292                                                                           action: NULL keyEquivalent: @""];
5293                     [menuItem setTag: hb_audio_mixdowns[3].amixdown];
5294                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[3].amixdown;
5295                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[3].amixdown);
5296                 }
5297                 
5298                 /* do we want to add a 6-channel discrete option? */
5299                 if (audioCodecsSupport6Ch == 1 && layout == HB_INPUT_CH_LAYOUT_3F2R && (audio->in.channel_layout & HB_INPUT_CH_LAYOUT_HAS_LFE))
5300                 {
5301                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5302                                             [NSString stringWithCString: hb_audio_mixdowns[4].human_readable_name]
5303                                                                           action: NULL keyEquivalent: @""];
5304                     [menuItem setTag: hb_audio_mixdowns[4].amixdown];
5305                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[4].amixdown;
5306                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[4].amixdown);
5307                 }
5308                 
5309                 /* do we want to add an AC-3 passthrough option? */
5310                 if (audio->in.codec == HB_ACODEC_AC3 && acodec == HB_ACODEC_AC3) 
5311                 {
5312                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5313                                             [NSString stringWithCString: hb_audio_mixdowns[5].human_readable_name]
5314                                                                           action: NULL keyEquivalent: @""];
5315                     [menuItem setTag: HB_ACODEC_AC3];
5316                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[5].amixdown;
5317                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[5].amixdown);
5318                 }
5319                 
5320                 /* do we want to add a DTS Passthru option ? HB_ACODEC_DCA*/
5321                 if (audio->in.codec == HB_ACODEC_DCA && acodec == HB_ACODEC_DCA) 
5322                 {
5323                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5324                                             [NSString stringWithCString: hb_audio_mixdowns[5].human_readable_name]
5325                                                                           action: NULL keyEquivalent: @""];
5326                     [menuItem setTag: HB_ACODEC_DCA];
5327                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[5].amixdown;
5328                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[5].amixdown);
5329                 }
5330                 
5331                 /* auto-select the best mixdown based on our saved mixdown preference */
5332                 
5333                 /* for now, this is hard-coded to a "best" mixdown of HB_AMIXDOWN_DOLBYPLII */
5334                 /* ultimately this should be a prefs option */
5335                 int useMixdown;
5336                 
5337                 /* if we passed in a mixdown to use - in order to load a preset - then try and use it */
5338                 if (mixdownToUse > 0)
5339                 {
5340                     useMixdown = mixdownToUse;
5341                 }
5342                 else
5343                 {
5344                     useMixdown = HB_AMIXDOWN_DOLBYPLII;
5345                 }
5346                 
5347                 /* if useMixdown > maxMixdownUsed, then use maxMixdownUsed */
5348                 if (useMixdown > maxMixdownUsed)
5349                 { 
5350                     useMixdown = maxMixdownUsed;
5351                 }
5352                 
5353                 /* if useMixdown < minMixdownUsed, then use minMixdownUsed */
5354                 if (useMixdown < minMixdownUsed)
5355                 { 
5356                     useMixdown = minMixdownUsed;
5357                 }
5358                 
5359                 /* select the (possibly-amended) preferred mixdown */
5360                 [mixdownPopUp selectItemWithTag: useMixdown];
5361
5362             }
5363             /* In the case of a source track that is not AC3 and the user tries to use AC3 Passthru (which does not work)
5364              * we force the Audio Codec choice back to a workable codec. We use MP3 for avi and aac for all
5365              * other containers.
5366              */
5367             if (audio->in.codec != HB_ACODEC_AC3 && [[audiocodecPopUp selectedItem] tag] == HB_ACODEC_AC3)
5368             {
5369                 /* If we are using the avi container, we select MP3 as there is no aac available*/
5370                 if ([[fDstFormatPopUp selectedItem] tag] == HB_MUX_AVI)
5371                 {
5372                     [audiocodecPopUp selectItemWithTag: HB_ACODEC_LAME];
5373                 }
5374                 else
5375                 {
5376                     [audiocodecPopUp selectItemWithTag: HB_ACODEC_FAAC];
5377                 }
5378             }
5379             
5380             /* In the case of a source track that is not DTS and the user tries to use DTS Passthru (which does not work)
5381              * we force the Audio Codec choice back to a workable codec. We use MP3 for avi and aac for all
5382              * other containers.
5383              */
5384             if (audio->in.codec != HB_ACODEC_DCA && [[audiocodecPopUp selectedItem] tag] == HB_ACODEC_DCA)
5385             {
5386                 /* If we are using the avi container, we select MP3 as there is no aac available*/
5387                 if ([[fDstFormatPopUp selectedItem] tag] == HB_MUX_AVI)
5388                 {
5389                     [audiocodecPopUp selectItemWithTag: HB_ACODEC_LAME];
5390                 }
5391                 else
5392                 {
5393                     [audiocodecPopUp selectItemWithTag: HB_ACODEC_FAAC];
5394                 }
5395             }
5396             
5397             /* Setup our samplerate and bitrate popups we will need based on mixdown */
5398             [self audioTrackMixdownChanged: mixdownPopUp];             
5399         }
5400     
5401     }
5402     if( [fDstFormatPopUp indexOfSelectedItem] == 0 )
5403     {
5404         [self autoSetM4vExtension: sender];
5405     }
5406 }
5407
5408 - (IBAction) audioTrackMixdownChanged: (id) sender
5409 {
5410     
5411     int acodec;
5412     /* setup pointers to all of the other audio track controls
5413     * we will need later
5414     */
5415     NSPopUpButton * mixdownPopUp;
5416     NSPopUpButton * sampleratePopUp;
5417     NSPopUpButton * bitratePopUp;
5418     NSPopUpButton * audiocodecPopUp;
5419     NSPopUpButton * audiotrackPopUp;
5420     NSSlider * drcSlider;
5421     NSTextField * drcField;
5422     if (sender == fAudTrack1MixPopUp)
5423     {
5424         audiotrackPopUp = fAudLang1PopUp;
5425         audiocodecPopUp = fAudTrack1CodecPopUp;
5426         mixdownPopUp = fAudTrack1MixPopUp;
5427         sampleratePopUp = fAudTrack1RatePopUp;
5428         bitratePopUp = fAudTrack1BitratePopUp;
5429         drcSlider = fAudTrack1DrcSlider;
5430         drcField = fAudTrack1DrcField;
5431     }
5432     else if (sender == fAudTrack2MixPopUp)
5433     {
5434         audiotrackPopUp = fAudLang2PopUp;
5435         audiocodecPopUp = fAudTrack2CodecPopUp;
5436         mixdownPopUp = fAudTrack2MixPopUp;
5437         sampleratePopUp = fAudTrack2RatePopUp;
5438         bitratePopUp = fAudTrack2BitratePopUp;
5439         drcSlider = fAudTrack2DrcSlider;
5440         drcField = fAudTrack2DrcField;
5441     }
5442     else if (sender == fAudTrack3MixPopUp)
5443     {
5444         audiotrackPopUp = fAudLang3PopUp;
5445         audiocodecPopUp = fAudTrack3CodecPopUp;
5446         mixdownPopUp = fAudTrack3MixPopUp;
5447         sampleratePopUp = fAudTrack3RatePopUp;
5448         bitratePopUp = fAudTrack3BitratePopUp;
5449         drcSlider = fAudTrack3DrcSlider;
5450         drcField = fAudTrack3DrcField;
5451     }
5452     else
5453     {
5454         audiotrackPopUp = fAudLang4PopUp;
5455         audiocodecPopUp = fAudTrack4CodecPopUp;
5456         mixdownPopUp = fAudTrack4MixPopUp;
5457         sampleratePopUp = fAudTrack4RatePopUp;
5458         bitratePopUp = fAudTrack4BitratePopUp;
5459         drcSlider = fAudTrack4DrcSlider;
5460         drcField = fAudTrack4DrcField;
5461     }
5462     acodec = [[audiocodecPopUp selectedItem] tag];
5463     /* storage variable for the min and max bitrate allowed for this codec */
5464     int minbitrate;
5465     int maxbitrate;
5466     
5467     switch( acodec )
5468     {
5469         case HB_ACODEC_FAAC:
5470             /* check if we have a 6ch discrete conversion in either audio track */
5471             if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH)
5472             {
5473                 /* FAAC is happy using our min bitrate of 32 kbps, even for 6ch */
5474                 minbitrate = 32;
5475                 /* If either mixdown popup includes 6-channel discrete, then allow up to 384 kbps */
5476                 maxbitrate = 384;
5477                 break;
5478             }
5479             else
5480             {
5481                 /* FAAC is happy using our min bitrate of 32 kbps for stereo or mono */
5482                 minbitrate = 32;
5483                 /* FAAC won't honour anything more than 160 for stereo, so let's not offer it */
5484                 /* note: haven't dealt with mono separately here, FAAC will just use the max it can */
5485                 maxbitrate = 160;
5486                 break;
5487             }
5488
5489         case HB_ACODEC_CA_AAC:
5490             /* check if we have a 6ch discrete conversion in either audio track */
5491             if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH)
5492             {
5493                 minbitrate = 128;
5494                 maxbitrate = 768;
5495                 break;
5496             }
5497             else
5498             {
5499                 minbitrate = 64;
5500                 maxbitrate = 320;
5501                 break;
5502             }
5503
5504             case HB_ACODEC_LAME:
5505             /* Lame is happy using our min bitrate of 32 kbps */
5506             minbitrate = 32;
5507             /* Lame won't encode if the bitrate is higher than 320 kbps */
5508             maxbitrate = 320;
5509             break;
5510             
5511             case HB_ACODEC_VORBIS:
5512             if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH)
5513             {
5514                 /* Vorbis causes a crash if we use a bitrate below 192 kbps with 6 channel */
5515                 minbitrate = 192;
5516                 /* If either mixdown popup includes 6-channel discrete, then allow up to 384 kbps */
5517                 maxbitrate = 384;
5518                 break;
5519             }
5520             else
5521             {
5522                 /* Vorbis causes a crash if we use a bitrate below 48 kbps */
5523                 minbitrate = 48;
5524                 /* Vorbis can cope with 384 kbps quite happily, even for stereo */
5525                 maxbitrate = 384;
5526                 break;
5527             }
5528             
5529             default:
5530             /* AC3 passthru disables the bitrate dropdown anyway, so we might as well just use the min and max bitrate */
5531             minbitrate = 32;
5532             maxbitrate = 384;
5533             
5534     }
5535     
5536     /* make sure we have a selected title before continuing */
5537     if (fTitle == NULL) return;
5538     /* get the audio so we can find out what input rates are*/
5539     hb_audio_config_t * audio;
5540     audio = (hb_audio_config_t *) hb_list_audio_config_item( fTitle->list_audio, [audiotrackPopUp indexOfSelectedItem] - 1 );
5541     int inputbitrate = audio->in.bitrate / 1000;
5542     int inputsamplerate = audio->in.samplerate;
5543     
5544     if ([[mixdownPopUp selectedItem] tag] != HB_ACODEC_AC3 && [[mixdownPopUp selectedItem] tag] != HB_ACODEC_DCA)
5545     {
5546         [bitratePopUp removeAllItems];
5547         
5548         for( int i = 0; i < hb_audio_bitrates_count; i++ )
5549         {
5550             if (hb_audio_bitrates[i].rate >= minbitrate && hb_audio_bitrates[i].rate <= maxbitrate)
5551             {
5552                 /* add a new menuitem for this bitrate */
5553                 NSMenuItem *menuItem = [[bitratePopUp menu] addItemWithTitle:
5554                                         [NSString stringWithCString: hb_audio_bitrates[i].string]
5555                                                                       action: NULL keyEquivalent: @""];
5556                 /* set its tag to be the actual bitrate as an integer, so we can retrieve it later */
5557                 [menuItem setTag: hb_audio_bitrates[i].rate];
5558             }
5559         }
5560         
5561         /* select the default bitrate (but use 384 for 6-ch AAC) */
5562         if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH)
5563         {
5564             [bitratePopUp selectItemWithTag: 384];
5565         }
5566         else
5567         {
5568             [bitratePopUp selectItemWithTag: hb_audio_bitrates[hb_audio_bitrates_default].rate];
5569         }
5570     }
5571     /* populate and set the sample rate popup */
5572     /* Audio samplerate */
5573     [sampleratePopUp removeAllItems];
5574     /* we create a same as source selection (Auto) so that we can choose to use the input sample rate */
5575     NSMenuItem *menuItem = [[sampleratePopUp menu] addItemWithTitle: @"Auto" action: NULL keyEquivalent: @""];
5576     [menuItem setTag: inputsamplerate];
5577     
5578     for( int i = 0; i < hb_audio_rates_count; i++ )
5579     {
5580         NSMenuItem *menuItem = [[sampleratePopUp menu] addItemWithTitle:
5581                                 [NSString stringWithCString: hb_audio_rates[i].string]
5582                                                                  action: NULL keyEquivalent: @""];
5583         [menuItem setTag: hb_audio_rates[i].rate];
5584     }
5585     /* We use the input sample rate as the default sample rate as downsampling just makes audio worse
5586     * and there is no compelling reason to use anything else as default, though the users default
5587     * preset will likely override any setting chosen here.
5588     */
5589     [sampleratePopUp selectItemWithTag: inputsamplerate];
5590     
5591     
5592     /* Since AC3 Pass Thru and DTS Pass Thru uses the input bitrate and sample rate, we get the input tracks
5593     * bitrate and display it in the bitrate popup even though libhb happily ignores any bitrate input from
5594     * the gui. We do this for better user feedback in the audio tab as well as the queue for the most part
5595     */
5596     if ([[mixdownPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[mixdownPopUp selectedItem] tag] == HB_ACODEC_DCA)
5597     {
5598         
5599         /* lets also set the bitrate popup to the input bitrate as thats what passthru will use */
5600         [bitratePopUp removeAllItems];
5601         NSMenuItem *menuItem = [[bitratePopUp menu] addItemWithTitle:
5602                                 [NSString stringWithFormat:@"%d", inputbitrate]
5603                                                               action: NULL keyEquivalent: @""];
5604         [menuItem setTag: inputbitrate];
5605         /* For ac3 passthru we disable the sample rate and bitrate popups as well as the drc slider*/
5606         [bitratePopUp setEnabled: NO];
5607         [sampleratePopUp setEnabled: NO];
5608         
5609         [drcSlider setFloatValue: 1.00];
5610         [self audioDRCSliderChanged: drcSlider];
5611         [drcSlider setEnabled: NO];
5612         [drcField setEnabled: NO];
5613     }
5614     else
5615     {
5616         [sampleratePopUp setEnabled: YES];
5617         [bitratePopUp setEnabled: YES];
5618         [drcSlider setEnabled: YES];
5619         [drcField setEnabled: YES];
5620     }
5621 [self calculateBitrate:nil];    
5622 }
5623
5624 - (IBAction) audioDRCSliderChanged: (id) sender
5625 {
5626     NSSlider * drcSlider;
5627     NSTextField * drcField;
5628     if (sender == fAudTrack1DrcSlider)
5629     {
5630         drcSlider = fAudTrack1DrcSlider;
5631         drcField = fAudTrack1DrcField;
5632     }
5633     else if (sender == fAudTrack2DrcSlider)
5634     {
5635         drcSlider = fAudTrack2DrcSlider;
5636         drcField = fAudTrack2DrcField;
5637     }
5638     else if (sender == fAudTrack3DrcSlider)
5639     {
5640         drcSlider = fAudTrack3DrcSlider;
5641         drcField = fAudTrack3DrcField;
5642     }
5643     else
5644     {
5645         drcSlider = fAudTrack4DrcSlider;
5646         drcField = fAudTrack4DrcField;
5647     }
5648     
5649     /* If we are between 0.0 and 1.0 on the slider, snap it to 1.0 */
5650     if ([drcSlider floatValue] > 0.0 && [drcSlider floatValue] < 1.0)
5651     {
5652         [drcSlider setFloatValue:1.0];
5653     }
5654     
5655     
5656     [drcField setStringValue: [NSString stringWithFormat: @"%.2f", [drcSlider floatValue]]];
5657     /* For now, do not call this until we have an intelligent way to determine audio track selections
5658     * compared to presets
5659     */
5660     //[self customSettingUsed: sender];
5661 }
5662
5663
5664 #pragma mark -
5665 #pragma mark Open New Windows
5666
5667 - (IBAction) openHomepage: (id) sender
5668 {
5669     [[NSWorkspace sharedWorkspace] openURL: [NSURL
5670         URLWithString:@"http://handbrake.fr/"]];
5671 }
5672
5673 - (IBAction) openForums: (id) sender
5674 {
5675     [[NSWorkspace sharedWorkspace] openURL: [NSURL
5676         URLWithString:@"http://handbrake.fr/forum/"]];
5677 }
5678 - (IBAction) openUserGuide: (id) sender
5679 {
5680     [[NSWorkspace sharedWorkspace] openURL: [NSURL
5681         URLWithString:@"http://handbrake.fr/trac/wiki/HandBrakeGuide"]];
5682 }
5683
5684 /**
5685  * Shows debug output window.
5686  */
5687 - (IBAction)showDebugOutputPanel:(id)sender
5688 {
5689     [outputPanel showOutputPanel:sender];
5690 }
5691
5692 /**
5693  * Shows preferences window.
5694  */
5695 - (IBAction) showPreferencesWindow: (id) sender
5696 {
5697     NSWindow * window = [fPreferencesController window];
5698     if (![window isVisible])
5699         [window center];
5700
5701     [window makeKeyAndOrderFront: nil];
5702 }
5703
5704 /**
5705  * Shows queue window.
5706  */
5707 - (IBAction) showQueueWindow:(id)sender
5708 {
5709     [fQueueController showQueueWindow:sender];
5710 }
5711
5712
5713 - (IBAction) toggleDrawer:(id)sender {
5714     [fPresetDrawer toggle:self];
5715 }
5716
5717 /**
5718  * Shows Picture Settings Window.
5719  */
5720
5721 - (IBAction) showPicturePanel: (id) sender
5722 {
5723         [fPictureController showPictureWindow:sender];
5724 }
5725
5726 - (void) picturePanelFullScreen
5727 {
5728         [fPictureController setToFullScreenMode];
5729 }
5730
5731 - (void) picturePanelWindowed
5732 {
5733         [fPictureController setToWindowedMode];
5734 }
5735
5736 - (IBAction) showPreviewWindow: (id) sender
5737 {
5738         [fPictureController showPreviewWindow:sender];
5739 }
5740
5741 #pragma mark -
5742 #pragma mark Preset Outline View Methods
5743 #pragma mark - Required
5744 /* These are required by the NSOutlineView Datasource Delegate */
5745
5746
5747 /* used to specify the number of levels to show for each item */
5748 - (int)outlineView:(NSOutlineView *)fPresetsOutlineView numberOfChildrenOfItem:(id)item
5749 {
5750     /* currently use no levels to test outline view viability */
5751     if (item == nil) // for an outline view the root level of the hierarchy is always nil
5752     {
5753         return [UserPresets count];
5754     }
5755     else
5756     {
5757         /* we need to return the count of the array in ChildrenArray for this folder */
5758         NSArray *children = nil;
5759         children = [item objectForKey:@"ChildrenArray"];
5760         if ([children count] > 0)
5761         {
5762             return [children count];
5763         }
5764         else
5765         {
5766             return 0;
5767         }
5768     }
5769 }
5770
5771 /* We use this to deterimine children of an item */
5772 - (id)outlineView:(NSOutlineView *)fPresetsOutlineView child:(NSInteger)index ofItem:(id)item
5773 {
5774     
5775     /* we need to return the count of the array in ChildrenArray for this folder */
5776     NSArray *children = nil;
5777     if (item == nil)
5778     {
5779         children = UserPresets;
5780     }
5781     else
5782     {
5783         if ([item objectForKey:@"ChildrenArray"])
5784         {
5785             children = [item objectForKey:@"ChildrenArray"];
5786         }
5787     }   
5788     if ((children == nil) || ( [children count] <= (NSUInteger) index))
5789     {
5790         return nil;
5791     }
5792     else
5793     {
5794         return [children objectAtIndex:index];
5795     }
5796     
5797     
5798     // We are only one level deep, so we can't be asked about children
5799     //NSAssert (NO, @"Presets View outlineView:child:ofItem: currently can't handle nested items.");
5800     //return nil;
5801 }
5802
5803 /* We use this to determine if an item should be expandable */
5804 - (BOOL)outlineView:(NSOutlineView *)fPresetsOutlineView isItemExpandable:(id)item
5805 {
5806     
5807     /* we need to return the count of the array in ChildrenArray for this folder */
5808     NSArray *children= nil;
5809     if (item == nil)
5810     {
5811         children = UserPresets;
5812     }
5813     else
5814     {
5815         if ([item objectForKey:@"ChildrenArray"])
5816         {
5817             children = [item objectForKey:@"ChildrenArray"];
5818         }
5819     }   
5820     
5821     /* To deterimine if an item should show a disclosure triangle
5822      * we could do it by the children count as so:
5823      * if ([children count] < 1)
5824      * However, lets leave the triangle show even if there are no
5825      * children to help indicate a folder, just like folder in the
5826      * finder can show a disclosure triangle even when empty
5827      */
5828     
5829     /* We need to determine if the item is a folder */
5830    if ([[item objectForKey:@"Folder"] intValue] == 1)
5831    {
5832         return YES;
5833     }
5834     else
5835     {
5836         return NO;
5837     }
5838     
5839 }
5840
5841 - (BOOL)outlineView:(NSOutlineView *)outlineView shouldExpandItem:(id)item
5842 {
5843     // Our outline view has no levels, but we can still expand every item. Doing so
5844     // just makes the row taller. See heightOfRowByItem below.
5845 //return ![(HBQueueOutlineView*)outlineView isDragging];
5846
5847 return YES;
5848 }
5849
5850
5851 /* Used to tell the outline view which information is to be displayed per item */
5852 - (id)outlineView:(NSOutlineView *)fPresetsOutlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
5853 {
5854         /* We have two columns right now, icon and PresetName */
5855         
5856     if ([[tableColumn identifier] isEqualToString:@"PresetName"])
5857     {
5858         return [item objectForKey:@"PresetName"];
5859     }
5860     else
5861     {
5862         //return @"";
5863         return nil;
5864     }
5865 }
5866
5867 - (id)outlineView:(NSOutlineView *)outlineView itemForPersistentObject:(id)object
5868 {
5869     return [NSKeyedUnarchiver unarchiveObjectWithData:object];
5870 }
5871 - (id)outlineView:(NSOutlineView *)outlineView persistentObjectForItem:(id)item
5872 {
5873     return [NSKeyedArchiver archivedDataWithRootObject:item];
5874 }
5875
5876 #pragma mark - Added Functionality (optional)
5877 /* Use to customize the font and display characteristics of the title cell */
5878 - (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
5879 {
5880     if ([[tableColumn identifier] isEqualToString:@"PresetName"])
5881     {
5882         NSFont *txtFont;
5883         NSColor *fontColor;
5884         NSColor *shadowColor;
5885         txtFont = [NSFont systemFontOfSize: [NSFont smallSystemFontSize]];
5886         /*check to see if its a selected row */
5887         if ([fPresetsOutlineView selectedRow] == [fPresetsOutlineView rowForItem:item])
5888         {
5889             
5890             fontColor = [NSColor blackColor];
5891             shadowColor = [NSColor colorWithDeviceRed:(127.0/255.0) green:(140.0/255.0) blue:(160.0/255.0) alpha:1.0];
5892         }
5893         else
5894         {
5895             if ([[item objectForKey:@"Type"] intValue] == 0)
5896             {
5897                 fontColor = [NSColor blueColor];
5898             }
5899             else // User created preset, use a black font
5900             {
5901                 fontColor = [NSColor blackColor];
5902             }
5903             /* check to see if its a folder */
5904             //if ([[item objectForKey:@"Folder"] intValue] == 1)
5905             //{
5906             //fontColor = [NSColor greenColor];
5907             //}
5908             
5909             
5910         }
5911         /* We use Bold Text for the HB Default */
5912         if ([[item objectForKey:@"Default"] intValue] == 1)// 1 is HB default
5913         {
5914             txtFont = [NSFont boldSystemFontOfSize: [NSFont smallSystemFontSize]];
5915         }
5916         /* We use Bold Text for the User Specified Default */
5917         if ([[item objectForKey:@"Default"] intValue] == 2)// 2 is User default
5918         {
5919             txtFont = [NSFont boldSystemFontOfSize: [NSFont smallSystemFontSize]];
5920         }
5921         
5922         
5923         [cell setTextColor:fontColor];
5924         [cell setFont:txtFont];
5925         
5926     }
5927 }
5928
5929 /* We use this to edit the name field in the outline view */
5930 - (void)outlineView:(NSOutlineView *)outlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
5931 {
5932     if ([[tableColumn identifier] isEqualToString:@"PresetName"])
5933     {
5934         id theRecord;
5935         
5936         theRecord = item;
5937         [theRecord setObject:object forKey:@"PresetName"];
5938         
5939         [self sortPresets];
5940         
5941         [fPresetsOutlineView reloadData];
5942         /* We save all of the preset data here */
5943         [self savePreset];
5944     }
5945 }
5946 /* We use this to provide tooltips for the items in the presets outline view */
5947 - (NSString *)outlineView:(NSOutlineView *)fPresetsOutlineView toolTipForCell:(NSCell *)cell rect:(NSRectPointer)rect tableColumn:(NSTableColumn *)tc item:(id)item mouseLocation:(NSPoint)mouseLocation
5948 {
5949     //if ([[tc identifier] isEqualToString:@"PresetName"])
5950     //{
5951         /* initialize the tooltip contents variable */
5952         NSString *loc_tip;
5953         /* if there is a description for the preset, we show it in the tooltip */
5954         if ([item objectForKey:@"PresetDescription"])
5955         {
5956             loc_tip = [item objectForKey:@"PresetDescription"];
5957             return (loc_tip);
5958         }
5959         else
5960         {
5961             loc_tip = @"No description available";
5962         }
5963         return (loc_tip);
5964     //}
5965 }
5966
5967 #pragma mark -
5968 #pragma mark Preset Outline View Methods (dragging related)
5969
5970
5971 - (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard
5972 {
5973         // Dragging is only allowed for custom presets.
5974     //[[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Default"] intValue] != 1
5975         if ([[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Type"] intValue] == 0) // 0 is built in preset
5976     {
5977         return NO;
5978     }
5979     // Don't retain since this is just holding temporaral drag information, and it is
5980     //only used during a drag!  We could put this in the pboard actually.
5981     fDraggedNodes = items;
5982     // Provide data for our custom type, and simple NSStrings.
5983     [pboard declareTypes:[NSArray arrayWithObjects: DragDropSimplePboardType, nil] owner:self];
5984     
5985     // the actual data doesn't matter since DragDropSimplePboardType drags aren't recognized by anyone but us!.
5986     [pboard setData:[NSData data] forType:DragDropSimplePboardType]; 
5987     
5988     return YES;
5989 }
5990
5991 - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
5992 {
5993         
5994         // Don't allow dropping ONTO an item since they can't really contain any children.
5995     
5996     BOOL isOnDropTypeProposal = index == NSOutlineViewDropOnItemIndex;
5997     if (isOnDropTypeProposal)
5998         return NSDragOperationNone;
5999     
6000     // Don't allow dropping INTO an item since they can't really contain any children as of yet.
6001         if (item != nil)
6002         {
6003                 index = [fPresetsOutlineView rowForItem: item] + 1;
6004                 item = nil;
6005         }
6006     
6007     // Don't allow dropping into the Built In Presets.
6008     if (index < presetCurrentBuiltInCount)
6009     {
6010         return NSDragOperationNone;
6011         index = MAX (index, presetCurrentBuiltInCount);
6012         }    
6013         
6014     [outlineView setDropItem:item dropChildIndex:index];
6015     return NSDragOperationGeneric;
6016 }
6017
6018
6019
6020 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index
6021 {
6022     /* first, lets see if we are dropping into a folder */
6023     if ([[fPresetsOutlineView itemAtRow:index] objectForKey:@"Folder"] && [[[fPresetsOutlineView itemAtRow:index] objectForKey:@"Folder"] intValue] == 1) // if its a folder
6024         {
6025     NSMutableArray *childrenArray = [[NSMutableArray alloc] init];
6026     childrenArray = [[fPresetsOutlineView itemAtRow:index] objectForKey:@"ChildrenArray"];
6027     [childrenArray addObject:item];
6028     [[fPresetsOutlineView itemAtRow:index] setObject:[NSMutableArray arrayWithArray: childrenArray] forKey:@"ChildrenArray"];
6029     [childrenArray autorelease];
6030     }
6031     else // We are not, so we just move the preset into the existing array 
6032     {
6033         NSMutableIndexSet *moveItems = [NSMutableIndexSet indexSet];
6034         id obj;
6035         NSEnumerator *enumerator = [fDraggedNodes objectEnumerator];
6036         while (obj = [enumerator nextObject])
6037         {
6038             [moveItems addIndex:[UserPresets indexOfObject:obj]];
6039         }
6040         // Successful drop, lets rearrange the view and save it all
6041         [self moveObjectsInPresetsArray:UserPresets fromIndexes:moveItems toIndex: index];
6042     }
6043     [fPresetsOutlineView reloadData];
6044     [self savePreset];
6045     return YES;
6046 }
6047
6048 - (void)moveObjectsInPresetsArray:(NSMutableArray *)array fromIndexes:(NSIndexSet *)indexSet toIndex:(NSUInteger)insertIndex
6049 {
6050     NSUInteger index = [indexSet lastIndex];
6051     NSUInteger aboveInsertIndexCount = 0;
6052     
6053     NSUInteger removeIndex;
6054
6055     if (index >= insertIndex)
6056     {
6057         removeIndex = index + aboveInsertIndexCount;
6058         aboveInsertIndexCount++;
6059     }
6060     else
6061     {
6062         removeIndex = index;
6063         insertIndex--;
6064     }
6065
6066     id object = [[array objectAtIndex:removeIndex] retain];
6067     [array removeObjectAtIndex:removeIndex];
6068     [array insertObject:object atIndex:insertIndex];
6069     [object release];
6070
6071     index = [indexSet indexLessThanIndex:index];
6072 }
6073
6074
6075
6076 #pragma mark - Functional Preset NSOutlineView Methods
6077
6078 - (IBAction)selectPreset:(id)sender
6079 {
6080     
6081     if ([fPresetsOutlineView selectedRow] >= 0 && [[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Folder"] intValue] != 1)
6082     {
6083         chosenPreset = [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]];
6084         [fPresetSelectedDisplay setStringValue:[chosenPreset objectForKey:@"PresetName"]];
6085         
6086         if ([[chosenPreset objectForKey:@"Default"] intValue] == 1)
6087         {
6088             [fPresetSelectedDisplay setStringValue:[NSString stringWithFormat:@"%@ (Default)", [chosenPreset objectForKey:@"PresetName"]]];
6089         }
6090         else
6091         {
6092             [fPresetSelectedDisplay setStringValue:[chosenPreset objectForKey:@"PresetName"]];
6093         }
6094         
6095         /* File Format */
6096         [fDstFormatPopUp selectItemWithTitle:[chosenPreset objectForKey:@"FileFormat"]];
6097         [self formatPopUpChanged:nil];
6098         
6099         /* Chapter Markers*/
6100         [fCreateChapterMarkers setState:[[chosenPreset objectForKey:@"ChapterMarkers"] intValue]];
6101         /* check to see if we have only one chapter */
6102         [self chapterPopUpChanged:nil];
6103         
6104         /* Allow Mpeg4 64 bit formatting +4GB file sizes */
6105         [fDstMp4LargeFileCheck setState:[[chosenPreset objectForKey:@"Mp4LargeFile"] intValue]];
6106         /* Mux mp4 with http optimization */
6107         [fDstMp4HttpOptFileCheck setState:[[chosenPreset objectForKey:@"Mp4HttpOptimize"] intValue]];
6108         
6109         /* Video encoder */
6110         [fVidEncoderPopUp selectItemWithTitle:[chosenPreset objectForKey:@"VideoEncoder"]];
6111         /* We set the advanced opt string here if applicable*/
6112         [fAdvancedOptions setOptions:[chosenPreset objectForKey:@"x264Option"]];
6113         
6114         /* Lets run through the following functions to get variables set there */
6115         [self videoEncoderPopUpChanged:nil];
6116         /* Set the state of ipod compatible with Mp4iPodCompatible. Only for x264*/
6117         [fDstMp4iPodFileCheck setState:[[chosenPreset objectForKey:@"Mp4iPodCompatible"] intValue]];
6118         [self calculateBitrate:nil];
6119         
6120         /* Video quality */
6121         [fVidQualityMatrix selectCellAtRow:[[chosenPreset objectForKey:@"VideoQualityType"] intValue] column:0];
6122         
6123         [fVidTargetSizeField setStringValue:[chosenPreset objectForKey:@"VideoTargetSize"]];
6124         [fVidBitrateField setStringValue:[chosenPreset objectForKey:@"VideoAvgBitrate"]];
6125         
6126         /* Since we are now using RF Values for the slider, we detect if the preset uses an old quality float.
6127          * So, check to see if the quality value is less than 1.0 which should indicate the old ".062" type
6128          * quality preset. Caveat: in the case of x264, where the RF scale starts at 0, it would misinterpret
6129          * a preset that uses 0.0 - 0.99 for RF as an old style preset. Not sure how to get around that one yet,
6130          * though it should be a corner case since it would pretty much be a preset for lossless encoding. */
6131         if ([[chosenPreset objectForKey:@"VideoQualitySlider"] floatValue] < 1.0)
6132         {
6133             /* For the quality slider we need to convert the old percent's to the new rf scales */
6134             float rf =  (([fVidQualitySlider maxValue] - [fVidQualitySlider minValue]) * [[chosenPreset objectForKey:@"VideoQualitySlider"] floatValue]);
6135             [fVidQualitySlider setFloatValue:rf];
6136             
6137         }
6138         else
6139         {
6140             /* Since theora's qp value goes up from left to right, we can just set the slider float value */
6141             if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_THEORA)
6142             {
6143                 [fVidQualitySlider setFloatValue:[[chosenPreset objectForKey:@"VideoQualitySlider"] floatValue]];
6144             }
6145             else
6146             {
6147                 /* since ffmpeg and x264 use an "inverted" slider (lower qp/rf values indicate a higher quality) we invert the value on the slider */
6148                 [fVidQualitySlider setFloatValue:([fVidQualitySlider maxValue] + [fVidQualitySlider minValue]) - [[chosenPreset objectForKey:@"VideoQualitySlider"] floatValue]];
6149             }
6150         }
6151         
6152         [self videoMatrixChanged:nil];
6153         
6154         /* Video framerate */
6155         /* For video preset video framerate, we want to make sure that Same as source does not conflict with the
6156          detected framerate in the fVidRatePopUp so we use index 0*/
6157         if ([[chosenPreset objectForKey:@"VideoFramerate"] isEqualToString:@"Same as source"])
6158         {
6159             [fVidRatePopUp selectItemAtIndex: 0];
6160         }
6161         else
6162         {
6163             [fVidRatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"VideoFramerate"]];
6164         }
6165         
6166         
6167         /* 2 Pass Encoding */
6168         [fVidTwoPassCheck setState:[[chosenPreset objectForKey:@"VideoTwoPass"] intValue]];
6169         [self twoPassCheckboxChanged:nil];
6170         
6171         /* Turbo 1st pass for 2 Pass Encoding */
6172         [fVidTurboPassCheck setState:[[chosenPreset objectForKey:@"VideoTurboTwoPass"] intValue]];
6173         
6174         /*Audio*/
6175         /* First we check to see if we are using the current audio track layout based on AudioList array */
6176         if ([chosenPreset objectForKey:@"AudioList"])
6177         {
6178             /* Populate the audio widgets based on the contents of the AudioList array */
6179             int i = 0;
6180             NSEnumerator *enumerator = [[chosenPreset objectForKey:@"AudioList"] objectEnumerator];
6181             id tempObject;
6182             while (tempObject = [enumerator nextObject])
6183             {
6184                 i++;
6185                 if( i == 1 )
6186                 {
6187                     if ([fAudLang1PopUp indexOfSelectedItem] == 0)
6188                     {
6189                         [fAudLang1PopUp selectItemAtIndex: 1];
6190                     }
6191                     [self audioTrackPopUpChanged: fAudLang1PopUp];
6192                     [fAudTrack1CodecPopUp selectItemWithTitle:[tempObject objectForKey:@"AudioEncoder"]];
6193                     /* check our pref for core audio and use it in place of faac if applicable */
6194                     if ([[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES && 
6195                         [[tempObject objectForKey:@"AudioEncoder"] isEqualToString: @"AAC (faac)"])
6196                     {
6197                         [fAudTrack1CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"];
6198                     }                    
6199                     
6200                     [self audioTrackPopUpChanged: fAudTrack1CodecPopUp];
6201                     [fAudTrack1MixPopUp selectItemWithTitle:[tempObject objectForKey:@"AudioMixdown"]];
6202                     /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
6203                      * mixdown*/
6204                     if  ([fAudTrack1MixPopUp selectedItem] == nil)
6205                     {
6206                         [self audioTrackPopUpChanged: fAudTrack1CodecPopUp];
6207                     }
6208                     [fAudTrack1RatePopUp selectItemWithTitle:[tempObject objectForKey:@"AudioSamplerate"]];
6209                     /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
6210                     if (![[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"AC3 Passthru"])
6211                     {
6212                         [fAudTrack1BitratePopUp selectItemWithTitle:[tempObject objectForKey:@"AudioBitrate"]];
6213                     }
6214                     [fAudTrack1DrcSlider setFloatValue:[[tempObject objectForKey:@"AudioTrackDRCSlider"] floatValue]];
6215                     [self audioDRCSliderChanged: fAudTrack1DrcSlider];
6216                 }
6217                 
6218                 if( i == 2 )
6219                 {
6220                     
6221                     if ([fAudLang2PopUp indexOfSelectedItem] == 0)
6222                     {
6223                         [fAudLang2PopUp selectItemAtIndex: 1];
6224                     }
6225                     [self audioTrackPopUpChanged: fAudLang2PopUp];
6226                     [fAudTrack2CodecPopUp selectItemWithTitle:[tempObject objectForKey:@"AudioEncoder"]];
6227                     /* check our pref for core audio and use it in place of faac if applicable */
6228                     if ([[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES && 
6229                         [[tempObject objectForKey:@"AudioEncoder"] isEqualToString: @"AAC (faac)"])
6230                     {
6231                         [fAudTrack2CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"];
6232                     }
6233                     [self audioTrackPopUpChanged: fAudTrack2CodecPopUp];
6234                     [fAudTrack2MixPopUp selectItemWithTitle:[tempObject objectForKey:@"AudioMixdown"]];
6235                     /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
6236                      * mixdown*/
6237                     if  ([fAudTrack2MixPopUp selectedItem] == nil)
6238                     {
6239                         [self audioTrackPopUpChanged: fAudTrack2CodecPopUp];
6240                     }
6241                     [fAudTrack2RatePopUp selectItemWithTitle:[tempObject objectForKey:@"AudioSamplerate"]];
6242                     /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
6243                     if (![[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"AC3 Passthru"])
6244                     {
6245                         [fAudTrack2BitratePopUp selectItemWithTitle:[tempObject objectForKey:@"AudioBitrate"]];
6246                     }
6247                     [fAudTrack2DrcSlider setFloatValue:[[tempObject objectForKey:@"AudioTrackDRCSlider"] floatValue]];
6248                     [self audioDRCSliderChanged: fAudTrack2DrcSlider];
6249                     
6250                 }
6251                 
6252             }
6253             
6254              /* We now cleanup any extra audio tracks that may have been previously set if we need to */
6255             
6256             if (i < 4)
6257             {
6258                 [fAudLang4PopUp selectItemAtIndex: 0];
6259                 [self audioTrackPopUpChanged: fAudLang4PopUp];
6260                 
6261                 if (i < 3)
6262                 {
6263                     [fAudLang3PopUp selectItemAtIndex: 0];
6264                     [self audioTrackPopUpChanged: fAudLang3PopUp];
6265                     
6266                     if (i < 2)
6267                     {
6268                         [fAudLang2PopUp selectItemAtIndex: 0];
6269                         [self audioTrackPopUpChanged: fAudLang2PopUp];
6270                     }
6271                 }
6272             }
6273             
6274         }
6275         else
6276         {
6277             if ([chosenPreset objectForKey:@"Audio1Track"] > 0)
6278             {
6279                 if ([fAudLang1PopUp indexOfSelectedItem] == 0)
6280                 {
6281                     [fAudLang1PopUp selectItemAtIndex: 1];
6282                 }
6283                 [self audioTrackPopUpChanged: fAudLang1PopUp];
6284                 [fAudTrack1CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Encoder"]];
6285                 /* check our pref for core audio and use it in place of faac if applicable */
6286                 if ([[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES && 
6287                     [[chosenPreset objectForKey:@"Audio1Encoder"] isEqualToString: @"AAC (faac)"])
6288                 {
6289                     [fAudTrack1CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"];
6290                 }
6291                 [self audioTrackPopUpChanged: fAudTrack1CodecPopUp];
6292                 [fAudTrack1MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Mixdown"]];
6293                 /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
6294                  * mixdown*/
6295                 if  ([fAudTrack1MixPopUp selectedItem] == nil)
6296                 {
6297                     [self audioTrackPopUpChanged: fAudTrack1CodecPopUp];
6298                 }
6299                 [fAudTrack1RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Samplerate"]];
6300                 /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
6301                 if (![[chosenPreset objectForKey:@"Audio1Encoder"] isEqualToString:@"AC3 Passthru"])
6302                 {
6303                     [fAudTrack1BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Bitrate"]];
6304                 }
6305                 [fAudTrack1DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio1TrackDRCSlider"] floatValue]];
6306                 [self audioDRCSliderChanged: fAudTrack1DrcSlider];
6307             }
6308             if ([chosenPreset objectForKey:@"Audio2Track"] > 0)
6309             {
6310                 if ([fAudLang2PopUp indexOfSelectedItem] == 0)
6311                 {
6312                     [fAudLang2PopUp selectItemAtIndex: 1];
6313                 }
6314                 [self audioTrackPopUpChanged: fAudLang2PopUp];
6315                 [fAudTrack2CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Encoder"]];
6316                 /* check our pref for core audio and use it in place of faac if applicable */
6317                 if ([[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES && 
6318                     [[chosenPreset objectForKey:@"Audio2Encoder"] isEqualToString: @"AAC (faac)"])
6319                 {
6320                     [fAudTrack2CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"];
6321                 }
6322                 [self audioTrackPopUpChanged: fAudTrack2CodecPopUp];
6323                 [fAudTrack2MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Mixdown"]];
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:[chosenPreset objectForKey:@"Audio2Samplerate"]];
6331                 /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
6332                 if (![[chosenPreset objectForKey:@"Audio2Encoder"] isEqualToString:@"AC3 Passthru"])
6333                 {
6334                     [fAudTrack2BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Bitrate"]];
6335                 }
6336                 [fAudTrack2DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio2TrackDRCSlider"] floatValue]];
6337                 [self audioDRCSliderChanged: fAudTrack2DrcSlider];
6338             }
6339             if ([chosenPreset objectForKey:@"Audio3Track"] > 0)
6340             {
6341                 if ([fAudLang3PopUp indexOfSelectedItem] == 0)
6342                 {
6343                     [fAudLang3PopUp selectItemAtIndex: 1];
6344                 }
6345                 [self audioTrackPopUpChanged: fAudLang3PopUp];
6346                 [fAudTrack3CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Encoder"]];
6347                 /* check our pref for core audio and use it in place of faac if applicable */
6348                 if ([[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES && 
6349                     [[chosenPreset objectForKey:@"Audio3Encoder"] isEqualToString: @"AAC (faac)"])
6350                 {
6351                     [fAudTrack3CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"];
6352                 }
6353                 [self audioTrackPopUpChanged: fAudTrack3CodecPopUp];
6354                 [fAudTrack3MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Mixdown"]];
6355                 /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
6356                  * mixdown*/
6357                 if  ([fAudTrack3MixPopUp selectedItem] == nil)
6358                 {
6359                     [self audioTrackPopUpChanged: fAudTrack3CodecPopUp];
6360                 }
6361                 [fAudTrack3RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Samplerate"]];
6362                 /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
6363                 if (![[chosenPreset objectForKey:@"Audio3Encoder"] isEqualToString: @"AC3 Passthru"])
6364                 {
6365                     [fAudTrack3BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Bitrate"]];
6366                 }
6367                 [fAudTrack3DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio3TrackDRCSlider"] floatValue]];
6368                 [self audioDRCSliderChanged: fAudTrack3DrcSlider];
6369             }
6370             if ([chosenPreset objectForKey:@"Audio4Track"] > 0)
6371             {
6372                 if ([fAudLang4PopUp indexOfSelectedItem] == 0)
6373                 {
6374                     [fAudLang4PopUp selectItemAtIndex: 1];
6375                 }
6376                 [self audioTrackPopUpChanged: fAudLang4PopUp];
6377                 [fAudTrack4CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Encoder"]];
6378                 /* check our pref for core audio and use it in place of faac if applicable */
6379                 if ([[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES && 
6380                     [[chosenPreset objectForKey:@"Audio4Encoder"] isEqualToString: @"AAC (faac)"])
6381                 {
6382                     [fAudTrack4CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"];
6383                 }
6384                 [self audioTrackPopUpChanged: fAudTrack4CodecPopUp];
6385                 [fAudTrack4MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Mixdown"]];
6386                 /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
6387                  * mixdown*/
6388                 if  ([fAudTrack4MixPopUp selectedItem] == nil)
6389                 {
6390                     [self audioTrackPopUpChanged: fAudTrack4CodecPopUp];
6391                 }
6392                 [fAudTrack4RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Samplerate"]];
6393                 /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
6394                 if (![[chosenPreset objectForKey:@"Audio4Encoder"] isEqualToString:@"AC3 Passthru"])
6395                 {
6396                     [fAudTrack4BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Bitrate"]];
6397                 }
6398                 [fAudTrack4DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio4TrackDRCSlider"] floatValue]];
6399                 [self audioDRCSliderChanged: fAudTrack4DrcSlider];
6400             }
6401             
6402             /* We now cleanup any extra audio tracks that may have been previously set if we need to */
6403             
6404             if (![chosenPreset objectForKey:@"Audio2Track"] || [chosenPreset objectForKey:@"Audio2Track"] == 0)
6405             {
6406                 [fAudLang2PopUp selectItemAtIndex: 0];
6407                 [self audioTrackPopUpChanged: fAudLang2PopUp];
6408             }
6409             if (![chosenPreset objectForKey:@"Audio3Track"] || [chosenPreset objectForKey:@"Audio3Track"] > 0)
6410             {
6411                 [fAudLang3PopUp selectItemAtIndex: 0];
6412                 [self audioTrackPopUpChanged: fAudLang3PopUp];
6413             }
6414             if (![chosenPreset objectForKey:@"Audio4Track"] || [chosenPreset objectForKey:@"Audio4Track"] > 0)
6415             {
6416                 [fAudLang4PopUp selectItemAtIndex: 0];
6417                 [self audioTrackPopUpChanged: fAudLang4PopUp];
6418             }
6419         }
6420         
6421         /*Subtitles*/
6422         [fSubPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Subtitles"]];
6423         /* Forced Subtitles */
6424         [fSubForcedCheck setState:[[chosenPreset objectForKey:@"SubtitlesForced"] intValue]];
6425         
6426         /* Picture Settings */
6427         /* Note: objectForKey:@"UsesPictureSettings" refers to picture size, which encompasses:
6428          * height, width, keep ar, anamorphic and crop settings.
6429          * picture filters are handled separately below.
6430          */
6431         /* Check to see if the objectForKey:@"UsesPictureSettings is greater than 0, as 0 means use picture sizing "None" 
6432          * ( 2 is use max for source and 1 is use exact size when the preset was created ) and the 
6433          * preset completely ignores any picture sizing values in the preset.
6434          */
6435         if ([[chosenPreset objectForKey:@"UsesPictureSettings"]  intValue] > 0)
6436         {
6437             hb_job_t * job = fTitle->job;
6438             
6439             /* If Cropping is set to custom, then recall all four crop values from
6440              when the preset was created and apply them */
6441             if ([[chosenPreset objectForKey:@"PictureAutoCrop"]  intValue] == 0)
6442             {
6443                 [fPictureController setAutoCrop:NO];
6444                 
6445                 /* Here we use the custom crop values saved at the time the preset was saved */
6446                 job->crop[0] = [[chosenPreset objectForKey:@"PictureTopCrop"]  intValue];
6447                 job->crop[1] = [[chosenPreset objectForKey:@"PictureBottomCrop"]  intValue];
6448                 job->crop[2] = [[chosenPreset objectForKey:@"PictureLeftCrop"]  intValue];
6449                 job->crop[3] = [[chosenPreset objectForKey:@"PictureRightCrop"]  intValue];
6450                 
6451             }
6452             else /* if auto crop has been saved in preset, set to auto and use post scan auto crop */
6453             {
6454                 [fPictureController setAutoCrop:YES];
6455                 /* Here we use the auto crop values determined right after scan */
6456                 job->crop[0] = AutoCropTop;
6457                 job->crop[1] = AutoCropBottom;
6458                 job->crop[2] = AutoCropLeft;
6459                 job->crop[3] = AutoCropRight;
6460                 
6461             }
6462             
6463             
6464             /* Check to see if the objectForKey:@"UsesPictureSettings is 2 which is "Use Max for the source */
6465             if ([[chosenPreset objectForKey:@"UsesPictureSettings"]  intValue] == 2 || [[chosenPreset objectForKey:@"UsesMaxPictureSettings"]  intValue] == 1)
6466             {
6467                 /* Use Max Picture settings for whatever the dvd is.*/
6468                 [self revertPictureSizeToMax:nil];
6469                 job->keep_ratio = [[chosenPreset objectForKey:@"PictureKeepRatio"]  intValue];
6470                 if (job->keep_ratio == 1)
6471                 {
6472                     hb_fix_aspect( job, HB_KEEP_WIDTH );
6473                     if( job->height > fTitle->height )
6474                     {
6475                         job->height = fTitle->height;
6476                         hb_fix_aspect( job, HB_KEEP_HEIGHT );
6477                     }
6478                 }
6479                 job->anamorphic.mode = [[chosenPreset objectForKey:@"PicturePAR"]  intValue];
6480             }
6481             else // /* If not 0 or 2 we assume objectForKey:@"UsesPictureSettings is 1 which is "Use picture sizing from when the preset was set" */
6482             {
6483                 /* we check to make sure the presets width/height does not exceed the sources width/height */
6484                 if (fTitle->width < [[chosenPreset objectForKey:@"PictureWidth"]  intValue] || fTitle->height < [[chosenPreset objectForKey:@"PictureHeight"]  intValue])
6485                 {
6486                     /* if so, then we use the sources height and width to avoid scaling up */
6487                     //job->width = fTitle->width;
6488                     //job->height = fTitle->height;
6489                     [self revertPictureSizeToMax:nil];
6490                 }
6491                 else // source width/height is >= the preset height/width
6492                 {
6493                     /* we can go ahead and use the presets values for height and width */
6494                     job->width = [[chosenPreset objectForKey:@"PictureWidth"]  intValue];
6495                     job->height = [[chosenPreset objectForKey:@"PictureHeight"]  intValue];
6496                 }
6497                 job->keep_ratio = [[chosenPreset objectForKey:@"PictureKeepRatio"]  intValue];
6498                 if (job->keep_ratio == 1)
6499                 {
6500                     hb_fix_aspect( job, HB_KEEP_WIDTH );
6501                     if( job->height > fTitle->height )
6502                     {
6503                         job->height = fTitle->height;
6504                         hb_fix_aspect( job, HB_KEEP_HEIGHT );
6505                     }
6506                 }
6507                 job->anamorphic.mode = [[chosenPreset objectForKey:@"PicturePAR"]  intValue];
6508                 
6509             }
6510             
6511             
6512         }
6513         /* If the preset has an objectForKey:@"UsesPictureFilters", and handle the filters here */
6514         if ([chosenPreset objectForKey:@"UsesPictureFilters"] && [[chosenPreset objectForKey:@"UsesPictureFilters"]  intValue] > 0)
6515         {
6516             /* Filters */
6517             
6518             /* We only allow *either* Decomb or Deinterlace. So check for the PictureDecombDeinterlace key.
6519              * also, older presets may not have this key, in which case we also check to see if that preset had  PictureDecomb
6520              * specified, in which case we use decomb and ignore any possible Deinterlace settings as using both was less than
6521              * sane.
6522              */
6523             [fPictureController setUseDecomb:1];
6524             [fPictureController setDecomb:0];
6525             [fPictureController setDeinterlace:0];
6526             if ([[chosenPreset objectForKey:@"PictureDecombDeinterlace"] intValue] == 1 || [[chosenPreset objectForKey:@"PictureDecomb"] intValue] > 0)
6527             {
6528                 /* we are using decomb */
6529                 /* Decomb */
6530                 if ([[chosenPreset objectForKey:@"PictureDecomb"] intValue] > 0)
6531                 {
6532                     [fPictureController setDecomb:[[chosenPreset objectForKey:@"PictureDecomb"] intValue]];
6533                     
6534                     /* if we are using "Custom" in the decomb setting, also set the custom string*/
6535                     if ([[chosenPreset objectForKey:@"PictureDecomb"] intValue] == 2)
6536                     {
6537                         [fPictureController setDecombCustomString:[chosenPreset objectForKey:@"PictureDecombCustom"]];    
6538                     }
6539                 }
6540              }
6541             else
6542             {
6543                 /* We are using Deinterlace */
6544                 /* Deinterlace */
6545                 if ([[chosenPreset objectForKey:@"PictureDeinterlace"] intValue] > 0)
6546                 {
6547                     [fPictureController setUseDecomb:0];
6548                     [fPictureController setDeinterlace:[[chosenPreset objectForKey:@"PictureDeinterlace"] intValue]];
6549                     /* if we are using "Custom" in the deinterlace setting, also set the custom string*/
6550                     if ([[chosenPreset objectForKey:@"PictureDeinterlace"] intValue] == 4)
6551                     {
6552                         [fPictureController setDeinterlaceCustomString:[chosenPreset objectForKey:@"PictureDeinterlaceCustom"]];    
6553                     }
6554                 }
6555             }
6556             
6557             
6558             /* Detelecine */
6559             if ([[chosenPreset objectForKey:@"PictureDetelecine"] intValue] > 0)
6560             {
6561                 [fPictureController setDetelecine:[[chosenPreset objectForKey:@"PictureDetelecine"] intValue]];
6562                 /* if we are using "Custom" in the detelecine setting, also set the custom string*/
6563                 if ([[chosenPreset objectForKey:@"PictureDetelecine"] intValue] == 2)
6564                 {
6565                     [fPictureController setDetelecineCustomString:[chosenPreset objectForKey:@"PictureDetelecineCustom"]];    
6566                 }
6567             }
6568             else
6569             {
6570                 [fPictureController setDetelecine:0];
6571             }
6572             
6573             /* Denoise */
6574             if ([[chosenPreset objectForKey:@"PictureDenoise"] intValue] > 0)
6575             {
6576                 [fPictureController setDenoise:[[chosenPreset objectForKey:@"PictureDenoise"] intValue]];
6577                 /* if we are using "Custom" in the denoise setting, also set the custom string*/
6578                 if ([[chosenPreset objectForKey:@"PictureDenoise"] intValue] == 4)
6579                 {
6580                     [fPictureController setDenoiseCustomString:[chosenPreset objectForKey:@"PictureDenoiseCustom"]];    
6581                 }
6582             }
6583             else
6584             {
6585                 [fPictureController setDenoise:0];
6586             }   
6587             
6588             /* Deblock */
6589             if ([[chosenPreset objectForKey:@"PictureDeblock"] intValue] == 1)
6590             {
6591                 /* if its a one, then its the old on/off deblock, set on to 5*/
6592                 [fPictureController setDeblock:5];
6593             }
6594             else
6595             {
6596                 /* use the settings intValue */
6597                 [fPictureController setDeblock:[[chosenPreset objectForKey:@"PictureDeblock"] intValue]];
6598             }
6599             
6600             if ([[chosenPreset objectForKey:@"VideoGrayScale"] intValue] == 1)
6601             {
6602                 [fPictureController setGrayscale:1];
6603             }
6604             else
6605             {
6606                 [fPictureController setGrayscale:0];
6607             }
6608         }
6609         /* we call SetTitle: in fPictureController so we get an instant update in the Picture Settings window */
6610         [fPictureController SetTitle:fTitle];
6611         [fPictureController SetTitle:fTitle];
6612         [self calculatePictureSizing:nil];
6613     }
6614 }
6615
6616
6617 #pragma mark -
6618 #pragma mark Manage Presets
6619
6620 - (void) loadPresets {
6621         /* We declare the default NSFileManager into fileManager */
6622         NSFileManager * fileManager = [NSFileManager defaultManager];
6623         /*We define the location of the user presets file */
6624     UserPresetsFile = @"~/Library/Application Support/HandBrake/UserPresets.plist";
6625         UserPresetsFile = [[UserPresetsFile stringByExpandingTildeInPath]retain];
6626     /* We check for the presets.plist */
6627         if ([fileManager fileExistsAtPath:UserPresetsFile] == 0)
6628         {
6629                 [fileManager createFileAtPath:UserPresetsFile contents:nil attributes:nil];
6630         }
6631
6632         UserPresets = [[NSMutableArray alloc] initWithContentsOfFile:UserPresetsFile];
6633         if (nil == UserPresets)
6634         {
6635                 UserPresets = [[NSMutableArray alloc] init];
6636                 [self addFactoryPresets:nil];
6637         }
6638         [fPresetsOutlineView reloadData];
6639     
6640     [self checkBuiltInsForUpdates];
6641 }
6642
6643 - (void) checkBuiltInsForUpdates {
6644     
6645         BOOL updateBuiltInPresets = NO;
6646     int i = 0;
6647     NSEnumerator *enumerator = [UserPresets objectEnumerator];
6648     id tempObject;
6649     while (tempObject = [enumerator nextObject])
6650     {
6651         /* iterate through the built in presets to see if any have an old build number */
6652         NSMutableDictionary *thisPresetDict = tempObject;
6653         /*Key Type == 0 is built in, and key PresetBuildNumber is the build number it was created with */
6654         if ([[thisPresetDict objectForKey:@"Type"] intValue] == 0)              
6655         {
6656                         if (![thisPresetDict objectForKey:@"PresetBuildNumber"] || [[thisPresetDict objectForKey:@"PresetBuildNumber"] intValue] < [[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] intValue])
6657             {
6658                 updateBuiltInPresets = YES;
6659             }   
6660                 }
6661         i++;
6662     }
6663     /* if we have built in presets to update, then do so AlertBuiltInPresetUpdate*/
6664     if ( updateBuiltInPresets == YES)
6665     {
6666         if( [[NSUserDefaults standardUserDefaults] boolForKey:@"AlertBuiltInPresetUpdate"] == YES)
6667         {
6668             /* Show an alert window that built in presets will be updated */
6669             /*On Screen Notification*/
6670             int status;
6671             NSBeep();
6672             status = NSRunAlertPanel(@"HandBrake has determined your built in presets are out of date...",@"HandBrake will now update your built-in presets.", @"OK", nil, nil);
6673             [NSApp requestUserAttention:NSCriticalRequest];
6674         }
6675         /* when alert is dismissed, go ahead and update the built in presets */
6676         [self addFactoryPresets:nil];
6677     }
6678     
6679 }
6680
6681
6682 - (IBAction) showAddPresetPanel: (id) sender
6683 {
6684     /* Deselect the currently selected Preset if there is one*/
6685     [fPresetsOutlineView deselectRow:[fPresetsOutlineView selectedRow]];
6686
6687     /* Populate the preset picture settings popup here */
6688     [fPresetNewPicSettingsPopUp removeAllItems];
6689     [fPresetNewPicSettingsPopUp addItemWithTitle:@"None"];
6690     [fPresetNewPicSettingsPopUp addItemWithTitle:@"Current"];
6691     [fPresetNewPicSettingsPopUp addItemWithTitle:@"Source Maximum (post source scan)"];
6692     [fPresetNewPicSettingsPopUp selectItemAtIndex: 0];  
6693     /* Uncheck the preset use filters checkbox */
6694     [fPresetNewPicFiltersCheck setState:NSOffState];
6695     // fPresetNewFolderCheck
6696     [fPresetNewFolderCheck setState:NSOffState];
6697     /* Erase info from the input fields*/
6698         [fPresetNewName setStringValue: @""];
6699         [fPresetNewDesc setStringValue: @""];
6700         /* Show the panel */
6701         [NSApp beginSheet:fAddPresetPanel modalForWindow:fWindow modalDelegate:nil didEndSelector:NULL contextInfo:NULL];
6702 }
6703
6704 - (IBAction) closeAddPresetPanel: (id) sender
6705 {
6706     [NSApp endSheet: fAddPresetPanel];
6707     [fAddPresetPanel orderOut: self];
6708 }
6709
6710 - (IBAction)addUserPreset:(id)sender
6711 {
6712     if (![[fPresetNewName stringValue] length])
6713             NSRunAlertPanel(@"Warning!", @"You need to insert a name for the preset.", @"OK", nil , nil);
6714     else
6715     {
6716         /* Here we create a custom user preset */
6717         [UserPresets addObject:[self createPreset]];
6718         [self addPreset];
6719
6720         [self closeAddPresetPanel:nil];
6721     }
6722 }
6723 - (void)addPreset
6724 {
6725
6726         
6727         /* We Reload the New Table data for presets */
6728     [fPresetsOutlineView reloadData];
6729    /* We save all of the preset data here */
6730     [self savePreset];
6731 }
6732
6733 - (void)sortPresets
6734 {
6735
6736         
6737         /* We Sort the Presets By Factory or Custom */
6738         NSSortDescriptor * presetTypeDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"Type" 
6739                                                     ascending:YES] autorelease];
6740         /* We Sort the Presets Alphabetically by name  We do not use this now as we have drag and drop*/
6741         /*
6742     NSSortDescriptor * presetNameDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"PresetName" 
6743                                                     ascending:YES selector:@selector(caseInsensitiveCompare:)] autorelease];
6744         //NSArray *sortDescriptors=[NSArray arrayWithObjects:presetTypeDescriptor,presetNameDescriptor,nil];
6745     
6746     */
6747     /* Since we can drag and drop our custom presets, lets just sort by type and not name */
6748     NSArray *sortDescriptors=[NSArray arrayWithObjects:presetTypeDescriptor,nil];
6749         NSArray *sortedArray=[UserPresets sortedArrayUsingDescriptors:sortDescriptors];
6750         [UserPresets setArray:sortedArray];
6751         
6752
6753 }
6754
6755 - (IBAction)insertPreset:(id)sender
6756 {
6757     int index = [fPresetsOutlineView selectedRow];
6758     [UserPresets insertObject:[self createPreset] atIndex:index];
6759     [fPresetsOutlineView reloadData];
6760     [self savePreset];
6761 }
6762
6763 - (NSDictionary *)createPreset
6764 {
6765     NSMutableDictionary *preset = [[NSMutableDictionary alloc] init];
6766     /* Preset build number */
6767     [preset setObject:[NSString stringWithFormat: @"%d", [[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] intValue]] forKey:@"PresetBuildNumber"];
6768     [preset setObject:[fPresetNewName stringValue] forKey:@"PresetName"];
6769         /* Get the New Preset Name from the field in the AddPresetPanel */
6770     [preset setObject:[fPresetNewName stringValue] forKey:@"PresetName"];
6771     /* Set whether or not this is to be a folder fPresetNewFolderCheck*/
6772     [preset setObject:[NSNumber numberWithBool:[fPresetNewFolderCheck state]] forKey:@"Folder"];
6773         /*Set whether or not this is a user preset or factory 0 is factory, 1 is user*/
6774         [preset setObject:[NSNumber numberWithInt:1] forKey:@"Type"];
6775         /*Set whether or not this is default, at creation set to 0*/
6776         [preset setObject:[NSNumber numberWithInt:0] forKey:@"Default"];
6777     if ([fPresetNewFolderCheck state] == YES)
6778     {
6779         /* initialize and set an empty array for children here since we are a new folder */
6780         NSMutableArray *childrenArray = [[NSMutableArray alloc] init];
6781         [preset setObject:[NSMutableArray arrayWithArray: childrenArray] forKey:@"ChildrenArray"];
6782         [childrenArray autorelease];
6783     }
6784     else // we are not creating a preset folder, so we go ahead with the rest of the preset info
6785     {
6786         /*Get the whether or not to apply pic Size and Cropping (includes Anamorphic)*/
6787         [preset setObject:[NSNumber numberWithInt:[fPresetNewPicSettingsPopUp indexOfSelectedItem]] forKey:@"UsesPictureSettings"];
6788         /* Get whether or not to use the current Picture Filter settings for the preset */
6789         [preset setObject:[NSNumber numberWithInt:[fPresetNewPicFiltersCheck state]] forKey:@"UsesPictureFilters"];
6790         
6791         /* Get New Preset Description from the field in the AddPresetPanel*/
6792         [preset setObject:[fPresetNewDesc stringValue] forKey:@"PresetDescription"];
6793         /* File Format */
6794         [preset setObject:[fDstFormatPopUp titleOfSelectedItem] forKey:@"FileFormat"];
6795         /* Chapter Markers fCreateChapterMarkers*/
6796         [preset setObject:[NSNumber numberWithInt:[fCreateChapterMarkers state]] forKey:@"ChapterMarkers"];
6797         /* Allow Mpeg4 64 bit formatting +4GB file sizes */
6798         [preset setObject:[NSNumber numberWithInt:[fDstMp4LargeFileCheck state]] forKey:@"Mp4LargeFile"];
6799         /* Mux mp4 with http optimization */
6800         [preset setObject:[NSNumber numberWithInt:[fDstMp4HttpOptFileCheck state]] forKey:@"Mp4HttpOptimize"];
6801         /* Add iPod uuid atom */
6802         [preset setObject:[NSNumber numberWithInt:[fDstMp4iPodFileCheck state]] forKey:@"Mp4iPodCompatible"];
6803         
6804         /* Codecs */
6805         /* Video encoder */
6806         [preset setObject:[fVidEncoderPopUp titleOfSelectedItem] forKey:@"VideoEncoder"];
6807         /* x264 Option String */
6808         [preset setObject:[fAdvancedOptions optionsString] forKey:@"x264Option"];
6809         
6810         [preset setObject:[NSNumber numberWithInt:[fVidQualityMatrix selectedRow]] forKey:@"VideoQualityType"];
6811         [preset setObject:[fVidTargetSizeField stringValue] forKey:@"VideoTargetSize"];
6812         [preset setObject:[fVidBitrateField stringValue] forKey:@"VideoAvgBitrate"];
6813         [preset setObject:[NSNumber numberWithFloat:[fVidQualityRFField floatValue]] forKey:@"VideoQualitySlider"];
6814         
6815         /* Video framerate */
6816         if ([fVidRatePopUp indexOfSelectedItem] == 0) // Same as source is selected
6817         {
6818             [preset setObject:@"Same as source" forKey:@"VideoFramerate"];
6819         }
6820         else // we can record the actual titleOfSelectedItem
6821         {
6822             [preset setObject:[fVidRatePopUp titleOfSelectedItem] forKey:@"VideoFramerate"];
6823         }
6824         
6825         /* 2 Pass Encoding */
6826         [preset setObject:[NSNumber numberWithInt:[fVidTwoPassCheck state]] forKey:@"VideoTwoPass"];
6827         /* Turbo 2 pass Encoding fVidTurboPassCheck*/
6828         [preset setObject:[NSNumber numberWithInt:[fVidTurboPassCheck state]] forKey:@"VideoTurboTwoPass"];
6829         /*Picture Settings*/
6830         hb_job_t * job = fTitle->job;
6831         /* Picture Sizing */
6832         /* Use Max Picture settings for whatever the dvd is.*/
6833         [preset setObject:[NSNumber numberWithInt:0] forKey:@"UsesMaxPictureSettings"];
6834         [preset setObject:[NSNumber numberWithInt:fTitle->job->width] forKey:@"PictureWidth"];
6835         [preset setObject:[NSNumber numberWithInt:fTitle->job->height] forKey:@"PictureHeight"];
6836         [preset setObject:[NSNumber numberWithInt:fTitle->job->keep_ratio] forKey:@"PictureKeepRatio"];
6837         [preset setObject:[NSNumber numberWithInt:fTitle->job->anamorphic.mode] forKey:@"PicturePAR"];
6838         
6839         /* Set crop settings here */
6840         [preset setObject:[NSNumber numberWithInt:[fPictureController autoCrop]] forKey:@"PictureAutoCrop"];
6841         [preset setObject:[NSNumber numberWithInt:job->crop[0]] forKey:@"PictureTopCrop"];
6842         [preset setObject:[NSNumber numberWithInt:job->crop[1]] forKey:@"PictureBottomCrop"];
6843         [preset setObject:[NSNumber numberWithInt:job->crop[2]] forKey:@"PictureLeftCrop"];
6844         [preset setObject:[NSNumber numberWithInt:job->crop[3]] forKey:@"PictureRightCrop"];
6845         
6846         /* Picture Filters */
6847         [preset setObject:[NSNumber numberWithInt:[fPictureController useDecomb]] forKey:@"PictureDecombDeinterlace"];
6848         [preset setObject:[NSNumber numberWithInt:[fPictureController deinterlace]] forKey:@"PictureDeinterlace"];
6849         [preset setObject:[fPictureController deinterlaceCustomString] forKey:@"PictureDeinterlaceCustom"];
6850         [preset setObject:[NSNumber numberWithInt:[fPictureController detelecine]] forKey:@"PictureDetelecine"];
6851         [preset setObject:[fPictureController detelecineCustomString] forKey:@"PictureDetelecineCustom"];
6852         [preset setObject:[NSNumber numberWithInt:[fPictureController denoise]] forKey:@"PictureDenoise"];
6853         [preset setObject:[fPictureController denoiseCustomString] forKey:@"PictureDenoiseCustom"];
6854         [preset setObject:[NSNumber numberWithInt:[fPictureController deblock]] forKey:@"PictureDeblock"]; 
6855         [preset setObject:[NSNumber numberWithInt:[fPictureController decomb]] forKey:@"PictureDecomb"];
6856         [preset setObject:[fPictureController decombCustomString] forKey:@"PictureDecombCustom"];
6857         [preset setObject:[NSNumber numberWithInt:[fPictureController grayscale]] forKey:@"VideoGrayScale"];
6858         
6859         /*Audio*/
6860         NSMutableArray *audioListArray = [[NSMutableArray alloc] init];
6861         /* we actually call the methods for the nests here */
6862         if ([fAudLang1PopUp indexOfSelectedItem] > 0)
6863         {
6864             NSMutableDictionary *audioTrack1Array = [[NSMutableDictionary alloc] init];
6865             [audioTrack1Array setObject:[NSNumber numberWithInt:[fAudLang1PopUp indexOfSelectedItem]] forKey:@"AudioTrack"];
6866             [audioTrack1Array setObject:[fAudLang1PopUp titleOfSelectedItem] forKey:@"AudioTrackDescription"];
6867             [audioTrack1Array setObject:[fAudTrack1CodecPopUp titleOfSelectedItem] forKey:@"AudioEncoder"];
6868             [audioTrack1Array setObject:[fAudTrack1MixPopUp titleOfSelectedItem] forKey:@"AudioMixdown"];
6869             [audioTrack1Array setObject:[fAudTrack1RatePopUp titleOfSelectedItem] forKey:@"AudioSamplerate"];
6870             [audioTrack1Array setObject:[fAudTrack1BitratePopUp titleOfSelectedItem] forKey:@"AudioBitrate"];
6871             [audioTrack1Array setObject:[NSNumber numberWithFloat:[fAudTrack1DrcSlider floatValue]] forKey:@"AudioTrackDRCSlider"];
6872             [audioTrack1Array autorelease];
6873             [audioListArray addObject:audioTrack1Array];
6874         }
6875         
6876         if ([fAudLang2PopUp indexOfSelectedItem] > 0)
6877         {
6878             NSMutableDictionary *audioTrack2Array = [[NSMutableDictionary alloc] init];
6879             [audioTrack2Array setObject:[NSNumber numberWithInt:[fAudLang2PopUp indexOfSelectedItem]] forKey:@"AudioTrack"];
6880             [audioTrack2Array setObject:[fAudLang2PopUp titleOfSelectedItem] forKey:@"AudioTrackDescription"];
6881             [audioTrack2Array setObject:[fAudTrack2CodecPopUp titleOfSelectedItem] forKey:@"AudioEncoder"];
6882             [audioTrack2Array setObject:[fAudTrack2MixPopUp titleOfSelectedItem] forKey:@"AudioMixdown"];
6883             [audioTrack2Array setObject:[fAudTrack2RatePopUp titleOfSelectedItem] forKey:@"AudioSamplerate"];
6884             [audioTrack2Array setObject:[fAudTrack2BitratePopUp titleOfSelectedItem] forKey:@"AudioBitrate"];
6885             [audioTrack2Array setObject:[NSNumber numberWithFloat:[fAudTrack2DrcSlider floatValue]] forKey:@"AudioTrackDRCSlider"];
6886             [audioTrack2Array autorelease];
6887             [audioListArray addObject:audioTrack2Array];
6888         }
6889         
6890         if ([fAudLang3PopUp indexOfSelectedItem] > 0)
6891         {
6892             NSMutableDictionary *audioTrack3Array = [[NSMutableDictionary alloc] init];
6893             [audioTrack3Array setObject:[NSNumber numberWithInt:[fAudLang3PopUp indexOfSelectedItem]] forKey:@"AudioTrack"];
6894             [audioTrack3Array setObject:[fAudLang3PopUp titleOfSelectedItem] forKey:@"AudioTrackDescription"];
6895             [audioTrack3Array setObject:[fAudTrack3CodecPopUp titleOfSelectedItem] forKey:@"AudioEncoder"];
6896             [audioTrack3Array setObject:[fAudTrack3MixPopUp titleOfSelectedItem] forKey:@"AudioMixdown"];
6897             [audioTrack3Array setObject:[fAudTrack3RatePopUp titleOfSelectedItem] forKey:@"AudioSamplerate"];
6898             [audioTrack3Array setObject:[fAudTrack3BitratePopUp titleOfSelectedItem] forKey:@"AudioBitrate"];
6899             [audioTrack3Array setObject:[NSNumber numberWithFloat:[fAudTrack3DrcSlider floatValue]] forKey:@"AudioTrackDRCSlider"];
6900             [audioTrack3Array autorelease];
6901             [audioListArray addObject:audioTrack3Array];
6902         }
6903         
6904         if ([fAudLang4PopUp indexOfSelectedItem] > 0)
6905         {
6906             NSMutableDictionary *audioTrack4Array = [[NSMutableDictionary alloc] init];
6907             [audioTrack4Array setObject:[NSNumber numberWithInt:[fAudLang4PopUp indexOfSelectedItem]] forKey:@"AudioTrack"];
6908             [audioTrack4Array setObject:[fAudLang4PopUp titleOfSelectedItem] forKey:@"AudioTrackDescription"];
6909             [audioTrack4Array setObject:[fAudTrack4CodecPopUp titleOfSelectedItem] forKey:@"AudioEncoder"];
6910             [audioTrack4Array setObject:[fAudTrack4MixPopUp titleOfSelectedItem] forKey:@"AudioMixdown"];
6911             [audioTrack4Array setObject:[fAudTrack4RatePopUp titleOfSelectedItem] forKey:@"AudioSamplerate"];
6912             [audioTrack4Array setObject:[fAudTrack4BitratePopUp titleOfSelectedItem] forKey:@"AudioBitrate"];
6913             [audioTrack4Array setObject:[NSNumber numberWithFloat:[fAudTrack4DrcSlider floatValue]] forKey:@"AudioTrackDRCSlider"];
6914             [audioTrack4Array autorelease];
6915             [audioListArray addObject:audioTrack4Array];
6916         }
6917         
6918         
6919         [preset setObject:[NSMutableArray arrayWithArray: audioListArray] forKey:@"AudioList"];
6920
6921         
6922         /* Temporarily remove subtitles from creating a new preset as it has to be converted over to use the new
6923          * subititle array code. */
6924         /* Subtitles*/
6925         //[preset setObject:[fSubPopUp titleOfSelectedItem] forKey:@"Subtitles"];
6926         /* Forced Subtitles */
6927         //[preset setObject:[NSNumber numberWithInt:[fSubForcedCheck state]] forKey:@"SubtitlesForced"];
6928     }
6929     [preset autorelease];
6930     return preset;
6931     
6932 }
6933
6934 - (void)savePreset
6935 {
6936     [UserPresets writeToFile:UserPresetsFile atomically:YES];
6937         /* We get the default preset in case it changed */
6938         [self getDefaultPresets:nil];
6939
6940 }
6941
6942 - (IBAction)deletePreset:(id)sender
6943 {
6944     
6945     
6946     if ( [fPresetsOutlineView numberOfSelectedRows] == 0 )
6947     {
6948         return;
6949     }
6950     /* Alert user before deleting preset */
6951         int status;
6952     status = NSRunAlertPanel(@"Warning!", @"Are you sure that you want to delete the selected preset?", @"OK", @"Cancel", nil);
6953     
6954     if ( status == NSAlertDefaultReturn ) 
6955     {
6956         int presetToModLevel = [fPresetsOutlineView levelForItem: [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]]];
6957         NSDictionary *presetToMod = [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]];
6958         NSDictionary *presetToModParent = [fPresetsOutlineView parentForItem: presetToMod];
6959         
6960         NSEnumerator *enumerator;
6961         NSMutableArray *presetsArrayToMod;
6962         NSMutableArray *tempArray;
6963         id tempObject;
6964         /* If we are a root level preset, we are modding the UserPresets array */
6965         if (presetToModLevel == 0)
6966         {
6967             presetsArrayToMod = UserPresets;
6968         }
6969         else // We have a parent preset, so we modify the chidren array object for key
6970         {
6971             presetsArrayToMod = [presetToModParent objectForKey:@"ChildrenArray"]; 
6972         }
6973         
6974         enumerator = [presetsArrayToMod objectEnumerator];
6975         tempArray = [NSMutableArray array];
6976         
6977         while (tempObject = [enumerator nextObject]) 
6978         {
6979             NSDictionary *thisPresetDict = tempObject;
6980             if (thisPresetDict == presetToMod)
6981             {
6982                 [tempArray addObject:tempObject];
6983             }
6984         }
6985         
6986         [presetsArrayToMod removeObjectsInArray:tempArray];
6987         [fPresetsOutlineView reloadData];
6988         [self savePreset];   
6989     }
6990 }
6991
6992
6993 #pragma mark -
6994 #pragma mark Import Export Preset(s)
6995
6996 - (IBAction) browseExportPresetFile: (id) sender
6997 {
6998     /* Open a panel to let the user choose where and how to save the export file */
6999     NSSavePanel * panel = [NSSavePanel savePanel];
7000         /* We get the current file name and path from the destination field here */
7001     NSString *defaultExportDirectory = [NSString stringWithFormat: @"%@/Desktop/", NSHomeDirectory()];
7002
7003         [panel beginSheetForDirectory: defaultExportDirectory file: @"HB_Export.plist"
7004                                    modalForWindow: fWindow modalDelegate: self
7005                                    didEndSelector: @selector( browseExportPresetFileDone:returnCode:contextInfo: )
7006                                           contextInfo: NULL];
7007 }
7008
7009 - (void) browseExportPresetFileDone: (NSSavePanel *) sheet
7010                    returnCode: (int) returnCode contextInfo: (void *) contextInfo
7011 {
7012     if( returnCode == NSOKButton )
7013     {
7014         NSString *presetExportDirectory = [[sheet filename] stringByDeletingLastPathComponent];
7015         NSString *exportPresetsFile = [sheet filename];
7016         [[NSUserDefaults standardUserDefaults] setObject:presetExportDirectory forKey:@"LastPresetExportDirectory"];
7017         /* We check for the presets.plist */
7018         if ([[NSFileManager defaultManager] fileExistsAtPath:exportPresetsFile] == 0)
7019         {
7020             [[NSFileManager defaultManager] createFileAtPath:exportPresetsFile contents:nil attributes:nil];
7021         }
7022         NSMutableArray * presetsToExport = [[NSMutableArray alloc] initWithContentsOfFile:exportPresetsFile];
7023         if (nil == presetsToExport)
7024         {
7025             presetsToExport = [[NSMutableArray alloc] init];
7026             
7027             /* now get and add selected presets to export */
7028             
7029         }
7030         if ([fPresetsOutlineView selectedRow] >= 0 && [[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Folder"] intValue] != 1)
7031         {
7032             [presetsToExport addObject:[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]]];
7033             [presetsToExport writeToFile:exportPresetsFile atomically:YES];
7034             
7035         }
7036         
7037     }
7038 }
7039
7040
7041 - (IBAction) browseImportPresetFile: (id) sender
7042 {
7043
7044     NSOpenPanel * panel;
7045         
7046     panel = [NSOpenPanel openPanel];
7047     [panel setAllowsMultipleSelection: NO];
7048     [panel setCanChooseFiles: YES];
7049     [panel setCanChooseDirectories: NO ];
7050     NSString * sourceDirectory;
7051         if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastPresetImportDirectory"])
7052         {
7053                 sourceDirectory = [[NSUserDefaults standardUserDefaults] stringForKey:@"LastPresetImportDirectory"];
7054         }
7055         else
7056         {
7057                 sourceDirectory = @"~/Desktop";
7058                 sourceDirectory = [sourceDirectory stringByExpandingTildeInPath];
7059         }
7060     /* we open up the browse sources sheet here and call for browseSourcesDone after the sheet is closed
7061         * to evaluate whether we want to specify a title, we pass the sender in the contextInfo variable
7062         */
7063     /* set this for allowed file types, not sure if we should allow xml or not */
7064     NSArray *fileTypes = [NSArray arrayWithObjects:@"plist", @"xml", nil];
7065     [panel beginSheetForDirectory: sourceDirectory file: nil types: fileTypes
7066                    modalForWindow: fWindow modalDelegate: self
7067                    didEndSelector: @selector( browseImportPresetDone:returnCode:contextInfo: )
7068                       contextInfo: sender];
7069 }
7070
7071 - (void) browseImportPresetDone: (NSSavePanel *) sheet
7072                      returnCode: (int) returnCode contextInfo: (void *) contextInfo
7073 {
7074     if( returnCode == NSOKButton )
7075     {
7076         NSString *importPresetsDirectory = [[sheet filename] stringByDeletingLastPathComponent];
7077         NSString *importPresetsFile = [sheet filename];
7078         [[NSUserDefaults standardUserDefaults] setObject:importPresetsDirectory forKey:@"LastPresetImportDirectory"];
7079         /* NOTE: here we need to do some sanity checking to verify we do not hose up our presets file   */
7080         NSMutableArray * presetsToImport = [[NSMutableArray alloc] initWithContentsOfFile:importPresetsFile];
7081         /* iterate though the new array of presets to import and add them to our presets array */
7082         int i = 0;
7083         NSEnumerator *enumerator = [presetsToImport objectEnumerator];
7084         id tempObject;
7085         while (tempObject = [enumerator nextObject])
7086         {
7087             /* make any changes to the incoming preset we see fit */
7088             /* make sure the incoming preset is not tagged as default */
7089             [tempObject setObject:[NSNumber numberWithInt:0] forKey:@"Default"];
7090             /* prepend "(imported) to the name of the incoming preset for clarification since it can be changed */
7091             NSString * prependedName = [@"(import) " stringByAppendingString:[tempObject objectForKey:@"PresetName"]] ;
7092             [tempObject setObject:prependedName forKey:@"PresetName"];
7093             
7094             /* actually add the new preset to our presets array */
7095             [UserPresets addObject:tempObject];
7096             i++;
7097         }
7098         [presetsToImport autorelease];
7099         [self sortPresets];
7100         [self addPreset];
7101         
7102     }
7103 }
7104
7105 #pragma mark -
7106 #pragma mark Manage Default Preset
7107
7108 - (IBAction)getDefaultPresets:(id)sender
7109 {
7110         presetHbDefault = nil;
7111     presetUserDefault = nil;
7112     presetUserDefaultParent = nil;
7113     presetUserDefaultParentParent = nil;
7114     NSMutableDictionary *presetHbDefaultParent = nil;
7115     NSMutableDictionary *presetHbDefaultParentParent = nil;
7116     
7117     int i = 0;
7118     BOOL userDefaultFound = NO;
7119     presetCurrentBuiltInCount = 0;
7120     /* First we iterate through the root UserPresets array to check for defaults */
7121     NSEnumerator *enumerator = [UserPresets objectEnumerator];
7122         id tempObject;
7123         while (tempObject = [enumerator nextObject])
7124         {
7125                 NSMutableDictionary *thisPresetDict = tempObject;
7126                 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 1) // 1 is HB default
7127                 {
7128                         presetHbDefault = thisPresetDict;       
7129                 }
7130                 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 2) // 2 is User specified default
7131                 {
7132                         presetUserDefault = thisPresetDict;
7133             userDefaultFound = YES;
7134         }
7135         if ([[thisPresetDict objectForKey:@"Type"] intValue] == 0) // Type 0 is a built in preset               
7136         {
7137                         presetCurrentBuiltInCount++; // <--increment the current number of built in presets     
7138                 }
7139                 i++;
7140         
7141         /* if we run into a folder, go to level 1 and iterate through the children arrays for the default */
7142         if ([thisPresetDict objectForKey:@"ChildrenArray"])
7143         {
7144             NSMutableDictionary *thisPresetDictParent = thisPresetDict;
7145             NSEnumerator *enumerator = [[thisPresetDict objectForKey:@"ChildrenArray"] objectEnumerator];
7146             id tempObject;
7147             while (tempObject = [enumerator nextObject])
7148             {
7149                 NSMutableDictionary *thisPresetDict = tempObject;
7150                 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 1) // 1 is HB default
7151                 {
7152                     presetHbDefault = thisPresetDict;
7153                     presetHbDefaultParent = thisPresetDictParent;
7154                 }
7155                 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 2) // 2 is User specified default
7156                 {
7157                     presetUserDefault = thisPresetDict;
7158                     presetUserDefaultParent = thisPresetDictParent;
7159                     userDefaultFound = YES;
7160                 }
7161                 
7162                 /* if we run into a folder, go to level 2 and iterate through the children arrays for the default */
7163                 if ([thisPresetDict objectForKey:@"ChildrenArray"])
7164                 {
7165                     NSMutableDictionary *thisPresetDictParentParent = thisPresetDict;
7166                     NSEnumerator *enumerator = [[thisPresetDict objectForKey:@"ChildrenArray"] objectEnumerator];
7167                     id tempObject;
7168                     while (tempObject = [enumerator nextObject])
7169                     {
7170                         NSMutableDictionary *thisPresetDict = tempObject;
7171                         if ([[thisPresetDict objectForKey:@"Default"] intValue] == 1) // 1 is HB default
7172                         {
7173                             presetHbDefault = thisPresetDict;
7174                             presetHbDefaultParent = thisPresetDictParent;
7175                             presetHbDefaultParentParent = thisPresetDictParentParent;   
7176                         }
7177                         if ([[thisPresetDict objectForKey:@"Default"] intValue] == 2) // 2 is User specified default
7178                         {
7179                             presetUserDefault = thisPresetDict;
7180                             presetUserDefaultParent = thisPresetDictParent;
7181                             presetUserDefaultParentParent = thisPresetDictParentParent;
7182                             userDefaultFound = YES;     
7183                         }
7184                         
7185                     }
7186                 }
7187             }
7188         }
7189         
7190         }
7191     /* check to see if a user specified preset was found, if not then assign the parents for
7192      * the presetHbDefault so that we can open the parents for the nested presets
7193      */
7194     if (userDefaultFound == NO)
7195     {
7196         presetUserDefaultParent = presetHbDefaultParent;
7197         presetUserDefaultParentParent = presetHbDefaultParentParent;
7198     }
7199 }
7200
7201 - (IBAction)setDefaultPreset:(id)sender
7202 {
7203 /* We need to determine if the item is a folder */
7204    if ([[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Folder"] intValue] == 1)
7205    {
7206    return;
7207    }
7208
7209     int i = 0;
7210     NSEnumerator *enumerator = [UserPresets objectEnumerator];
7211         id tempObject;
7212         /* First make sure the old user specified default preset is removed */
7213     while (tempObject = [enumerator nextObject])
7214         {
7215                 NSMutableDictionary *thisPresetDict = tempObject;
7216                 if ([[tempObject objectForKey:@"Default"] intValue] != 1) // if not the default HB Preset, set to 0
7217                 {
7218                         [[UserPresets objectAtIndex:i] setObject:[NSNumber numberWithInt:0] forKey:@"Default"]; 
7219                 }
7220                 
7221                 /* if we run into a folder, go to level 1 and iterate through the children arrays for the default */
7222         if ([thisPresetDict objectForKey:@"ChildrenArray"])
7223         {
7224             NSEnumerator *enumerator = [[thisPresetDict objectForKey:@"ChildrenArray"] objectEnumerator];
7225             id tempObject;
7226             int ii = 0;
7227             while (tempObject = [enumerator nextObject])
7228             {
7229                 NSMutableDictionary *thisPresetDict1 = tempObject;
7230                 if ([[tempObject objectForKey:@"Default"] intValue] != 1) // if not the default HB Preset, set to 0
7231                 {
7232                     [[[thisPresetDict objectForKey:@"ChildrenArray"] objectAtIndex:ii] setObject:[NSNumber numberWithInt:0] forKey:@"Default"]; 
7233                 }
7234                 /* if we run into a folder, go to level 2 and iterate through the children arrays for the default */
7235                 if ([thisPresetDict1 objectForKey:@"ChildrenArray"])
7236                 {
7237                     NSEnumerator *enumerator = [[thisPresetDict1 objectForKey:@"ChildrenArray"] objectEnumerator];
7238                     id tempObject;
7239                     int iii = 0;
7240                     while (tempObject = [enumerator nextObject])
7241                     {
7242                         if ([[tempObject objectForKey:@"Default"] intValue] != 1) // if not the default HB Preset, set to 0
7243                         {
7244                             [[[thisPresetDict1 objectForKey:@"ChildrenArray"] objectAtIndex:iii] setObject:[NSNumber numberWithInt:0] forKey:@"Default"];       
7245                         }
7246                         iii++;
7247                     }
7248                 }
7249                 ii++;
7250             }
7251             
7252         }
7253         i++; 
7254         }
7255     
7256     
7257     int presetToModLevel = [fPresetsOutlineView levelForItem: [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]]];
7258     NSDictionary *presetToMod = [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]];
7259     NSDictionary *presetToModParent = [fPresetsOutlineView parentForItem: presetToMod];
7260     
7261     
7262     NSMutableArray *presetsArrayToMod;
7263     NSMutableArray *tempArray;
7264     
7265     /* If we are a root level preset, we are modding the UserPresets array */
7266     if (presetToModLevel == 0)
7267     {
7268         presetsArrayToMod = UserPresets;
7269     }
7270     else // We have a parent preset, so we modify the chidren array object for key
7271     {
7272         presetsArrayToMod = [presetToModParent objectForKey:@"ChildrenArray"]; 
7273     }
7274     
7275     enumerator = [presetsArrayToMod objectEnumerator];
7276     tempArray = [NSMutableArray array];
7277     int iiii = 0;
7278     while (tempObject = [enumerator nextObject]) 
7279     {
7280         NSDictionary *thisPresetDict = tempObject;
7281         if (thisPresetDict == presetToMod)
7282         {
7283             if ([[tempObject objectForKey:@"Default"] intValue] != 1) // if not the default HB Preset, set to 2
7284             {
7285                 [[presetsArrayToMod objectAtIndex:iiii] setObject:[NSNumber numberWithInt:2] forKey:@"Default"];        
7286             }
7287         }
7288      iiii++;
7289      }
7290     
7291     
7292     /* We save all of the preset data here */
7293     [self savePreset];
7294     /* We Reload the New Table data for presets */
7295     [fPresetsOutlineView reloadData];
7296 }
7297
7298 - (IBAction)selectDefaultPreset:(id)sender
7299 {
7300         NSMutableDictionary *presetToMod;
7301     /* if there is a user specified default, we use it */
7302         if (presetUserDefault)
7303         {
7304         presetToMod = presetUserDefault;
7305     }
7306         else if (presetHbDefault) //else we use the built in default presetHbDefault
7307         {
7308         presetToMod = presetHbDefault;
7309         }
7310     else
7311     {
7312     return;
7313     }
7314     
7315     if (presetUserDefaultParent != nil)
7316     {
7317         [fPresetsOutlineView expandItem:presetUserDefaultParent];
7318         
7319     }
7320     if (presetUserDefaultParentParent != nil)
7321     {
7322         [fPresetsOutlineView expandItem:presetUserDefaultParentParent];
7323         
7324     }
7325     
7326     [fPresetsOutlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:[fPresetsOutlineView rowForItem: presetToMod]] byExtendingSelection:NO];
7327         [self selectPreset:nil];
7328 }
7329
7330
7331 #pragma mark -
7332 #pragma mark Manage Built In Presets
7333
7334
7335 - (IBAction)deleteFactoryPresets:(id)sender
7336 {
7337     //int status;
7338     NSEnumerator *enumerator = [UserPresets objectEnumerator];
7339         id tempObject;
7340     
7341         //NSNumber *index;
7342     NSMutableArray *tempArray;
7343
7344
7345         tempArray = [NSMutableArray array];
7346         /* we look here to see if the preset is we move on to the next one */
7347         while ( tempObject = [enumerator nextObject] )  
7348                 {
7349                         /* if the preset is "Factory" then we put it in the array of
7350                         presets to delete */
7351                         if ([[tempObject objectForKey:@"Type"] intValue] == 0)
7352                         {
7353                                 [tempArray addObject:tempObject];
7354                         }
7355         }
7356         
7357         [UserPresets removeObjectsInArray:tempArray];
7358         [fPresetsOutlineView reloadData];
7359         [self savePreset];   
7360
7361 }
7362
7363    /* We use this method to recreate new, updated factory presets */
7364 - (IBAction)addFactoryPresets:(id)sender
7365 {
7366     
7367     /* First, we delete any existing built in presets */
7368     [self deleteFactoryPresets: sender];
7369     /* Then we generate new built in presets programmatically with fPresetsBuiltin
7370      * which is all setup in HBPresets.h and  HBPresets.m*/
7371     [fPresetsBuiltin generateBuiltinPresets:UserPresets];
7372     /* update build number for built in presets */
7373     /* iterate though the new array of presets to import and add them to our presets array */
7374     int i = 0;
7375     NSEnumerator *enumerator = [UserPresets objectEnumerator];
7376     id tempObject;
7377     while (tempObject = [enumerator nextObject])
7378     {
7379         /* Record the apps current build number in the PresetBuildNumber key */
7380         if ([[tempObject objectForKey:@"Type"] intValue] == 0) // Type 0 is a built in preset           
7381         {
7382             /* Preset build number */
7383             [[UserPresets objectAtIndex:i] setObject:[NSNumber numberWithInt:[[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] intValue]] forKey:@"PresetBuildNumber"];
7384         }
7385         i++;
7386     }
7387     /* report the built in preset updating to the activity log */
7388     [self writeToActivityLog: "built in presets updated to build number: %d", [[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] intValue]];
7389     
7390     [self sortPresets];
7391     [self addPreset];
7392     
7393 }
7394
7395
7396 @end
7397
7398 /*******************************
7399  * Subclass of the HBPresetsOutlineView *
7400  *******************************/
7401
7402 @implementation HBPresetsOutlineView
7403 - (NSImage *)dragImageForRowsWithIndexes:(NSIndexSet *)dragRows tableColumns:(NSArray *)tableColumns event:(NSEvent*)dragEvent offset:(NSPointPointer)dragImageOffset
7404 {
7405     fIsDragging = YES;
7406
7407     // By default, NSTableView only drags an image of the first column. Change this to
7408     // drag an image of the queue's icon and PresetName columns.
7409     NSArray * cols = [NSArray arrayWithObjects: [self tableColumnWithIdentifier:@"PresetName"], nil];
7410     return [super dragImageForRowsWithIndexes:dragRows tableColumns:cols event:dragEvent offset:dragImageOffset];
7411 }
7412
7413
7414
7415 - (void) mouseDown:(NSEvent *)theEvent
7416 {
7417     [super mouseDown:theEvent];
7418         fIsDragging = NO;
7419 }
7420
7421
7422
7423 - (BOOL) isDragging;
7424 {
7425     return fIsDragging;
7426 }
7427 @end
7428
7429
7430