OSDN Git Service

MacGui: Multiple subtitle tracks initial implementation
[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             hb_subtitle_t * subt;
2840             hb_subtitle_config_t sub_config;
2841             
2842             subt = (hb_subtitle_t *)hb_list_item(title->list_subtitle, subtitle);
2843             sub_config = subt->config;
2844             
2845             /* if i is 0, then we are in the first item of the subtitles which we need to 
2846              * check for the "Foreign Audio Search" which would be subtitleSourceTrackNum of 1
2847              * bearing in mind that for all tracks subtitleSourceTrackNum of 0 is None.
2848              */
2849             
2850             /* if we are on the first track and using "Foreign Audio Search" */ 
2851             if (i == 0 && subtitle == 1)
2852             {
2853                 /* NOTE: Currently foreign language search is borked for preview.
2854                  * Commented out but left in for initial commit. */
2855                 
2856                 /*
2857                 [self writeToActivityLog: "Foreign Language Search: %d", 1];
2858                 
2859                 job->indepth_scan = 1;
2860                 if (burned == 1 || job->mux != HB_MUX_MP4)
2861                 {
2862                     if (burned != 1 && job->mux == HB_MUX_MKV)
2863                     {
2864                         job->select_subtitle_config.dest = hb_subtitle_config_s::PASSTHRUSUB;
2865                     }
2866                     
2867                     job->select_subtitle_config.force = force;
2868                     job->select_subtitle_config.default_track = def;
2869                     
2870                 }
2871                */ 
2872                 
2873             }
2874             else
2875             {
2876                 
2877                 /* for the actual source tracks, we must subtract the non source entries so 
2878                  * that the menu index matches the source subtitle_list index for convenience */
2879                 if (i == 0)
2880                 {
2881                     /* for the first track, the source tracks start at menu index 2 ( None is 0,
2882                      * Foreign Language Search is 1) so subtract 2 */
2883                     subtitle = subtitle - 2;
2884                 }
2885                 else
2886                 {
2887                     /* for all other tracks, the source tracks start at menu index 1 (None is 0)
2888                      * so subtract 1. */
2889                     
2890                     subtitle = subtitle - 1;
2891                 }
2892                 
2893                 /* We are setting a source subtitle so access the source subtitle info */  
2894                 hb_subtitle_t * subt;
2895                 hb_subtitle_config_t sub_config;
2896                 
2897                 subt = (hb_subtitle_t *)hb_list_item(title->list_subtitle, subtitle);
2898                 sub_config = subt->config;
2899                 
2900                 if (subt != NULL)
2901                 {
2902                     [self writeToActivityLog: "Setting Subtitle: %s", subt];
2903                     
2904                     if (!burned && job->mux == HB_MUX_MKV && 
2905                         subt->format == hb_subtitle_s::PICTURESUB)
2906                     {
2907                         sub_config.dest = hb_subtitle_config_s::PASSTHRUSUB;
2908                     }
2909                     else if (!burned && job->mux == HB_MUX_MP4 && 
2910                              subt->format == hb_subtitle_s::PICTURESUB)
2911                     {
2912                         // Skip any non-burned vobsubs when output is mp4
2913                         continue;
2914                     }
2915                     else if ( burned && subt->format == hb_subtitle_s::PICTURESUB )
2916                     {
2917                         // Only allow one subtitle to be burned into the video
2918                         if (one_burned)
2919                             continue;
2920                         one_burned = TRUE;
2921                     }
2922                     sub_config.force = force;
2923                     sub_config.default_track = def;
2924                     hb_subtitle_add( job, &sub_config, subtitle );
2925                 }   
2926                 
2927             }
2928         }
2929         i++;
2930     }
2931    
2932     
2933     
2934 [subtitlesArray autorelease];    
2935     
2936     
2937     /* Audio tracks and mixdowns */
2938     /* Lets make sure there arent any erroneous audio tracks in the job list, so lets make sure its empty*/
2939     int audiotrack_count = hb_list_count(job->list_audio);
2940     for( int i = 0; i < audiotrack_count;i++)
2941     {
2942         hb_audio_t * temp_audio = (hb_audio_t*) hb_list_item( job->list_audio, 0 );
2943         hb_list_rem(job->list_audio, temp_audio);
2944     }
2945     /* Now lets add our new tracks to the audio list here */
2946     if ([fAudLang1PopUp indexOfSelectedItem] > 0)
2947     {
2948         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
2949         hb_audio_config_init(audio);
2950         audio->in.track = [fAudLang1PopUp indexOfSelectedItem] - 1;
2951         /* We go ahead and assign values to our audio->out.<properties> */
2952         audio->out.track = [fAudLang1PopUp indexOfSelectedItem] - 1;
2953         audio->out.codec = [[fAudTrack1CodecPopUp selectedItem] tag];
2954         audio->out.mixdown = [[fAudTrack1MixPopUp selectedItem] tag];
2955         audio->out.bitrate = [[fAudTrack1BitratePopUp selectedItem] tag];
2956         audio->out.samplerate = [[fAudTrack1RatePopUp selectedItem] tag];
2957         audio->out.dynamic_range_compression = [fAudTrack1DrcField floatValue];
2958         
2959         hb_audio_add( job, audio );
2960         free(audio);
2961     }  
2962     if ([fAudLang2PopUp indexOfSelectedItem] > 0)
2963     {
2964         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
2965         hb_audio_config_init(audio);
2966         audio->in.track = [fAudLang2PopUp indexOfSelectedItem] - 1;
2967         /* We go ahead and assign values to our audio->out.<properties> */
2968         audio->out.track = [fAudLang2PopUp indexOfSelectedItem] - 1;
2969         audio->out.codec = [[fAudTrack2CodecPopUp selectedItem] tag];
2970         audio->out.mixdown = [[fAudTrack2MixPopUp selectedItem] tag];
2971         audio->out.bitrate = [[fAudTrack2BitratePopUp selectedItem] tag];
2972         audio->out.samplerate = [[fAudTrack2RatePopUp selectedItem] tag];
2973         audio->out.dynamic_range_compression = [fAudTrack2DrcField floatValue];
2974         
2975         hb_audio_add( job, audio );
2976         free(audio);
2977         
2978     }
2979     
2980     if ([fAudLang3PopUp indexOfSelectedItem] > 0)
2981     {
2982         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
2983         hb_audio_config_init(audio);
2984         audio->in.track = [fAudLang3PopUp indexOfSelectedItem] - 1;
2985         /* We go ahead and assign values to our audio->out.<properties> */
2986         audio->out.track = [fAudLang3PopUp indexOfSelectedItem] - 1;
2987         audio->out.codec = [[fAudTrack3CodecPopUp selectedItem] tag];
2988         audio->out.mixdown = [[fAudTrack3MixPopUp selectedItem] tag];
2989         audio->out.bitrate = [[fAudTrack3BitratePopUp selectedItem] tag];
2990         audio->out.samplerate = [[fAudTrack3RatePopUp selectedItem] tag];
2991         audio->out.dynamic_range_compression = [fAudTrack3DrcField floatValue];
2992         
2993         hb_audio_add( job, audio );
2994         free(audio);
2995         
2996     }
2997
2998     if ([fAudLang4PopUp indexOfSelectedItem] > 0)
2999     {
3000         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3001         hb_audio_config_init(audio);
3002         audio->in.track = [fAudLang4PopUp indexOfSelectedItem] - 1;
3003         /* We go ahead and assign values to our audio->out.<properties> */
3004         audio->out.track = [fAudLang4PopUp indexOfSelectedItem] - 1;
3005         audio->out.codec = [[fAudTrack4CodecPopUp selectedItem] tag];
3006         audio->out.mixdown = [[fAudTrack4MixPopUp selectedItem] tag];
3007         audio->out.bitrate = [[fAudTrack4BitratePopUp selectedItem] tag];
3008         audio->out.samplerate = [[fAudTrack4RatePopUp selectedItem] tag];
3009         audio->out.dynamic_range_compression = [fAudTrack4DrcField floatValue];
3010         
3011         hb_audio_add( job, audio );
3012         free(audio);
3013         
3014     }
3015
3016     
3017     
3018     /* Filters */
3019     
3020     /* Though Grayscale is not really a filter, per se
3021      * we put it here since its in the filters panel
3022      */
3023      
3024     if ([fPictureController grayscale])
3025     {
3026         job->grayscale = 1;
3027     }
3028     else
3029     {
3030         job->grayscale = 0;
3031     }
3032     
3033     /* Initialize the filters list */
3034     job->filters = hb_list_init();
3035     
3036     /* Now lets call the filters if applicable.
3037     * The order of the filters is critical
3038     */
3039     
3040         /* Detelecine */
3041     if ([fPictureController detelecine] == 1)
3042     {
3043         hb_list_add( job->filters, &hb_filter_detelecine );
3044     }
3045     if ([fPictureController detelecine] == 2)
3046     {
3047         /* use a custom detelecine string */
3048         hb_filter_detelecine.settings = (char *) [[fPictureController detelecineCustomString] UTF8String];
3049         hb_list_add( job->filters, &hb_filter_detelecine );
3050     }
3051     if ([fPictureController useDecomb] == 1)
3052     {
3053         /* Decomb */
3054         if ([fPictureController decomb] == 1)
3055         {
3056             /* Run old deinterlacer fd by default */
3057             //hb_filter_decomb.settings = (char *) [[fPicSettingDecomb stringValue] UTF8String];
3058             hb_list_add( job->filters, &hb_filter_decomb );
3059         }
3060         /* we add the custom string if present */
3061         if ([fPictureController decomb] == 2)
3062         {
3063             /* use a custom decomb string */
3064             hb_filter_decomb.settings = (char *) [[fPictureController decombCustomString] UTF8String];
3065             hb_list_add( job->filters, &hb_filter_decomb );
3066         }
3067     }
3068     else
3069     {
3070         
3071         /* Deinterlace */
3072         if ([fPictureController deinterlace] == 1)
3073         {
3074             /* Run old deinterlacer fd by default */
3075             hb_filter_deinterlace.settings = "-1"; 
3076             hb_list_add( job->filters, &hb_filter_deinterlace );
3077         }
3078         else if ([fPictureController deinterlace] == 2)
3079         {
3080             /* Yadif mode 0 (without spatial deinterlacing.) */
3081             hb_filter_deinterlace.settings = "2"; 
3082             hb_list_add( job->filters, &hb_filter_deinterlace );            
3083         }
3084         else if ([fPictureController deinterlace] == 3)
3085         {
3086             /* Yadif (with spatial deinterlacing) */
3087             hb_filter_deinterlace.settings = "0"; 
3088             hb_list_add( job->filters, &hb_filter_deinterlace );            
3089         }
3090         else if ([fPictureController deinterlace] == 4)
3091         {
3092             /* we add the custom string if present */
3093             hb_filter_deinterlace.settings = (char *) [[fPictureController deinterlaceCustomString] UTF8String];
3094             hb_list_add( job->filters, &hb_filter_deinterlace );            
3095         }
3096         }
3097     
3098     /* Denoise */
3099         if ([fPictureController denoise] == 1) // Weak in popup
3100         {
3101                 hb_filter_denoise.settings = "2:1:2:3"; 
3102         hb_list_add( job->filters, &hb_filter_denoise );        
3103         }
3104         else if ([fPictureController denoise] == 2) // Medium in popup
3105         {
3106                 hb_filter_denoise.settings = "3:2:2:3"; 
3107         hb_list_add( job->filters, &hb_filter_denoise );        
3108         }
3109         else if ([fPictureController denoise] == 3) // Strong in popup
3110         {
3111                 hb_filter_denoise.settings = "7:7:5:5"; 
3112         hb_list_add( job->filters, &hb_filter_denoise );        
3113         }
3114     else if ([fPictureController denoise] == 4) // custom in popup
3115         {
3116                 /* we add the custom string if present */
3117         hb_filter_denoise.settings = (char *) [[fPictureController denoiseCustomString] UTF8String]; 
3118         hb_list_add( job->filters, &hb_filter_denoise );        
3119         }
3120     
3121     /* Deblock  (uses pp7 default) */
3122     /* NOTE: even though there is a valid deblock setting of 0 for the filter, for 
3123      * the macgui's purposes a value of 0 actually means to not even use the filter
3124      * current hb_filter_deblock.settings valid ranges are from 5 - 15 
3125      */
3126     if ([fPictureController deblock] != 0)
3127     {
3128         NSString *deblockStringValue = [NSString stringWithFormat: @"%d",[fPictureController deblock]];
3129         hb_filter_deblock.settings = (char *) [deblockStringValue UTF8String];
3130         hb_list_add( job->filters, &hb_filter_deblock );
3131     }
3132
3133 }
3134
3135
3136 #pragma mark -
3137 #pragma mark Job Handling
3138
3139
3140 - (void) prepareJob
3141 {
3142     
3143     NSMutableDictionary * queueToApply = [QueueFileArray objectAtIndex:currentQueueEncodeIndex];
3144     hb_list_t  * list  = hb_get_titles( fQueueEncodeLibhb );
3145     hb_title_t * title = (hb_title_t *) hb_list_item( list,0 ); // is always zero since now its a single title scan
3146     hb_job_t * job = title->job;
3147     hb_audio_config_t * audio;
3148     /* Title Angle for dvdnav */
3149     job->angle = [[queueToApply objectForKey:@"TitleAngle"] intValue];
3150     /* Chapter selection */
3151     job->chapter_start = [[queueToApply objectForKey:@"JobChapterStart"] intValue];
3152     job->chapter_end   = [[queueToApply objectForKey:@"JobChapterEnd"] intValue];
3153         
3154     /* Format (Muxer) and Video Encoder */
3155     job->mux = [[queueToApply objectForKey:@"JobFileFormatMux"] intValue];
3156     job->vcodec = [[queueToApply objectForKey:@"JobVideoEncoderVcodec"] intValue];
3157     
3158     
3159     /* If mpeg-4, then set mpeg-4 specific options like chapters and > 4gb file sizes */
3160     if( [[queueToApply objectForKey:@"Mp4LargeFile"] intValue] == 1)
3161     {
3162         job->largeFileSize = 1;
3163     }
3164     else
3165     {
3166         job->largeFileSize = 0;
3167     }
3168     /* We set http optimized mp4 here */
3169     if( [[queueToApply objectForKey:@"Mp4HttpOptimize"] intValue] == 1 )
3170     {
3171         job->mp4_optimize = 1;
3172     }
3173     else
3174     {
3175         job->mp4_optimize = 0;
3176     }
3177
3178         
3179     /* We set the chapter marker extraction here based on the format being
3180      mpeg4 or mkv and the checkbox being checked */
3181     if ([[queueToApply objectForKey:@"ChapterMarkers"] intValue] == 1)
3182     {
3183         job->chapter_markers = 1;
3184         
3185         /* now lets get our saved chapter names out the array in the queue file
3186          * and insert them back into the title chapter list. We have it here,
3187          * because unless we are inserting chapter markers there is no need to
3188          * spend the overhead of iterating through the chapter names array imo
3189          * Also, note that if for some reason we don't apply chapter names, the
3190          * chapters just come out 001, 002, etc. etc.
3191          */
3192          
3193         NSMutableArray *ChapterNamesArray = [queueToApply objectForKey:@"ChapterNames"];
3194         int i = 0;
3195         NSEnumerator *enumerator = [ChapterNamesArray objectEnumerator];
3196         id tempObject;
3197         while (tempObject = [enumerator nextObject])
3198         {
3199             hb_chapter_t *chapter = (hb_chapter_t *) hb_list_item( title->list_chapter, i );
3200             if( chapter != NULL )
3201             {
3202                 strncpy( chapter->title, [tempObject UTF8String], 1023);
3203                 chapter->title[1023] = '\0';
3204             }
3205             i++;
3206         }
3207     }
3208     else
3209     {
3210         job->chapter_markers = 0;
3211     }
3212     
3213     if( job->vcodec & HB_VCODEC_X264 )
3214     {
3215                 if ([[queueToApply objectForKey:@"Mp4iPodCompatible"] intValue] == 1)
3216             {
3217             job->ipod_atom = 1;
3218                 }
3219         else
3220         {
3221             job->ipod_atom = 0;
3222         }
3223                 
3224                 /* Set this flag to switch from Constant Quantizer(default) to Constant Rate Factor Thanks jbrjake
3225          Currently only used with Constant Quality setting*/
3226                 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultCrf"] > 0 && [[queueToApply objectForKey:@"VideoQualityType"] intValue] == 2)
3227                 {
3228                 job->crf = 1;
3229                 }
3230                 /* Below Sends x264 options to the core library if x264 is selected*/
3231                 /* Lets use this as per Nyx, Thanks Nyx!*/
3232                 job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */
3233                 /* Turbo first pass if two pass and Turbo First pass is selected */
3234                 if( [[queueToApply objectForKey:@"VideoTwoPass"] intValue] == 1 && [[queueToApply objectForKey:@"VideoTurboTwoPass"] intValue] == 1 )
3235                 {
3236                         /* pass the "Turbo" string to be appended to the existing x264 opts string into a variable for the first pass */
3237                         NSString *firstPassOptStringTurbo = @":ref=1:subme=1:me=dia:analyse=none:trellis=0:no-fast-pskip=0:8x8dct=0:weightb=0";
3238                         /* append the "Turbo" string variable to the existing opts string.
3239              Note: the "Turbo" string must be appended, not prepended to work properly*/
3240                         NSString *firstPassOptStringCombined = [[queueToApply objectForKey:@"x264Option"] stringByAppendingString:firstPassOptStringTurbo];
3241                         strcpy(job->x264opts, [firstPassOptStringCombined UTF8String]);
3242                 }
3243                 else
3244                 {
3245                         strcpy(job->x264opts, [[queueToApply objectForKey:@"x264Option"] UTF8String]);
3246                 }
3247         
3248     }
3249     
3250     
3251     /* Picture Size Settings */
3252     job->width = [[queueToApply objectForKey:@"PictureWidth"]  intValue];
3253     job->height = [[queueToApply objectForKey:@"PictureHeight"]  intValue];
3254     
3255     job->keep_ratio = [[queueToApply objectForKey:@"PictureKeepRatio"]  intValue];
3256     job->anamorphic.mode = [[queueToApply objectForKey:@"PicturePAR"]  intValue];
3257     if ([[queueToApply objectForKey:@"PicturePAR"]  intValue] == 3)
3258     {
3259         /* insert our custom values here for capuj */
3260         job->width = [[queueToApply objectForKey:@"PicturePARStorageWidth"]  intValue];
3261         job->height = [[queueToApply objectForKey:@"PicturePARStorageHeight"]  intValue];
3262         
3263         job->anamorphic.par_width = [[queueToApply objectForKey:@"PicturePARPixelWidth"]  intValue];
3264         job->anamorphic.par_height = [[queueToApply objectForKey:@"PicturePARPixelHeight"]  intValue];
3265         
3266         job->anamorphic.dar_width = [[queueToApply objectForKey:@"PicturePARDisplayWidth"]  floatValue];
3267         job->anamorphic.dar_height = [[queueToApply objectForKey:@"PicturePARDisplayHeight"]  floatValue];
3268     }
3269     
3270     /* Here we use the crop values saved at the time the preset was saved */
3271     job->crop[0] = [[queueToApply objectForKey:@"PictureTopCrop"]  intValue];
3272     job->crop[1] = [[queueToApply objectForKey:@"PictureBottomCrop"]  intValue];
3273     job->crop[2] = [[queueToApply objectForKey:@"PictureLeftCrop"]  intValue];
3274     job->crop[3] = [[queueToApply objectForKey:@"PictureRightCrop"]  intValue];
3275     
3276     /* Video settings */
3277     /* Framerate */
3278     
3279     /* Set vfr to 0 as it's only on if using same as source in the framerate popup
3280      * and detelecine is on, so we handle that in the logic below
3281      */
3282     job->vfr = 0;
3283     if( [[queueToApply objectForKey:@"JobIndexVideoFramerate"] intValue] > 0 )
3284     {
3285         /* a specific framerate has been chosen */
3286         job->vrate      = 27000000;
3287         job->vrate_base = hb_video_rates[[[queueToApply objectForKey:@"JobIndexVideoFramerate"] intValue]-1].rate;
3288         /* We are not same as source so we set job->cfr to 1 
3289          * to enable constant frame rate since user has specified
3290          * a specific framerate*/
3291         job->cfr = 1;
3292     }
3293     else
3294     {
3295         /* We are same as source (variable) */
3296         job->vrate      = [[queueToApply objectForKey:@"JobVrate"] intValue];
3297         job->vrate_base = [[queueToApply objectForKey:@"JobVrateBase"] intValue];
3298         /* We are same as source so we set job->cfr to 0 
3299          * to enable true same as source framerate */
3300         job->cfr = 0;
3301         /* If we are same as source and we have detelecine on, we need to turn on
3302          * job->vfr
3303          */
3304         if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] == 1)
3305         {
3306             job->vfr = 1;
3307         }
3308     }
3309     
3310     if ( [[queueToApply objectForKey:@"VideoQualityType"] intValue] != 2 )
3311     {
3312         /* Target size.
3313          Bitrate should already have been calculated and displayed
3314          in fVidBitrateField, so let's just use it same as abr*/
3315         job->vquality = -1.0;
3316         job->vbitrate = [[queueToApply objectForKey:@"VideoAvgBitrate"] intValue];
3317     }
3318     if ( [[queueToApply objectForKey:@"VideoQualityType"] intValue] == 2 )
3319     {
3320         job->vquality = [[queueToApply objectForKey:@"VideoQualitySlider"] floatValue];
3321         job->vbitrate = 0;
3322         
3323     }
3324     
3325     job->grayscale = [[queueToApply objectForKey:@"VideoGrayScale"] intValue];
3326     
3327
3328
3329 #pragma mark -
3330 #pragma mark Process Subtitles to libhb
3331
3332 /* Map the settings in the dictionaries for the SubtitleList array to match title->list_subtitle
3333  * which means that we need to account for the offset of non source language settings in from
3334  * the NSPopUpCell menu. For all of the objects in the SubtitleList array this means 0 is "None"
3335  * from the popup menu, additionally the first track has "Foreign Audio Search" at 1. So we use
3336  * an int to offset the index number for the objectForKey:@"subtitleSourceTrackNum" to map that
3337  * to the source tracks position in title->list_subtitle.
3338  */
3339
3340 int subtitle = nil;
3341 int force;
3342 int burned;
3343 int def;
3344 bool one_burned = FALSE;
3345
3346     int i = 0;
3347     NSEnumerator *enumerator = [[queueToApply objectForKey:@"SubtitleList"] objectEnumerator];
3348     id tempObject;
3349     while (tempObject = [enumerator nextObject])
3350     {
3351         
3352         subtitle = [[tempObject objectForKey:@"subtitleSourceTrackNum"] intValue];
3353         force = [[tempObject objectForKey:@"subtitleTrackForced"] intValue];
3354         burned = [[tempObject objectForKey:@"subtitleTrackBurned"] intValue];
3355         def = [[tempObject objectForKey:@"subtitleTrackDefault"] intValue];
3356         
3357         /* since the subtitleSourceTrackNum 0 is "None" in our array of the subtitle popups,
3358          * we want to ignore it for display as well as encoding.
3359          */
3360         if (subtitle > 0)
3361         {
3362             hb_subtitle_t * subt;
3363             hb_subtitle_config_t sub_config;
3364             
3365             subt = (hb_subtitle_t *)hb_list_item(title->list_subtitle, subtitle);
3366             sub_config = subt->config;
3367             
3368             /* if i is 0, then we are in the first item of the subtitles which we need to 
3369              * check for the "Foreign Audio Search" which would be subtitleSourceTrackNum of 1
3370              * bearing in mind that for all tracks subtitleSourceTrackNum of 0 is None.
3371              */
3372             
3373             /* if we are on the first track and using "Foreign Audio Search" */ 
3374             if (i == 0 && subtitle == 1)
3375             {
3376                 [self writeToActivityLog: "Foreign Language Search: %d", 1];
3377                 
3378                 job->indepth_scan = 1;
3379                 if (burned == 1 || job->mux != HB_MUX_MP4)
3380                 {
3381                     if (burned != 1 && job->mux == HB_MUX_MKV)
3382                     {
3383                         job->select_subtitle_config.dest = hb_subtitle_config_s::PASSTHRUSUB;
3384                     }
3385                     
3386                     job->select_subtitle_config.force = force;
3387                     job->select_subtitle_config.default_track = def;
3388                 }
3389                 
3390                 
3391             }
3392             else
3393             {
3394                 
3395                 /* for the actual source tracks, we must subtract the non source entries so 
3396                  * that the menu index matches the source subtitle_list index for convenience */
3397                 if (i == 0)
3398                 {
3399                     /* for the first track, the source tracks start at menu index 2 ( None is 0,
3400                      * Foreign Language Search is 1) so subtract 2 */
3401                     subtitle = subtitle - 2;
3402                 }
3403                 else
3404                 {
3405                     /* for all other tracks, the source tracks start at menu index 1 (None is 0)
3406                      * so subtract 1. */
3407                     
3408                     subtitle = subtitle - 1;
3409                 }
3410                 
3411                 /* We are setting a source subtitle so access the source subtitle info */  
3412                 hb_subtitle_t * subt;
3413                 hb_subtitle_config_t sub_config;
3414                 
3415                 subt = (hb_subtitle_t *)hb_list_item(title->list_subtitle, subtitle);
3416                 sub_config = subt->config;
3417                 
3418                 if (subt != NULL)
3419                 {
3420                     [self writeToActivityLog: "Setting Subtitle: %s", subt];
3421                     
3422                     if (!burned && job->mux == HB_MUX_MKV && 
3423                         subt->format == hb_subtitle_s::PICTURESUB)
3424                     {
3425                         sub_config.dest = hb_subtitle_config_s::PASSTHRUSUB;
3426                     }
3427                     else if (!burned && job->mux == HB_MUX_MP4 && 
3428                              subt->format == hb_subtitle_s::PICTURESUB)
3429                     {
3430                         // Skip any non-burned vobsubs when output is mp4
3431                         continue;
3432                     }
3433                     else if ( burned && subt->format == hb_subtitle_s::PICTURESUB )
3434                     {
3435                         // Only allow one subtitle to be burned into the video
3436                         if (one_burned)
3437                             continue;
3438                         one_burned = TRUE;
3439                     }
3440                     sub_config.force = force;
3441                     sub_config.default_track = def;
3442                     hb_subtitle_add( job, &sub_config, subtitle );
3443                 }   
3444                 
3445             }
3446         }
3447         i++;
3448     }
3449
3450 #pragma mark -
3451
3452    
3453     /* Audio tracks and mixdowns */
3454     /* Lets make sure there arent any erroneous audio tracks in the job list, so lets make sure its empty*/
3455     int audiotrack_count = hb_list_count(job->list_audio);
3456     for( int i = 0; i < audiotrack_count;i++)
3457     {
3458         hb_audio_t * temp_audio = (hb_audio_t*) hb_list_item( job->list_audio, 0 );
3459         hb_list_rem(job->list_audio, temp_audio);
3460     }
3461     /* Now lets add our new tracks to the audio list here */
3462     if ([[queueToApply objectForKey:@"Audio1Track"] intValue] > 0)
3463     {
3464         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3465         hb_audio_config_init(audio);
3466         audio->in.track = [[queueToApply objectForKey:@"Audio1Track"] intValue] - 1;
3467         /* We go ahead and assign values to our audio->out.<properties> */
3468         audio->out.track = [[queueToApply objectForKey:@"Audio1Track"] intValue] - 1;
3469         audio->out.codec = [[queueToApply objectForKey:@"JobAudio1Encoder"] intValue];
3470         audio->out.mixdown = [[queueToApply objectForKey:@"JobAudio1Mixdown"] intValue];
3471         audio->out.bitrate = [[queueToApply objectForKey:@"JobAudio1Bitrate"] intValue];
3472         audio->out.samplerate = [[queueToApply objectForKey:@"JobAudio1Samplerate"] intValue];
3473         audio->out.dynamic_range_compression = [[queueToApply objectForKey:@"Audio1TrackDRCSlider"] floatValue];
3474         
3475         hb_audio_add( job, audio );
3476         free(audio);
3477     }  
3478     if ([[queueToApply objectForKey:@"Audio2Track"] intValue] > 0)
3479     {
3480         
3481         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3482         hb_audio_config_init(audio);
3483         audio->in.track = [[queueToApply objectForKey:@"Audio2Track"] intValue] - 1;
3484         [self writeToActivityLog: "prepareJob audiotrack 2 is: %d", audio->in.track];
3485         /* We go ahead and assign values to our audio->out.<properties> */
3486         audio->out.track = [[queueToApply objectForKey:@"Audio2Track"] intValue] - 1;
3487         audio->out.codec = [[queueToApply objectForKey:@"JobAudio2Encoder"] intValue];
3488         audio->out.mixdown = [[queueToApply objectForKey:@"JobAudio2Mixdown"] intValue];
3489         audio->out.bitrate = [[queueToApply objectForKey:@"JobAudio2Bitrate"] intValue];
3490         audio->out.samplerate = [[queueToApply objectForKey:@"JobAudio2Samplerate"] intValue];
3491         audio->out.dynamic_range_compression = [[queueToApply objectForKey:@"Audio2TrackDRCSlider"] floatValue];
3492         
3493         hb_audio_add( job, audio );
3494         free(audio);
3495     }
3496     
3497     if ([[queueToApply objectForKey:@"Audio3Track"] intValue] > 0)
3498     {
3499         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3500         hb_audio_config_init(audio);
3501         audio->in.track = [[queueToApply objectForKey:@"Audio3Track"] intValue] - 1;
3502         /* We go ahead and assign values to our audio->out.<properties> */
3503         audio->out.track = [[queueToApply objectForKey:@"Audio3Track"] intValue] - 1;
3504         audio->out.codec = [[queueToApply objectForKey:@"JobAudio3Encoder"] intValue];
3505         audio->out.mixdown = [[queueToApply objectForKey:@"JobAudio3Mixdown"] intValue];
3506         audio->out.bitrate = [[queueToApply objectForKey:@"JobAudio3Bitrate"] intValue];
3507         audio->out.samplerate = [[queueToApply objectForKey:@"JobAudio3Samplerate"] intValue];
3508         audio->out.dynamic_range_compression = [[queueToApply objectForKey:@"Audio3TrackDRCSlider"] floatValue];
3509         
3510         hb_audio_add( job, audio );
3511         free(audio);        
3512     }
3513     
3514     if ([[queueToApply objectForKey:@"Audio4Track"] intValue] > 0)
3515     {
3516         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3517         hb_audio_config_init(audio);
3518         audio->in.track = [[queueToApply objectForKey:@"Audio4Track"] intValue] - 1;
3519         /* We go ahead and assign values to our audio->out.<properties> */
3520         audio->out.track = [[queueToApply objectForKey:@"Audio4Track"] intValue] - 1;
3521         audio->out.codec = [[queueToApply objectForKey:@"JobAudio4Encoder"] intValue];
3522         audio->out.mixdown = [[queueToApply objectForKey:@"JobAudio4Mixdown"] intValue];
3523         audio->out.bitrate = [[queueToApply objectForKey:@"JobAudio4Bitrate"] intValue];
3524         audio->out.samplerate = [[queueToApply objectForKey:@"JobAudio4Samplerate"] intValue];
3525         audio->out.dynamic_range_compression = [[queueToApply objectForKey:@"Audio4TrackDRCSlider"] floatValue];
3526         
3527         hb_audio_add( job, audio );
3528         
3529
3530     }
3531     
3532     /* Filters */ 
3533     job->filters = hb_list_init();
3534     
3535     /* Now lets call the filters if applicable.
3536      * The order of the filters is critical
3537      */
3538     /* Detelecine */
3539     if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] == 1)
3540     {
3541         //if ([queueToApply objectForKey:@"PictureDetelecineCustom"])
3542         hb_list_add( job->filters, &hb_filter_detelecine );
3543     }
3544     if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] == 2)
3545     {
3546         /* use a custom detelecine string */
3547         hb_filter_detelecine.settings = (char *) [[queueToApply objectForKey:@"PictureDetelecineCustom"] UTF8String];
3548         hb_list_add( job->filters, &hb_filter_detelecine );
3549     }
3550     
3551     if ([[queueToApply objectForKey:@"PictureDecombDeinterlace"] intValue] == 1)
3552     {
3553         /* Decomb */
3554         if ([[queueToApply objectForKey:@"PictureDecomb"] intValue] == 1)
3555         {
3556             /* Run old deinterlacer fd by default */
3557             hb_list_add( job->filters, &hb_filter_decomb );
3558         }
3559         /* we add the custom string if present */
3560         if ([[queueToApply objectForKey:@"PictureDecomb"] intValue] == 2)
3561         {
3562             /* use a custom decomb string */
3563             hb_filter_decomb.settings = (char *) [[queueToApply objectForKey:@"PictureDecombCustom"] UTF8String];
3564             hb_list_add( job->filters, &hb_filter_decomb );
3565         }
3566         
3567     }
3568     else
3569     {
3570         
3571         /* Deinterlace */
3572         if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 1)
3573         {
3574             /* Run old deinterlacer fd by default */
3575             hb_filter_deinterlace.settings = "-1"; 
3576             hb_list_add( job->filters, &hb_filter_deinterlace );
3577         }
3578         else if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 2)
3579         {
3580             /* Yadif mode 0 (without spatial deinterlacing.) */
3581             hb_filter_deinterlace.settings = "2"; 
3582             hb_list_add( job->filters, &hb_filter_deinterlace );            
3583         }
3584         else if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 3)
3585         {
3586             /* Yadif (with spatial deinterlacing) */
3587             hb_filter_deinterlace.settings = "0"; 
3588             hb_list_add( job->filters, &hb_filter_deinterlace );            
3589         }
3590         else if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 4)
3591         {
3592             /* we add the custom string if present */
3593             hb_filter_deinterlace.settings = (char *) [[queueToApply objectForKey:@"PictureDeinterlaceCustom"] UTF8String];
3594             hb_list_add( job->filters, &hb_filter_deinterlace );            
3595         }
3596         
3597         
3598     }
3599     /* Denoise */
3600         if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 1) // Weak in popup
3601         {
3602                 hb_filter_denoise.settings = "2:1:2:3"; 
3603         hb_list_add( job->filters, &hb_filter_denoise );        
3604         }
3605         else if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 2) // Medium in popup
3606         {
3607                 hb_filter_denoise.settings = "3:2:2:3"; 
3608         hb_list_add( job->filters, &hb_filter_denoise );        
3609         }
3610         else if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 3) // Strong in popup
3611         {
3612                 hb_filter_denoise.settings = "7:7:5:5"; 
3613         hb_list_add( job->filters, &hb_filter_denoise );        
3614         }
3615     else if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 4) // Custom in popup
3616         {
3617                 /* we add the custom string if present */
3618         hb_filter_denoise.settings = (char *) [[queueToApply objectForKey:@"PictureDenoiseCustom"] UTF8String];
3619         hb_list_add( job->filters, &hb_filter_denoise );        
3620         }
3621     
3622     /* Deblock  (uses pp7 default) */
3623     /* NOTE: even though there is a valid deblock setting of 0 for the filter, for 
3624      * the macgui's purposes a value of 0 actually means to not even use the filter
3625      * current hb_filter_deblock.settings valid ranges are from 5 - 15 
3626      */
3627     if ([[queueToApply objectForKey:@"PictureDeblock"] intValue] != 0)
3628     {
3629         hb_filter_deblock.settings = (char *) [[queueToApply objectForKey:@"PictureDeblock"] UTF8String];
3630         hb_list_add( job->filters, &hb_filter_deblock );
3631     }
3632 [self writeToActivityLog: "prepareJob exiting"];    
3633 }
3634
3635
3636
3637 /* addToQueue: puts up an alert before ultimately calling doAddToQueue
3638 */
3639 - (IBAction) addToQueue: (id) sender
3640 {
3641         /* We get the destination directory from the destination field here */
3642         NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
3643         /* We check for a valid destination here */
3644         if ([[NSFileManager defaultManager] fileExistsAtPath:destinationDirectory] == 0) 
3645         {
3646                 NSRunAlertPanel(@"Warning!", @"This is not a valid destination directory!", @"OK", nil, nil);
3647         return;
3648         }
3649     
3650     BOOL fileExists;
3651     fileExists = NO;
3652     
3653     BOOL fileExistsInQueue;
3654     fileExistsInQueue = NO;
3655     
3656     /* We check for and existing file here */
3657     if([[NSFileManager defaultManager] fileExistsAtPath: [fDstFile2Field stringValue]])
3658     {
3659         fileExists = YES;
3660     }
3661     
3662     /* We now run through the queue and make sure we are not overwriting an exisiting queue item */
3663     int i = 0;
3664     NSEnumerator *enumerator = [QueueFileArray objectEnumerator];
3665         id tempObject;
3666         while (tempObject = [enumerator nextObject])
3667         {
3668                 NSDictionary *thisQueueDict = tempObject;
3669                 if ([[thisQueueDict objectForKey:@"DestinationPath"] isEqualToString: [fDstFile2Field stringValue]])
3670                 {
3671                         fileExistsInQueue = YES;        
3672                 }
3673         i++;
3674         }
3675     
3676     
3677         if(fileExists == YES)
3678     {
3679         NSBeginCriticalAlertSheet( NSLocalizedString( @"File already exists.", @"" ),
3680                                   NSLocalizedString( @"Cancel", @"" ), NSLocalizedString( @"Overwrite", @"" ), nil, fWindow, self,
3681                                   @selector( overwriteAddToQueueAlertDone:returnCode:contextInfo: ),
3682                                   NULL, NULL, [NSString stringWithFormat:
3683                                                NSLocalizedString( @"Do you want to overwrite %@?", @"" ),
3684                                                [fDstFile2Field stringValue]] );
3685     }
3686     else if (fileExistsInQueue == YES)
3687     {
3688     NSBeginCriticalAlertSheet( NSLocalizedString( @"There is already a queue item for this destination.", @"" ),
3689                                   NSLocalizedString( @"Cancel", @"" ), NSLocalizedString( @"Overwrite", @"" ), nil, fWindow, self,
3690                                   @selector( overwriteAddToQueueAlertDone:returnCode:contextInfo: ),
3691                                   NULL, NULL, [NSString stringWithFormat:
3692                                                NSLocalizedString( @"Do you want to overwrite %@?", @"" ),
3693                                                [fDstFile2Field stringValue]] );
3694     }
3695     else
3696     {
3697         [self doAddToQueue];
3698     }
3699 }
3700
3701 /* overwriteAddToQueueAlertDone: called from the alert posted by addToQueue that asks
3702    the user if they want to overwrite an exiting movie file.
3703 */
3704 - (void) overwriteAddToQueueAlertDone: (NSWindow *) sheet
3705     returnCode: (int) returnCode contextInfo: (void *) contextInfo
3706 {
3707     if( returnCode == NSAlertAlternateReturn )
3708         [self doAddToQueue];
3709 }
3710
3711 - (void) doAddToQueue
3712 {
3713     [self addQueueFileItem ];
3714 }
3715
3716
3717
3718 /* Rip: puts up an alert before ultimately calling doRip
3719 */
3720 - (IBAction) Rip: (id) sender
3721 {
3722     [self writeToActivityLog: "Rip: Pending queue count is %d", fPendingCount];
3723     /* Rip or Cancel ? */
3724     hb_state_t s;
3725     hb_get_state2( fQueueEncodeLibhb, &s );
3726     
3727     if(s.state == HB_STATE_WORKING || s.state == HB_STATE_PAUSED)
3728         {
3729         [self Cancel: sender];
3730         return;
3731     }
3732     
3733     /* We check to see if we need to warn the user that the computer will go to sleep
3734                  or shut down when encoding is finished */
3735                 [self remindUserOfSleepOrShutdown];
3736     
3737     // If there are pending jobs in the queue, then this is a rip the queue
3738     if (fPendingCount > 0)
3739     {
3740         /* here lets start the queue with the first pending item */
3741         [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]]; 
3742         
3743         return;
3744     }
3745     
3746     // Before adding jobs to the queue, check for a valid destination.
3747     
3748     NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
3749     if ([[NSFileManager defaultManager] fileExistsAtPath:destinationDirectory] == 0) 
3750     {
3751         NSRunAlertPanel(@"Warning!", @"This is not a valid destination directory!", @"OK", nil, nil);
3752         return;
3753     }
3754     
3755     /* We check for duplicate name here */
3756     if( [[NSFileManager defaultManager] fileExistsAtPath:[fDstFile2Field stringValue]] )
3757     {
3758         NSBeginCriticalAlertSheet( NSLocalizedString( @"File already exists", @"" ),
3759                                   NSLocalizedString( @"Cancel", "" ), NSLocalizedString( @"Overwrite", @"" ), nil, fWindow, self,
3760                                   @selector( overWriteAlertDone:returnCode:contextInfo: ),
3761                                   NULL, NULL, [NSString stringWithFormat:
3762                                                NSLocalizedString( @"Do you want to overwrite %@?", @"" ),
3763                                                [fDstFile2Field stringValue]] );
3764         
3765         // overWriteAlertDone: will be called when the alert is dismissed. It will call doRip.
3766     }
3767     else
3768     {
3769         /* if there are no pending jobs in the queue, then add this one to the queue and rip
3770          otherwise, just rip the queue */
3771         if(fPendingCount == 0)
3772         {
3773             [self writeToActivityLog: "Rip: No pending jobs, so sending this one to doAddToQueue"];
3774             [self doAddToQueue];
3775         }
3776         
3777         /* go right to processing the new queue encode */
3778         [self writeToActivityLog: "Rip: Going right to performNewQueueScan"];
3779         [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]]; 
3780         
3781     }
3782 }
3783
3784 /* overWriteAlertDone: called from the alert posted by Rip: that asks the user if they
3785    want to overwrite an exiting movie file.
3786 */
3787 - (void) overWriteAlertDone: (NSWindow *) sheet
3788     returnCode: (int) returnCode contextInfo: (void *) contextInfo
3789 {
3790     if( returnCode == NSAlertAlternateReturn )
3791     {
3792         /* if there are no jobs in the queue, then add this one to the queue and rip 
3793         otherwise, just rip the queue */
3794         if( fPendingCount == 0 )
3795         {
3796             [self doAddToQueue];
3797         }
3798
3799         NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
3800         [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];
3801         [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]]; 
3802       
3803     }
3804 }
3805
3806 - (void) remindUserOfSleepOrShutdown
3807 {
3808        if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Put Computer To Sleep"])
3809        {
3810                /*Warn that computer will sleep after encoding*/
3811                int reminduser;
3812                NSBeep();
3813                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);
3814                [NSApp requestUserAttention:NSCriticalRequest];
3815                if ( reminduser == NSAlertAlternateReturn )
3816                {
3817                        [self showPreferencesWindow:nil];
3818                }
3819        }
3820        else if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Shut Down Computer"])
3821        {
3822                /*Warn that computer will shut down after encoding*/
3823                int reminduser;
3824                NSBeep();
3825                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);
3826                [NSApp requestUserAttention:NSCriticalRequest];
3827                if ( reminduser == NSAlertAlternateReturn )
3828                {
3829                        [self showPreferencesWindow:nil];
3830                }
3831        }
3832
3833 }
3834
3835
3836 - (void) doRip
3837 {
3838     /* Let libhb do the job */
3839     hb_start( fQueueEncodeLibhb );
3840     /*set the fEncodeState State */
3841         fEncodeState = 1;
3842 }
3843
3844
3845 //------------------------------------------------------------------------------------
3846 // Displays an alert asking user if the want to cancel encoding of current job.
3847 // Cancel: returns immediately after posting the alert. Later, when the user
3848 // acknowledges the alert, doCancelCurrentJob is called.
3849 //------------------------------------------------------------------------------------
3850 - (IBAction)Cancel: (id)sender
3851 {
3852     if (!fQueueController) return;
3853     
3854   hb_pause( fQueueEncodeLibhb );
3855     NSString * alertTitle = [NSString stringWithFormat:NSLocalizedString(@"You are currently encoding. What would you like to do ?", nil)];
3856    
3857     // Which window to attach the sheet to?
3858     NSWindow * docWindow;
3859     if ([sender respondsToSelector: @selector(window)])
3860         docWindow = [sender window];
3861     else
3862         docWindow = fWindow;
3863         
3864     NSBeginCriticalAlertSheet(
3865             alertTitle,
3866             NSLocalizedString(@"Continue Encoding", nil),
3867             NSLocalizedString(@"Cancel Current and Stop", nil),
3868             NSLocalizedString(@"Cancel Current and Continue", nil),
3869             docWindow, self,
3870             nil, @selector(didDimissCancel:returnCode:contextInfo:), nil,
3871             NSLocalizedString(@"Your encode will be cancelled if you don't continue encoding.", nil));
3872     
3873     // didDimissCancelCurrentJob:returnCode:contextInfo: will be called when the dialog is dismissed
3874 }
3875
3876 - (void) didDimissCancel: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo
3877 {
3878    hb_resume( fQueueEncodeLibhb );
3879      if (returnCode == NSAlertOtherReturn)
3880     {
3881         [self doCancelCurrentJob];  // <- this also stops libhb
3882     }
3883     if (returnCode == NSAlertAlternateReturn)
3884     {
3885     [self doCancelCurrentJobAndStop];
3886     }
3887 }
3888
3889 //------------------------------------------------------------------------------------
3890 // Cancels and deletes the current job and stops libhb from processing the remaining
3891 // encodes.
3892 //------------------------------------------------------------------------------------
3893 - (void) doCancelCurrentJob
3894 {
3895     // Stop the current job. hb_stop will only cancel the current pass and then set
3896     // its state to HB_STATE_WORKDONE. It also does this asynchronously. So when we
3897     // see the state has changed to HB_STATE_WORKDONE (in updateUI), we'll delete the
3898     // remaining passes of the job and then start the queue back up if there are any
3899     // remaining jobs.
3900      
3901     
3902     hb_stop( fQueueEncodeLibhb );
3903     
3904     // Delete all remaining jobs since libhb doesn't do this on its own.
3905             hb_job_t * job;
3906             while( ( job = hb_job(fQueueEncodeLibhb, 0) ) )
3907                 hb_rem( fQueueEncodeLibhb, job );
3908                 
3909     fEncodeState = 2;   // don't alert at end of processing since this was a cancel
3910     
3911     // now that we've stopped the currently encoding job, lets mark it as cancelled
3912     [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] setObject:[NSNumber numberWithInt:3] forKey:@"Status"];
3913     // and as always, save it in the queue .plist...
3914     /* We save all of the Queue data here */
3915     [self saveQueueFileItem];
3916     // so now lets move to 
3917     currentQueueEncodeIndex++ ;
3918     // ... and see if there are more items left in our queue
3919     int queueItems = [QueueFileArray count];
3920     /* If we still have more items in our queue, lets go to the next one */
3921     if (currentQueueEncodeIndex < queueItems)
3922     {
3923     [self writeToActivityLog: "doCancelCurrentJob currentQueueEncodeIndex is incremented to: %d", currentQueueEncodeIndex];
3924     [self writeToActivityLog: "doCancelCurrentJob moving to the next job"];
3925     
3926     [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]];
3927     }
3928     else
3929     {
3930         [self writeToActivityLog: "doCancelCurrentJob the item queue is complete"];
3931     }
3932
3933 }
3934
3935 - (void) doCancelCurrentJobAndStop
3936 {
3937     hb_stop( fQueueEncodeLibhb );
3938     
3939     // Delete all remaining jobs since libhb doesn't do this on its own.
3940             hb_job_t * job;
3941             while( ( job = hb_job(fQueueEncodeLibhb, 0) ) )
3942                 hb_rem( fQueueEncodeLibhb, job );
3943                 
3944                 
3945     fEncodeState = 2;   // don't alert at end of processing since this was a cancel
3946     
3947     // now that we've stopped the currently encoding job, lets mark it as cancelled
3948     [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] setObject:[NSNumber numberWithInt:3] forKey:@"Status"];
3949     // and as always, save it in the queue .plist...
3950     /* We save all of the Queue data here */
3951     [self saveQueueFileItem];
3952     // so now lets move to 
3953     currentQueueEncodeIndex++ ;
3954     [self writeToActivityLog: "cancelling current job and stopping the queue"];
3955 }
3956 - (IBAction) Pause: (id) sender
3957 {
3958     hb_state_t s;
3959     hb_get_state2( fQueueEncodeLibhb, &s );
3960
3961     if( s.state == HB_STATE_PAUSED )
3962     {
3963         hb_resume( fQueueEncodeLibhb );
3964     }
3965     else
3966     {
3967         hb_pause( fQueueEncodeLibhb );
3968     }
3969 }
3970
3971 #pragma mark -
3972 #pragma mark GUI Controls Changed Methods
3973
3974 - (IBAction) titlePopUpChanged: (id) sender
3975 {
3976     hb_list_t  * list  = hb_get_titles( fHandle );
3977     hb_title_t * title = (hb_title_t*)
3978         hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
3979
3980     /* If Auto Naming is on. We create an output filename of dvd name - title number */
3981     if( [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultAutoNaming"] > 0 && ( hb_list_count( list ) > 1 ) )
3982         {
3983                 [fDstFile2Field setStringValue: [NSString stringWithFormat:
3984                         @"%@/%@-%d.%@", [[fDstFile2Field stringValue] stringByDeletingLastPathComponent],
3985                         [browsedSourceDisplayName stringByDeletingPathExtension],
3986             title->index,
3987                         [[fDstFile2Field stringValue] pathExtension]]]; 
3988         }
3989
3990     /* Update chapter popups */
3991     [fSrcChapterStartPopUp removeAllItems];
3992     [fSrcChapterEndPopUp   removeAllItems];
3993     for( int i = 0; i < hb_list_count( title->list_chapter ); i++ )
3994     {
3995         [fSrcChapterStartPopUp addItemWithTitle: [NSString
3996             stringWithFormat: @"%d", i + 1]];
3997         [fSrcChapterEndPopUp addItemWithTitle: [NSString
3998             stringWithFormat: @"%d", i + 1]];
3999     }
4000
4001     [fSrcChapterStartPopUp selectItemAtIndex: 0];
4002     [fSrcChapterEndPopUp   selectItemAtIndex:
4003         hb_list_count( title->list_chapter ) - 1];
4004     [self chapterPopUpChanged:nil];
4005     
4006     /* if using dvd nav, show the angle widget */
4007     if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"UseDvdNav"] boolValue])
4008     {
4009         [fSrcAngleLabel setHidden:NO];
4010         [fSrcAnglePopUp setHidden:NO];
4011         
4012         [fSrcAnglePopUp removeAllItems];
4013         for( int i = 0; i < title->angle_count; i++ )
4014         {
4015             [fSrcAnglePopUp addItemWithTitle: [NSString stringWithFormat: @"%d", i + 1]];
4016         }
4017         [fSrcAnglePopUp selectItemAtIndex: 0];
4018     }
4019     else
4020     {
4021         [fSrcAngleLabel setHidden:YES];
4022         [fSrcAnglePopUp setHidden:YES];
4023     }
4024     
4025     /* Start Get and set the initial pic size for display */
4026         hb_job_t * job = title->job;
4027         fTitle = title;
4028     
4029     /* Set Auto Crop to on upon selecting a new title  */
4030     [fPictureController setAutoCrop:YES];
4031     
4032         /* We get the originial output picture width and height and put them
4033         in variables for use with some presets later on */
4034         PicOrigOutputWidth = job->width;
4035         PicOrigOutputHeight = job->height;
4036         AutoCropTop = job->crop[0];
4037         AutoCropBottom = job->crop[1];
4038         AutoCropLeft = job->crop[2];
4039         AutoCropRight = job->crop[3];
4040
4041         /* Reset the new title in fPictureController &&  fPreviewController*/
4042     [fPictureController SetTitle:title];
4043
4044         
4045     /* Update Subtitle Table */
4046     [fSubtitlesDelegate resetWithTitle:title];
4047     [fSubtitlesTable reloadData];
4048     
4049
4050     /* Update chapter table */
4051     [fChapterTitlesDelegate resetWithTitle:title];
4052     [fChapterTable reloadData];
4053
4054    /* Lets make sure there arent any erroneous audio tracks in the job list, so lets make sure its empty*/
4055     int audiotrack_count = hb_list_count(job->list_audio);
4056     for( int i = 0; i < audiotrack_count;i++)
4057     {
4058         hb_audio_t * temp_audio = (hb_audio_t*) hb_list_item( job->list_audio, 0 );
4059         hb_list_rem(job->list_audio, temp_audio);
4060     }
4061
4062     /* Update audio popups */
4063     [self addAllAudioTracksToPopUp: fAudLang1PopUp];
4064     [self addAllAudioTracksToPopUp: fAudLang2PopUp];
4065     [self addAllAudioTracksToPopUp: fAudLang3PopUp];
4066     [self addAllAudioTracksToPopUp: fAudLang4PopUp];
4067     /* search for the first instance of our prefs default language for track 1, and set track 2 to "none" */
4068         NSString * audioSearchPrefix = [[NSUserDefaults standardUserDefaults] stringForKey:@"DefaultLanguage"];
4069         [self selectAudioTrackInPopUp: fAudLang1PopUp searchPrefixString: audioSearchPrefix selectIndexIfNotFound: 1];
4070     [self selectAudioTrackInPopUp:fAudLang2PopUp searchPrefixString:nil selectIndexIfNotFound:0];
4071     [self selectAudioTrackInPopUp:fAudLang3PopUp searchPrefixString:nil selectIndexIfNotFound:0];
4072     [self selectAudioTrackInPopUp:fAudLang4PopUp searchPrefixString:nil selectIndexIfNotFound:0];
4073
4074         /* changing the title may have changed the audio channels on offer, */
4075         /* so call audioTrackPopUpChanged for both audio tracks to update the mixdown popups */
4076         [self audioTrackPopUpChanged: fAudLang1PopUp];
4077         [self audioTrackPopUpChanged: fAudLang2PopUp];
4078     [self audioTrackPopUpChanged: fAudLang3PopUp];
4079     [self audioTrackPopUpChanged: fAudLang4PopUp];
4080
4081     [fVidRatePopUp selectItemAtIndex: 0];
4082
4083     /* we run the picture size values through calculatePictureSizing to get all picture setting information*/
4084         [self calculatePictureSizing:nil];
4085
4086    /* lets call tableViewSelected to make sure that any preset we have selected is enforced after a title change */
4087     [self selectPreset:nil];
4088 }
4089
4090 - (IBAction) chapterPopUpChanged: (id) sender
4091 {
4092
4093         /* If start chapter popup is greater than end chapter popup,
4094         we set the end chapter popup to the same as start chapter popup */
4095         if ([fSrcChapterStartPopUp indexOfSelectedItem] > [fSrcChapterEndPopUp indexOfSelectedItem])
4096         {
4097                 [fSrcChapterEndPopUp selectItemAtIndex: [fSrcChapterStartPopUp indexOfSelectedItem]];
4098     }
4099
4100                 
4101         hb_list_t  * list  = hb_get_titles( fHandle );
4102     hb_title_t * title = (hb_title_t *)
4103         hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
4104
4105     hb_chapter_t * chapter;
4106     int64_t        duration = 0;
4107     for( int i = [fSrcChapterStartPopUp indexOfSelectedItem];
4108          i <= [fSrcChapterEndPopUp indexOfSelectedItem]; i++ )
4109     {
4110         chapter = (hb_chapter_t *) hb_list_item( title->list_chapter, i );
4111         duration += chapter->duration;
4112     }
4113     
4114     duration /= 90000; /* pts -> seconds */
4115     [fSrcDuration2Field setStringValue: [NSString stringWithFormat:
4116         @"%02lld:%02lld:%02lld", duration / 3600, ( duration / 60 ) % 60,
4117         duration % 60]];
4118
4119     [self calculateBitrate: sender];
4120     
4121     if ( [fSrcChapterStartPopUp indexOfSelectedItem] ==  [fSrcChapterEndPopUp indexOfSelectedItem] )
4122     {
4123     /* Disable chapter markers for any source with less than two chapters as it makes no sense. */
4124     [fCreateChapterMarkers setEnabled: NO];
4125     [fCreateChapterMarkers setState: NSOffState];
4126     }
4127     else
4128     {
4129     [fCreateChapterMarkers setEnabled: YES];
4130     }
4131 }
4132
4133 - (IBAction) formatPopUpChanged: (id) sender
4134 {
4135     NSString * string = [fDstFile2Field stringValue];
4136     int format = [fDstFormatPopUp indexOfSelectedItem];
4137     char * ext = NULL;
4138         /* Initially set the large file (64 bit formatting) output checkbox to hidden */
4139     [fDstMp4LargeFileCheck setHidden: YES];
4140     [fDstMp4HttpOptFileCheck setHidden: YES];
4141     [fDstMp4iPodFileCheck setHidden: YES];
4142     
4143     /* Update the Video Codec PopUp */
4144     /* lets get the tag of the currently selected item first so we might reset it later */
4145     int selectedVidEncoderTag;
4146     selectedVidEncoderTag = [[fVidEncoderPopUp selectedItem] tag];
4147     
4148     /* Note: we now store the video encoder int values from common.c in the tags of each popup for easy retrieval later */
4149     [fVidEncoderPopUp removeAllItems];
4150     NSMenuItem *menuItem;
4151     /* These video encoders are available to all of our current muxers, so lets list them once here */
4152     menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"MPEG-4 (FFmpeg)" action: NULL keyEquivalent: @""];
4153     [menuItem setTag: HB_VCODEC_FFMPEG];
4154     
4155     switch( format )
4156     {
4157         case 0:
4158                         /*Get Default MP4 File Extension*/
4159                         if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultMpegName"] > 0)
4160                         {
4161                                 ext = "m4v";
4162                         }
4163                         else
4164                         {
4165                                 ext = "mp4";
4166                         }
4167             /* Add additional video encoders here */
4168             menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"H.264 (x264)" action: NULL keyEquivalent: @""];
4169             [menuItem setTag: HB_VCODEC_X264];
4170             /* We show the mp4 option checkboxes here since we are mp4 */
4171             [fCreateChapterMarkers setEnabled: YES];
4172                         [fDstMp4LargeFileCheck setHidden: NO];
4173                         [fDstMp4HttpOptFileCheck setHidden: NO];
4174             [fDstMp4iPodFileCheck setHidden: NO];
4175             break;
4176             
4177             case 1:
4178             ext = "mkv";
4179             /* Add additional video encoders here */
4180             menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"H.264 (x264)" action: NULL keyEquivalent: @""];
4181             [menuItem setTag: HB_VCODEC_X264];
4182             menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"VP3 (Theora)" action: NULL keyEquivalent: @""];
4183             [menuItem setTag: HB_VCODEC_THEORA];
4184             /* We enable the create chapters checkbox here */
4185                         [fCreateChapterMarkers setEnabled: YES];
4186                         break;
4187             
4188
4189     }
4190     /* tell fSubtitlesDelegate we have a new video container */
4191     
4192     [fSubtitlesDelegate containerChanged:[[fDstFormatPopUp selectedItem] tag]];
4193     [fSubtitlesTable reloadData];
4194     /* if we have a previously selected vid encoder tag, then try to select it */
4195     if (selectedVidEncoderTag)
4196     {
4197         [fVidEncoderPopUp selectItemWithTag: selectedVidEncoderTag];
4198     }
4199     else
4200     {
4201         [fVidEncoderPopUp selectItemAtIndex: 0];
4202     }
4203
4204     [self audioAddAudioTrackCodecs: fAudTrack1CodecPopUp];
4205     [self audioAddAudioTrackCodecs: fAudTrack2CodecPopUp];
4206     [self audioAddAudioTrackCodecs: fAudTrack3CodecPopUp];
4207     [self audioAddAudioTrackCodecs: fAudTrack4CodecPopUp];
4208
4209     if( format == 0 )
4210         [self autoSetM4vExtension: sender];
4211     else
4212         [fDstFile2Field setStringValue: [NSString stringWithFormat:@"%@.%s", [string stringByDeletingPathExtension], ext]];
4213
4214     if( SuccessfulScan )
4215     {
4216         /* Add/replace to the correct extension */
4217         [self audioTrackPopUpChanged: fAudLang1PopUp];
4218         [self audioTrackPopUpChanged: fAudLang2PopUp];
4219         [self audioTrackPopUpChanged: fAudLang3PopUp];
4220         [self audioTrackPopUpChanged: fAudLang4PopUp];
4221
4222         if( [fVidEncoderPopUp selectedItem] == nil )
4223         {
4224
4225             [fVidEncoderPopUp selectItemAtIndex:0];
4226             [self videoEncoderPopUpChanged:nil];
4227
4228             /* changing the format may mean that we can / can't offer mono or 6ch, */
4229             /* so call audioTrackPopUpChanged for both audio tracks to update the mixdown popups */
4230
4231             /* We call the method to properly enable/disable turbo 2 pass */
4232             [self twoPassCheckboxChanged: sender];
4233             /* We call method method to change UI to reflect whether a preset is used or not*/
4234         }
4235     }
4236         [self customSettingUsed: sender];
4237 }
4238
4239 - (IBAction) autoSetM4vExtension: (id) sender
4240 {
4241     if ( [fDstFormatPopUp indexOfSelectedItem] )
4242         return;
4243
4244     NSString * extension = @"mp4";
4245
4246     if( [[fAudTrack1CodecPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack2CodecPopUp selectedItem] tag] == HB_ACODEC_AC3 ||
4247                                                         [[fAudTrack3CodecPopUp selectedItem] tag] == HB_ACODEC_AC3 ||
4248                                                         [[fAudTrack4CodecPopUp selectedItem] tag] == HB_ACODEC_AC3 ||
4249                                                         [fCreateChapterMarkers state] == NSOnState ||
4250                                                         [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultMpegName"] > 0 )
4251     {
4252         extension = @"m4v";
4253     }
4254
4255     if( [extension isEqualTo: [[fDstFile2Field stringValue] pathExtension]] )
4256         return;
4257     else
4258         [fDstFile2Field setStringValue: [NSString stringWithFormat:@"%@.%@",
4259                                     [[fDstFile2Field stringValue] stringByDeletingPathExtension], extension]];
4260 }
4261
4262 /* Method to determine if we should change the UI
4263 To reflect whether or not a Preset is being used or if
4264 the user is using "Custom" settings by determining the sender*/
4265 - (IBAction) customSettingUsed: (id) sender
4266 {
4267         if ([sender stringValue])
4268         {
4269                 /* Deselect the currently selected Preset if there is one*/
4270                 [fPresetsOutlineView deselectRow:[fPresetsOutlineView selectedRow]];
4271                 /* Change UI to show "Custom" settings are being used */
4272                 [fPresetSelectedDisplay setStringValue: @"Custom"];
4273
4274                 curUserPresetChosenNum = nil;
4275         }
4276 [self calculateBitrate:nil];
4277 }
4278
4279
4280 #pragma mark -
4281 #pragma mark - Video
4282
4283 - (IBAction) videoEncoderPopUpChanged: (id) sender
4284 {
4285     hb_job_t * job = fTitle->job;
4286     int videoEncoder = [[fVidEncoderPopUp selectedItem] tag];
4287     
4288     [fAdvancedOptions setHidden:YES];
4289     /* If we are using x264 then show the x264 advanced panel*/
4290     if (videoEncoder == HB_VCODEC_X264)
4291     {
4292         [fAdvancedOptions setHidden:NO];
4293         [self autoSetM4vExtension: sender];
4294     }
4295     
4296     /* We need to set loose anamorphic as available depending on whether or not the ffmpeg encoder
4297     is being used as it borks up loose anamorphic .
4298     For convenience lets use the titleOfSelected index. Probably should revisit whether or not we want
4299     to use the index itself but this is easier */
4300     if (videoEncoder == HB_VCODEC_FFMPEG)
4301     {
4302         if (job->anamorphic.mode == 2)
4303         {
4304             job->anamorphic.mode = 0;
4305         }
4306         [fPictureController setAllowLooseAnamorphic:NO];
4307         /* We set the iPod atom checkbox to disabled and uncheck it as its only for x264 in the mp4
4308          container. Format is taken care of in formatPopUpChanged method by hiding and unchecking
4309          anything other than MP4.
4310          */ 
4311         [fDstMp4iPodFileCheck setEnabled: NO];
4312         [fDstMp4iPodFileCheck setState: NSOffState];
4313     }
4314     else
4315     {
4316         [fPictureController setAllowLooseAnamorphic:YES];
4317         [fDstMp4iPodFileCheck setEnabled: YES];
4318     }
4319     [self setupQualitySlider];
4320         [self calculatePictureSizing: sender];
4321         [self twoPassCheckboxChanged: sender];
4322 }
4323
4324
4325 - (IBAction) twoPassCheckboxChanged: (id) sender
4326 {
4327         /* check to see if x264 is chosen */
4328         if([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_X264)
4329     {
4330                 if( [fVidTwoPassCheck state] == NSOnState)
4331                 {
4332                         [fVidTurboPassCheck setHidden: NO];
4333                 }
4334                 else
4335                 {
4336                         [fVidTurboPassCheck setHidden: YES];
4337                         [fVidTurboPassCheck setState: NSOffState];
4338                 }
4339                 /* Make sure Two Pass is checked if Turbo is checked */
4340                 if( [fVidTurboPassCheck state] == NSOnState)
4341                 {
4342                         [fVidTwoPassCheck setState: NSOnState];
4343                 }
4344         }
4345         else
4346         {
4347                 [fVidTurboPassCheck setHidden: YES];
4348                 [fVidTurboPassCheck setState: NSOffState];
4349         }
4350         
4351         /* We call method method to change UI to reflect whether a preset is used or not*/
4352         [self customSettingUsed: sender];
4353 }
4354
4355 - (IBAction ) videoFrameRateChanged: (id) sender
4356 {
4357     /* We call method method to calculatePictureSizing to error check detelecine*/
4358     [self calculatePictureSizing: sender];
4359
4360     /* We call method method to change UI to reflect whether a preset is used or not*/
4361         [self customSettingUsed: sender];
4362 }
4363 - (IBAction) videoMatrixChanged: (id) sender;
4364 {
4365     bool target, bitrate, quality;
4366
4367     target = bitrate = quality = false;
4368     if( [fVidQualityMatrix isEnabled] )
4369     {
4370         switch( [fVidQualityMatrix selectedRow] )
4371         {
4372             case 0:
4373                 target = true;
4374                 break;
4375             case 1:
4376                 bitrate = true;
4377                 break;
4378             case 2:
4379                 quality = true;
4380                 break;
4381         }
4382     }
4383     [fVidTargetSizeField  setEnabled: target];
4384     [fVidBitrateField     setEnabled: bitrate];
4385     [fVidQualitySlider    setEnabled: quality];
4386     [fVidQualityRFField   setEnabled: quality];
4387     [fVidQualityRFLabel    setEnabled: quality];
4388     [fVidTwoPassCheck     setEnabled: !quality &&
4389         [fVidQualityMatrix isEnabled]];
4390     if( quality )
4391     {
4392         [fVidTwoPassCheck setState: NSOffState];
4393                 [fVidTurboPassCheck setHidden: YES];
4394                 [fVidTurboPassCheck setState: NSOffState];
4395     }
4396
4397     [self qualitySliderChanged: sender];
4398     [self calculateBitrate: sender];
4399         [self customSettingUsed: sender];
4400 }
4401
4402 /* Use this method to setup the quality slider for cq/rf values depending on
4403  * the video encoder selected.
4404  */
4405 - (void) setupQualitySlider
4406 {
4407     /* Get the current slider maxValue to check for a change in slider scale later
4408      * so that we can choose a new similar value on the new slider scale */
4409     float previousMaxValue = [fVidQualitySlider maxValue];
4410     float previousPercentOfSliderScale = [fVidQualitySlider floatValue] / ([fVidQualitySlider maxValue] - [fVidQualitySlider minValue] + 1);
4411     NSString * qpRFLabelString = @"QP:";
4412     /* x264 0-51 */
4413     if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_X264)
4414     {
4415         [fVidQualitySlider setMinValue:0.0];
4416         [fVidQualitySlider setMaxValue:51.0];
4417         /* As x264 allows for qp/rf values that are fractional, we get the value from the preferences */
4418         int fractionalGranularity = 1 / [[NSUserDefaults standardUserDefaults] floatForKey:@"x264CqSliderFractional"];
4419         [fVidQualitySlider setNumberOfTickMarks:(([fVidQualitySlider maxValue] - [fVidQualitySlider minValue]) * fractionalGranularity) + 1];
4420         if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultCrf"] > 0)
4421         {
4422             qpRFLabelString = @"RF:";
4423         }
4424     }
4425     /* ffmpeg  1-31 */
4426     if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_FFMPEG )
4427     {
4428         [fVidQualitySlider setMinValue:1.0];
4429         [fVidQualitySlider setMaxValue:31.0];
4430         [fVidQualitySlider setNumberOfTickMarks:31];
4431     }
4432     /* Theora 0-63 */
4433     if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_THEORA)
4434     {
4435         [fVidQualitySlider setMinValue:0.0];
4436         [fVidQualitySlider setMaxValue:63.0];
4437         [fVidQualitySlider setNumberOfTickMarks:64];
4438     }
4439     [fVidQualityRFLabel setStringValue:qpRFLabelString];
4440     
4441     /* check to see if we have changed slider scales */
4442     if (previousMaxValue != [fVidQualitySlider maxValue])
4443     {
4444         /* if so, convert the old setting to the new scale as close as possible based on percentages */
4445         float rf =  ([fVidQualitySlider maxValue] - [fVidQualitySlider minValue] + 1) * previousPercentOfSliderScale;
4446         [fVidQualitySlider setFloatValue:rf];
4447     }
4448     
4449     [self qualitySliderChanged:nil];
4450 }
4451
4452 - (IBAction) qualitySliderChanged: (id) sender
4453 {
4454     /* Our constant quality slider is in a range based
4455      * on each encoders qp/rf values. The range depends
4456      * on the encoder. Also, the range is inverse of quality
4457      * for all of the encoders *except* for theora
4458      * (ie. as the "quality" goes up, the cq or rf value
4459      * actually goes down). Since the IB sliders always set
4460      * their max value at the right end of the slider, we
4461      * will calculate the inverse, so as the slider floatValue
4462      * goes up, we will show the inverse in the rf field
4463      * so, the floatValue at the right for x264 would be 51
4464      * and our rf field needs to show 0 and vice versa.
4465      */
4466     
4467     float sliderRfInverse = ([fVidQualitySlider maxValue] - [fVidQualitySlider floatValue]) + [fVidQualitySlider minValue];
4468     /* If the encoder is theora, use the float, otherwise use the inverse float*/
4469     float sliderRfToPercent;
4470     if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_THEORA)
4471     {
4472         [fVidQualityRFField setStringValue: [NSString stringWithFormat: @"%.2f", [fVidQualitySlider floatValue]]];
4473         sliderRfToPercent = [fVidQualityRFField floatValue] / ([fVidQualitySlider maxValue] - [fVidQualitySlider minValue]);   
4474     }
4475     else
4476     {
4477         [fVidQualityRFField setStringValue: [NSString stringWithFormat: @"%.2f", sliderRfInverse]];
4478         sliderRfToPercent = ( ([fVidQualitySlider maxValue] - [fVidQualitySlider minValue])  - ([fVidQualityRFField floatValue] - [fVidQualitySlider minValue])) / ([fVidQualitySlider maxValue] - [fVidQualitySlider minValue]);
4479     }
4480     [fVidConstantCell setTitle: [NSString stringWithFormat:
4481                                  NSLocalizedString( @"Constant quality: %.2f %%", @"" ), 100 * sliderRfToPercent]];
4482     
4483     [self customSettingUsed: sender];
4484 }
4485
4486 - (void) controlTextDidChange: (NSNotification *) notification
4487 {
4488     [self calculateBitrate:nil];
4489 }
4490
4491 - (IBAction) calculateBitrate: (id) sender
4492 {
4493     if( !fHandle || [fVidQualityMatrix selectedRow] != 0 || !SuccessfulScan )
4494     {
4495         return;
4496     }
4497
4498     hb_list_t  * list  = hb_get_titles( fHandle );
4499     hb_title_t * title = (hb_title_t *) hb_list_item( list,
4500             [fSrcTitlePopUp indexOfSelectedItem] );
4501     hb_job_t * job = title->job;
4502     hb_audio_config_t * audio;
4503     /* For  hb_calc_bitrate in addition to the Target Size in MB out of the
4504      * Target Size Field, we also need the job info for the Muxer, the Chapters
4505      * as well as all of the audio track info.
4506      * This used to be accomplished by simply calling prepareJob here, however
4507      * since the resilient queue sets the queue array values instead of the job
4508      * values directly, we duplicate the old prepareJob code here for the variables
4509      * needed
4510      */
4511     job->chapter_start = [fSrcChapterStartPopUp indexOfSelectedItem] + 1;
4512     job->chapter_end = [fSrcChapterEndPopUp indexOfSelectedItem] + 1; 
4513     job->mux = [[fDstFormatPopUp selectedItem] tag];
4514     
4515     /* Audio goes here */
4516     int audiotrack_count = hb_list_count(job->list_audio);
4517     for( int i = 0; i < audiotrack_count;i++)
4518     {
4519         hb_audio_t * temp_audio = (hb_audio_t*) hb_list_item( job->list_audio, 0 );
4520         hb_list_rem(job->list_audio, temp_audio);
4521     }
4522     /* Now we need our audio info here for each track if applicable */
4523     if ([fAudLang1PopUp indexOfSelectedItem] > 0)
4524     {
4525         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
4526         hb_audio_config_init(audio);
4527         audio->in.track = [fAudLang1PopUp indexOfSelectedItem] - 1;
4528         /* We go ahead and assign values to our audio->out.<properties> */
4529         audio->out.track = [fAudLang1PopUp indexOfSelectedItem] - 1;
4530         audio->out.codec = [[fAudTrack1CodecPopUp selectedItem] tag];
4531         audio->out.mixdown = [[fAudTrack1MixPopUp selectedItem] tag];
4532         audio->out.bitrate = [[fAudTrack1BitratePopUp selectedItem] tag];
4533         audio->out.samplerate = [[fAudTrack1RatePopUp selectedItem] tag];
4534         audio->out.dynamic_range_compression = [fAudTrack1DrcField floatValue];
4535         
4536         hb_audio_add( job, audio );
4537         free(audio);
4538     }  
4539     if ([fAudLang2PopUp indexOfSelectedItem] > 0)
4540     {
4541         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
4542         hb_audio_config_init(audio);
4543         audio->in.track = [fAudLang2PopUp indexOfSelectedItem] - 1;
4544         /* We go ahead and assign values to our audio->out.<properties> */
4545         audio->out.track = [fAudLang2PopUp indexOfSelectedItem] - 1;
4546         audio->out.codec = [[fAudTrack2CodecPopUp selectedItem] tag];
4547         audio->out.mixdown = [[fAudTrack2MixPopUp selectedItem] tag];
4548         audio->out.bitrate = [[fAudTrack2BitratePopUp selectedItem] tag];
4549         audio->out.samplerate = [[fAudTrack2RatePopUp selectedItem] tag];
4550         audio->out.dynamic_range_compression = [fAudTrack2DrcField floatValue];
4551         
4552         hb_audio_add( job, audio );
4553         free(audio);
4554         
4555     }
4556     
4557     if ([fAudLang3PopUp indexOfSelectedItem] > 0)
4558     {
4559         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
4560         hb_audio_config_init(audio);
4561         audio->in.track = [fAudLang3PopUp indexOfSelectedItem] - 1;
4562         /* We go ahead and assign values to our audio->out.<properties> */
4563         audio->out.track = [fAudLang3PopUp indexOfSelectedItem] - 1;
4564         audio->out.codec = [[fAudTrack3CodecPopUp selectedItem] tag];
4565         audio->out.mixdown = [[fAudTrack3MixPopUp selectedItem] tag];
4566         audio->out.bitrate = [[fAudTrack3BitratePopUp selectedItem] tag];
4567         audio->out.samplerate = [[fAudTrack3RatePopUp selectedItem] tag];
4568         audio->out.dynamic_range_compression = [fAudTrack3DrcField floatValue];
4569         
4570         hb_audio_add( job, audio );
4571         free(audio);
4572         
4573     }
4574
4575     if ([fAudLang4PopUp indexOfSelectedItem] > 0)
4576     {
4577         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
4578         hb_audio_config_init(audio);
4579         audio->in.track = [fAudLang4PopUp indexOfSelectedItem] - 1;
4580         /* We go ahead and assign values to our audio->out.<properties> */
4581         audio->out.track = [fAudLang4PopUp indexOfSelectedItem] - 1;
4582         audio->out.codec = [[fAudTrack4CodecPopUp selectedItem] tag];
4583         audio->out.mixdown = [[fAudTrack4MixPopUp selectedItem] tag];
4584         audio->out.bitrate = [[fAudTrack4BitratePopUp selectedItem] tag];
4585         audio->out.samplerate = [[fAudTrack4RatePopUp selectedItem] tag];
4586         audio->out.dynamic_range_compression = [fAudTrack4DrcField floatValue];
4587         
4588         hb_audio_add( job, audio );
4589         free(audio);
4590         
4591     }
4592        
4593 [fVidBitrateField setIntValue: hb_calc_bitrate( job, [fVidTargetSizeField intValue] )];
4594 }
4595
4596 #pragma mark -
4597 #pragma mark - Picture
4598
4599 /* lets set the picture size back to the max from right after title scan
4600    Lets use an IBAction here as down the road we could always use a checkbox
4601    in the gui to easily take the user back to max. Remember, the compiler
4602    resolves IBActions down to -(void) during compile anyway */
4603 - (IBAction) revertPictureSizeToMax: (id) sender
4604 {
4605         hb_job_t * job = fTitle->job;
4606         /* Here we apply the title source and height */
4607     job->width = fTitle->width;
4608     job->height = fTitle->height;
4609     
4610     [self calculatePictureSizing: sender];
4611     /* We call method to change UI to reflect whether a preset is used or not*/    
4612     [self customSettingUsed: sender];
4613 }
4614
4615 /**
4616  * Registers changes made in the Picture Settings Window.
4617  */
4618
4619 - (void)pictureSettingsDidChange 
4620 {
4621         [self calculatePictureSizing:nil];
4622 }
4623
4624 /* Get and Display Current Pic Settings in main window */
4625 - (IBAction) calculatePictureSizing: (id) sender
4626 {
4627         if (fTitle->job->anamorphic.mode > 0)
4628         {
4629         fTitle->job->keep_ratio = 0;
4630         }
4631     
4632     [fPictureSizeField setStringValue: [NSString stringWithFormat:@"Picture Size: %@", [fPictureController getPictureSizeInfoString]]];
4633     
4634     NSString *picCropping;
4635     /* Set the display field for crop as per boolean */
4636         if (![fPictureController autoCrop])
4637         {
4638         picCropping =  @"Custom";
4639         }
4640         else
4641         {
4642                 picCropping =  @"Auto";
4643         }
4644     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]]];
4645     
4646     [fPictureCroppingField setStringValue: [NSString stringWithFormat:@"Picture Cropping: %@",picCropping]];
4647     
4648     NSString *videoFilters;
4649     videoFilters = @"";
4650     /* Detelecine */
4651     if ([fPictureController detelecine] == 1) 
4652     {
4653         videoFilters = [videoFilters stringByAppendingString:@" - Detelecine (Default)"];
4654     }
4655     else if ([fPictureController detelecine] == 2) 
4656     {
4657         videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Detelecine (%@)",[fPictureController detelecineCustomString]]];
4658     }
4659     
4660     
4661     if ([fPictureController useDecomb] == 1)
4662     {
4663         /* Decomb */
4664         if ([fPictureController decomb] == 1)
4665         {
4666             videoFilters = [videoFilters stringByAppendingString:@" - Decomb (Default)"];
4667         }
4668         else if ([fPictureController decomb] == 2)
4669         {
4670             videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Decomb (%@)",[fPictureController decombCustomString]]];
4671         }
4672     }
4673     else
4674     {
4675         /* Deinterlace */
4676         if ([fPictureController deinterlace] > 0)
4677         {
4678             fTitle->job->deinterlace  = 1;
4679         }
4680         else
4681         {
4682             fTitle->job->deinterlace  = 0;
4683         }
4684         
4685         if ([fPictureController deinterlace] == 1)
4686         {
4687             videoFilters = [videoFilters stringByAppendingString:@" - Deinterlace (Fast)"];
4688         }
4689         else if ([fPictureController deinterlace] == 2)
4690         {
4691             videoFilters = [videoFilters stringByAppendingString:@" - Deinterlace (Slow)"];
4692         }
4693         else if ([fPictureController deinterlace] == 3)
4694         {
4695             videoFilters = [videoFilters stringByAppendingString:@" - Deinterlace (Slower)"];
4696         }
4697         else if ([fPictureController deinterlace] == 4)
4698         {
4699             videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Deinterlace (%@)",[fPictureController deinterlaceCustomString]]];
4700         }
4701         }
4702     
4703     
4704     /* Denoise */
4705         if ([fPictureController denoise] == 1)
4706         {
4707                 videoFilters = [videoFilters stringByAppendingString:@" - Denoise (Weak)"];
4708     }
4709         else if ([fPictureController denoise] == 2)
4710         {
4711                 videoFilters = [videoFilters stringByAppendingString:@" - Denoise (Medium)"];
4712     }
4713         else if ([fPictureController denoise] == 3)
4714         {
4715                 videoFilters = [videoFilters stringByAppendingString:@" - Denoise (Strong)"];
4716         }
4717     else if ([fPictureController denoise] == 4)
4718         {
4719                 videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Denoise (%@)",[fPictureController denoiseCustomString]]];
4720         }
4721     
4722     /* Deblock */
4723     if ([fPictureController deblock] > 0) 
4724     {
4725         videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Deblock (%d)",[fPictureController deblock]]];
4726     }
4727         
4728     /* Grayscale */
4729     if ([fPictureController grayscale]) 
4730     {
4731         videoFilters = [videoFilters stringByAppendingString:@" - Grayscale"];
4732     }
4733     [fVideoFiltersField setStringValue: [NSString stringWithFormat:@"Video Filters: %@", videoFilters]];
4734     
4735     //[fPictureController reloadStillPreview]; 
4736 }
4737
4738
4739 #pragma mark -
4740 #pragma mark - Audio and Subtitles
4741 - (IBAction) audioCodecsPopUpChanged: (id) sender
4742 {
4743     
4744     NSPopUpButton * audiotrackPopUp;
4745     NSPopUpButton * sampleratePopUp;
4746     NSPopUpButton * bitratePopUp;
4747     NSPopUpButton * audiocodecPopUp;
4748     if (sender == fAudTrack1CodecPopUp)
4749     {
4750         audiotrackPopUp = fAudLang1PopUp;
4751         audiocodecPopUp = fAudTrack1CodecPopUp;
4752         sampleratePopUp = fAudTrack1RatePopUp;
4753         bitratePopUp = fAudTrack1BitratePopUp;
4754     }
4755     else if (sender == fAudTrack2CodecPopUp)
4756     {
4757         audiotrackPopUp = fAudLang2PopUp;
4758         audiocodecPopUp = fAudTrack2CodecPopUp;
4759         sampleratePopUp = fAudTrack2RatePopUp;
4760         bitratePopUp = fAudTrack2BitratePopUp;
4761     }
4762     else if (sender == fAudTrack3CodecPopUp)
4763     {
4764         audiotrackPopUp = fAudLang3PopUp;
4765         audiocodecPopUp = fAudTrack3CodecPopUp;
4766         sampleratePopUp = fAudTrack3RatePopUp;
4767         bitratePopUp = fAudTrack3BitratePopUp;
4768     }
4769     else
4770     {
4771         audiotrackPopUp = fAudLang4PopUp;
4772         audiocodecPopUp = fAudTrack4CodecPopUp;
4773         sampleratePopUp = fAudTrack4RatePopUp;
4774         bitratePopUp = fAudTrack4BitratePopUp;
4775     }
4776         
4777     /* changing the codecs on offer may mean that we can / can't offer mono or 6ch, */
4778         /* so call audioTrackPopUpChanged for both audio tracks to update the mixdown popups */
4779     [self audioTrackPopUpChanged: audiotrackPopUp];
4780     
4781 }
4782
4783 - (IBAction) setEnabledStateOfAudioMixdownControls: (id) sender
4784 {
4785     /* We will be setting the enabled/disabled state of each tracks audio controls based on
4786      * the settings of the source audio for that track. We leave the samplerate and bitrate
4787      * to audiotrackMixdownChanged
4788      */
4789     
4790     /* We will first verify that a lower track number has been selected before enabling each track
4791      * for example, make sure a track is selected for track 1 before enabling track 2, etc.
4792      */
4793     if ([fAudLang1PopUp indexOfSelectedItem] == 0)
4794     {
4795         [fAudLang2PopUp setEnabled: NO];
4796         [fAudLang2PopUp selectItemAtIndex: 0];
4797     }
4798     else
4799     {
4800         [fAudLang2PopUp setEnabled: YES];
4801     }
4802     
4803     if ([fAudLang2PopUp indexOfSelectedItem] == 0)
4804     {
4805         [fAudLang3PopUp setEnabled: NO];
4806         [fAudLang3PopUp selectItemAtIndex: 0];
4807     }
4808     else
4809     {
4810         [fAudLang3PopUp setEnabled: YES];
4811     }
4812     if ([fAudLang3PopUp indexOfSelectedItem] == 0)
4813     {
4814         [fAudLang4PopUp setEnabled: NO];
4815         [fAudLang4PopUp selectItemAtIndex: 0];
4816     }
4817     else
4818     {
4819         [fAudLang4PopUp setEnabled: YES];
4820     }
4821     /* enable/disable the mixdown text and popupbutton for audio track 1 */
4822     [fAudTrack1CodecPopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
4823     [fAudTrack1MixPopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
4824     [fAudTrack1RatePopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
4825     [fAudTrack1BitratePopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
4826     [fAudTrack1DrcSlider setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
4827     [fAudTrack1DrcField setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
4828     if ([fAudLang1PopUp indexOfSelectedItem] == 0)
4829     {
4830         [fAudTrack1CodecPopUp removeAllItems];
4831         [fAudTrack1MixPopUp removeAllItems];
4832         [fAudTrack1RatePopUp removeAllItems];
4833         [fAudTrack1BitratePopUp removeAllItems];
4834         [fAudTrack1DrcSlider setFloatValue: 1.00];
4835         [self audioDRCSliderChanged: fAudTrack1DrcSlider];
4836     }
4837     else if ([[fAudTrack1MixPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack1MixPopUp selectedItem] tag] == HB_ACODEC_DCA)
4838     {
4839         [fAudTrack1RatePopUp setEnabled: NO];
4840         [fAudTrack1BitratePopUp setEnabled: NO];
4841         [fAudTrack1DrcSlider setEnabled: NO];
4842         [fAudTrack1DrcField setEnabled: NO];
4843     }
4844     
4845     /* enable/disable the mixdown text and popupbutton for audio track 2 */
4846     [fAudTrack2CodecPopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
4847     [fAudTrack2MixPopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
4848     [fAudTrack2RatePopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
4849     [fAudTrack2BitratePopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
4850     [fAudTrack2DrcSlider setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
4851     [fAudTrack2DrcField setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
4852     if ([fAudLang2PopUp indexOfSelectedItem] == 0)
4853     {
4854         [fAudTrack2CodecPopUp removeAllItems];
4855         [fAudTrack2MixPopUp removeAllItems];
4856         [fAudTrack2RatePopUp removeAllItems];
4857         [fAudTrack2BitratePopUp removeAllItems];
4858         [fAudTrack2DrcSlider setFloatValue: 1.00];
4859         [self audioDRCSliderChanged: fAudTrack2DrcSlider];
4860     }
4861     else if ([[fAudTrack2MixPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack2MixPopUp selectedItem] tag] == HB_ACODEC_DCA)
4862     {
4863         [fAudTrack2RatePopUp setEnabled: NO];
4864         [fAudTrack2BitratePopUp setEnabled: NO];
4865         [fAudTrack2DrcSlider setEnabled: NO];
4866         [fAudTrack2DrcField setEnabled: NO];
4867     }
4868     
4869     /* enable/disable the mixdown text and popupbutton for audio track 3 */
4870     [fAudTrack3CodecPopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
4871     [fAudTrack3MixPopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
4872     [fAudTrack3RatePopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
4873     [fAudTrack3BitratePopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
4874     [fAudTrack3DrcSlider setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
4875     [fAudTrack3DrcField setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
4876     if ([fAudLang3PopUp indexOfSelectedItem] == 0)
4877     {
4878         [fAudTrack3CodecPopUp removeAllItems];
4879         [fAudTrack3MixPopUp removeAllItems];
4880         [fAudTrack3RatePopUp removeAllItems];
4881         [fAudTrack3BitratePopUp removeAllItems];
4882         [fAudTrack3DrcSlider setFloatValue: 1.00];
4883         [self audioDRCSliderChanged: fAudTrack3DrcSlider];
4884     }
4885     else if ([[fAudTrack3MixPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack3MixPopUp selectedItem] tag] == HB_ACODEC_DCA)
4886     {
4887         [fAudTrack3RatePopUp setEnabled: NO];
4888         [fAudTrack3BitratePopUp setEnabled: NO];
4889         [fAudTrack3DrcSlider setEnabled: NO];
4890         [fAudTrack3DrcField setEnabled: NO];
4891     }
4892     
4893     /* enable/disable the mixdown text and popupbutton for audio track 4 */
4894     [fAudTrack4CodecPopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
4895     [fAudTrack4MixPopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
4896     [fAudTrack4RatePopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
4897     [fAudTrack4BitratePopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
4898     [fAudTrack4DrcSlider setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
4899     [fAudTrack4DrcField setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
4900     if ([fAudLang4PopUp indexOfSelectedItem] == 0)
4901     {
4902         [fAudTrack4CodecPopUp removeAllItems];
4903         [fAudTrack4MixPopUp removeAllItems];
4904         [fAudTrack4RatePopUp removeAllItems];
4905         [fAudTrack4BitratePopUp removeAllItems];
4906         [fAudTrack4DrcSlider setFloatValue: 1.00];
4907         [self audioDRCSliderChanged: fAudTrack4DrcSlider];
4908     }
4909     else if ([[fAudTrack4MixPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack4MixPopUp selectedItem] tag] == HB_ACODEC_DCA)
4910     {
4911         [fAudTrack4RatePopUp setEnabled: NO];
4912         [fAudTrack4BitratePopUp setEnabled: NO];
4913         [fAudTrack4DrcSlider setEnabled: NO];
4914         [fAudTrack4DrcField setEnabled: NO];
4915     }
4916     
4917 }
4918
4919 - (IBAction) addAllAudioTracksToPopUp: (id) sender
4920 {
4921
4922     hb_list_t  * list  = hb_get_titles( fHandle );
4923     hb_title_t * title = (hb_title_t*)
4924         hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
4925
4926         hb_audio_config_t * audio;
4927
4928     [sender removeAllItems];
4929     [sender addItemWithTitle: NSLocalizedString( @"None", @"" )];
4930     for( int i = 0; i < hb_list_count( title->list_audio ); i++ )
4931     {
4932         audio = (hb_audio_config_t *) hb_list_audio_config_item( title->list_audio, i );
4933         [[sender menu] addItemWithTitle:
4934             [NSString stringWithCString: audio->lang.description]
4935             action: NULL keyEquivalent: @""];
4936     }
4937     [sender selectItemAtIndex: 0];
4938
4939 }
4940
4941 - (IBAction) selectAudioTrackInPopUp: (id) sender searchPrefixString: (NSString *) searchPrefixString selectIndexIfNotFound: (int) selectIndexIfNotFound
4942 {
4943
4944     /* this method can be used to find a language, or a language-and-source-format combination, by passing in the appropriate string */
4945     /* e.g. to find the first French track, pass in an NSString * of "Francais" */
4946     /* e.g. to find the first English 5.1 AC3 track, pass in an NSString * of "English (AC3) (5.1 ch)" */
4947     /* if no matching track is found, then selectIndexIfNotFound is used to choose which track to select instead */
4948
4949         if (searchPrefixString)
4950         {
4951
4952         for( int i = 0; i < [sender numberOfItems]; i++ )
4953         {
4954             /* Try to find the desired search string */
4955             if ([[[sender itemAtIndex: i] title] hasPrefix:searchPrefixString])
4956             {
4957                 [sender selectItemAtIndex: i];
4958                 return;
4959             }
4960         }
4961         /* couldn't find the string, so select the requested "search string not found" item */
4962         /* index of 0 means select the "none" item */
4963         /* index of 1 means select the first audio track */
4964         [sender selectItemAtIndex: selectIndexIfNotFound];
4965         }
4966     else
4967     {
4968         /* if no search string is provided, then select the selectIndexIfNotFound item */
4969         [sender selectItemAtIndex: selectIndexIfNotFound];
4970     }
4971
4972 }
4973 - (IBAction) audioAddAudioTrackCodecs: (id)sender
4974 {
4975     int format = [fDstFormatPopUp indexOfSelectedItem];
4976     
4977     /* setup pointers to the appropriate popups for the correct track */
4978     NSPopUpButton * audiocodecPopUp;
4979     NSPopUpButton * audiotrackPopUp;
4980     if (sender == fAudTrack1CodecPopUp)
4981     {
4982         audiotrackPopUp = fAudLang1PopUp;
4983         audiocodecPopUp = fAudTrack1CodecPopUp;
4984     }
4985     else if (sender == fAudTrack2CodecPopUp)
4986     {
4987         audiotrackPopUp = fAudLang2PopUp;
4988         audiocodecPopUp = fAudTrack2CodecPopUp;
4989     }
4990     else if (sender == fAudTrack3CodecPopUp)
4991     {
4992         audiotrackPopUp = fAudLang3PopUp;
4993         audiocodecPopUp = fAudTrack3CodecPopUp;
4994     }
4995     else
4996     {
4997         audiotrackPopUp = fAudLang4PopUp;
4998         audiocodecPopUp = fAudTrack4CodecPopUp;
4999     }
5000     
5001     [audiocodecPopUp removeAllItems];
5002     /* Make sure "None" isnt selected in the source track */
5003     if ([audiotrackPopUp indexOfSelectedItem] > 0)
5004     {
5005         [audiocodecPopUp setEnabled:YES];
5006         NSMenuItem *menuItem;
5007         /* We setup our appropriate popups for codecs and put the int value in the popup tag for easy retrieval */
5008         switch( format )
5009         {
5010             case 0:
5011                 /* MP4 */
5012                 // FAAC
5013                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (faac)" action: NULL keyEquivalent: @""];
5014                 [menuItem setTag: HB_ACODEC_FAAC];
5015
5016                 // CA_AAC
5017                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (CoreAudio)" action: NULL keyEquivalent: @""];
5018                 [menuItem setTag: HB_ACODEC_CA_AAC];
5019
5020                 // AC3 Passthru
5021                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AC3 Passthru" action: NULL keyEquivalent: @""];
5022                 [menuItem setTag: HB_ACODEC_AC3];
5023                 break;
5024                 
5025             case 1:
5026                 /* MKV */
5027                 // FAAC
5028                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (faac)" action: NULL keyEquivalent: @""];
5029                 [menuItem setTag: HB_ACODEC_FAAC];
5030                 // CA_AAC
5031                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (CoreAudio)" action: NULL keyEquivalent: @""];
5032                 [menuItem setTag: HB_ACODEC_CA_AAC];
5033                 // AC3 Passthru
5034                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AC3 Passthru" action: NULL keyEquivalent: @""];
5035                 [menuItem setTag: HB_ACODEC_AC3];
5036                 // DTS Passthru
5037                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"DTS Passthru" action: NULL keyEquivalent: @""];
5038                 [menuItem setTag: HB_ACODEC_DCA];
5039                 // MP3
5040                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"MP3 (lame)" action: NULL keyEquivalent: @""];
5041                 [menuItem setTag: HB_ACODEC_LAME];
5042                 // Vorbis
5043                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"Vorbis (vorbis)" action: NULL keyEquivalent: @""];
5044                 [menuItem setTag: HB_ACODEC_VORBIS];
5045                 break;
5046                 
5047             case 2: 
5048                 /* AVI */
5049                 // MP3
5050                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"MP3 (lame)" action: NULL keyEquivalent: @""];
5051                 [menuItem setTag: HB_ACODEC_LAME];
5052                 // AC3 Passthru
5053                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AC3 Passthru" action: NULL keyEquivalent: @""];
5054                 [menuItem setTag: HB_ACODEC_AC3];
5055                 break;
5056                 
5057             case 3:
5058                 /* OGM */
5059                 // Vorbis
5060                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"Vorbis (vorbis)" action: NULL keyEquivalent: @""];
5061                 [menuItem setTag: HB_ACODEC_VORBIS];
5062                 // MP3
5063                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"MP3 (lame)" action: NULL keyEquivalent: @""];
5064                 [menuItem setTag: HB_ACODEC_LAME];
5065                 break;
5066         }
5067         [audiocodecPopUp selectItemAtIndex:0];
5068     }
5069     else
5070     {
5071         [audiocodecPopUp setEnabled:NO];
5072     }
5073 }
5074
5075 - (IBAction) audioTrackPopUpChanged: (id) sender
5076 {
5077     /* utility function to call audioTrackPopUpChanged without passing in a mixdown-to-use */
5078     [self audioTrackPopUpChanged: sender mixdownToUse: 0];
5079 }
5080
5081 - (IBAction) audioTrackPopUpChanged: (id) sender mixdownToUse: (int) mixdownToUse
5082 {
5083     
5084     /* make sure we have a selected title before continuing */
5085     if (fTitle == NULL) return;
5086     /* if the sender is the lanaguage popup and there is nothing in the codec popup, lets call
5087     * audioAddAudioTrackCodecs on the codec popup to populate it properly before moving on
5088     */
5089     if (sender == fAudLang1PopUp && [[fAudTrack1CodecPopUp menu] numberOfItems] == 0)
5090     {
5091         [self audioAddAudioTrackCodecs: fAudTrack1CodecPopUp];
5092     }
5093     if (sender == fAudLang2PopUp && [[fAudTrack2CodecPopUp menu] numberOfItems] == 0)
5094     {
5095         [self audioAddAudioTrackCodecs: fAudTrack2CodecPopUp];
5096     }
5097     if (sender == fAudLang3PopUp && [[fAudTrack3CodecPopUp menu] numberOfItems] == 0)
5098     {
5099         [self audioAddAudioTrackCodecs: fAudTrack3CodecPopUp];
5100     }
5101     if (sender == fAudLang4PopUp && [[fAudTrack4CodecPopUp menu] numberOfItems] == 0)
5102     {
5103         [self audioAddAudioTrackCodecs: fAudTrack4CodecPopUp];
5104     }
5105     
5106     /* Now lets make the sender the appropriate Audio Track popup from this point on */
5107     if (sender == fAudTrack1CodecPopUp || sender == fAudTrack1MixPopUp)
5108     {
5109         sender = fAudLang1PopUp;
5110     }
5111     if (sender == fAudTrack2CodecPopUp || sender == fAudTrack2MixPopUp)
5112     {
5113         sender = fAudLang2PopUp;
5114     }
5115     if (sender == fAudTrack3CodecPopUp || sender == fAudTrack3MixPopUp)
5116     {
5117         sender = fAudLang3PopUp;
5118     }
5119     if (sender == fAudTrack4CodecPopUp || sender == fAudTrack4MixPopUp)
5120     {
5121         sender = fAudLang4PopUp;
5122     }
5123     
5124     /* pointer to this track's mixdown, codec, sample rate and bitrate NSPopUpButton's */
5125     NSPopUpButton * mixdownPopUp;
5126     NSPopUpButton * audiocodecPopUp;
5127     NSPopUpButton * sampleratePopUp;
5128     NSPopUpButton * bitratePopUp;
5129     if (sender == fAudLang1PopUp)
5130     {
5131         mixdownPopUp = fAudTrack1MixPopUp;
5132         audiocodecPopUp = fAudTrack1CodecPopUp;
5133         sampleratePopUp = fAudTrack1RatePopUp;
5134         bitratePopUp = fAudTrack1BitratePopUp;
5135     }
5136     else if (sender == fAudLang2PopUp)
5137     {
5138         mixdownPopUp = fAudTrack2MixPopUp;
5139         audiocodecPopUp = fAudTrack2CodecPopUp;
5140         sampleratePopUp = fAudTrack2RatePopUp;
5141         bitratePopUp = fAudTrack2BitratePopUp;
5142     }
5143     else if (sender == fAudLang3PopUp)
5144     {
5145         mixdownPopUp = fAudTrack3MixPopUp;
5146         audiocodecPopUp = fAudTrack3CodecPopUp;
5147         sampleratePopUp = fAudTrack3RatePopUp;
5148         bitratePopUp = fAudTrack3BitratePopUp;
5149     }
5150     else
5151     {
5152         mixdownPopUp = fAudTrack4MixPopUp;
5153         audiocodecPopUp = fAudTrack4CodecPopUp;
5154         sampleratePopUp = fAudTrack4RatePopUp;
5155         bitratePopUp = fAudTrack4BitratePopUp;
5156     }
5157
5158     /* get the index of the selected audio Track*/
5159     int thisAudioIndex = [sender indexOfSelectedItem] - 1;
5160
5161     /* pointer for the hb_audio_s struct we will use later on */
5162     hb_audio_config_t * audio;
5163
5164     int acodec;
5165     /* check if the audio mixdown controls need their enabled state changing */
5166     [self setEnabledStateOfAudioMixdownControls:nil];
5167
5168     if (thisAudioIndex != -1)
5169     {
5170
5171         /* get the audio */
5172         audio = (hb_audio_config_t *) hb_list_audio_config_item( fTitle->list_audio, thisAudioIndex );// Should "fTitle" be title and be setup ?
5173
5174         /* actually manipulate the proper mixdowns here */
5175         /* delete the previous audio mixdown options */
5176         [mixdownPopUp removeAllItems];
5177
5178         acodec = [[audiocodecPopUp selectedItem] tag];
5179
5180         if (audio != NULL)
5181         {
5182
5183             /* find out if our selected output audio codec supports mono and / or 6ch */
5184             /* we also check for an input codec of AC3 or DCA,
5185              as they are the only libraries able to do the mixdown to mono / conversion to 6-ch */
5186             /* audioCodecsSupportMono and audioCodecsSupport6Ch are the same for now,
5187              but this may change in the future, so they are separated for flexibility */
5188             int audioCodecsSupportMono =
5189                     (audio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)) &&
5190                     (acodec != HB_ACODEC_LAME);
5191             int audioCodecsSupport6Ch =
5192                     (audio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)) &&
5193                     (acodec != HB_ACODEC_LAME);
5194             
5195             /* check for AC-3 passthru */
5196             if (audio->in.codec == HB_ACODEC_AC3 && acodec == HB_ACODEC_AC3)
5197             {
5198                 
5199             NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5200                  [NSString stringWithCString: "AC3 Passthru"]
5201                                                action: NULL keyEquivalent: @""];
5202              [menuItem setTag: HB_ACODEC_AC3];   
5203             }
5204             else if (audio->in.codec == HB_ACODEC_DCA && acodec == HB_ACODEC_DCA)
5205             {
5206             NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5207                  [NSString stringWithCString: "DTS Passthru"]
5208                                                action: NULL keyEquivalent: @""];
5209              [menuItem setTag: HB_ACODEC_DCA]; 
5210             }
5211             else
5212             {
5213                 
5214                 /* add the appropriate audio mixdown menuitems to the popupbutton */
5215                 /* in each case, we set the new menuitem's tag to be the amixdown value for that mixdown,
5216                  so that we can reference the mixdown later */
5217                 
5218                 /* keep a track of the min and max mixdowns we used, so we can select the best match later */
5219                 int minMixdownUsed = 0;
5220                 int maxMixdownUsed = 0;
5221                 
5222                 /* get the input channel layout without any lfe channels */
5223                 int layout = audio->in.channel_layout & HB_INPUT_CH_LAYOUT_DISCRETE_NO_LFE_MASK;
5224                 
5225                 /* do we want to add a mono option? */
5226                 if (audioCodecsSupportMono == 1)
5227                 {
5228                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5229                                             [NSString stringWithCString: hb_audio_mixdowns[0].human_readable_name]
5230                                                                           action: NULL keyEquivalent: @""];
5231                     [menuItem setTag: hb_audio_mixdowns[0].amixdown];
5232                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[0].amixdown;
5233                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[0].amixdown);
5234                 }
5235                 
5236                 /* do we want to add a stereo option? */
5237                 /* offer stereo if we have a mono source and non-mono-supporting codecs, as otherwise we won't have a mixdown at all */
5238                 /* also offer stereo if we have a stereo-or-better source */
5239                 if ((layout == HB_INPUT_CH_LAYOUT_MONO && audioCodecsSupportMono == 0) || layout >= HB_INPUT_CH_LAYOUT_STEREO)
5240                 {
5241                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5242                                             [NSString stringWithCString: hb_audio_mixdowns[1].human_readable_name]
5243                                                                           action: NULL keyEquivalent: @""];
5244                     [menuItem setTag: hb_audio_mixdowns[1].amixdown];
5245                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[1].amixdown;
5246                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[1].amixdown);
5247                 }
5248                 
5249                 /* do we want to add a dolby surround (DPL1) option? */
5250                 if (layout == HB_INPUT_CH_LAYOUT_3F1R || layout == HB_INPUT_CH_LAYOUT_3F2R || layout == HB_INPUT_CH_LAYOUT_DOLBY)
5251                 {
5252                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5253                                             [NSString stringWithCString: hb_audio_mixdowns[2].human_readable_name]
5254                                                                           action: NULL keyEquivalent: @""];
5255                     [menuItem setTag: hb_audio_mixdowns[2].amixdown];
5256                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[2].amixdown;
5257                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[2].amixdown);
5258                 }
5259                 
5260                 /* do we want to add a dolby pro logic 2 (DPL2) option? */
5261                 if (layout == HB_INPUT_CH_LAYOUT_3F2R)
5262                 {
5263                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5264                                             [NSString stringWithCString: hb_audio_mixdowns[3].human_readable_name]
5265                                                                           action: NULL keyEquivalent: @""];
5266                     [menuItem setTag: hb_audio_mixdowns[3].amixdown];
5267                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[3].amixdown;
5268                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[3].amixdown);
5269                 }
5270                 
5271                 /* do we want to add a 6-channel discrete option? */
5272                 if (audioCodecsSupport6Ch == 1 && layout == HB_INPUT_CH_LAYOUT_3F2R && (audio->in.channel_layout & HB_INPUT_CH_LAYOUT_HAS_LFE))
5273                 {
5274                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5275                                             [NSString stringWithCString: hb_audio_mixdowns[4].human_readable_name]
5276                                                                           action: NULL keyEquivalent: @""];
5277                     [menuItem setTag: hb_audio_mixdowns[4].amixdown];
5278                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[4].amixdown;
5279                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[4].amixdown);
5280                 }
5281                 
5282                 /* do we want to add an AC-3 passthrough option? */
5283                 if (audio->in.codec == HB_ACODEC_AC3 && acodec == HB_ACODEC_AC3) 
5284                 {
5285                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5286                                             [NSString stringWithCString: hb_audio_mixdowns[5].human_readable_name]
5287                                                                           action: NULL keyEquivalent: @""];
5288                     [menuItem setTag: HB_ACODEC_AC3];
5289                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[5].amixdown;
5290                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[5].amixdown);
5291                 }
5292                 
5293                 /* do we want to add a DTS Passthru option ? HB_ACODEC_DCA*/
5294                 if (audio->in.codec == HB_ACODEC_DCA && acodec == HB_ACODEC_DCA) 
5295                 {
5296                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5297                                             [NSString stringWithCString: hb_audio_mixdowns[5].human_readable_name]
5298                                                                           action: NULL keyEquivalent: @""];
5299                     [menuItem setTag: HB_ACODEC_DCA];
5300                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[5].amixdown;
5301                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[5].amixdown);
5302                 }
5303                 
5304                 /* auto-select the best mixdown based on our saved mixdown preference */
5305                 
5306                 /* for now, this is hard-coded to a "best" mixdown of HB_AMIXDOWN_DOLBYPLII */
5307                 /* ultimately this should be a prefs option */
5308                 int useMixdown;
5309                 
5310                 /* if we passed in a mixdown to use - in order to load a preset - then try and use it */
5311                 if (mixdownToUse > 0)
5312                 {
5313                     useMixdown = mixdownToUse;
5314                 }
5315                 else
5316                 {
5317                     useMixdown = HB_AMIXDOWN_DOLBYPLII;
5318                 }
5319                 
5320                 /* if useMixdown > maxMixdownUsed, then use maxMixdownUsed */
5321                 if (useMixdown > maxMixdownUsed)
5322                 { 
5323                     useMixdown = maxMixdownUsed;
5324                 }
5325                 
5326                 /* if useMixdown < minMixdownUsed, then use minMixdownUsed */
5327                 if (useMixdown < minMixdownUsed)
5328                 { 
5329                     useMixdown = minMixdownUsed;
5330                 }
5331                 
5332                 /* select the (possibly-amended) preferred mixdown */
5333                 [mixdownPopUp selectItemWithTag: useMixdown];
5334
5335             }
5336             /* In the case of a source track that is not AC3 and the user tries to use AC3 Passthru (which does not work)
5337              * we force the Audio Codec choice back to a workable codec. We use MP3 for avi and aac for all
5338              * other containers.
5339              */
5340             if (audio->in.codec != HB_ACODEC_AC3 && [[audiocodecPopUp selectedItem] tag] == HB_ACODEC_AC3)
5341             {
5342                 /* If we are using the avi container, we select MP3 as there is no aac available*/
5343                 if ([[fDstFormatPopUp selectedItem] tag] == HB_MUX_AVI)
5344                 {
5345                     [audiocodecPopUp selectItemWithTag: HB_ACODEC_LAME];
5346                 }
5347                 else
5348                 {
5349                     [audiocodecPopUp selectItemWithTag: HB_ACODEC_FAAC];
5350                 }
5351             }
5352             
5353             /* In the case of a source track that is not DTS and the user tries to use DTS Passthru (which does not work)
5354              * we force the Audio Codec choice back to a workable codec. We use MP3 for avi and aac for all
5355              * other containers.
5356              */
5357             if (audio->in.codec != HB_ACODEC_DCA && [[audiocodecPopUp selectedItem] tag] == HB_ACODEC_DCA)
5358             {
5359                 /* If we are using the avi container, we select MP3 as there is no aac available*/
5360                 if ([[fDstFormatPopUp selectedItem] tag] == HB_MUX_AVI)
5361                 {
5362                     [audiocodecPopUp selectItemWithTag: HB_ACODEC_LAME];
5363                 }
5364                 else
5365                 {
5366                     [audiocodecPopUp selectItemWithTag: HB_ACODEC_FAAC];
5367                 }
5368             }
5369             
5370             /* Setup our samplerate and bitrate popups we will need based on mixdown */
5371             [self audioTrackMixdownChanged: mixdownPopUp];             
5372         }
5373     
5374     }
5375     if( [fDstFormatPopUp indexOfSelectedItem] == 0 )
5376     {
5377         [self autoSetM4vExtension: sender];
5378     }
5379 }
5380
5381 - (IBAction) audioTrackMixdownChanged: (id) sender
5382 {
5383     
5384     int acodec;
5385     /* setup pointers to all of the other audio track controls
5386     * we will need later
5387     */
5388     NSPopUpButton * mixdownPopUp;
5389     NSPopUpButton * sampleratePopUp;
5390     NSPopUpButton * bitratePopUp;
5391     NSPopUpButton * audiocodecPopUp;
5392     NSPopUpButton * audiotrackPopUp;
5393     NSSlider * drcSlider;
5394     NSTextField * drcField;
5395     if (sender == fAudTrack1MixPopUp)
5396     {
5397         audiotrackPopUp = fAudLang1PopUp;
5398         audiocodecPopUp = fAudTrack1CodecPopUp;
5399         mixdownPopUp = fAudTrack1MixPopUp;
5400         sampleratePopUp = fAudTrack1RatePopUp;
5401         bitratePopUp = fAudTrack1BitratePopUp;
5402         drcSlider = fAudTrack1DrcSlider;
5403         drcField = fAudTrack1DrcField;
5404     }
5405     else if (sender == fAudTrack2MixPopUp)
5406     {
5407         audiotrackPopUp = fAudLang2PopUp;
5408         audiocodecPopUp = fAudTrack2CodecPopUp;
5409         mixdownPopUp = fAudTrack2MixPopUp;
5410         sampleratePopUp = fAudTrack2RatePopUp;
5411         bitratePopUp = fAudTrack2BitratePopUp;
5412         drcSlider = fAudTrack2DrcSlider;
5413         drcField = fAudTrack2DrcField;
5414     }
5415     else if (sender == fAudTrack3MixPopUp)
5416     {
5417         audiotrackPopUp = fAudLang3PopUp;
5418         audiocodecPopUp = fAudTrack3CodecPopUp;
5419         mixdownPopUp = fAudTrack3MixPopUp;
5420         sampleratePopUp = fAudTrack3RatePopUp;
5421         bitratePopUp = fAudTrack3BitratePopUp;
5422         drcSlider = fAudTrack3DrcSlider;
5423         drcField = fAudTrack3DrcField;
5424     }
5425     else
5426     {
5427         audiotrackPopUp = fAudLang4PopUp;
5428         audiocodecPopUp = fAudTrack4CodecPopUp;
5429         mixdownPopUp = fAudTrack4MixPopUp;
5430         sampleratePopUp = fAudTrack4RatePopUp;
5431         bitratePopUp = fAudTrack4BitratePopUp;
5432         drcSlider = fAudTrack4DrcSlider;
5433         drcField = fAudTrack4DrcField;
5434     }
5435     acodec = [[audiocodecPopUp selectedItem] tag];
5436     /* storage variable for the min and max bitrate allowed for this codec */
5437     int minbitrate;
5438     int maxbitrate;
5439     
5440     switch( acodec )
5441     {
5442         case HB_ACODEC_FAAC:
5443             /* check if we have a 6ch discrete conversion in either audio track */
5444             if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH)
5445             {
5446                 /* FAAC is happy using our min bitrate of 32 kbps, even for 6ch */
5447                 minbitrate = 32;
5448                 /* If either mixdown popup includes 6-channel discrete, then allow up to 384 kbps */
5449                 maxbitrate = 384;
5450                 break;
5451             }
5452             else
5453             {
5454                 /* FAAC is happy using our min bitrate of 32 kbps for stereo or mono */
5455                 minbitrate = 32;
5456                 /* FAAC won't honour anything more than 160 for stereo, so let's not offer it */
5457                 /* note: haven't dealt with mono separately here, FAAC will just use the max it can */
5458                 maxbitrate = 160;
5459                 break;
5460             }
5461
5462         case HB_ACODEC_CA_AAC:
5463             /* check if we have a 6ch discrete conversion in either audio track */
5464             if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH)
5465             {
5466                 minbitrate = 128;
5467                 maxbitrate = 768;
5468                 break;
5469             }
5470             else
5471             {
5472                 minbitrate = 64;
5473                 maxbitrate = 320;
5474                 break;
5475             }
5476
5477             case HB_ACODEC_LAME:
5478             /* Lame is happy using our min bitrate of 32 kbps */
5479             minbitrate = 32;
5480             /* Lame won't encode if the bitrate is higher than 320 kbps */
5481             maxbitrate = 320;
5482             break;
5483             
5484             case HB_ACODEC_VORBIS:
5485             if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH)
5486             {
5487                 /* Vorbis causes a crash if we use a bitrate below 192 kbps with 6 channel */
5488                 minbitrate = 192;
5489                 /* If either mixdown popup includes 6-channel discrete, then allow up to 384 kbps */
5490                 maxbitrate = 384;
5491                 break;
5492             }
5493             else
5494             {
5495                 /* Vorbis causes a crash if we use a bitrate below 48 kbps */
5496                 minbitrate = 48;
5497                 /* Vorbis can cope with 384 kbps quite happily, even for stereo */
5498                 maxbitrate = 384;
5499                 break;
5500             }
5501             
5502             default:
5503             /* AC3 passthru disables the bitrate dropdown anyway, so we might as well just use the min and max bitrate */
5504             minbitrate = 32;
5505             maxbitrate = 384;
5506             
5507     }
5508     
5509     /* make sure we have a selected title before continuing */
5510     if (fTitle == NULL) return;
5511     /* get the audio so we can find out what input rates are*/
5512     hb_audio_config_t * audio;
5513     audio = (hb_audio_config_t *) hb_list_audio_config_item( fTitle->list_audio, [audiotrackPopUp indexOfSelectedItem] - 1 );
5514     int inputbitrate = audio->in.bitrate / 1000;
5515     int inputsamplerate = audio->in.samplerate;
5516     
5517     if ([[mixdownPopUp selectedItem] tag] != HB_ACODEC_AC3 && [[mixdownPopUp selectedItem] tag] != HB_ACODEC_DCA)
5518     {
5519         [bitratePopUp removeAllItems];
5520         
5521         for( int i = 0; i < hb_audio_bitrates_count; i++ )
5522         {
5523             if (hb_audio_bitrates[i].rate >= minbitrate && hb_audio_bitrates[i].rate <= maxbitrate)
5524             {
5525                 /* add a new menuitem for this bitrate */
5526                 NSMenuItem *menuItem = [[bitratePopUp menu] addItemWithTitle:
5527                                         [NSString stringWithCString: hb_audio_bitrates[i].string]
5528                                                                       action: NULL keyEquivalent: @""];
5529                 /* set its tag to be the actual bitrate as an integer, so we can retrieve it later */
5530                 [menuItem setTag: hb_audio_bitrates[i].rate];
5531             }
5532         }
5533         
5534         /* select the default bitrate (but use 384 for 6-ch AAC) */
5535         if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH)
5536         {
5537             [bitratePopUp selectItemWithTag: 384];
5538         }
5539         else
5540         {
5541             [bitratePopUp selectItemWithTag: hb_audio_bitrates[hb_audio_bitrates_default].rate];
5542         }
5543     }
5544     /* populate and set the sample rate popup */
5545     /* Audio samplerate */
5546     [sampleratePopUp removeAllItems];
5547     /* we create a same as source selection (Auto) so that we can choose to use the input sample rate */
5548     NSMenuItem *menuItem = [[sampleratePopUp menu] addItemWithTitle: @"Auto" action: NULL keyEquivalent: @""];
5549     [menuItem setTag: inputsamplerate];
5550     
5551     for( int i = 0; i < hb_audio_rates_count; i++ )
5552     {
5553         NSMenuItem *menuItem = [[sampleratePopUp menu] addItemWithTitle:
5554                                 [NSString stringWithCString: hb_audio_rates[i].string]
5555                                                                  action: NULL keyEquivalent: @""];
5556         [menuItem setTag: hb_audio_rates[i].rate];
5557     }
5558     /* We use the input sample rate as the default sample rate as downsampling just makes audio worse
5559     * and there is no compelling reason to use anything else as default, though the users default
5560     * preset will likely override any setting chosen here.
5561     */
5562     [sampleratePopUp selectItemWithTag: inputsamplerate];
5563     
5564     
5565     /* Since AC3 Pass Thru and DTS Pass Thru uses the input bitrate and sample rate, we get the input tracks
5566     * bitrate and display it in the bitrate popup even though libhb happily ignores any bitrate input from
5567     * the gui. We do this for better user feedback in the audio tab as well as the queue for the most part
5568     */
5569     if ([[mixdownPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[mixdownPopUp selectedItem] tag] == HB_ACODEC_DCA)
5570     {
5571         
5572         /* lets also set the bitrate popup to the input bitrate as thats what passthru will use */
5573         [bitratePopUp removeAllItems];
5574         NSMenuItem *menuItem = [[bitratePopUp menu] addItemWithTitle:
5575                                 [NSString stringWithFormat:@"%d", inputbitrate]
5576                                                               action: NULL keyEquivalent: @""];
5577         [menuItem setTag: inputbitrate];
5578         /* For ac3 passthru we disable the sample rate and bitrate popups as well as the drc slider*/
5579         [bitratePopUp setEnabled: NO];
5580         [sampleratePopUp setEnabled: NO];
5581         
5582         [drcSlider setFloatValue: 1.00];
5583         [self audioDRCSliderChanged: drcSlider];
5584         [drcSlider setEnabled: NO];
5585         [drcField setEnabled: NO];
5586     }
5587     else
5588     {
5589         [sampleratePopUp setEnabled: YES];
5590         [bitratePopUp setEnabled: YES];
5591         [drcSlider setEnabled: YES];
5592         [drcField setEnabled: YES];
5593     }
5594 [self calculateBitrate:nil];    
5595 }
5596
5597 - (IBAction) audioDRCSliderChanged: (id) sender
5598 {
5599     NSSlider * drcSlider;
5600     NSTextField * drcField;
5601     if (sender == fAudTrack1DrcSlider)
5602     {
5603         drcSlider = fAudTrack1DrcSlider;
5604         drcField = fAudTrack1DrcField;
5605     }
5606     else if (sender == fAudTrack2DrcSlider)
5607     {
5608         drcSlider = fAudTrack2DrcSlider;
5609         drcField = fAudTrack2DrcField;
5610     }
5611     else if (sender == fAudTrack3DrcSlider)
5612     {
5613         drcSlider = fAudTrack3DrcSlider;
5614         drcField = fAudTrack3DrcField;
5615     }
5616     else
5617     {
5618         drcSlider = fAudTrack4DrcSlider;
5619         drcField = fAudTrack4DrcField;
5620     }
5621     
5622     /* If we are between 0.0 and 1.0 on the slider, snap it to 1.0 */
5623     if ([drcSlider floatValue] > 0.0 && [drcSlider floatValue] < 1.0)
5624     {
5625         [drcSlider setFloatValue:1.0];
5626     }
5627     
5628     
5629     [drcField setStringValue: [NSString stringWithFormat: @"%.2f", [drcSlider floatValue]]];
5630     /* For now, do not call this until we have an intelligent way to determine audio track selections
5631     * compared to presets
5632     */
5633     //[self customSettingUsed: sender];
5634 }
5635
5636
5637 #pragma mark -
5638 #pragma mark Open New Windows
5639
5640 - (IBAction) openHomepage: (id) sender
5641 {
5642     [[NSWorkspace sharedWorkspace] openURL: [NSURL
5643         URLWithString:@"http://handbrake.fr/"]];
5644 }
5645
5646 - (IBAction) openForums: (id) sender
5647 {
5648     [[NSWorkspace sharedWorkspace] openURL: [NSURL
5649         URLWithString:@"http://handbrake.fr/forum/"]];
5650 }
5651 - (IBAction) openUserGuide: (id) sender
5652 {
5653     [[NSWorkspace sharedWorkspace] openURL: [NSURL
5654         URLWithString:@"http://handbrake.fr/trac/wiki/HandBrakeGuide"]];
5655 }
5656
5657 /**
5658  * Shows debug output window.
5659  */
5660 - (IBAction)showDebugOutputPanel:(id)sender
5661 {
5662     [outputPanel showOutputPanel:sender];
5663 }
5664
5665 /**
5666  * Shows preferences window.
5667  */
5668 - (IBAction) showPreferencesWindow: (id) sender
5669 {
5670     NSWindow * window = [fPreferencesController window];
5671     if (![window isVisible])
5672         [window center];
5673
5674     [window makeKeyAndOrderFront: nil];
5675 }
5676
5677 /**
5678  * Shows queue window.
5679  */
5680 - (IBAction) showQueueWindow:(id)sender
5681 {
5682     [fQueueController showQueueWindow:sender];
5683 }
5684
5685
5686 - (IBAction) toggleDrawer:(id)sender {
5687     [fPresetDrawer toggle:self];
5688 }
5689
5690 /**
5691  * Shows Picture Settings Window.
5692  */
5693
5694 - (IBAction) showPicturePanel: (id) sender
5695 {
5696         [fPictureController showPictureWindow:sender];
5697 }
5698
5699 - (void) picturePanelFullScreen
5700 {
5701         [fPictureController setToFullScreenMode];
5702 }
5703
5704 - (void) picturePanelWindowed
5705 {
5706         [fPictureController setToWindowedMode];
5707 }
5708
5709 - (IBAction) showPreviewWindow: (id) sender
5710 {
5711         [fPictureController showPreviewWindow:sender];
5712 }
5713
5714 #pragma mark -
5715 #pragma mark Preset Outline View Methods
5716 #pragma mark - Required
5717 /* These are required by the NSOutlineView Datasource Delegate */
5718
5719
5720 /* used to specify the number of levels to show for each item */
5721 - (int)outlineView:(NSOutlineView *)fPresetsOutlineView numberOfChildrenOfItem:(id)item
5722 {
5723     /* currently use no levels to test outline view viability */
5724     if (item == nil) // for an outline view the root level of the hierarchy is always nil
5725     {
5726         return [UserPresets count];
5727     }
5728     else
5729     {
5730         /* we need to return the count of the array in ChildrenArray for this folder */
5731         NSArray *children = nil;
5732         children = [item objectForKey:@"ChildrenArray"];
5733         if ([children count] > 0)
5734         {
5735             return [children count];
5736         }
5737         else
5738         {
5739             return 0;
5740         }
5741     }
5742 }
5743
5744 /* We use this to deterimine children of an item */
5745 - (id)outlineView:(NSOutlineView *)fPresetsOutlineView child:(NSInteger)index ofItem:(id)item
5746 {
5747     
5748     /* we need to return the count of the array in ChildrenArray for this folder */
5749     NSArray *children = nil;
5750     if (item == nil)
5751     {
5752         children = UserPresets;
5753     }
5754     else
5755     {
5756         if ([item objectForKey:@"ChildrenArray"])
5757         {
5758             children = [item objectForKey:@"ChildrenArray"];
5759         }
5760     }   
5761     if ((children == nil) || ( [children count] <= (NSUInteger) index))
5762     {
5763         return nil;
5764     }
5765     else
5766     {
5767         return [children objectAtIndex:index];
5768     }
5769     
5770     
5771     // We are only one level deep, so we can't be asked about children
5772     //NSAssert (NO, @"Presets View outlineView:child:ofItem: currently can't handle nested items.");
5773     //return nil;
5774 }
5775
5776 /* We use this to determine if an item should be expandable */
5777 - (BOOL)outlineView:(NSOutlineView *)fPresetsOutlineView isItemExpandable:(id)item
5778 {
5779     
5780     /* we need to return the count of the array in ChildrenArray for this folder */
5781     NSArray *children= nil;
5782     if (item == nil)
5783     {
5784         children = UserPresets;
5785     }
5786     else
5787     {
5788         if ([item objectForKey:@"ChildrenArray"])
5789         {
5790             children = [item objectForKey:@"ChildrenArray"];
5791         }
5792     }   
5793     
5794     /* To deterimine if an item should show a disclosure triangle
5795      * we could do it by the children count as so:
5796      * if ([children count] < 1)
5797      * However, lets leave the triangle show even if there are no
5798      * children to help indicate a folder, just like folder in the
5799      * finder can show a disclosure triangle even when empty
5800      */
5801     
5802     /* We need to determine if the item is a folder */
5803    if ([[item objectForKey:@"Folder"] intValue] == 1)
5804    {
5805         return YES;
5806     }
5807     else
5808     {
5809         return NO;
5810     }
5811     
5812 }
5813
5814 - (BOOL)outlineView:(NSOutlineView *)outlineView shouldExpandItem:(id)item
5815 {
5816     // Our outline view has no levels, but we can still expand every item. Doing so
5817     // just makes the row taller. See heightOfRowByItem below.
5818 //return ![(HBQueueOutlineView*)outlineView isDragging];
5819
5820 return YES;
5821 }
5822
5823
5824 /* Used to tell the outline view which information is to be displayed per item */
5825 - (id)outlineView:(NSOutlineView *)fPresetsOutlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
5826 {
5827         /* We have two columns right now, icon and PresetName */
5828         
5829     if ([[tableColumn identifier] isEqualToString:@"PresetName"])
5830     {
5831         return [item objectForKey:@"PresetName"];
5832     }
5833     else
5834     {
5835         //return @"";
5836         return nil;
5837     }
5838 }
5839
5840 - (id)outlineView:(NSOutlineView *)outlineView itemForPersistentObject:(id)object
5841 {
5842     return [NSKeyedUnarchiver unarchiveObjectWithData:object];
5843 }
5844 - (id)outlineView:(NSOutlineView *)outlineView persistentObjectForItem:(id)item
5845 {
5846     return [NSKeyedArchiver archivedDataWithRootObject:item];
5847 }
5848
5849 #pragma mark - Added Functionality (optional)
5850 /* Use to customize the font and display characteristics of the title cell */
5851 - (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
5852 {
5853     if ([[tableColumn identifier] isEqualToString:@"PresetName"])
5854     {
5855         NSFont *txtFont;
5856         NSColor *fontColor;
5857         NSColor *shadowColor;
5858         txtFont = [NSFont systemFontOfSize: [NSFont smallSystemFontSize]];
5859         /*check to see if its a selected row */
5860         if ([fPresetsOutlineView selectedRow] == [fPresetsOutlineView rowForItem:item])
5861         {
5862             
5863             fontColor = [NSColor blackColor];
5864             shadowColor = [NSColor colorWithDeviceRed:(127.0/255.0) green:(140.0/255.0) blue:(160.0/255.0) alpha:1.0];
5865         }
5866         else
5867         {
5868             if ([[item objectForKey:@"Type"] intValue] == 0)
5869             {
5870                 fontColor = [NSColor blueColor];
5871             }
5872             else // User created preset, use a black font
5873             {
5874                 fontColor = [NSColor blackColor];
5875             }
5876             /* check to see if its a folder */
5877             //if ([[item objectForKey:@"Folder"] intValue] == 1)
5878             //{
5879             //fontColor = [NSColor greenColor];
5880             //}
5881             
5882             
5883         }
5884         /* We use Bold Text for the HB Default */
5885         if ([[item objectForKey:@"Default"] intValue] == 1)// 1 is HB default
5886         {
5887             txtFont = [NSFont boldSystemFontOfSize: [NSFont smallSystemFontSize]];
5888         }
5889         /* We use Bold Text for the User Specified Default */
5890         if ([[item objectForKey:@"Default"] intValue] == 2)// 2 is User default
5891         {
5892             txtFont = [NSFont boldSystemFontOfSize: [NSFont smallSystemFontSize]];
5893         }
5894         
5895         
5896         [cell setTextColor:fontColor];
5897         [cell setFont:txtFont];
5898         
5899     }
5900 }
5901
5902 /* We use this to edit the name field in the outline view */
5903 - (void)outlineView:(NSOutlineView *)outlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
5904 {
5905     if ([[tableColumn identifier] isEqualToString:@"PresetName"])
5906     {
5907         id theRecord;
5908         
5909         theRecord = item;
5910         [theRecord setObject:object forKey:@"PresetName"];
5911         
5912         [self sortPresets];
5913         
5914         [fPresetsOutlineView reloadData];
5915         /* We save all of the preset data here */
5916         [self savePreset];
5917     }
5918 }
5919 /* We use this to provide tooltips for the items in the presets outline view */
5920 - (NSString *)outlineView:(NSOutlineView *)fPresetsOutlineView toolTipForCell:(NSCell *)cell rect:(NSRectPointer)rect tableColumn:(NSTableColumn *)tc item:(id)item mouseLocation:(NSPoint)mouseLocation
5921 {
5922     //if ([[tc identifier] isEqualToString:@"PresetName"])
5923     //{
5924         /* initialize the tooltip contents variable */
5925         NSString *loc_tip;
5926         /* if there is a description for the preset, we show it in the tooltip */
5927         if ([item objectForKey:@"PresetDescription"])
5928         {
5929             loc_tip = [item objectForKey:@"PresetDescription"];
5930             return (loc_tip);
5931         }
5932         else
5933         {
5934             loc_tip = @"No description available";
5935         }
5936         return (loc_tip);
5937     //}
5938 }
5939
5940 #pragma mark -
5941 #pragma mark Preset Outline View Methods (dragging related)
5942
5943
5944 - (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard
5945 {
5946         // Dragging is only allowed for custom presets.
5947     //[[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Default"] intValue] != 1
5948         if ([[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Type"] intValue] == 0) // 0 is built in preset
5949     {
5950         return NO;
5951     }
5952     // Don't retain since this is just holding temporaral drag information, and it is
5953     //only used during a drag!  We could put this in the pboard actually.
5954     fDraggedNodes = items;
5955     // Provide data for our custom type, and simple NSStrings.
5956     [pboard declareTypes:[NSArray arrayWithObjects: DragDropSimplePboardType, nil] owner:self];
5957     
5958     // the actual data doesn't matter since DragDropSimplePboardType drags aren't recognized by anyone but us!.
5959     [pboard setData:[NSData data] forType:DragDropSimplePboardType]; 
5960     
5961     return YES;
5962 }
5963
5964 - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
5965 {
5966         
5967         // Don't allow dropping ONTO an item since they can't really contain any children.
5968     
5969     BOOL isOnDropTypeProposal = index == NSOutlineViewDropOnItemIndex;
5970     if (isOnDropTypeProposal)
5971         return NSDragOperationNone;
5972     
5973     // Don't allow dropping INTO an item since they can't really contain any children as of yet.
5974         if (item != nil)
5975         {
5976                 index = [fPresetsOutlineView rowForItem: item] + 1;
5977                 item = nil;
5978         }
5979     
5980     // Don't allow dropping into the Built In Presets.
5981     if (index < presetCurrentBuiltInCount)
5982     {
5983         return NSDragOperationNone;
5984         index = MAX (index, presetCurrentBuiltInCount);
5985         }    
5986         
5987     [outlineView setDropItem:item dropChildIndex:index];
5988     return NSDragOperationGeneric;
5989 }
5990
5991
5992
5993 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index
5994 {
5995     /* first, lets see if we are dropping into a folder */
5996     if ([[fPresetsOutlineView itemAtRow:index] objectForKey:@"Folder"] && [[[fPresetsOutlineView itemAtRow:index] objectForKey:@"Folder"] intValue] == 1) // if its a folder
5997         {
5998     NSMutableArray *childrenArray = [[NSMutableArray alloc] init];
5999     childrenArray = [[fPresetsOutlineView itemAtRow:index] objectForKey:@"ChildrenArray"];
6000     [childrenArray addObject:item];
6001     [[fPresetsOutlineView itemAtRow:index] setObject:[NSMutableArray arrayWithArray: childrenArray] forKey:@"ChildrenArray"];
6002     [childrenArray autorelease];
6003     }
6004     else // We are not, so we just move the preset into the existing array 
6005     {
6006         NSMutableIndexSet *moveItems = [NSMutableIndexSet indexSet];
6007         id obj;
6008         NSEnumerator *enumerator = [fDraggedNodes objectEnumerator];
6009         while (obj = [enumerator nextObject])
6010         {
6011             [moveItems addIndex:[UserPresets indexOfObject:obj]];
6012         }
6013         // Successful drop, lets rearrange the view and save it all
6014         [self moveObjectsInPresetsArray:UserPresets fromIndexes:moveItems toIndex: index];
6015     }
6016     [fPresetsOutlineView reloadData];
6017     [self savePreset];
6018     return YES;
6019 }
6020
6021 - (void)moveObjectsInPresetsArray:(NSMutableArray *)array fromIndexes:(NSIndexSet *)indexSet toIndex:(NSUInteger)insertIndex
6022 {
6023     NSUInteger index = [indexSet lastIndex];
6024     NSUInteger aboveInsertIndexCount = 0;
6025     
6026     NSUInteger removeIndex;
6027
6028     if (index >= insertIndex)
6029     {
6030         removeIndex = index + aboveInsertIndexCount;
6031         aboveInsertIndexCount++;
6032     }
6033     else
6034     {
6035         removeIndex = index;
6036         insertIndex--;
6037     }
6038
6039     id object = [[array objectAtIndex:removeIndex] retain];
6040     [array removeObjectAtIndex:removeIndex];
6041     [array insertObject:object atIndex:insertIndex];
6042     [object release];
6043
6044     index = [indexSet indexLessThanIndex:index];
6045 }
6046
6047
6048
6049 #pragma mark - Functional Preset NSOutlineView Methods
6050
6051 - (IBAction)selectPreset:(id)sender
6052 {
6053     
6054     if ([fPresetsOutlineView selectedRow] >= 0 && [[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Folder"] intValue] != 1)
6055     {
6056         chosenPreset = [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]];
6057         [fPresetSelectedDisplay setStringValue:[chosenPreset objectForKey:@"PresetName"]];
6058         
6059         if ([[chosenPreset objectForKey:@"Default"] intValue] == 1)
6060         {
6061             [fPresetSelectedDisplay setStringValue:[NSString stringWithFormat:@"%@ (Default)", [chosenPreset objectForKey:@"PresetName"]]];
6062         }
6063         else
6064         {
6065             [fPresetSelectedDisplay setStringValue:[chosenPreset objectForKey:@"PresetName"]];
6066         }
6067         
6068         /* File Format */
6069         [fDstFormatPopUp selectItemWithTitle:[chosenPreset objectForKey:@"FileFormat"]];
6070         [self formatPopUpChanged:nil];
6071         
6072         /* Chapter Markers*/
6073         [fCreateChapterMarkers setState:[[chosenPreset objectForKey:@"ChapterMarkers"] intValue]];
6074         /* check to see if we have only one chapter */
6075         [self chapterPopUpChanged:nil];
6076         
6077         /* Allow Mpeg4 64 bit formatting +4GB file sizes */
6078         [fDstMp4LargeFileCheck setState:[[chosenPreset objectForKey:@"Mp4LargeFile"] intValue]];
6079         /* Mux mp4 with http optimization */
6080         [fDstMp4HttpOptFileCheck setState:[[chosenPreset objectForKey:@"Mp4HttpOptimize"] intValue]];
6081         
6082         /* Video encoder */
6083         [fVidEncoderPopUp selectItemWithTitle:[chosenPreset objectForKey:@"VideoEncoder"]];
6084         /* We set the advanced opt string here if applicable*/
6085         [fAdvancedOptions setOptions:[chosenPreset objectForKey:@"x264Option"]];
6086         
6087         /* Lets run through the following functions to get variables set there */
6088         [self videoEncoderPopUpChanged:nil];
6089         /* Set the state of ipod compatible with Mp4iPodCompatible. Only for x264*/
6090         [fDstMp4iPodFileCheck setState:[[chosenPreset objectForKey:@"Mp4iPodCompatible"] intValue]];
6091         [self calculateBitrate:nil];
6092         
6093         /* Video quality */
6094         [fVidQualityMatrix selectCellAtRow:[[chosenPreset objectForKey:@"VideoQualityType"] intValue] column:0];
6095         
6096         [fVidTargetSizeField setStringValue:[chosenPreset objectForKey:@"VideoTargetSize"]];
6097         [fVidBitrateField setStringValue:[chosenPreset objectForKey:@"VideoAvgBitrate"]];
6098         
6099         /* Since we are now using RF Values for the slider, we detect if the preset uses an old quality float.
6100          * So, check to see if the quality value is less than 1.0 which should indicate the old ".062" type
6101          * quality preset. Caveat: in the case of x264, where the RF scale starts at 0, it would misinterpret
6102          * a preset that uses 0.0 - 0.99 for RF as an old style preset. Not sure how to get around that one yet,
6103          * though it should be a corner case since it would pretty much be a preset for lossless encoding. */
6104         if ([[chosenPreset objectForKey:@"VideoQualitySlider"] floatValue] < 1.0)
6105         {
6106             /* For the quality slider we need to convert the old percent's to the new rf scales */
6107             float rf =  (([fVidQualitySlider maxValue] - [fVidQualitySlider minValue]) * [[chosenPreset objectForKey:@"VideoQualitySlider"] floatValue]);
6108             [fVidQualitySlider setFloatValue:rf];
6109             
6110         }
6111         else
6112         {
6113             [fVidQualitySlider setFloatValue:([fVidQualitySlider maxValue] - [fVidQualitySlider minValue]) - [[chosenPreset objectForKey:@"VideoQualitySlider"] floatValue]];
6114         }
6115         
6116         [self videoMatrixChanged:nil];
6117         
6118         /* Video framerate */
6119         /* For video preset video framerate, we want to make sure that Same as source does not conflict with the
6120          detected framerate in the fVidRatePopUp so we use index 0*/
6121         if ([[chosenPreset objectForKey:@"VideoFramerate"] isEqualToString:@"Same as source"])
6122         {
6123             [fVidRatePopUp selectItemAtIndex: 0];
6124         }
6125         else
6126         {
6127             [fVidRatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"VideoFramerate"]];
6128         }
6129         
6130         
6131         /* 2 Pass Encoding */
6132         [fVidTwoPassCheck setState:[[chosenPreset objectForKey:@"VideoTwoPass"] intValue]];
6133         [self twoPassCheckboxChanged:nil];
6134         
6135         /* Turbo 1st pass for 2 Pass Encoding */
6136         [fVidTurboPassCheck setState:[[chosenPreset objectForKey:@"VideoTurboTwoPass"] intValue]];
6137         
6138         /*Audio*/
6139         /* First we check to see if we are using the current audio track layout based on AudioList array */
6140         if ([chosenPreset objectForKey:@"AudioList"])
6141         {
6142             /* Populate the audio widgets based on the contents of the AudioList array */
6143             int i = 0;
6144             NSEnumerator *enumerator = [[chosenPreset objectForKey:@"AudioList"] objectEnumerator];
6145             id tempObject;
6146             while (tempObject = [enumerator nextObject])
6147             {
6148                 i++;
6149                 if( i == 1 )
6150                 {
6151                     if ([fAudLang1PopUp indexOfSelectedItem] == 0)
6152                     {
6153                         [fAudLang1PopUp selectItemAtIndex: 1];
6154                     }
6155                     [self audioTrackPopUpChanged: fAudLang1PopUp];
6156                     [fAudTrack1CodecPopUp selectItemWithTitle:[tempObject objectForKey:@"AudioEncoder"]];
6157                     /* check our pref for core audio and use it in place of faac if applicable */
6158                     if ([[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES && 
6159                         [[tempObject objectForKey:@"AudioEncoder"] isEqualToString: @"AAC (faac)"])
6160                     {
6161                         [fAudTrack1CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"];
6162                     }                    
6163                     
6164                     [self audioTrackPopUpChanged: fAudTrack1CodecPopUp];
6165                     [fAudTrack1MixPopUp selectItemWithTitle:[tempObject objectForKey:@"AudioMixdown"]];
6166                     /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
6167                      * mixdown*/
6168                     if  ([fAudTrack1MixPopUp selectedItem] == nil)
6169                     {
6170                         [self audioTrackPopUpChanged: fAudTrack1CodecPopUp];
6171                     }
6172                     [fAudTrack1RatePopUp selectItemWithTitle:[tempObject objectForKey:@"AudioSamplerate"]];
6173                     /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
6174                     if (![[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"AC3 Passthru"])
6175                     {
6176                         [fAudTrack1BitratePopUp selectItemWithTitle:[tempObject objectForKey:@"AudioBitrate"]];
6177                     }
6178                     [fAudTrack1DrcSlider setFloatValue:[[tempObject objectForKey:@"AudioTrackDRCSlider"] floatValue]];
6179                     [self audioDRCSliderChanged: fAudTrack1DrcSlider];
6180                 }
6181                 
6182                 if( i == 2 )
6183                 {
6184                     
6185                     if ([fAudLang2PopUp indexOfSelectedItem] == 0)
6186                     {
6187                         [fAudLang2PopUp selectItemAtIndex: 1];
6188                     }
6189                     [self audioTrackPopUpChanged: fAudLang2PopUp];
6190                     [fAudTrack2CodecPopUp selectItemWithTitle:[tempObject objectForKey:@"AudioEncoder"]];
6191                     /* check our pref for core audio and use it in place of faac if applicable */
6192                     if ([[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES && 
6193                         [[tempObject objectForKey:@"AudioEncoder"] isEqualToString: @"AAC (faac)"])
6194                     {
6195                         [fAudTrack2CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"];
6196                     }
6197                     [self audioTrackPopUpChanged: fAudTrack2CodecPopUp];
6198                     [fAudTrack2MixPopUp selectItemWithTitle:[tempObject objectForKey:@"AudioMixdown"]];
6199                     /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
6200                      * mixdown*/
6201                     if  ([fAudTrack2MixPopUp selectedItem] == nil)
6202                     {
6203                         [self audioTrackPopUpChanged: fAudTrack2CodecPopUp];
6204                     }
6205                     [fAudTrack2RatePopUp selectItemWithTitle:[tempObject objectForKey:@"AudioSamplerate"]];
6206                     /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
6207                     if (![[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"AC3 Passthru"])
6208                     {
6209                         [fAudTrack2BitratePopUp selectItemWithTitle:[tempObject objectForKey:@"AudioBitrate"]];
6210                     }
6211                     [fAudTrack2DrcSlider setFloatValue:[[tempObject objectForKey:@"AudioTrackDRCSlider"] floatValue]];
6212                     [self audioDRCSliderChanged: fAudTrack2DrcSlider];
6213                     
6214                 }
6215                 
6216             }
6217             
6218              /* We now cleanup any extra audio tracks that may have been previously set if we need to */
6219             
6220             if (i < 4)
6221             {
6222                 [fAudLang4PopUp selectItemAtIndex: 0];
6223                 [self audioTrackPopUpChanged: fAudLang4PopUp];
6224                 
6225                 if (i < 3)
6226                 {
6227                     [fAudLang3PopUp selectItemAtIndex: 0];
6228                     [self audioTrackPopUpChanged: fAudLang3PopUp];
6229                     
6230                     if (i < 2)
6231                     {
6232                         [fAudLang2PopUp selectItemAtIndex: 0];
6233                         [self audioTrackPopUpChanged: fAudLang2PopUp];
6234                     }
6235                 }
6236             }
6237             
6238         }
6239         else
6240         {
6241             if ([chosenPreset objectForKey:@"Audio1Track"] > 0)
6242             {
6243                 if ([fAudLang1PopUp indexOfSelectedItem] == 0)
6244                 {
6245                     [fAudLang1PopUp selectItemAtIndex: 1];
6246                 }
6247                 [self audioTrackPopUpChanged: fAudLang1PopUp];
6248                 [fAudTrack1CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Encoder"]];
6249                 /* check our pref for core audio and use it in place of faac if applicable */
6250                 if ([[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES && 
6251                     [[chosenPreset objectForKey:@"Audio1Encoder"] isEqualToString: @"AAC (faac)"])
6252                 {
6253                     [fAudTrack1CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"];
6254                 }
6255                 [self audioTrackPopUpChanged: fAudTrack1CodecPopUp];
6256                 [fAudTrack1MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Mixdown"]];
6257                 /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
6258                  * mixdown*/
6259                 if  ([fAudTrack1MixPopUp selectedItem] == nil)
6260                 {
6261                     [self audioTrackPopUpChanged: fAudTrack1CodecPopUp];
6262                 }
6263                 [fAudTrack1RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Samplerate"]];
6264                 /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
6265                 if (![[chosenPreset objectForKey:@"Audio1Encoder"] isEqualToString:@"AC3 Passthru"])
6266                 {
6267                     [fAudTrack1BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Bitrate"]];
6268                 }
6269                 [fAudTrack1DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio1TrackDRCSlider"] floatValue]];
6270                 [self audioDRCSliderChanged: fAudTrack1DrcSlider];
6271             }
6272             if ([chosenPreset objectForKey:@"Audio2Track"] > 0)
6273             {
6274                 if ([fAudLang2PopUp indexOfSelectedItem] == 0)
6275                 {
6276                     [fAudLang2PopUp selectItemAtIndex: 1];
6277                 }
6278                 [self audioTrackPopUpChanged: fAudLang2PopUp];
6279                 [fAudTrack2CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Encoder"]];
6280                 /* check our pref for core audio and use it in place of faac if applicable */
6281                 if ([[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES && 
6282                     [[chosenPreset objectForKey:@"Audio2Encoder"] isEqualToString: @"AAC (faac)"])
6283                 {
6284                     [fAudTrack2CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"];
6285                 }
6286                 [self audioTrackPopUpChanged: fAudTrack2CodecPopUp];
6287                 [fAudTrack2MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Mixdown"]];
6288                 /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
6289                  * mixdown*/
6290                 if  ([fAudTrack2MixPopUp selectedItem] == nil)
6291                 {
6292                     [self audioTrackPopUpChanged: fAudTrack2CodecPopUp];
6293                 }
6294                 [fAudTrack2RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Samplerate"]];
6295                 /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
6296                 if (![[chosenPreset objectForKey:@"Audio2Encoder"] isEqualToString:@"AC3 Passthru"])
6297                 {
6298                     [fAudTrack2BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Bitrate"]];
6299                 }
6300                 [fAudTrack2DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio2TrackDRCSlider"] floatValue]];
6301                 [self audioDRCSliderChanged: fAudTrack2DrcSlider];
6302             }
6303             if ([chosenPreset objectForKey:@"Audio3Track"] > 0)
6304             {
6305                 if ([fAudLang3PopUp indexOfSelectedItem] == 0)
6306                 {
6307                     [fAudLang3PopUp selectItemAtIndex: 1];
6308                 }
6309                 [self audioTrackPopUpChanged: fAudLang3PopUp];
6310                 [fAudTrack3CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Encoder"]];
6311                 /* check our pref for core audio and use it in place of faac if applicable */
6312                 if ([[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES && 
6313                     [[chosenPreset objectForKey:@"Audio3Encoder"] isEqualToString: @"AAC (faac)"])
6314                 {
6315                     [fAudTrack3CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"];
6316                 }
6317                 [self audioTrackPopUpChanged: fAudTrack3CodecPopUp];
6318                 [fAudTrack3MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Mixdown"]];
6319                 /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
6320                  * mixdown*/
6321                 if  ([fAudTrack3MixPopUp selectedItem] == nil)
6322                 {
6323                     [self audioTrackPopUpChanged: fAudTrack3CodecPopUp];
6324                 }
6325                 [fAudTrack3RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Samplerate"]];
6326                 /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
6327                 if (![[chosenPreset objectForKey:@"Audio3Encoder"] isEqualToString: @"AC3 Passthru"])
6328                 {
6329                     [fAudTrack3BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Bitrate"]];
6330                 }
6331                 [fAudTrack3DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio3TrackDRCSlider"] floatValue]];
6332                 [self audioDRCSliderChanged: fAudTrack3DrcSlider];
6333             }
6334             if ([chosenPreset objectForKey:@"Audio4Track"] > 0)
6335             {
6336                 if ([fAudLang4PopUp indexOfSelectedItem] == 0)
6337                 {
6338                     [fAudLang4PopUp selectItemAtIndex: 1];
6339                 }
6340                 [self audioTrackPopUpChanged: fAudLang4PopUp];
6341                 [fAudTrack4CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Encoder"]];
6342                 /* check our pref for core audio and use it in place of faac if applicable */
6343                 if ([[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES && 
6344                     [[chosenPreset objectForKey:@"Audio4Encoder"] isEqualToString: @"AAC (faac)"])
6345                 {
6346                     [fAudTrack4CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"];
6347                 }
6348                 [self audioTrackPopUpChanged: fAudTrack4CodecPopUp];
6349                 [fAudTrack4MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Mixdown"]];
6350                 /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
6351                  * mixdown*/
6352                 if  ([fAudTrack4MixPopUp selectedItem] == nil)
6353                 {
6354                     [self audioTrackPopUpChanged: fAudTrack4CodecPopUp];
6355                 }
6356                 [fAudTrack4RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Samplerate"]];
6357                 /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
6358                 if (![[chosenPreset objectForKey:@"Audio4Encoder"] isEqualToString:@"AC3 Passthru"])
6359                 {
6360                     [fAudTrack4BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Bitrate"]];
6361                 }
6362                 [fAudTrack4DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio4TrackDRCSlider"] floatValue]];
6363                 [self audioDRCSliderChanged: fAudTrack4DrcSlider];
6364             }
6365             
6366             /* We now cleanup any extra audio tracks that may have been previously set if we need to */
6367             
6368             if (![chosenPreset objectForKey:@"Audio2Track"] || [chosenPreset objectForKey:@"Audio2Track"] == 0)
6369             {
6370                 [fAudLang2PopUp selectItemAtIndex: 0];
6371                 [self audioTrackPopUpChanged: fAudLang2PopUp];
6372             }
6373             if (![chosenPreset objectForKey:@"Audio3Track"] || [chosenPreset objectForKey:@"Audio3Track"] > 0)
6374             {
6375                 [fAudLang3PopUp selectItemAtIndex: 0];
6376                 [self audioTrackPopUpChanged: fAudLang3PopUp];
6377             }
6378             if (![chosenPreset objectForKey:@"Audio4Track"] || [chosenPreset objectForKey:@"Audio4Track"] > 0)
6379             {
6380                 [fAudLang4PopUp selectItemAtIndex: 0];
6381                 [self audioTrackPopUpChanged: fAudLang4PopUp];
6382             }
6383         }
6384         
6385         /*Subtitles*/
6386         [fSubPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Subtitles"]];
6387         /* Forced Subtitles */
6388         [fSubForcedCheck setState:[[chosenPreset objectForKey:@"SubtitlesForced"] intValue]];
6389         
6390         /* Picture Settings */
6391         /* Note: objectForKey:@"UsesPictureSettings" refers to picture size, which encompasses:
6392          * height, width, keep ar, anamorphic and crop settings.
6393          * picture filters are handled separately below.
6394          */
6395         /* Check to see if the objectForKey:@"UsesPictureSettings is greater than 0, as 0 means use picture sizing "None" 
6396          * ( 2 is use max for source and 1 is use exact size when the preset was created ) and the 
6397          * preset completely ignores any picture sizing values in the preset.
6398          */
6399         if ([[chosenPreset objectForKey:@"UsesPictureSettings"]  intValue] > 0)
6400         {
6401             hb_job_t * job = fTitle->job;
6402             
6403             /* If Cropping is set to custom, then recall all four crop values from
6404              when the preset was created and apply them */
6405             if ([[chosenPreset objectForKey:@"PictureAutoCrop"]  intValue] == 0)
6406             {
6407                 [fPictureController setAutoCrop:NO];
6408                 
6409                 /* Here we use the custom crop values saved at the time the preset was saved */
6410                 job->crop[0] = [[chosenPreset objectForKey:@"PictureTopCrop"]  intValue];
6411                 job->crop[1] = [[chosenPreset objectForKey:@"PictureBottomCrop"]  intValue];
6412                 job->crop[2] = [[chosenPreset objectForKey:@"PictureLeftCrop"]  intValue];
6413                 job->crop[3] = [[chosenPreset objectForKey:@"PictureRightCrop"]  intValue];
6414                 
6415             }
6416             else /* if auto crop has been saved in preset, set to auto and use post scan auto crop */
6417             {
6418                 [fPictureController setAutoCrop:YES];
6419                 /* Here we use the auto crop values determined right after scan */
6420                 job->crop[0] = AutoCropTop;
6421                 job->crop[1] = AutoCropBottom;
6422                 job->crop[2] = AutoCropLeft;
6423                 job->crop[3] = AutoCropRight;
6424                 
6425             }
6426             
6427             
6428             /* Check to see if the objectForKey:@"UsesPictureSettings is 2 which is "Use Max for the source */
6429             if ([[chosenPreset objectForKey:@"UsesPictureSettings"]  intValue] == 2 || [[chosenPreset objectForKey:@"UsesMaxPictureSettings"]  intValue] == 1)
6430             {
6431                 /* Use Max Picture settings for whatever the dvd is.*/
6432                 [self revertPictureSizeToMax:nil];
6433                 job->keep_ratio = [[chosenPreset objectForKey:@"PictureKeepRatio"]  intValue];
6434                 if (job->keep_ratio == 1)
6435                 {
6436                     hb_fix_aspect( job, HB_KEEP_WIDTH );
6437                     if( job->height > fTitle->height )
6438                     {
6439                         job->height = fTitle->height;
6440                         hb_fix_aspect( job, HB_KEEP_HEIGHT );
6441                     }
6442                 }
6443                 job->anamorphic.mode = [[chosenPreset objectForKey:@"PicturePAR"]  intValue];
6444             }
6445             else // /* If not 0 or 2 we assume objectForKey:@"UsesPictureSettings is 1 which is "Use picture sizing from when the preset was set" */
6446             {
6447                 /* we check to make sure the presets width/height does not exceed the sources width/height */
6448                 if (fTitle->width < [[chosenPreset objectForKey:@"PictureWidth"]  intValue] || fTitle->height < [[chosenPreset objectForKey:@"PictureHeight"]  intValue])
6449                 {
6450                     /* if so, then we use the sources height and width to avoid scaling up */
6451                     //job->width = fTitle->width;
6452                     //job->height = fTitle->height;
6453                     [self revertPictureSizeToMax:nil];
6454                 }
6455                 else // source width/height is >= the preset height/width
6456                 {
6457                     /* we can go ahead and use the presets values for height and width */
6458                     job->width = [[chosenPreset objectForKey:@"PictureWidth"]  intValue];
6459                     job->height = [[chosenPreset objectForKey:@"PictureHeight"]  intValue];
6460                 }
6461                 job->keep_ratio = [[chosenPreset objectForKey:@"PictureKeepRatio"]  intValue];
6462                 if (job->keep_ratio == 1)
6463                 {
6464                     hb_fix_aspect( job, HB_KEEP_WIDTH );
6465                     if( job->height > fTitle->height )
6466                     {
6467                         job->height = fTitle->height;
6468                         hb_fix_aspect( job, HB_KEEP_HEIGHT );
6469                     }
6470                 }
6471                 job->anamorphic.mode = [[chosenPreset objectForKey:@"PicturePAR"]  intValue];
6472                 
6473             }
6474             
6475             
6476         }
6477         /* If the preset has an objectForKey:@"UsesPictureFilters", and handle the filters here */
6478         if ([chosenPreset objectForKey:@"UsesPictureFilters"] && [[chosenPreset objectForKey:@"UsesPictureFilters"]  intValue] > 0)
6479         {
6480             /* Filters */
6481             
6482             /* We only allow *either* Decomb or Deinterlace. So check for the PictureDecombDeinterlace key.
6483              * also, older presets may not have this key, in which case we also check to see if that preset had  PictureDecomb
6484              * specified, in which case we use decomb and ignore any possible Deinterlace settings as using both was less than
6485              * sane.
6486              */
6487             [fPictureController setUseDecomb:1];
6488             [fPictureController setDecomb:0];
6489             [fPictureController setDeinterlace:0];
6490             if ([[chosenPreset objectForKey:@"PictureDecombDeinterlace"] intValue] == 1 || [[chosenPreset objectForKey:@"PictureDecomb"] intValue] > 0)
6491             {
6492                 /* we are using decomb */
6493                 /* Decomb */
6494                 if ([[chosenPreset objectForKey:@"PictureDecomb"] intValue] > 0)
6495                 {
6496                     [fPictureController setDecomb:[[chosenPreset objectForKey:@"PictureDecomb"] intValue]];
6497                     
6498                     /* if we are using "Custom" in the decomb setting, also set the custom string*/
6499                     if ([[chosenPreset objectForKey:@"PictureDecomb"] intValue] == 2)
6500                     {
6501                         [fPictureController setDecombCustomString:[chosenPreset objectForKey:@"PictureDecombCustom"]];    
6502                     }
6503                 }
6504              }
6505             else
6506             {
6507                 /* We are using Deinterlace */
6508                 /* Deinterlace */
6509                 if ([[chosenPreset objectForKey:@"PictureDeinterlace"] intValue] > 0)
6510                 {
6511                     [fPictureController setUseDecomb:0];
6512                     [fPictureController setDeinterlace:[[chosenPreset objectForKey:@"PictureDeinterlace"] intValue]];
6513                     /* if we are using "Custom" in the deinterlace setting, also set the custom string*/
6514                     if ([[chosenPreset objectForKey:@"PictureDeinterlace"] intValue] == 4)
6515                     {
6516                         [fPictureController setDeinterlaceCustomString:[chosenPreset objectForKey:@"PictureDeinterlaceCustom"]];    
6517                     }
6518                 }
6519             }
6520             
6521             
6522             /* Detelecine */
6523             if ([[chosenPreset objectForKey:@"PictureDetelecine"] intValue] > 0)
6524             {
6525                 [fPictureController setDetelecine:[[chosenPreset objectForKey:@"PictureDetelecine"] intValue]];
6526                 /* if we are using "Custom" in the detelecine setting, also set the custom string*/
6527                 if ([[chosenPreset objectForKey:@"PictureDetelecine"] intValue] == 2)
6528                 {
6529                     [fPictureController setDetelecineCustomString:[chosenPreset objectForKey:@"PictureDetelecineCustom"]];    
6530                 }
6531             }
6532             else
6533             {
6534                 [fPictureController setDetelecine:0];
6535             }
6536             
6537             /* Denoise */
6538             if ([[chosenPreset objectForKey:@"PictureDenoise"] intValue] > 0)
6539             {
6540                 [fPictureController setDenoise:[[chosenPreset objectForKey:@"PictureDenoise"] intValue]];
6541                 /* if we are using "Custom" in the denoise setting, also set the custom string*/
6542                 if ([[chosenPreset objectForKey:@"PictureDenoise"] intValue] == 4)
6543                 {
6544                     [fPictureController setDenoiseCustomString:[chosenPreset objectForKey:@"PictureDenoiseCustom"]];    
6545                 }
6546             }
6547             else
6548             {
6549                 [fPictureController setDenoise:0];
6550             }   
6551             
6552             /* Deblock */
6553             if ([[chosenPreset objectForKey:@"PictureDeblock"] intValue] == 1)
6554             {
6555                 /* if its a one, then its the old on/off deblock, set on to 5*/
6556                 [fPictureController setDeblock:5];
6557             }
6558             else
6559             {
6560                 /* use the settings intValue */
6561                 [fPictureController setDeblock:[[chosenPreset objectForKey:@"PictureDeblock"] intValue]];
6562             }
6563             
6564             if ([[chosenPreset objectForKey:@"VideoGrayScale"] intValue] == 1)
6565             {
6566                 [fPictureController setGrayscale:1];
6567             }
6568             else
6569             {
6570                 [fPictureController setGrayscale:0];
6571             }
6572         }
6573         /* we call SetTitle: in fPictureController so we get an instant update in the Picture Settings window */
6574         [fPictureController SetTitle:fTitle];
6575         [fPictureController SetTitle:fTitle];
6576         [self calculatePictureSizing:nil];
6577     }
6578 }
6579
6580
6581 #pragma mark -
6582 #pragma mark Manage Presets
6583
6584 - (void) loadPresets {
6585         /* We declare the default NSFileManager into fileManager */
6586         NSFileManager * fileManager = [NSFileManager defaultManager];
6587         /*We define the location of the user presets file */
6588     UserPresetsFile = @"~/Library/Application Support/HandBrake/UserPresets.plist";
6589         UserPresetsFile = [[UserPresetsFile stringByExpandingTildeInPath]retain];
6590     /* We check for the presets.plist */
6591         if ([fileManager fileExistsAtPath:UserPresetsFile] == 0)
6592         {
6593                 [fileManager createFileAtPath:UserPresetsFile contents:nil attributes:nil];
6594         }
6595
6596         UserPresets = [[NSMutableArray alloc] initWithContentsOfFile:UserPresetsFile];
6597         if (nil == UserPresets)
6598         {
6599                 UserPresets = [[NSMutableArray alloc] init];
6600                 [self addFactoryPresets:nil];
6601         }
6602         [fPresetsOutlineView reloadData];
6603     
6604     [self checkBuiltInsForUpdates];
6605 }
6606
6607 - (void) checkBuiltInsForUpdates {
6608     
6609         BOOL updateBuiltInPresets = NO;
6610     int i = 0;
6611     NSEnumerator *enumerator = [UserPresets objectEnumerator];
6612     id tempObject;
6613     while (tempObject = [enumerator nextObject])
6614     {
6615         /* iterate through the built in presets to see if any have an old build number */
6616         NSMutableDictionary *thisPresetDict = tempObject;
6617         /*Key Type == 0 is built in, and key PresetBuildNumber is the build number it was created with */
6618         if ([[thisPresetDict objectForKey:@"Type"] intValue] == 0)              
6619         {
6620                         if (![thisPresetDict objectForKey:@"PresetBuildNumber"] || [[thisPresetDict objectForKey:@"PresetBuildNumber"] intValue] < [[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] intValue])
6621             {
6622                 updateBuiltInPresets = YES;
6623             }   
6624                 }
6625         i++;
6626     }
6627     /* if we have built in presets to update, then do so AlertBuiltInPresetUpdate*/
6628     if ( updateBuiltInPresets == YES)
6629     {
6630         if( [[NSUserDefaults standardUserDefaults] boolForKey:@"AlertBuiltInPresetUpdate"] == YES)
6631         {
6632             /* Show an alert window that built in presets will be updated */
6633             /*On Screen Notification*/
6634             int status;
6635             NSBeep();
6636             status = NSRunAlertPanel(@"HandBrake has determined your built in presets are out of date...",@"HandBrake will now update your built-in presets.", @"OK", nil, nil);
6637             [NSApp requestUserAttention:NSCriticalRequest];
6638         }
6639         /* when alert is dismissed, go ahead and update the built in presets */
6640         [self addFactoryPresets:nil];
6641     }
6642     
6643 }
6644
6645
6646 - (IBAction) showAddPresetPanel: (id) sender
6647 {
6648     /* Deselect the currently selected Preset if there is one*/
6649     [fPresetsOutlineView deselectRow:[fPresetsOutlineView selectedRow]];
6650
6651     /* Populate the preset picture settings popup here */
6652     [fPresetNewPicSettingsPopUp removeAllItems];
6653     [fPresetNewPicSettingsPopUp addItemWithTitle:@"None"];
6654     [fPresetNewPicSettingsPopUp addItemWithTitle:@"Current"];
6655     [fPresetNewPicSettingsPopUp addItemWithTitle:@"Source Maximum (post source scan)"];
6656     [fPresetNewPicSettingsPopUp selectItemAtIndex: 0];  
6657     /* Uncheck the preset use filters checkbox */
6658     [fPresetNewPicFiltersCheck setState:NSOffState];
6659     // fPresetNewFolderCheck
6660     [fPresetNewFolderCheck setState:NSOffState];
6661     /* Erase info from the input fields*/
6662         [fPresetNewName setStringValue: @""];
6663         [fPresetNewDesc setStringValue: @""];
6664         /* Show the panel */
6665         [NSApp beginSheet:fAddPresetPanel modalForWindow:fWindow modalDelegate:nil didEndSelector:NULL contextInfo:NULL];
6666 }
6667
6668 - (IBAction) closeAddPresetPanel: (id) sender
6669 {
6670     [NSApp endSheet: fAddPresetPanel];
6671     [fAddPresetPanel orderOut: self];
6672 }
6673
6674 - (IBAction)addUserPreset:(id)sender
6675 {
6676     if (![[fPresetNewName stringValue] length])
6677             NSRunAlertPanel(@"Warning!", @"You need to insert a name for the preset.", @"OK", nil , nil);
6678     else
6679     {
6680         /* Here we create a custom user preset */
6681         [UserPresets addObject:[self createPreset]];
6682         [self addPreset];
6683
6684         [self closeAddPresetPanel:nil];
6685     }
6686 }
6687 - (void)addPreset
6688 {
6689
6690         
6691         /* We Reload the New Table data for presets */
6692     [fPresetsOutlineView reloadData];
6693    /* We save all of the preset data here */
6694     [self savePreset];
6695 }
6696
6697 - (void)sortPresets
6698 {
6699
6700         
6701         /* We Sort the Presets By Factory or Custom */
6702         NSSortDescriptor * presetTypeDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"Type" 
6703                                                     ascending:YES] autorelease];
6704         /* We Sort the Presets Alphabetically by name  We do not use this now as we have drag and drop*/
6705         /*
6706     NSSortDescriptor * presetNameDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"PresetName" 
6707                                                     ascending:YES selector:@selector(caseInsensitiveCompare:)] autorelease];
6708         //NSArray *sortDescriptors=[NSArray arrayWithObjects:presetTypeDescriptor,presetNameDescriptor,nil];
6709     
6710     */
6711     /* Since we can drag and drop our custom presets, lets just sort by type and not name */
6712     NSArray *sortDescriptors=[NSArray arrayWithObjects:presetTypeDescriptor,nil];
6713         NSArray *sortedArray=[UserPresets sortedArrayUsingDescriptors:sortDescriptors];
6714         [UserPresets setArray:sortedArray];
6715         
6716
6717 }
6718
6719 - (IBAction)insertPreset:(id)sender
6720 {
6721     int index = [fPresetsOutlineView selectedRow];
6722     [UserPresets insertObject:[self createPreset] atIndex:index];
6723     [fPresetsOutlineView reloadData];
6724     [self savePreset];
6725 }
6726
6727 - (NSDictionary *)createPreset
6728 {
6729     NSMutableDictionary *preset = [[NSMutableDictionary alloc] init];
6730     /* Preset build number */
6731     [preset setObject:[NSString stringWithFormat: @"%d", [[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] intValue]] forKey:@"PresetBuildNumber"];
6732     [preset setObject:[fPresetNewName stringValue] forKey:@"PresetName"];
6733         /* Get the New Preset Name from the field in the AddPresetPanel */
6734     [preset setObject:[fPresetNewName stringValue] forKey:@"PresetName"];
6735     /* Set whether or not this is to be a folder fPresetNewFolderCheck*/
6736     [preset setObject:[NSNumber numberWithBool:[fPresetNewFolderCheck state]] forKey:@"Folder"];
6737         /*Set whether or not this is a user preset or factory 0 is factory, 1 is user*/
6738         [preset setObject:[NSNumber numberWithInt:1] forKey:@"Type"];
6739         /*Set whether or not this is default, at creation set to 0*/
6740         [preset setObject:[NSNumber numberWithInt:0] forKey:@"Default"];
6741     if ([fPresetNewFolderCheck state] == YES)
6742     {
6743         /* initialize and set an empty array for children here since we are a new folder */
6744         NSMutableArray *childrenArray = [[NSMutableArray alloc] init];
6745         [preset setObject:[NSMutableArray arrayWithArray: childrenArray] forKey:@"ChildrenArray"];
6746         [childrenArray autorelease];
6747     }
6748     else // we are not creating a preset folder, so we go ahead with the rest of the preset info
6749     {
6750         /*Get the whether or not to apply pic Size and Cropping (includes Anamorphic)*/
6751         [preset setObject:[NSNumber numberWithInt:[fPresetNewPicSettingsPopUp indexOfSelectedItem]] forKey:@"UsesPictureSettings"];
6752         /* Get whether or not to use the current Picture Filter settings for the preset */
6753         [preset setObject:[NSNumber numberWithInt:[fPresetNewPicFiltersCheck state]] forKey:@"UsesPictureFilters"];
6754         
6755         /* Get New Preset Description from the field in the AddPresetPanel*/
6756         [preset setObject:[fPresetNewDesc stringValue] forKey:@"PresetDescription"];
6757         /* File Format */
6758         [preset setObject:[fDstFormatPopUp titleOfSelectedItem] forKey:@"FileFormat"];
6759         /* Chapter Markers fCreateChapterMarkers*/
6760         [preset setObject:[NSNumber numberWithInt:[fCreateChapterMarkers state]] forKey:@"ChapterMarkers"];
6761         /* Allow Mpeg4 64 bit formatting +4GB file sizes */
6762         [preset setObject:[NSNumber numberWithInt:[fDstMp4LargeFileCheck state]] forKey:@"Mp4LargeFile"];
6763         /* Mux mp4 with http optimization */
6764         [preset setObject:[NSNumber numberWithInt:[fDstMp4HttpOptFileCheck state]] forKey:@"Mp4HttpOptimize"];
6765         /* Add iPod uuid atom */
6766         [preset setObject:[NSNumber numberWithInt:[fDstMp4iPodFileCheck state]] forKey:@"Mp4iPodCompatible"];
6767         
6768         /* Codecs */
6769         /* Video encoder */
6770         [preset setObject:[fVidEncoderPopUp titleOfSelectedItem] forKey:@"VideoEncoder"];
6771         /* x264 Option String */
6772         [preset setObject:[fAdvancedOptions optionsString] forKey:@"x264Option"];
6773         
6774         [preset setObject:[NSNumber numberWithInt:[fVidQualityMatrix selectedRow]] forKey:@"VideoQualityType"];
6775         [preset setObject:[fVidTargetSizeField stringValue] forKey:@"VideoTargetSize"];
6776         [preset setObject:[fVidBitrateField stringValue] forKey:@"VideoAvgBitrate"];
6777         [preset setObject:[NSNumber numberWithFloat:[fVidQualityRFField floatValue]] forKey:@"VideoQualitySlider"];
6778         
6779         /* Video framerate */
6780         if ([fVidRatePopUp indexOfSelectedItem] == 0) // Same as source is selected
6781         {
6782             [preset setObject:@"Same as source" forKey:@"VideoFramerate"];
6783         }
6784         else // we can record the actual titleOfSelectedItem
6785         {
6786             [preset setObject:[fVidRatePopUp titleOfSelectedItem] forKey:@"VideoFramerate"];
6787         }
6788         
6789         /* 2 Pass Encoding */
6790         [preset setObject:[NSNumber numberWithInt:[fVidTwoPassCheck state]] forKey:@"VideoTwoPass"];
6791         /* Turbo 2 pass Encoding fVidTurboPassCheck*/
6792         [preset setObject:[NSNumber numberWithInt:[fVidTurboPassCheck state]] forKey:@"VideoTurboTwoPass"];
6793         /*Picture Settings*/
6794         hb_job_t * job = fTitle->job;
6795         /* Picture Sizing */
6796         /* Use Max Picture settings for whatever the dvd is.*/
6797         [preset setObject:[NSNumber numberWithInt:0] forKey:@"UsesMaxPictureSettings"];
6798         [preset setObject:[NSNumber numberWithInt:fTitle->job->width] forKey:@"PictureWidth"];
6799         [preset setObject:[NSNumber numberWithInt:fTitle->job->height] forKey:@"PictureHeight"];
6800         [preset setObject:[NSNumber numberWithInt:fTitle->job->keep_ratio] forKey:@"PictureKeepRatio"];
6801         [preset setObject:[NSNumber numberWithInt:fTitle->job->anamorphic.mode] forKey:@"PicturePAR"];
6802         
6803         /* Set crop settings here */
6804         [preset setObject:[NSNumber numberWithInt:[fPictureController autoCrop]] forKey:@"PictureAutoCrop"];
6805         [preset setObject:[NSNumber numberWithInt:job->crop[0]] forKey:@"PictureTopCrop"];
6806         [preset setObject:[NSNumber numberWithInt:job->crop[1]] forKey:@"PictureBottomCrop"];
6807         [preset setObject:[NSNumber numberWithInt:job->crop[2]] forKey:@"PictureLeftCrop"];
6808         [preset setObject:[NSNumber numberWithInt:job->crop[3]] forKey:@"PictureRightCrop"];
6809         
6810         /* Picture Filters */
6811         [preset setObject:[NSNumber numberWithInt:[fPictureController useDecomb]] forKey:@"PictureDecombDeinterlace"];
6812         [preset setObject:[NSNumber numberWithInt:[fPictureController deinterlace]] forKey:@"PictureDeinterlace"];
6813         [preset setObject:[fPictureController deinterlaceCustomString] forKey:@"PictureDeinterlaceCustom"];
6814         [preset setObject:[NSNumber numberWithInt:[fPictureController detelecine]] forKey:@"PictureDetelecine"];
6815         [preset setObject:[fPictureController detelecineCustomString] forKey:@"PictureDetelecineCustom"];
6816         [preset setObject:[NSNumber numberWithInt:[fPictureController denoise]] forKey:@"PictureDenoise"];
6817         [preset setObject:[fPictureController denoiseCustomString] forKey:@"PictureDenoiseCustom"];
6818         [preset setObject:[NSNumber numberWithInt:[fPictureController deblock]] forKey:@"PictureDeblock"]; 
6819         [preset setObject:[NSNumber numberWithInt:[fPictureController decomb]] forKey:@"PictureDecomb"];
6820         [preset setObject:[fPictureController decombCustomString] forKey:@"PictureDecombCustom"];
6821         [preset setObject:[NSNumber numberWithInt:[fPictureController grayscale]] forKey:@"VideoGrayScale"];
6822         
6823         /*Audio*/
6824         NSMutableArray *audioListArray = [[NSMutableArray alloc] init];
6825         /* we actually call the methods for the nests here */
6826         if ([fAudLang1PopUp indexOfSelectedItem] > 0)
6827         {
6828             NSMutableDictionary *audioTrack1Array = [[NSMutableDictionary alloc] init];
6829             [audioTrack1Array setObject:[NSNumber numberWithInt:[fAudLang1PopUp indexOfSelectedItem]] forKey:@"AudioTrack"];
6830             [audioTrack1Array setObject:[fAudLang1PopUp titleOfSelectedItem] forKey:@"AudioTrackDescription"];
6831             [audioTrack1Array setObject:[fAudTrack1CodecPopUp titleOfSelectedItem] forKey:@"AudioEncoder"];
6832             [audioTrack1Array setObject:[fAudTrack1MixPopUp titleOfSelectedItem] forKey:@"AudioMixdown"];
6833             [audioTrack1Array setObject:[fAudTrack1RatePopUp titleOfSelectedItem] forKey:@"AudioSamplerate"];
6834             [audioTrack1Array setObject:[fAudTrack1BitratePopUp titleOfSelectedItem] forKey:@"AudioBitrate"];
6835             [audioTrack1Array setObject:[NSNumber numberWithFloat:[fAudTrack1DrcSlider floatValue]] forKey:@"AudioTrackDRCSlider"];
6836             [audioTrack1Array autorelease];
6837             [audioListArray addObject:audioTrack1Array];
6838         }
6839         
6840         if ([fAudLang2PopUp indexOfSelectedItem] > 0)
6841         {
6842             NSMutableDictionary *audioTrack2Array = [[NSMutableDictionary alloc] init];
6843             [audioTrack2Array setObject:[NSNumber numberWithInt:[fAudLang2PopUp indexOfSelectedItem]] forKey:@"AudioTrack"];
6844             [audioTrack2Array setObject:[fAudLang2PopUp titleOfSelectedItem] forKey:@"AudioTrackDescription"];
6845             [audioTrack2Array setObject:[fAudTrack2CodecPopUp titleOfSelectedItem] forKey:@"AudioEncoder"];
6846             [audioTrack2Array setObject:[fAudTrack2MixPopUp titleOfSelectedItem] forKey:@"AudioMixdown"];
6847             [audioTrack2Array setObject:[fAudTrack2RatePopUp titleOfSelectedItem] forKey:@"AudioSamplerate"];
6848             [audioTrack2Array setObject:[fAudTrack2BitratePopUp titleOfSelectedItem] forKey:@"AudioBitrate"];
6849             [audioTrack2Array setObject:[NSNumber numberWithFloat:[fAudTrack2DrcSlider floatValue]] forKey:@"AudioTrackDRCSlider"];
6850             [audioTrack2Array autorelease];
6851             [audioListArray addObject:audioTrack2Array];
6852         }
6853         
6854         if ([fAudLang3PopUp indexOfSelectedItem] > 0)
6855         {
6856             NSMutableDictionary *audioTrack3Array = [[NSMutableDictionary alloc] init];
6857             [audioTrack3Array setObject:[NSNumber numberWithInt:[fAudLang3PopUp indexOfSelectedItem]] forKey:@"AudioTrack"];
6858             [audioTrack3Array setObject:[fAudLang3PopUp titleOfSelectedItem] forKey:@"AudioTrackDescription"];
6859             [audioTrack3Array setObject:[fAudTrack3CodecPopUp titleOfSelectedItem] forKey:@"AudioEncoder"];
6860             [audioTrack3Array setObject:[fAudTrack3MixPopUp titleOfSelectedItem] forKey:@"AudioMixdown"];
6861             [audioTrack3Array setObject:[fAudTrack3RatePopUp titleOfSelectedItem] forKey:@"AudioSamplerate"];
6862             [audioTrack3Array setObject:[fAudTrack3BitratePopUp titleOfSelectedItem] forKey:@"AudioBitrate"];
6863             [audioTrack3Array setObject:[NSNumber numberWithFloat:[fAudTrack3DrcSlider floatValue]] forKey:@"AudioTrackDRCSlider"];
6864             [audioTrack3Array autorelease];
6865             [audioListArray addObject:audioTrack3Array];
6866         }
6867         
6868         if ([fAudLang4PopUp indexOfSelectedItem] > 0)
6869         {
6870             NSMutableDictionary *audioTrack4Array = [[NSMutableDictionary alloc] init];
6871             [audioTrack4Array setObject:[NSNumber numberWithInt:[fAudLang4PopUp indexOfSelectedItem]] forKey:@"AudioTrack"];
6872             [audioTrack4Array setObject:[fAudLang4PopUp titleOfSelectedItem] forKey:@"AudioTrackDescription"];
6873             [audioTrack4Array setObject:[fAudTrack4CodecPopUp titleOfSelectedItem] forKey:@"AudioEncoder"];
6874             [audioTrack4Array setObject:[fAudTrack4MixPopUp titleOfSelectedItem] forKey:@"AudioMixdown"];
6875             [audioTrack4Array setObject:[fAudTrack4RatePopUp titleOfSelectedItem] forKey:@"AudioSamplerate"];
6876             [audioTrack4Array setObject:[fAudTrack4BitratePopUp titleOfSelectedItem] forKey:@"AudioBitrate"];
6877             [audioTrack4Array setObject:[NSNumber numberWithFloat:[fAudTrack4DrcSlider floatValue]] forKey:@"AudioTrackDRCSlider"];
6878             [audioTrack4Array autorelease];
6879             [audioListArray addObject:audioTrack4Array];
6880         }
6881         
6882         
6883         [preset setObject:[NSMutableArray arrayWithArray: audioListArray] forKey:@"AudioList"];
6884
6885         
6886         
6887         /* Subtitles*/
6888         [preset setObject:[fSubPopUp titleOfSelectedItem] forKey:@"Subtitles"];
6889         /* Forced Subtitles */
6890         [preset setObject:[NSNumber numberWithInt:[fSubForcedCheck state]] forKey:@"SubtitlesForced"];
6891     }
6892     [preset autorelease];
6893     return preset;
6894     
6895 }
6896
6897 - (void)savePreset
6898 {
6899     [UserPresets writeToFile:UserPresetsFile atomically:YES];
6900         /* We get the default preset in case it changed */
6901         [self getDefaultPresets:nil];
6902
6903 }
6904
6905 - (IBAction)deletePreset:(id)sender
6906 {
6907     
6908     
6909     if ( [fPresetsOutlineView numberOfSelectedRows] == 0 )
6910     {
6911         return;
6912     }
6913     /* Alert user before deleting preset */
6914         int status;
6915     status = NSRunAlertPanel(@"Warning!", @"Are you sure that you want to delete the selected preset?", @"OK", @"Cancel", nil);
6916     
6917     if ( status == NSAlertDefaultReturn ) 
6918     {
6919         int presetToModLevel = [fPresetsOutlineView levelForItem: [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]]];
6920         NSDictionary *presetToMod = [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]];
6921         NSDictionary *presetToModParent = [fPresetsOutlineView parentForItem: presetToMod];
6922         
6923         NSEnumerator *enumerator;
6924         NSMutableArray *presetsArrayToMod;
6925         NSMutableArray *tempArray;
6926         id tempObject;
6927         /* If we are a root level preset, we are modding the UserPresets array */
6928         if (presetToModLevel == 0)
6929         {
6930             presetsArrayToMod = UserPresets;
6931         }
6932         else // We have a parent preset, so we modify the chidren array object for key
6933         {
6934             presetsArrayToMod = [presetToModParent objectForKey:@"ChildrenArray"]; 
6935         }
6936         
6937         enumerator = [presetsArrayToMod objectEnumerator];
6938         tempArray = [NSMutableArray array];
6939         
6940         while (tempObject = [enumerator nextObject]) 
6941         {
6942             NSDictionary *thisPresetDict = tempObject;
6943             if (thisPresetDict == presetToMod)
6944             {
6945                 [tempArray addObject:tempObject];
6946             }
6947         }
6948         
6949         [presetsArrayToMod removeObjectsInArray:tempArray];
6950         [fPresetsOutlineView reloadData];
6951         [self savePreset];   
6952     }
6953 }
6954
6955
6956 #pragma mark -
6957 #pragma mark Import Export Preset(s)
6958
6959 - (IBAction) browseExportPresetFile: (id) sender
6960 {
6961     /* Open a panel to let the user choose where and how to save the export file */
6962     NSSavePanel * panel = [NSSavePanel savePanel];
6963         /* We get the current file name and path from the destination field here */
6964     NSString *defaultExportDirectory = [NSString stringWithFormat: @"%@/Desktop/", NSHomeDirectory()];
6965
6966         [panel beginSheetForDirectory: defaultExportDirectory file: @"HB_Export.plist"
6967                                    modalForWindow: fWindow modalDelegate: self
6968                                    didEndSelector: @selector( browseExportPresetFileDone:returnCode:contextInfo: )
6969                                           contextInfo: NULL];
6970 }
6971
6972 - (void) browseExportPresetFileDone: (NSSavePanel *) sheet
6973                    returnCode: (int) returnCode contextInfo: (void *) contextInfo
6974 {
6975     if( returnCode == NSOKButton )
6976     {
6977         NSString *presetExportDirectory = [[sheet filename] stringByDeletingLastPathComponent];
6978         NSString *exportPresetsFile = [sheet filename];
6979         [[NSUserDefaults standardUserDefaults] setObject:presetExportDirectory forKey:@"LastPresetExportDirectory"];
6980         /* We check for the presets.plist */
6981         if ([[NSFileManager defaultManager] fileExistsAtPath:exportPresetsFile] == 0)
6982         {
6983             [[NSFileManager defaultManager] createFileAtPath:exportPresetsFile contents:nil attributes:nil];
6984         }
6985         NSMutableArray * presetsToExport = [[NSMutableArray alloc] initWithContentsOfFile:exportPresetsFile];
6986         if (nil == presetsToExport)
6987         {
6988             presetsToExport = [[NSMutableArray alloc] init];
6989             
6990             /* now get and add selected presets to export */
6991             
6992         }
6993         if ([fPresetsOutlineView selectedRow] >= 0 && [[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Folder"] intValue] != 1)
6994         {
6995             [presetsToExport addObject:[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]]];
6996             [presetsToExport writeToFile:exportPresetsFile atomically:YES];
6997             
6998         }
6999         
7000     }
7001 }
7002
7003
7004 - (IBAction) browseImportPresetFile: (id) sender
7005 {
7006
7007     NSOpenPanel * panel;
7008         
7009     panel = [NSOpenPanel openPanel];
7010     [panel setAllowsMultipleSelection: NO];
7011     [panel setCanChooseFiles: YES];
7012     [panel setCanChooseDirectories: NO ];
7013     NSString * sourceDirectory;
7014         if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastPresetImportDirectory"])
7015         {
7016                 sourceDirectory = [[NSUserDefaults standardUserDefaults] stringForKey:@"LastPresetImportDirectory"];
7017         }
7018         else
7019         {
7020                 sourceDirectory = @"~/Desktop";
7021                 sourceDirectory = [sourceDirectory stringByExpandingTildeInPath];
7022         }
7023     /* we open up the browse sources sheet here and call for browseSourcesDone after the sheet is closed
7024         * to evaluate whether we want to specify a title, we pass the sender in the contextInfo variable
7025         */
7026     /* set this for allowed file types, not sure if we should allow xml or not */
7027     NSArray *fileTypes = [NSArray arrayWithObjects:@"plist", @"xml", nil];
7028     [panel beginSheetForDirectory: sourceDirectory file: nil types: fileTypes
7029                    modalForWindow: fWindow modalDelegate: self
7030                    didEndSelector: @selector( browseImportPresetDone:returnCode:contextInfo: )
7031                       contextInfo: sender];
7032 }
7033
7034 - (void) browseImportPresetDone: (NSSavePanel *) sheet
7035                      returnCode: (int) returnCode contextInfo: (void *) contextInfo
7036 {
7037     if( returnCode == NSOKButton )
7038     {
7039         NSString *importPresetsDirectory = [[sheet filename] stringByDeletingLastPathComponent];
7040         NSString *importPresetsFile = [sheet filename];
7041         [[NSUserDefaults standardUserDefaults] setObject:importPresetsDirectory forKey:@"LastPresetImportDirectory"];
7042         /* NOTE: here we need to do some sanity checking to verify we do not hose up our presets file   */
7043         NSMutableArray * presetsToImport = [[NSMutableArray alloc] initWithContentsOfFile:importPresetsFile];
7044         /* iterate though the new array of presets to import and add them to our presets array */
7045         int i = 0;
7046         NSEnumerator *enumerator = [presetsToImport objectEnumerator];
7047         id tempObject;
7048         while (tempObject = [enumerator nextObject])
7049         {
7050             /* make any changes to the incoming preset we see fit */
7051             /* make sure the incoming preset is not tagged as default */
7052             [tempObject setObject:[NSNumber numberWithInt:0] forKey:@"Default"];
7053             /* prepend "(imported) to the name of the incoming preset for clarification since it can be changed */
7054             NSString * prependedName = [@"(import) " stringByAppendingString:[tempObject objectForKey:@"PresetName"]] ;
7055             [tempObject setObject:prependedName forKey:@"PresetName"];
7056             
7057             /* actually add the new preset to our presets array */
7058             [UserPresets addObject:tempObject];
7059             i++;
7060         }
7061         [presetsToImport autorelease];
7062         [self sortPresets];
7063         [self addPreset];
7064         
7065     }
7066 }
7067
7068 #pragma mark -
7069 #pragma mark Manage Default Preset
7070
7071 - (IBAction)getDefaultPresets:(id)sender
7072 {
7073         presetHbDefault = nil;
7074     presetUserDefault = nil;
7075     presetUserDefaultParent = nil;
7076     presetUserDefaultParentParent = nil;
7077     NSMutableDictionary *presetHbDefaultParent = nil;
7078     NSMutableDictionary *presetHbDefaultParentParent = nil;
7079     
7080     int i = 0;
7081     BOOL userDefaultFound = NO;
7082     presetCurrentBuiltInCount = 0;
7083     /* First we iterate through the root UserPresets array to check for defaults */
7084     NSEnumerator *enumerator = [UserPresets objectEnumerator];
7085         id tempObject;
7086         while (tempObject = [enumerator nextObject])
7087         {
7088                 NSMutableDictionary *thisPresetDict = tempObject;
7089                 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 1) // 1 is HB default
7090                 {
7091                         presetHbDefault = thisPresetDict;       
7092                 }
7093                 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 2) // 2 is User specified default
7094                 {
7095                         presetUserDefault = thisPresetDict;
7096             userDefaultFound = YES;
7097         }
7098         if ([[thisPresetDict objectForKey:@"Type"] intValue] == 0) // Type 0 is a built in preset               
7099         {
7100                         presetCurrentBuiltInCount++; // <--increment the current number of built in presets     
7101                 }
7102                 i++;
7103         
7104         /* if we run into a folder, go to level 1 and iterate through the children arrays for the default */
7105         if ([thisPresetDict objectForKey:@"ChildrenArray"])
7106         {
7107             NSMutableDictionary *thisPresetDictParent = thisPresetDict;
7108             NSEnumerator *enumerator = [[thisPresetDict objectForKey:@"ChildrenArray"] objectEnumerator];
7109             id tempObject;
7110             while (tempObject = [enumerator nextObject])
7111             {
7112                 NSMutableDictionary *thisPresetDict = tempObject;
7113                 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 1) // 1 is HB default
7114                 {
7115                     presetHbDefault = thisPresetDict;
7116                     presetHbDefaultParent = thisPresetDictParent;
7117                 }
7118                 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 2) // 2 is User specified default
7119                 {
7120                     presetUserDefault = thisPresetDict;
7121                     presetUserDefaultParent = thisPresetDictParent;
7122                     userDefaultFound = YES;
7123                 }
7124                 
7125                 /* if we run into a folder, go to level 2 and iterate through the children arrays for the default */
7126                 if ([thisPresetDict objectForKey:@"ChildrenArray"])
7127                 {
7128                     NSMutableDictionary *thisPresetDictParentParent = thisPresetDict;
7129                     NSEnumerator *enumerator = [[thisPresetDict objectForKey:@"ChildrenArray"] objectEnumerator];
7130                     id tempObject;
7131                     while (tempObject = [enumerator nextObject])
7132                     {
7133                         NSMutableDictionary *thisPresetDict = tempObject;
7134                         if ([[thisPresetDict objectForKey:@"Default"] intValue] == 1) // 1 is HB default
7135                         {
7136                             presetHbDefault = thisPresetDict;
7137                             presetHbDefaultParent = thisPresetDictParent;
7138                             presetHbDefaultParentParent = thisPresetDictParentParent;   
7139                         }
7140                         if ([[thisPresetDict objectForKey:@"Default"] intValue] == 2) // 2 is User specified default
7141                         {
7142                             presetUserDefault = thisPresetDict;
7143                             presetUserDefaultParent = thisPresetDictParent;
7144                             presetUserDefaultParentParent = thisPresetDictParentParent;
7145                             userDefaultFound = YES;     
7146                         }
7147                         
7148                     }
7149                 }
7150             }
7151         }
7152         
7153         }
7154     /* check to see if a user specified preset was found, if not then assign the parents for
7155      * the presetHbDefault so that we can open the parents for the nested presets
7156      */
7157     if (userDefaultFound == NO)
7158     {
7159         presetUserDefaultParent = presetHbDefaultParent;
7160         presetUserDefaultParentParent = presetHbDefaultParentParent;
7161     }
7162 }
7163
7164 - (IBAction)setDefaultPreset:(id)sender
7165 {
7166 /* We need to determine if the item is a folder */
7167    if ([[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Folder"] intValue] == 1)
7168    {
7169    return;
7170    }
7171
7172     int i = 0;
7173     NSEnumerator *enumerator = [UserPresets objectEnumerator];
7174         id tempObject;
7175         /* First make sure the old user specified default preset is removed */
7176     while (tempObject = [enumerator nextObject])
7177         {
7178                 NSMutableDictionary *thisPresetDict = tempObject;
7179                 if ([[tempObject objectForKey:@"Default"] intValue] != 1) // if not the default HB Preset, set to 0
7180                 {
7181                         [[UserPresets objectAtIndex:i] setObject:[NSNumber numberWithInt:0] forKey:@"Default"]; 
7182                 }
7183                 
7184                 /* if we run into a folder, go to level 1 and iterate through the children arrays for the default */
7185         if ([thisPresetDict objectForKey:@"ChildrenArray"])
7186         {
7187             NSEnumerator *enumerator = [[thisPresetDict objectForKey:@"ChildrenArray"] objectEnumerator];
7188             id tempObject;
7189             int ii = 0;
7190             while (tempObject = [enumerator nextObject])
7191             {
7192                 NSMutableDictionary *thisPresetDict1 = tempObject;
7193                 if ([[tempObject objectForKey:@"Default"] intValue] != 1) // if not the default HB Preset, set to 0
7194                 {
7195                     [[[thisPresetDict objectForKey:@"ChildrenArray"] objectAtIndex:ii] setObject:[NSNumber numberWithInt:0] forKey:@"Default"]; 
7196                 }
7197                 /* if we run into a folder, go to level 2 and iterate through the children arrays for the default */
7198                 if ([thisPresetDict1 objectForKey:@"ChildrenArray"])
7199                 {
7200                     NSEnumerator *enumerator = [[thisPresetDict1 objectForKey:@"ChildrenArray"] objectEnumerator];
7201                     id tempObject;
7202                     int iii = 0;
7203                     while (tempObject = [enumerator nextObject])
7204                     {
7205                         if ([[tempObject objectForKey:@"Default"] intValue] != 1) // if not the default HB Preset, set to 0
7206                         {
7207                             [[[thisPresetDict1 objectForKey:@"ChildrenArray"] objectAtIndex:iii] setObject:[NSNumber numberWithInt:0] forKey:@"Default"];       
7208                         }
7209                         iii++;
7210                     }
7211                 }
7212                 ii++;
7213             }
7214             
7215         }
7216         i++; 
7217         }
7218     
7219     
7220     int presetToModLevel = [fPresetsOutlineView levelForItem: [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]]];
7221     NSDictionary *presetToMod = [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]];
7222     NSDictionary *presetToModParent = [fPresetsOutlineView parentForItem: presetToMod];
7223     
7224     
7225     NSMutableArray *presetsArrayToMod;
7226     NSMutableArray *tempArray;
7227     
7228     /* If we are a root level preset, we are modding the UserPresets array */
7229     if (presetToModLevel == 0)
7230     {
7231         presetsArrayToMod = UserPresets;
7232     }
7233     else // We have a parent preset, so we modify the chidren array object for key
7234     {
7235         presetsArrayToMod = [presetToModParent objectForKey:@"ChildrenArray"]; 
7236     }
7237     
7238     enumerator = [presetsArrayToMod objectEnumerator];
7239     tempArray = [NSMutableArray array];
7240     int iiii = 0;
7241     while (tempObject = [enumerator nextObject]) 
7242     {
7243         NSDictionary *thisPresetDict = tempObject;
7244         if (thisPresetDict == presetToMod)
7245         {
7246             if ([[tempObject objectForKey:@"Default"] intValue] != 1) // if not the default HB Preset, set to 2
7247             {
7248                 [[presetsArrayToMod objectAtIndex:iiii] setObject:[NSNumber numberWithInt:2] forKey:@"Default"];        
7249             }
7250         }
7251      iiii++;
7252      }
7253     
7254     
7255     /* We save all of the preset data here */
7256     [self savePreset];
7257     /* We Reload the New Table data for presets */
7258     [fPresetsOutlineView reloadData];
7259 }
7260
7261 - (IBAction)selectDefaultPreset:(id)sender
7262 {
7263         NSMutableDictionary *presetToMod;
7264     /* if there is a user specified default, we use it */
7265         if (presetUserDefault)
7266         {
7267         presetToMod = presetUserDefault;
7268     }
7269         else if (presetHbDefault) //else we use the built in default presetHbDefault
7270         {
7271         presetToMod = presetHbDefault;
7272         }
7273     else
7274     {
7275     return;
7276     }
7277     
7278     if (presetUserDefaultParent != nil)
7279     {
7280         [fPresetsOutlineView expandItem:presetUserDefaultParent];
7281         
7282     }
7283     if (presetUserDefaultParentParent != nil)
7284     {
7285         [fPresetsOutlineView expandItem:presetUserDefaultParentParent];
7286         
7287     }
7288     
7289     [fPresetsOutlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:[fPresetsOutlineView rowForItem: presetToMod]] byExtendingSelection:NO];
7290         [self selectPreset:nil];
7291 }
7292
7293
7294 #pragma mark -
7295 #pragma mark Manage Built In Presets
7296
7297
7298 - (IBAction)deleteFactoryPresets:(id)sender
7299 {
7300     //int status;
7301     NSEnumerator *enumerator = [UserPresets objectEnumerator];
7302         id tempObject;
7303     
7304         //NSNumber *index;
7305     NSMutableArray *tempArray;
7306
7307
7308         tempArray = [NSMutableArray array];
7309         /* we look here to see if the preset is we move on to the next one */
7310         while ( tempObject = [enumerator nextObject] )  
7311                 {
7312                         /* if the preset is "Factory" then we put it in the array of
7313                         presets to delete */
7314                         if ([[tempObject objectForKey:@"Type"] intValue] == 0)
7315                         {
7316                                 [tempArray addObject:tempObject];
7317                         }
7318         }
7319         
7320         [UserPresets removeObjectsInArray:tempArray];
7321         [fPresetsOutlineView reloadData];
7322         [self savePreset];   
7323
7324 }
7325
7326    /* We use this method to recreate new, updated factory presets */
7327 - (IBAction)addFactoryPresets:(id)sender
7328 {
7329     
7330     /* First, we delete any existing built in presets */
7331     [self deleteFactoryPresets: sender];
7332     /* Then we generate new built in presets programmatically with fPresetsBuiltin
7333      * which is all setup in HBPresets.h and  HBPresets.m*/
7334     [fPresetsBuiltin generateBuiltinPresets:UserPresets];
7335     /* update build number for built in presets */
7336     /* iterate though the new array of presets to import and add them to our presets array */
7337     int i = 0;
7338     NSEnumerator *enumerator = [UserPresets objectEnumerator];
7339     id tempObject;
7340     while (tempObject = [enumerator nextObject])
7341     {
7342         /* Record the apps current build number in the PresetBuildNumber key */
7343         if ([[tempObject objectForKey:@"Type"] intValue] == 0) // Type 0 is a built in preset           
7344         {
7345             /* Preset build number */
7346             [[UserPresets objectAtIndex:i] setObject:[NSNumber numberWithInt:[[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] intValue]] forKey:@"PresetBuildNumber"];
7347         }
7348         i++;
7349     }
7350     /* report the built in preset updating to the activity log */
7351     [self writeToActivityLog: "built in presets updated to build number: %d", [[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] intValue]];
7352     
7353     [self sortPresets];
7354     [self addPreset];
7355     
7356 }
7357
7358
7359 @end
7360
7361 /*******************************
7362  * Subclass of the HBPresetsOutlineView *
7363  *******************************/
7364
7365 @implementation HBPresetsOutlineView
7366 - (NSImage *)dragImageForRowsWithIndexes:(NSIndexSet *)dragRows tableColumns:(NSArray *)tableColumns event:(NSEvent*)dragEvent offset:(NSPointPointer)dragImageOffset
7367 {
7368     fIsDragging = YES;
7369
7370     // By default, NSTableView only drags an image of the first column. Change this to
7371     // drag an image of the queue's icon and PresetName columns.
7372     NSArray * cols = [NSArray arrayWithObjects: [self tableColumnWithIdentifier:@"PresetName"], nil];
7373     return [super dragImageForRowsWithIndexes:dragRows tableColumns:cols event:dragEvent offset:dragImageOffset];
7374 }
7375
7376
7377
7378 - (void) mouseDown:(NSEvent *)theEvent
7379 {
7380     [super mouseDown:theEvent];
7381         fIsDragging = NO;
7382 }
7383
7384
7385
7386 - (BOOL) isDragging;
7387 {
7388     return fIsDragging;
7389 }
7390 @end
7391
7392
7393