OSDN Git Service

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