OSDN Git Service

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