OSDN Git Service

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