OSDN Git Service

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