OSDN Git Service

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