OSDN Git Service

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