OSDN Git Service

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