OSDN Git Service

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