OSDN Git Service

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