OSDN Git Service

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