OSDN Git Service

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