OSDN Git Service

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