OSDN Git Service

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