OSDN Git Service

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