OSDN Git Service

MacGui: Use nil instead of NULL. Patch by blindjimmy. http://handbrake.djgraphite...
[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 #include "Controller.h"
8 #include "a52dec/a52.h"
9 #import "HBOutputPanelController.h"
10 #import "HBPreferencesController.h"
11 /* Added to integrate scanning into HBController */
12 #include <IOKit/IOKitLib.h>
13 #include <IOKit/storage/IOMedia.h>
14 #include <IOKit/storage/IODVDMedia.h>
15 #include "HBDVDDetector.h"
16 #include "dvdread/dvd_reader.h"
17 #include "HBPresets.h"
18
19 #define DragDropSimplePboardType        @"MyCustomOutlineViewPboardType"
20
21 /* We setup the toolbar values here */
22 static NSString *        ToggleDrawerIdentifier             = @"Toggle Drawer Item Identifier";
23 static NSString *        StartEncodingIdentifier            = @"Start Encoding Item Identifier";
24 static NSString *        PauseEncodingIdentifier            = @"Pause Encoding Item Identifier";
25 static NSString *        ShowQueueIdentifier                = @"Show Queue Item Identifier";
26 static NSString *        AddToQueueIdentifier               = @"Add to Queue Item Identifier";
27 static NSString *        ShowActivityIdentifier             = @"Debug Output Item Identifier";
28 static NSString *        ChooseSourceIdentifier             = @"Choose Source Item Identifier";
29
30
31 /*******************************
32  * HBController implementation *
33  *******************************/
34 @implementation HBController
35
36 - (id)init
37 {
38     self = [super init];
39     if( !self )
40     {
41         return nil;
42     }
43
44     [HBPreferencesController registerUserDefaults];
45     fHandle = NULL;
46
47     /* Check for check for the app support directory here as
48      * outputPanel needs it right away, as may other future methods
49      */
50     NSString *libraryDir = [NSSearchPathForDirectoriesInDomains( NSLibraryDirectory,
51                                                                  NSUserDomainMask,
52                                                                  YES ) objectAtIndex:0];
53     AppSupportDirectory = [[libraryDir stringByAppendingPathComponent:@"Application Support"]
54                                        stringByAppendingPathComponent:@"HandBrake"];
55     if( ![[NSFileManager defaultManager] fileExistsAtPath:AppSupportDirectory] )
56     {
57         [[NSFileManager defaultManager] createDirectoryAtPath:AppSupportDirectory
58                                                    attributes:nil];
59     }
60
61     outputPanel = [[HBOutputPanelController alloc] init];
62     fPictureController = [[PictureController alloc] initWithDelegate:self];
63     fQueueController = [[HBQueueController alloc] init];
64     fAdvancedOptions = [[HBAdvancedController alloc] init];
65     /* we init the HBPresets class which currently is only used
66     * for updating built in presets, may move more functionality
67     * there in the future
68     */
69     fPresetsBuiltin = [[HBPresets alloc] init];
70     fPreferencesController = [[HBPreferencesController alloc] init];
71     /* Lets report the HandBrake version number here to the activity log and text log file */
72     NSString *versionStringFull = [[NSString stringWithFormat: @"Handbrake Version: %@", [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleGetInfoString"]] stringByAppendingString: [NSString stringWithFormat: @" (%@)", [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"]]];
73     [self writeToActivityLog: "%s", [versionStringFull UTF8String]];    
74     
75     return self;
76 }
77
78
79 - (void) applicationDidFinishLaunching: (NSNotification *) notification
80 {
81     /* Init libhb with check for updates libhb style set to "0" so its ignored and lets sparkle take care of it */
82     fHandle = hb_init(HB_DEBUG_ALL, 0);
83
84         // Set the Growl Delegate
85     [GrowlApplicationBridge setGrowlDelegate: self];
86     /* Init others controllers */
87     [fPictureController SetHandle: fHandle];
88     [fQueueController   setHandle: fHandle];
89     [fQueueController   setHBController: self];
90
91     fChapterTitlesDelegate = [[ChapterTitles alloc] init];
92     [fChapterTable setDataSource:fChapterTitlesDelegate];
93     [fChapterTable setDelegate:fChapterTitlesDelegate];
94
95     /* Call UpdateUI every 1/2 sec */
96     [[NSRunLoop currentRunLoop] addTimer:[NSTimer
97                                           scheduledTimerWithTimeInterval:0.5 target:self
98                                           selector:@selector(updateUI:) userInfo:nil repeats:YES]
99                                  forMode:NSEventTrackingRunLoopMode];
100
101     // Open debug output window now if it was visible when HB was closed
102     if ([[NSUserDefaults standardUserDefaults] boolForKey:@"OutputPanelIsOpen"])
103         [self showDebugOutputPanel:nil];
104
105     // Open queue window now if it was visible when HB was closed
106     if ([[NSUserDefaults standardUserDefaults] boolForKey:@"QueueWindowIsOpen"])
107         [self showQueueWindow:nil];
108
109         [self openMainWindow:nil];
110
111     /* Show Browse Sources Window ASAP */
112     [self performSelectorOnMainThread:@selector(browseSources:)
113                            withObject:nil waitUntilDone:NO];
114 }
115
116 - (NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication *) app
117 {
118     // Warn if encoding a movie
119     hb_state_t s;
120     hb_get_state( fHandle, &s );
121     HBJobGroup * jobGroup = [fQueueController currentJobGroup];
122     if ( jobGroup && ( s.state != HB_STATE_IDLE ) )
123     {
124         int result = NSRunCriticalAlertPanel(
125                 NSLocalizedString(@"Are you sure you want to quit HandBrake?", nil),
126                 NSLocalizedString(@"%@ is currently encoding. If you quit HandBrake, your movie will be lost. Do you want to quit anyway?", nil),
127                 NSLocalizedString(@"Quit", nil), NSLocalizedString(@"Don't Quit", nil), nil,
128                 jobGroup ? [jobGroup name] : @"A movie" );
129         
130         if (result == NSAlertDefaultReturn)
131         {
132             [self doCancelCurrentJob];
133             return NSTerminateNow;
134         }
135         else
136             return NSTerminateCancel;
137     }
138     
139     // Warn if items still in the queue
140     else if ( hb_count( fHandle ) > 0 )
141     {
142         int result = NSRunCriticalAlertPanel(
143                 NSLocalizedString(@"Are you sure you want to quit HandBrake?", nil),
144                 NSLocalizedString(@"One or more encodes are queued for encoding. Do you want to quit anyway?", nil),
145                 NSLocalizedString(@"Quit", nil), NSLocalizedString(@"Don't Quit", nil), nil);
146         
147         if ( result == NSAlertDefaultReturn )
148             return NSTerminateNow;
149         else
150             return NSTerminateCancel;
151     }
152     
153     return NSTerminateNow;
154 }
155
156 - (void)applicationWillTerminate:(NSNotification *)aNotification
157 {
158         [browsedSourceDisplayName release];
159     [outputPanel release];
160         [fQueueController release];
161         hb_close(&fHandle);
162 }
163
164
165 - (void) awakeFromNib
166 {
167     [fWindow center];
168     [fWindow setExcludedFromWindowsMenu:YES];
169     [fAdvancedOptions setView:fAdvancedView];
170
171     /* lets setup our presets drawer for drag and drop here */
172     [fPresetsOutlineView registerForDraggedTypes: [NSArray arrayWithObject:DragDropSimplePboardType] ];
173     [fPresetsOutlineView setDraggingSourceOperationMask:NSDragOperationEvery forLocal:YES];
174     [fPresetsOutlineView setVerticalMotionCanBeginDrag: YES];
175
176     /* Initialize currentScanCount so HB can use it to
177                 evaluate successive scans */
178         currentScanCount = 0;
179
180     /* Init UserPresets .plist */
181         [self loadPresets];
182
183         fRipIndicatorShown = NO;  // initially out of view in the nib
184
185         /* Show/Dont Show Presets drawer upon launch based
186                 on user preference DefaultPresetsDrawerShow*/
187         if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultPresetsDrawerShow"] > 0)
188         {
189                 [fPresetDrawer open];
190         }
191         
192         
193     
194     /* Destination box*/
195     NSMenuItem *menuItem;
196     [fDstFormatPopUp removeAllItems];
197     // MP4 file
198     menuItem = [[fDstFormatPopUp menu] addItemWithTitle:@"MP4 file" action: NULL keyEquivalent: @""];
199     [menuItem setTag: HB_MUX_MP4];
200         // MKV file
201     menuItem = [[fDstFormatPopUp menu] addItemWithTitle:@"MKV file" action: NULL keyEquivalent: @""];
202     [menuItem setTag: HB_MUX_MKV];
203     // AVI file
204     menuItem = [[fDstFormatPopUp menu] addItemWithTitle:@"AVI file" action: NULL keyEquivalent: @""];
205     [menuItem setTag: HB_MUX_AVI];
206     // OGM file
207     menuItem = [[fDstFormatPopUp menu] addItemWithTitle:@"OGM file" action: NULL keyEquivalent: @""];
208     [menuItem setTag: HB_MUX_OGM];
209     [fDstFormatPopUp selectItemAtIndex: 0];
210
211     [self formatPopUpChanged:nil];
212
213         /* We enable the create chapters checkbox here since we are .mp4 */
214         [fCreateChapterMarkers setEnabled: YES];
215         if ([fDstFormatPopUp indexOfSelectedItem] == 0 && [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultChapterMarkers"] > 0)
216         {
217                 [fCreateChapterMarkers setState: NSOnState];
218         }
219
220
221
222
223     [fDstFile2Field setStringValue: [NSString stringWithFormat:
224         @"%@/Desktop/Movie.mp4", NSHomeDirectory()]];
225
226     /* Video encoder */
227     [fVidEncoderPopUp removeAllItems];
228     [fVidEncoderPopUp addItemWithTitle: @"FFmpeg"];
229     [fVidEncoderPopUp addItemWithTitle: @"XviD"];
230
231
232
233     /* Video quality */
234     [fVidTargetSizeField setIntValue: 700];
235         [fVidBitrateField    setIntValue: 1000];
236
237     [fVidQualityMatrix   selectCell: fVidBitrateCell];
238     [self videoMatrixChanged:nil];
239
240     /* Video framerate */
241     [fVidRatePopUp removeAllItems];
242         [fVidRatePopUp addItemWithTitle: NSLocalizedString( @"Same as source", @"" )];
243     for( int i = 0; i < hb_video_rates_count; i++ )
244     {
245         if ([[NSString stringWithCString: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%.3f",23.976]])
246                 {
247                         [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@",
248                                 [NSString stringWithCString: hb_video_rates[i].string], @" (NTSC Film)"]];
249                 }
250                 else if ([[NSString stringWithCString: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%d",25]])
251                 {
252                         [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@",
253                                 [NSString stringWithCString: hb_video_rates[i].string], @" (PAL Film/Video)"]];
254                 }
255                 else if ([[NSString stringWithCString: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%.2f",29.97]])
256                 {
257                         [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@",
258                                 [NSString stringWithCString: hb_video_rates[i].string], @" (NTSC Video)"]];
259                 }
260                 else
261                 {
262                         [fVidRatePopUp addItemWithTitle:
263                                 [NSString stringWithCString: hb_video_rates[i].string]];
264                 }
265     }
266     [fVidRatePopUp selectItemAtIndex: 0];
267         
268         /* Set Auto Crop to On at launch */
269     [fPictureController setAutoCrop:YES];
270         
271         /* Audio bitrate */
272     [fAudTrack1BitratePopUp removeAllItems];
273     for( int i = 0; i < hb_audio_bitrates_count; i++ )
274     {
275         [fAudTrack1BitratePopUp addItemWithTitle:
276                                 [NSString stringWithCString: hb_audio_bitrates[i].string]];
277
278     }
279     [fAudTrack1BitratePopUp selectItemAtIndex: hb_audio_bitrates_default];
280         
281     /* Audio samplerate */
282     [fAudTrack1RatePopUp removeAllItems];
283     for( int i = 0; i < hb_audio_rates_count; i++ )
284     {
285         [fAudTrack1RatePopUp addItemWithTitle:
286             [NSString stringWithCString: hb_audio_rates[i].string]];
287     }
288     [fAudTrack1RatePopUp selectItemAtIndex: hb_audio_rates_default];
289         
290     /* Bottom */
291     [fStatusField setStringValue: @""];
292
293     [self enableUI: NO];
294         [self setupToolbar];
295
296         /* We disable the Turbo 1st pass checkbox since we are not x264 */
297         [fVidTurboPassCheck setEnabled: NO];
298         [fVidTurboPassCheck setState: NSOffState];
299
300
301         /* lets get our default prefs here */
302         [self getDefaultPresets:nil];
303         /* lets initialize the current successful scancount here to 0 */
304         currentSuccessfulScanCount = 0;
305 }
306
307 - (void) enableUI: (bool) b
308 {
309     NSControl * controls[] =
310       { fSrcTitleField, fSrcTitlePopUp,
311         fSrcChapterField, fSrcChapterStartPopUp, fSrcChapterToField,
312         fSrcChapterEndPopUp, fSrcDuration1Field, fSrcDuration2Field,
313         fDstFormatField, fDstFormatPopUp, fDstFile1Field, fDstFile2Field,
314         fDstBrowseButton, fVidRateField, fVidRatePopUp,
315         fVidEncoderField, fVidEncoderPopUp, fVidQualityField,
316         fVidQualityMatrix, fVidGrayscaleCheck, fSubField, fSubPopUp,
317         fAudSourceLabel, fAudCodecLabel, fAudMixdownLabel, fAudSamplerateLabel, fAudBitrateLabel,
318         fAudTrack1Label, fAudTrack2Label, fAudTrack3Label, fAudTrack4Label,
319         fAudLang1PopUp, fAudLang2PopUp, fAudLang3PopUp, fAudLang4PopUp,
320         fAudTrack1CodecPopUp, fAudTrack2CodecPopUp, fAudTrack3CodecPopUp, fAudTrack4CodecPopUp,
321         fAudTrack1MixPopUp, fAudTrack2MixPopUp, fAudTrack3MixPopUp, fAudTrack4MixPopUp,
322         fAudTrack1RatePopUp, fAudTrack2RatePopUp, fAudTrack3RatePopUp, fAudTrack4RatePopUp,
323         fAudTrack1BitratePopUp, fAudTrack2BitratePopUp, fAudTrack3BitratePopUp, fAudTrack4BitratePopUp,
324         fAudDrcLabel, fAudTrack1DrcSlider, fAudTrack1DrcField, fAudTrack2DrcSlider,
325         fAudTrack2DrcField, fAudTrack3DrcSlider, fAudTrack3DrcField, fAudTrack4DrcSlider,fAudTrack4DrcField,
326         fPictureButton,fQueueStatus,fPicSettingARkeep, fPicSettingDeinterlace,fPicLabelSettings,fPicLabelSrc,
327         fPicLabelOutp,fPicSettingsSrc,fPicSettingsOutp,fPicSettingsAnamorphic,
328                 fPicLabelAr,fPicLabelDeinterlace,fPicSettingPAR,fPicLabelAnamorphic,fPresetsAdd,fPresetsDelete,
329                 fCreateChapterMarkers,fVidTurboPassCheck,fDstMp4LargeFileCheck,fPicLabelAutoCrop,
330                 fPicSettingAutoCrop,fPicSettingDetelecine,fPicLabelDetelecine,fPicLabelDenoise,fPicSettingDenoise,
331         fSubForcedCheck,fPicSettingDeblock,fPicLabelDeblock,fPicLabelDecomb,fPicSettingDecomb,fPresetsOutlineView,
332         fAudDrcLabel,fDstMp4HttpOptFileCheck,fDstMp4iPodFileCheck};
333
334     for( unsigned i = 0;
335          i < sizeof( controls ) / sizeof( NSControl * ); i++ )
336     {
337         if( [[controls[i] className] isEqualToString: @"NSTextField"] )
338         {
339             NSTextField * tf = (NSTextField *) controls[i];
340             if( ![tf isBezeled] )
341             {
342                 [tf setTextColor: b ? [NSColor controlTextColor] :
343                     [NSColor disabledControlTextColor]];
344                 continue;
345             }
346         }
347         [controls[i] setEnabled: b];
348
349     }
350
351         if (b) {
352
353         /* if we're enabling the interface, check if the audio mixdown controls need to be enabled or not */
354         /* these will have been enabled by the mass control enablement above anyway, so we're sense-checking it here */
355         [self setEnabledStateOfAudioMixdownControls:nil];
356         /* we also call calculatePictureSizing here to sense check if we already have vfr selected */
357         [self calculatePictureSizing:nil];
358
359         } else {
360
361                 [fPresetsOutlineView setEnabled: NO];
362
363         }
364
365     [self videoMatrixChanged:nil];
366     [fAdvancedOptions enableUI:b];
367 }
368
369
370 /***********************************************************************
371  * UpdateDockIcon
372  ***********************************************************************
373  * Shows a progression bar on the dock icon, filled according to
374  * 'progress' (0.0 <= progress <= 1.0).
375  * Called with progress < 0.0 or progress > 1.0, restores the original
376  * icon.
377  **********************************************************************/
378 - (void) UpdateDockIcon: (float) progress
379 {
380     NSImage * icon;
381     NSData * tiff;
382     NSBitmapImageRep * bmp;
383     uint32_t * pen;
384     uint32_t black = htonl( 0x000000FF );
385     uint32_t red   = htonl( 0xFF0000FF );
386     uint32_t white = htonl( 0xFFFFFFFF );
387     int row_start, row_end;
388     int i, j;
389
390     /* Get application original icon */
391     icon = [NSImage imageNamed: @"NSApplicationIcon"];
392
393     if( progress < 0.0 || progress > 1.0 )
394     {
395         [NSApp setApplicationIconImage: icon];
396         return;
397     }
398
399     /* Get it in a raw bitmap form */
400     tiff = [icon TIFFRepresentationUsingCompression:
401             NSTIFFCompressionNone factor: 1.0];
402     bmp = [NSBitmapImageRep imageRepWithData: tiff];
403     
404     /* Draw the progression bar */
405     /* It's pretty simple (ugly?) now, but I'm no designer */
406
407     row_start = 3 * (int) [bmp size].height / 4;
408     row_end   = 7 * (int) [bmp size].height / 8;
409
410     for( i = row_start; i < row_start + 2; i++ )
411     {
412         pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
413         for( j = 0; j < (int) [bmp size].width; j++ )
414         {
415             pen[j] = black;
416         }
417     }
418     for( i = row_start + 2; i < row_end - 2; i++ )
419     {
420         pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
421         pen[0] = black;
422         pen[1] = black;
423         for( j = 2; j < (int) [bmp size].width - 2; j++ )
424         {
425             if( j < 2 + (int) ( ( [bmp size].width - 4.0 ) * progress ) )
426             {
427                 pen[j] = red;
428             }
429             else
430             {
431                 pen[j] = white;
432             }
433         }
434         pen[j]   = black;
435         pen[j+1] = black;
436     }
437     for( i = row_end - 2; i < row_end; i++ )
438     {
439         pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
440         for( j = 0; j < (int) [bmp size].width; j++ )
441         {
442             pen[j] = black;
443         }
444     }
445
446     /* Now update the dock icon */
447     tiff = [bmp TIFFRepresentationUsingCompression:
448             NSTIFFCompressionNone factor: 1.0];
449     icon = [[NSImage alloc] initWithData: tiff];
450     [NSApp setApplicationIconImage: icon];
451     [icon release];
452 }
453
454 - (void) updateUI: (NSTimer *) timer
455 {
456
457     hb_list_t  * list;
458     list = hb_get_titles( fHandle );    
459     /* check to see if there has been a new scan done
460         this bypasses the constraints of HB_STATE_WORKING
461         not allowing setting a newly scanned source */
462         int checkScanCount = hb_get_scancount( fHandle );
463         if (checkScanCount > currentScanCount)
464         {
465                 currentScanCount = checkScanCount;
466         [fScanIndicator setIndeterminate: NO];
467         [fScanIndicator setDoubleValue: 0.0];
468         [fScanIndicator setHidden: YES];
469                 [self showNewScan:nil];
470         }
471
472     hb_state_t s;
473     hb_get_state( fHandle, &s );
474
475
476     switch( s.state )
477     {
478         case HB_STATE_IDLE:
479                 break;
480 #define p s.param.scanning
481         case HB_STATE_SCANNING:
482                 {
483             [fSrcDVD2Field setStringValue: [NSString stringWithFormat:
484                                             NSLocalizedString( @"Scanning title %d of %d...", @"" ),
485                                             p.title_cur, p.title_count]];
486             [fScanIndicator setHidden: NO];
487             [fScanIndicator setDoubleValue: 100.0 * ( p.title_cur - 1 ) / p.title_count];
488             break;
489                 }
490 #undef p
491
492 #define p s.param.scandone
493         case HB_STATE_SCANDONE:
494         {
495             [fScanIndicator setIndeterminate: NO];
496             [fScanIndicator setDoubleValue: 0.0];
497             [fScanIndicator setHidden: YES];
498                         [self showNewScan:nil];
499             [[fWindow toolbar] validateVisibleItems];
500                         break;
501         }
502 #undef p
503
504 #define p s.param.working
505         case HB_STATE_WORKING:
506         {
507             float progress_total;
508             NSMutableString * string;
509                         /* Currently, p.job_cur and p.job_count get screwed up when adding
510                                 jobs during encoding, if they cannot be fixed in libhb, will implement a
511                                 nasty but working cocoa solution */
512                         /* Update text field */
513                         string = [NSMutableString stringWithFormat: NSLocalizedString( @"Encoding: task %d of %d, %.2f %%", @"" ), p.job_cur, p.job_count, 100.0 * p.progress];
514             
515                         if( p.seconds > -1 )
516             {
517                 [string appendFormat:
518                     NSLocalizedString( @" (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)", @"" ),
519                     p.rate_cur, p.rate_avg, p.hours, p.minutes, p.seconds];
520             }
521             [fStatusField setStringValue: string];
522                         
523             /* Update slider */
524                         progress_total = ( p.progress + p.job_cur - 1 ) / p.job_count;
525             [fRipIndicator setIndeterminate: NO];
526             [fRipIndicator setDoubleValue: 100.0 * progress_total];
527                         
528             // If progress bar hasn't been revealed at the bottom of the window, do
529             // that now. This code used to be in doRip. I moved it to here to handle
530             // the case where hb_start is called by HBQueueController and not from
531             // HBController.
532             if (!fRipIndicatorShown)
533             {
534                 NSRect frame = [fWindow frame];
535                 if (frame.size.width <= 591)
536                     frame.size.width = 591;
537                 frame.size.height += 36;
538                 frame.origin.y -= 36;
539                 [fWindow setFrame:frame display:YES animate:YES];
540                 fRipIndicatorShown = YES;
541                 /* We check to see if we need to warn the user that the computer will go to sleep
542                    or shut down when encoding is finished */
543                 [self remindUserOfSleepOrShutdown];
544             }
545
546             /* Update dock icon */
547             [self UpdateDockIcon: progress_total];
548                         
549             // Has current job changed? That means the queue has probably changed as
550                         // well so update it
551             [fQueueController libhbStateChanged: s];
552             
553             break;
554         }
555 #undef p
556                         
557 #define p s.param.muxing
558         case HB_STATE_MUXING:
559         {
560             NSMutableString * string;
561                         
562             /* Update text field */
563             string = [NSMutableString stringWithFormat:
564                 NSLocalizedString( @"Muxing...", @"" )];
565             [fStatusField setStringValue: string];
566                         
567             /* Update slider */
568             [fRipIndicator setIndeterminate: YES];
569             [fRipIndicator startAnimation: nil];
570                         
571             /* Update dock icon */
572             [self UpdateDockIcon: 1.0];
573                         
574                         // Pass along the info to HBQueueController
575             [fQueueController libhbStateChanged: s];
576                         
577             break;
578         }
579 #undef p
580                         
581         case HB_STATE_PAUSED:
582                     [fStatusField setStringValue: NSLocalizedString( @"Paused", @"" )];
583             
584                         // Pass along the info to HBQueueController
585             [fQueueController libhbStateChanged: s];
586
587             break;
588                         
589         case HB_STATE_WORKDONE:
590         {
591             // HB_STATE_WORKDONE happpens as a result of libhb finishing all its jobs
592             // or someone calling hb_stop. In the latter case, hb_stop does not clear
593             // out the remaining passes/jobs in the queue. We'll do that here.
594
595             // Delete all remaining jobs of this encode.
596             hb_job_t * job;
597             while( ( job = hb_job( fHandle, 0 ) ) && ( !IsFirstPass(job->sequence_id) ) )
598                 hb_rem( fHandle, job );
599
600             [fStatusField setStringValue: NSLocalizedString( @"Done.", @"" )];
601             [fRipIndicator setIndeterminate: NO];
602             [fRipIndicator setDoubleValue: 0.0];
603             [[fWindow toolbar] validateVisibleItems];
604
605             /* Restore dock icon */
606             [self UpdateDockIcon: -1.0];
607
608             if (fRipIndicatorShown)
609             {
610                 NSRect frame = [fWindow frame];
611                 if (frame.size.width <= 591)
612                                     frame.size.width = 591;
613                 frame.size.height += -36;
614                 frame.origin.y -= -36;
615                 [fWindow setFrame:frame display:YES animate:YES];
616                                 fRipIndicatorShown = NO;
617                         }
618                         
619                         // Pass along the info to HBQueueController
620             [fQueueController libhbStateChanged: s];
621                         
622             /* Check to see if the encode state has not been cancelled
623                                 to determine if we should check for encode done notifications */
624                         if (fEncodeState != 2)                  {
625                                 /* If Alert Window or Window and Growl has been selected */
626                                 if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window"] || 
627                                         [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window And Growl"])
628                 {
629                                         /*On Screen Notification*/
630                                         int status;
631                                         NSBeep();
632                                         status = NSRunAlertPanel(@"Put down that cocktail...",@"Your HandBrake encode is done!", @"OK", nil, nil);
633                                         [NSApp requestUserAttention:NSCriticalRequest];
634                                         if ( status == NSAlertDefaultReturn ) 
635                                         {
636                                                 [self enableUI: YES];
637                                         }
638                 }
639                                 else
640                                 {
641                                         [self enableUI: YES];
642                                 }
643                                    /* If sleep has been selected */
644             if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Put Computer To Sleep"])
645                 {
646                /* Sleep */
647                NSDictionary* errorDict;
648                NSAppleEventDescriptor* returnDescriptor = nil;
649                NSAppleScript* scriptObject = [[NSAppleScript alloc] initWithSource:
650                         @"tell application \"Finder\" to sleep"];
651                returnDescriptor = [scriptObject executeAndReturnError: &errorDict];
652                [scriptObject release];
653                [self enableUI: YES];
654                 }
655             /* If Shutdown has been selected */
656             if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Shut Down Computer"])
657                 {
658                /* Shut Down */
659                NSDictionary* errorDict;
660                NSAppleEventDescriptor* returnDescriptor = nil;
661                NSAppleScript* scriptObject = [[NSAppleScript alloc] initWithSource:
662                         @"tell application \"Finder\" to shut down"];
663                returnDescriptor = [scriptObject executeAndReturnError: &errorDict];
664                [scriptObject release];
665                [self enableUI: YES];
666                 }
667
668             }
669                         else
670                         {
671                                 [self enableUI: YES];
672                         }
673             break;
674         }
675     }
676         
677     /* Lets show the queue status here in the main window */
678         int queue_count = [fQueueController pendingCount];
679         if( queue_count == 1)
680                 [fQueueStatus setStringValue: NSLocalizedString( @"1 encode queued", @"" ) ];
681     else if (queue_count > 1)
682                 [fQueueStatus setStringValue: [NSString stringWithFormat: NSLocalizedString( @"%d encodes queued", @"" ), queue_count]];
683         else
684                 [fQueueStatus setStringValue: @""];
685 }
686
687 /* We use this to write messages to stderr from the macgui which show up in the activity window and log*/
688 - (void) writeToActivityLog:(char *) format, ...
689 {
690     va_list args;
691     va_start(args, format);
692     if (format != nil)
693     {
694         char str[1024];
695         vsnprintf( str, 1024, format, args );
696
697         time_t _now = time( NULL );
698         struct tm * now  = localtime( &_now );
699         fprintf(stderr, "[%02d:%02d:%02d] macgui: %s\n", now->tm_hour, now->tm_min, now->tm_sec, str );
700     }
701     va_end(args);
702 }
703
704 #pragma mark -
705 #pragma mark Toolbar
706 // ============================================================
707 // NSToolbar Related Methods
708 // ============================================================
709
710 - (void) setupToolbar {
711     NSToolbar *toolbar = [[[NSToolbar alloc] initWithIdentifier: @"HandBrake Toolbar"] autorelease];
712
713     [toolbar setAllowsUserCustomization: YES];
714     [toolbar setAutosavesConfiguration: YES];
715     [toolbar setDisplayMode: NSToolbarDisplayModeIconAndLabel];
716
717     [toolbar setDelegate: self];
718
719     [fWindow setToolbar: toolbar];
720 }
721
722 - (NSToolbarItem *) toolbar: (NSToolbar *)toolbar itemForItemIdentifier:
723     (NSString *) itemIdent willBeInsertedIntoToolbar:(BOOL) willBeInserted {
724     NSToolbarItem * item = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdent] autorelease];
725
726     if ([itemIdent isEqualToString: ToggleDrawerIdentifier])
727     {
728         [item setLabel: @"Toggle Presets"];
729         [item setPaletteLabel: @"Toggler Presets"];
730         [item setToolTip: @"Open/Close Preset Drawer"];
731         [item setImage: [NSImage imageNamed: @"Drawer"]];
732         [item setTarget: self];
733         [item setAction: @selector(toggleDrawer:)];
734         [item setAutovalidates: NO];
735     }
736     else if ([itemIdent isEqualToString: StartEncodingIdentifier])
737     {
738         [item setLabel: @"Start"];
739         [item setPaletteLabel: @"Start Encoding"];
740         [item setToolTip: @"Start Encoding"];
741         [item setImage: [NSImage imageNamed: @"Play"]];
742         [item setTarget: self];
743         [item setAction: @selector(Rip:)];
744     }
745     else if ([itemIdent isEqualToString: ShowQueueIdentifier])
746     {
747         [item setLabel: @"Show Queue"];
748         [item setPaletteLabel: @"Show Queue"];
749         [item setToolTip: @"Show Queue"];
750         [item setImage: [NSImage imageNamed: @"Queue"]];
751         [item setTarget: self];
752         [item setAction: @selector(showQueueWindow:)];
753         [item setAutovalidates: NO];
754     }
755     else if ([itemIdent isEqualToString: AddToQueueIdentifier])
756     {
757         [item setLabel: @"Add to Queue"];
758         [item setPaletteLabel: @"Add to Queue"];
759         [item setToolTip: @"Add to Queue"];
760         [item setImage: [NSImage imageNamed: @"AddToQueue"]];
761         [item setTarget: self];
762         [item setAction: @selector(addToQueue:)];
763     }
764     else if ([itemIdent isEqualToString: PauseEncodingIdentifier])
765     {
766         [item setLabel: @"Pause"];
767         [item setPaletteLabel: @"Pause Encoding"];
768         [item setToolTip: @"Pause Encoding"];
769         [item setImage: [NSImage imageNamed: @"Pause"]];
770         [item setTarget: self];
771         [item setAction: @selector(Pause:)];
772     }
773     else if ([itemIdent isEqualToString: ShowActivityIdentifier]) {
774         [item setLabel: @"Activity Window"];
775         [item setPaletteLabel: @"Show Activity Window"];
776         [item setToolTip: @"Show Activity Window"];
777         [item setImage: [NSImage imageNamed: @"ActivityWindow"]];
778         [item setTarget: self];
779         [item setAction: @selector(showDebugOutputPanel:)];
780         [item setAutovalidates: NO];
781     }
782     else if ([itemIdent isEqualToString: ChooseSourceIdentifier])
783     {
784         [item setLabel: @"Source"];
785         [item setPaletteLabel: @"Source"];
786         [item setToolTip: @"Choose Video Source"];
787         [item setImage: [NSImage imageNamed: @"Source"]];
788         [item setTarget: self];
789         [item setAction: @selector(browseSources:)];
790     }
791     else
792     {
793         return nil;
794     }
795
796     return item;
797 }
798
799 - (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar
800 {
801     return [NSArray arrayWithObjects: ChooseSourceIdentifier, NSToolbarSeparatorItemIdentifier, StartEncodingIdentifier,
802         PauseEncodingIdentifier, AddToQueueIdentifier, ShowQueueIdentifier, NSToolbarFlexibleSpaceItemIdentifier, 
803                 NSToolbarSpaceItemIdentifier, ShowActivityIdentifier, ToggleDrawerIdentifier, nil];
804 }
805
806 - (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) toolbar
807 {
808     return [NSArray arrayWithObjects:  StartEncodingIdentifier, PauseEncodingIdentifier, AddToQueueIdentifier,
809         ChooseSourceIdentifier, ShowQueueIdentifier, ShowActivityIdentifier, ToggleDrawerIdentifier,
810         NSToolbarCustomizeToolbarItemIdentifier, NSToolbarFlexibleSpaceItemIdentifier,
811         NSToolbarSpaceItemIdentifier, NSToolbarSeparatorItemIdentifier, nil];
812 }
813
814 - (BOOL) validateToolbarItem: (NSToolbarItem *) toolbarItem
815 {
816     NSString * ident = [toolbarItem itemIdentifier];
817         
818     if (fHandle)
819     {
820         hb_state_t s;
821         hb_get_state2( fHandle, &s );
822         
823         if (s.state == HB_STATE_WORKING || s.state == HB_STATE_MUXING)
824         {
825             if ([ident isEqualToString: StartEncodingIdentifier])
826             {
827                 [toolbarItem setImage: [NSImage imageNamed: @"Stop"]];
828                 [toolbarItem setLabel: @"Stop"];
829                 [toolbarItem setPaletteLabel: @"Stop"];
830                 [toolbarItem setToolTip: @"Stop Encoding"];
831                 return YES;
832             }
833             if ([ident isEqualToString: PauseEncodingIdentifier])
834             {
835                 [toolbarItem setImage: [NSImage imageNamed: @"Pause"]];
836                 [toolbarItem setLabel: @"Pause"];
837                 [toolbarItem setPaletteLabel: @"Pause Encoding"];
838                 [toolbarItem setToolTip: @"Pause Encoding"];
839                 return YES;
840             }
841             if (SuccessfulScan)
842                 if ([ident isEqualToString: AddToQueueIdentifier])
843                     return YES;
844         }
845         else if (s.state == HB_STATE_PAUSED)
846         {
847             if ([ident isEqualToString: PauseEncodingIdentifier])
848             {
849                 [toolbarItem setImage: [NSImage imageNamed: @"Play"]];
850                 [toolbarItem setLabel: @"Resume"];
851                 [toolbarItem setPaletteLabel: @"Resume Encoding"];
852                 [toolbarItem setToolTip: @"Resume Encoding"];
853                 return YES;
854             }
855             if ([ident isEqualToString: StartEncodingIdentifier])
856                 return YES;
857             if ([ident isEqualToString: AddToQueueIdentifier])
858                 return YES;
859         }
860         else if (s.state == HB_STATE_SCANNING)
861             return NO;
862         else if (s.state == HB_STATE_WORKDONE || s.state == HB_STATE_SCANDONE || SuccessfulScan)
863         {
864             if ([ident isEqualToString: StartEncodingIdentifier])
865             {
866                 [toolbarItem setImage: [NSImage imageNamed: @"Play"]];
867                 if (hb_count(fHandle) > 0)
868                     [toolbarItem setLabel: @"Start Queue"];
869                 else
870                     [toolbarItem setLabel: @"Start"];
871                 [toolbarItem setPaletteLabel: @"Start Encoding"];
872                 [toolbarItem setToolTip: @"Start Encoding"];
873                 return YES;
874             }
875             if ([ident isEqualToString: AddToQueueIdentifier])
876                 return YES;
877         }
878
879     }
880     
881     if ([ident isEqualToString: ShowQueueIdentifier])
882         return YES;
883     if ([ident isEqualToString: ToggleDrawerIdentifier])
884         return YES;
885     if ([ident isEqualToString: ChooseSourceIdentifier])
886         return YES;
887     if ([ident isEqualToString: ShowActivityIdentifier])
888         return YES;
889     
890     return NO;
891 }
892
893 - (BOOL) validateMenuItem: (NSMenuItem *) menuItem
894 {
895     SEL action = [menuItem action];
896     
897     hb_state_t s;
898     hb_get_state2( fHandle, &s );
899     
900     if (fHandle)
901     {
902         if (action == @selector(addToQueue:) || action == @selector(showPicturePanel:) || action == @selector(showAddPresetPanel:))
903             return SuccessfulScan && [fWindow attachedSheet] == nil;
904         
905         if (action == @selector(browseSources:))
906         {
907             if (s.state == HB_STATE_SCANNING)
908                 return NO;
909             else
910                 return [fWindow attachedSheet] == nil;
911         }
912         if (action == @selector(selectDefaultPreset:))
913             return [fPresetsOutlineView selectedRow] >= 0 && [fWindow attachedSheet] == nil;
914         if (action == @selector(Pause:))
915         {
916             if (s.state == HB_STATE_WORKING)
917             {
918                 if(![[menuItem title] isEqualToString:@"Pause Encoding"])
919                     [menuItem setTitle:@"Pause Encoding"];
920                 return YES;
921             }
922             else if (s.state == HB_STATE_PAUSED)
923             {
924                 if(![[menuItem title] isEqualToString:@"Resume Encoding"])
925                     [menuItem setTitle:@"Resume Encoding"];
926                 return YES;
927             }
928             else
929                 return NO;
930         }
931         if (action == @selector(Rip:))
932         {
933             if (s.state == HB_STATE_WORKING || s.state == HB_STATE_MUXING || s.state == HB_STATE_PAUSED)
934             {
935                 if(![[menuItem title] isEqualToString:@"Stop Encoding"])
936                     [menuItem setTitle:@"Stop Encoding"];
937                 return YES;
938             }
939             else if (SuccessfulScan)
940             {
941                 if(![[menuItem title] isEqualToString:@"Start Encoding"])
942                     [menuItem setTitle:@"Start Encoding"];
943                 return [fWindow attachedSheet] == nil;
944             }
945             else
946                 return NO;
947         }
948     }
949     if( action == @selector(setDefaultPreset:) )
950     {
951         return [fPresetsOutlineView selectedRow] != -1;
952     }
953
954     return YES;
955 }
956
957 #pragma mark -
958 #pragma mark Encode Done Actions
959 // register a test notification and make
960 // it enabled by default
961 #define SERVICE_NAME @"Encode Done"
962 - (NSDictionary *)registrationDictionaryForGrowl 
963
964     NSDictionary *registrationDictionary = [NSDictionary dictionaryWithObjectsAndKeys: 
965     [NSArray arrayWithObjects:SERVICE_NAME,nil], GROWL_NOTIFICATIONS_ALL, 
966     [NSArray arrayWithObjects:SERVICE_NAME,nil], GROWL_NOTIFICATIONS_DEFAULT, 
967     nil]; 
968
969     return registrationDictionary; 
970
971
972 -(void)showGrowlDoneNotification:(NSString *) filePath
973 {
974     /* This is called from HBQueueController as jobs roll off of the queue in currentJobChanged */
975     NSString * finishedEncode = filePath;
976     /* strip off the path to just show the file name */
977     finishedEncode = [finishedEncode lastPathComponent];
978     if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Growl Notification"] || 
979         [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window And Growl"])
980     {
981         NSString * growlMssg = [NSString stringWithFormat: @"your HandBrake encode %@ is done!",finishedEncode];
982         [GrowlApplicationBridge 
983          notifyWithTitle:@"Put down that cocktail..." 
984          description:growlMssg 
985          notificationName:SERVICE_NAME
986          iconData:nil 
987          priority:0 
988          isSticky:1 
989          clickContext:nil];
990     }
991     
992 }
993 -(void)sendToMetaX:(NSString *) filePath
994 {
995     /* This is called from HBQueueController as jobs roll off of the queue in currentJobChanged */
996     if([[NSUserDefaults standardUserDefaults] boolForKey: @"sendToMetaX"] == YES)
997     {
998         NSAppleScript *myScript = [[NSAppleScript alloc] initWithSource: [NSString stringWithFormat: @"%@%@%@", @"tell application \"MetaX\" to open (POSIX file \"", filePath, @"\")"]];
999         [myScript executeAndReturnError: nil];
1000         [myScript release];
1001     }
1002 }
1003 #pragma mark -
1004 #pragma mark Get New Source
1005
1006 /*Opens the source browse window, called from Open Source widgets */
1007 - (IBAction) browseSources: (id) sender
1008 {
1009     [self enableUI: NO];
1010     NSOpenPanel * panel;
1011         
1012     panel = [NSOpenPanel openPanel];
1013     [panel setAllowsMultipleSelection: NO];
1014     [panel setCanChooseFiles: YES];
1015     [panel setCanChooseDirectories: YES ];
1016     NSString * sourceDirectory;
1017         if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastSourceDirectory"])
1018         {
1019                 sourceDirectory = [[NSUserDefaults standardUserDefaults] stringForKey:@"LastSourceDirectory"];
1020         }
1021         else
1022         {
1023                 sourceDirectory = @"~/Desktop";
1024                 sourceDirectory = [sourceDirectory stringByExpandingTildeInPath];
1025         }
1026     /* we open up the browse sources sheet here and call for browseSourcesDone after the sheet is closed
1027         * to evaluate whether we want to specify a title, we pass the sender in the contextInfo variable
1028         */
1029     [panel beginSheetForDirectory: sourceDirectory file: nil types: nil
1030                    modalForWindow: fWindow modalDelegate: self
1031                    didEndSelector: @selector( browseSourcesDone:returnCode:contextInfo: )
1032                       contextInfo: sender]; 
1033 }
1034
1035 - (void) browseSourcesDone: (NSOpenPanel *) sheet
1036                 returnCode: (int) returnCode contextInfo: (void *) contextInfo
1037 {
1038     /* we convert the sender content of contextInfo back into a variable called sender
1039      * mostly just for consistency for evaluation later
1040      */
1041     id sender = (id)contextInfo;
1042     /* User selected a file to open */
1043         if( returnCode == NSOKButton )
1044     {
1045             /* Free display name allocated previously by this code */
1046         [browsedSourceDisplayName release];
1047        
1048         NSString *scanPath = [[sheet filenames] objectAtIndex: 0];
1049         /* we set the last searched source directory in the prefs here */
1050         NSString *sourceDirectory = [scanPath stringByDeletingLastPathComponent];
1051         [[NSUserDefaults standardUserDefaults] setObject:sourceDirectory forKey:@"LastSourceDirectory"];
1052         /* we order out sheet, which is the browse window as we need to open
1053          * the title selection sheet right away
1054          */
1055         [sheet orderOut: self];
1056         
1057         if (sender == fOpenSourceTitleMMenu)
1058         {
1059             /* We put the chosen source path in the source display text field for the
1060              * source title selection sheet in which the user specifies the specific title to be
1061              * scanned  as well as the short source name in fSrcDsplyNameTitleScan just for display
1062              * purposes in the title panel
1063              */
1064             /* Full Path */
1065             [fScanSrcTitlePathField setStringValue:scanPath];
1066             NSString *displayTitlescanSourceName;
1067
1068             if ([[scanPath lastPathComponent] isEqualToString: @"VIDEO_TS"])
1069             {
1070                 /* If VIDEO_TS Folder is chosen, choose its parent folder for the source display name
1071                  we have to use the title->dvd value so we get the proper name of the volume if a physical dvd is the source*/
1072                 displayTitlescanSourceName = [[scanPath stringByDeletingLastPathComponent] lastPathComponent];
1073             }
1074             else
1075             {
1076                 /* if not the VIDEO_TS Folder, we can assume the chosen folder is the source name */
1077                 displayTitlescanSourceName = [scanPath lastPathComponent];
1078             }
1079             /* we set the source display name in the title selection dialogue */
1080             [fSrcDsplyNameTitleScan setStringValue:displayTitlescanSourceName];
1081             /* we set the attempted scans display name for main window to displayTitlescanSourceName*/
1082             browsedSourceDisplayName = [displayTitlescanSourceName retain];
1083             /* We show the actual sheet where the user specifies the title to be scanned
1084              * as we are going to do a title specific scan
1085              */
1086             [self showSourceTitleScanPanel:nil];
1087         }
1088         else
1089         {
1090             /* We are just doing a standard full source scan, so we specify "0" to libhb */
1091             NSString *path = [[sheet filenames] objectAtIndex: 0];
1092             
1093             /* We check to see if the chosen file at path is a package */
1094             if ([[NSWorkspace sharedWorkspace] isFilePackageAtPath:path])
1095             {
1096                 [self writeToActivityLog: "trying to open a package at: %s", [path UTF8String]];
1097                 /* We check to see if this is an .eyetv package */
1098                 if ([[path pathExtension] isEqualToString: @"eyetv"])
1099                 {
1100                     [self writeToActivityLog:"trying to open eyetv package"];
1101                     /* We're looking at an EyeTV package - try to open its enclosed
1102                      .mpg media file */
1103                      browsedSourceDisplayName = [[[path stringByDeletingPathExtension] lastPathComponent] retain];
1104                     NSString *mpgname;
1105                     int n = [[path stringByAppendingString: @"/"]
1106                              completePathIntoString: &mpgname caseSensitive: NO
1107                              matchesIntoArray: nil
1108                              filterTypes: [NSArray arrayWithObject: @"mpg"]];
1109                     if (n > 0)
1110                     {
1111                         /* Found an mpeg inside the eyetv package, make it our scan path 
1112                         and call performScan on the enclosed mpeg */
1113                         path = mpgname;
1114                         [self writeToActivityLog:"found mpeg in eyetv package"];
1115                         [self performScan:path scanTitleNum:0];
1116                     }
1117                     else
1118                     {
1119                         /* We did not find an mpeg file in our package, so we do not call performScan */
1120                         [self writeToActivityLog:"no valid mpeg in eyetv package"];
1121                     }
1122                 }
1123                 /* We check to see if this is a .dvdmedia package */
1124                 else if ([[path pathExtension] isEqualToString: @"dvdmedia"])
1125                 {
1126                     /* path IS a package - but dvdmedia packages can be treaded like normal directories */
1127                     browsedSourceDisplayName = [[[path stringByDeletingPathExtension] lastPathComponent] retain];
1128                     [self writeToActivityLog:"trying to open dvdmedia package"];
1129                     [self performScan:path scanTitleNum:0];
1130                 }
1131                 else
1132                 {
1133                     /* The package is not an eyetv package, so we do not call performScan */
1134                     [self writeToActivityLog:"unable to open package"];
1135                 }
1136             }
1137             else // path is not a package, so we treat it as a dvd parent folder or VIDEO_TS folder
1138             {
1139                 /* path is not a package, so we call perform scan directly on our file */
1140                 if ([[path lastPathComponent] isEqualToString: @"VIDEO_TS"])
1141                 {
1142                     [self writeToActivityLog:"trying to open video_ts folder (video_ts folder chosen)"];
1143                     /* If VIDEO_TS Folder is chosen, choose its parent folder for the source display name*/
1144                     browsedSourceDisplayName = [[[path stringByDeletingLastPathComponent] lastPathComponent] retain];
1145                 }
1146                 else
1147                 {
1148                     [self writeToActivityLog:"trying to open video_ts folder (parent directory chosen)"];
1149                     /* if not the VIDEO_TS Folder, we can assume the chosen folder is the source name */
1150                     /* make sure we remove any path extension as this can also be an '.mpg' file */
1151                     browsedSourceDisplayName = [[path lastPathComponent] retain];
1152                 }
1153                 [self performScan:path scanTitleNum:0];
1154             }
1155
1156         }
1157
1158     }
1159     else // User clicked Cancel in browse window
1160     {
1161         /* if we have a title loaded up */
1162         if ([[fSrcDVD2Field stringValue] length] > 0 && SuccessfulScan)
1163         {
1164             [self enableUI: YES];
1165         }
1166     }
1167 }
1168
1169 /* Here we open the title selection sheet where we can specify an exact title to be scanned */
1170 - (IBAction) showSourceTitleScanPanel: (id) sender
1171 {
1172     /* We default the title number to be scanned to "0" which results in a full source scan, unless the
1173     * user changes it
1174     */
1175     [fScanSrcTitleNumField setStringValue: @"0"];
1176         /* Show the panel */
1177         [NSApp beginSheet:fScanSrcTitlePanel modalForWindow:fWindow modalDelegate:nil didEndSelector:NULL contextInfo:NULL];
1178 }
1179
1180 - (IBAction) closeSourceTitleScanPanel: (id) sender
1181 {
1182     [NSApp endSheet: fScanSrcTitlePanel];
1183     [fScanSrcTitlePanel orderOut: self];
1184
1185     if(sender == fScanSrcTitleOpenButton)
1186     {
1187         /* We setup the scan status in the main window to indicate a source title scan */
1188         [fSrcDVD2Field setStringValue: @"Opening a new source title ..."];
1189                 [fScanIndicator setHidden: NO];
1190         [fScanIndicator setIndeterminate: YES];
1191         [fScanIndicator startAnimation: nil];
1192                 
1193         /* We use the performScan method to actually perform the specified scan passing the path and the title
1194             * to be scanned
1195             */
1196         [self performScan:[fScanSrcTitlePathField stringValue] scanTitleNum:[fScanSrcTitleNumField intValue]];
1197     }
1198 }
1199
1200 /* Here we actually tell hb_scan to perform the source scan, using the path to source and title number*/
1201 - (void) performScan:(NSString *) scanPath scanTitleNum: (int) scanTitleNum
1202 {
1203     /* use a bool to determine whether or not we can decrypt using vlc */
1204     BOOL cancelScanDecrypt = 0;
1205     NSString *path = scanPath;
1206     HBDVDDetector *detector = [HBDVDDetector detectorForPath:path];
1207
1208     // Notify ChapterTitles that there's no title
1209     [fChapterTitlesDelegate resetWithTitle:nil];
1210     [fChapterTable reloadData];
1211
1212     if( [detector isVideoDVD] )
1213     {
1214         // The chosen path was actually on a DVD, so use the raw block
1215         // device path instead.
1216         path = [detector devicePath];
1217         [self writeToActivityLog: "trying to open a physical dvd at: %s", [scanPath UTF8String]];
1218
1219         /* lets check for vlc here to make sure we have a dylib available to use for decrypting */
1220         NSString *vlcPath = @"/Applications/VLC.app";
1221         NSFileManager * fileManager = [NSFileManager defaultManager];
1222             if ([fileManager fileExistsAtPath:vlcPath] == 0) 
1223             {
1224             /*vlc not found in /Applications so we set the bool to cancel scanning to 1 */
1225             cancelScanDecrypt = 1;
1226             [self writeToActivityLog: "VLC app not found for decrypting physical dvd"];
1227             int status;
1228             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");
1229             [NSApp requestUserAttention:NSCriticalRequest];
1230             
1231             if (status == NSAlertDefaultReturn)
1232             {
1233                 /* User chose to go download vlc (as they rightfully should) so we send them to the vlc site */
1234                 [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://www.videolan.org/"]];
1235             }
1236             else if (status == NSAlertAlternateReturn)
1237             {
1238             /* User chose to cancel the scan */
1239             [self writeToActivityLog: "cannot open physical dvd , scan cancelled"];
1240             }
1241             else
1242             {
1243             /* 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 */
1244             cancelScanDecrypt = 0;
1245             [self writeToActivityLog: "user overrode vlc warning -trying to open physical dvd without decryption"];
1246             }
1247
1248         }
1249         else
1250         {
1251             /* VLC was found in /Applications so all is well, we can carry on using vlc's libdvdcss.dylib for decrypting if needed */
1252             [self writeToActivityLog: "VLC app found for decrypting physical dvd"];
1253         }
1254     }
1255
1256     if (cancelScanDecrypt == 0)
1257     {
1258         /* we actually pass the scan off to libhb here */
1259         /* If there is no title number passed to scan, we use "0"
1260          * which causes the default behavior of a full source scan
1261          */
1262         if (!scanTitleNum)
1263         {
1264             scanTitleNum = 0;
1265         }
1266         if (scanTitleNum > 0)
1267         {
1268             [self writeToActivityLog: "scanning specifically for title: %d", scanTitleNum];
1269         }
1270
1271         hb_scan( fHandle, [path UTF8String], scanTitleNum );
1272         [fSrcDVD2Field setStringValue:@"Scanning new source ..."];
1273     }
1274     else
1275     {
1276             /* if we have a title loaded up */
1277         if ([[fSrcDVD2Field stringValue] length] > 0 && SuccessfulScan)
1278         {
1279             [self enableUI: YES];
1280         }
1281     }
1282 }
1283
1284 - (IBAction) showNewScan:(id)sender
1285 {
1286     hb_list_t  * list;
1287         hb_title_t * title;
1288         int indxpri=0;    // Used to search the longuest title (default in combobox)
1289         int longuestpri=0; // Used to search the longuest title (default in combobox)
1290
1291         list = hb_get_titles( fHandle );
1292
1293         if( !hb_list_count( list ) )
1294         {
1295                 /* We display a message if a valid dvd source was not chosen */
1296                 [fSrcDVD2Field setStringValue: @"No Valid Source Found"];
1297         SuccessfulScan = NO;
1298
1299         // Notify ChapterTitles that there's no title
1300         [fChapterTitlesDelegate resetWithTitle:nil];
1301         [fChapterTable reloadData];
1302         }
1303         else
1304         {
1305         /* We increment the successful scancount here by one,
1306         which we use at the end of this function to tell the gui
1307         if this is the first successful scan since launch and whether
1308         or not we should set all settings to the defaults */
1309
1310         currentSuccessfulScanCount++;
1311
1312         [[fWindow toolbar] validateVisibleItems];
1313
1314                 [fSrcTitlePopUp removeAllItems];
1315                 for( int i = 0; i < hb_list_count( list ); i++ )
1316                 {
1317                         title = (hb_title_t *) hb_list_item( list, i );
1318
1319             currentSource = [NSString stringWithUTF8String: title->name];
1320
1321             /*Set DVD Name at top of window with the browsedSourceDisplayName grokked right before -performScan */
1322                         [fSrcDVD2Field setStringValue:browsedSourceDisplayName];
1323
1324                         /* Use the dvd name in the default output field here
1325                                 May want to add code to remove blank spaces for some dvd names*/
1326                         /* Check to see if the last destination has been set,use if so, if not, use Desktop */
1327             if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastDestinationDirectory"])
1328                         {
1329                                 [fDstFile2Field setStringValue: [NSString stringWithFormat:
1330                                         @"%@/%@.mp4", [[NSUserDefaults standardUserDefaults] stringForKey:@"LastDestinationDirectory"],[browsedSourceDisplayName stringByDeletingPathExtension]]];
1331                         }
1332                         else
1333                         {
1334                                 [fDstFile2Field setStringValue: [NSString stringWithFormat:
1335                                         @"%@/Desktop/%@.mp4", NSHomeDirectory(),[browsedSourceDisplayName stringByDeletingPathExtension]]];
1336                         }
1337                         
1338                         if (longuestpri < title->hours*60*60 + title->minutes *60 + title->seconds)
1339                         {
1340                                 longuestpri=title->hours*60*60 + title->minutes *60 + title->seconds;
1341                                 indxpri=i;
1342                         }
1343                         /* Leave this here for now for reference. Keep it commented out or the macgui will crash in a
1344             * fiery wreck if you change sources.
1345             */
1346                         //[self formatPopUpChanged:NULL];
1347                         
1348             [fSrcTitlePopUp addItemWithTitle: [NSString
1349                 stringWithFormat: @"%d - %02dh%02dm%02ds",
1350                 title->index, title->hours, title->minutes,
1351                 title->seconds]];
1352                 }
1353
1354                 // Select the longuest title
1355                 [fSrcTitlePopUp selectItemAtIndex: indxpri];
1356                 [self titlePopUpChanged:nil];
1357
1358         SuccessfulScan = YES;
1359                 [self enableUI: YES];
1360
1361                 /* if its the initial successful scan after awakeFromNib */
1362         if (currentSuccessfulScanCount == 1)
1363         {
1364             [self selectDefaultPreset:nil];
1365             /* if Deinterlace upon launch is specified in the prefs, then set to 1 for "Fast",
1366              if not, then set to 0 for none */
1367             if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultDeinterlaceOn"] > 0)
1368             {
1369                 [fPictureController setDeinterlace:1];
1370             }
1371             else
1372             {
1373                 [fPictureController setDeinterlace:0];
1374             }
1375             /* lets set Denoise to index 0 or "None" since this is the first scan */
1376             [fPictureController setDenoise:0];
1377             
1378             [fPictureController setInitialPictureFilters];
1379         }
1380         
1381         }
1382 }
1383
1384
1385 #pragma mark -
1386 #pragma mark New Output Destination
1387
1388 - (IBAction) browseFile: (id) sender
1389 {
1390     /* Open a panel to let the user choose and update the text field */
1391     NSSavePanel * panel = [NSSavePanel savePanel];
1392         /* We get the current file name and path from the destination field here */
1393         [panel beginSheetForDirectory: [[fDstFile2Field stringValue] stringByDeletingLastPathComponent] file: [[fDstFile2Field stringValue] lastPathComponent]
1394                                    modalForWindow: fWindow modalDelegate: self
1395                                    didEndSelector: @selector( browseFileDone:returnCode:contextInfo: )
1396                                           contextInfo: NULL];
1397 }
1398
1399 - (void) browseFileDone: (NSSavePanel *) sheet
1400     returnCode: (int) returnCode contextInfo: (void *) contextInfo
1401 {
1402     if( returnCode == NSOKButton )
1403     {
1404         [fDstFile2Field setStringValue: [sheet filename]];
1405     }
1406 }
1407
1408
1409 #pragma mark -
1410 #pragma mark Main Window Control
1411
1412 - (IBAction) openMainWindow: (id) sender
1413 {
1414     [fWindow  makeKeyAndOrderFront:nil];
1415 }
1416
1417 - (BOOL) windowShouldClose: (id) sender
1418 {
1419     return YES;
1420 }
1421
1422 - (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag
1423 {
1424     if( !flag ) {
1425         [fWindow  makeKeyAndOrderFront:nil];
1426                 
1427         return YES;
1428     }
1429     
1430     return NO;
1431 }
1432
1433 #pragma mark -
1434 #pragma mark Job Handling
1435
1436
1437 - (void) prepareJob
1438 {
1439     hb_list_t  * list  = hb_get_titles( fHandle );
1440     hb_title_t * title = (hb_title_t *) hb_list_item( list,
1441             [fSrcTitlePopUp indexOfSelectedItem] );
1442     hb_job_t * job = title->job;
1443     hb_audio_config_t * audio;
1444
1445     /* Chapter selection */
1446     job->chapter_start = [fSrcChapterStartPopUp indexOfSelectedItem] + 1;
1447     job->chapter_end   = [fSrcChapterEndPopUp   indexOfSelectedItem] + 1;
1448         
1449     /* Format (Muxer) and Video Encoder */
1450     job->mux = [[fDstFormatPopUp selectedItem] tag];
1451     job->vcodec = [[fVidEncoderPopUp selectedItem] tag];
1452
1453
1454     /* If mpeg-4, then set mpeg-4 specific options like chapters and > 4gb file sizes */
1455         if ([fDstFormatPopUp indexOfSelectedItem] == 0)
1456         {
1457         /* We set the largeFileSize (64 bit formatting) variable here to allow for > 4gb files based on the format being
1458                 mpeg4 and the checkbox being checked 
1459                 *Note: this will break compatibility with some target devices like iPod, etc.!!!!*/
1460                 if ([fDstMp4LargeFileCheck state] == NSOnState)
1461                 {
1462                         job->largeFileSize = 1;
1463                 }
1464                 else
1465                 {
1466                         job->largeFileSize = 0;
1467                 }
1468         /* We set http optimized mp4 here */
1469         if ([fDstMp4HttpOptFileCheck state] == NSOnState)
1470                 {
1471         job->mp4_optimize = 1;
1472         }
1473         else
1474         {
1475         job->mp4_optimize = 0;
1476         }
1477     }
1478         if ([fDstFormatPopUp indexOfSelectedItem] == 0 || [fDstFormatPopUp indexOfSelectedItem] == 1)
1479         {
1480           /* We set the chapter marker extraction here based on the format being
1481                 mpeg4 or mkv and the checkbox being checked */
1482                 if ([fCreateChapterMarkers state] == NSOnState)
1483                 {
1484                         job->chapter_markers = 1;
1485                 }
1486                 else
1487                 {
1488                         job->chapter_markers = 0;
1489                 }
1490         }
1491         
1492     if( job->vcodec & HB_VCODEC_X264 )
1493     {
1494                 if ([fDstMp4iPodFileCheck state] == NSOnState)
1495             {
1496             job->ipod_atom = 1;
1497                 }
1498         else
1499         {
1500         job->ipod_atom = 0;
1501         }
1502                 
1503                 /* Set this flag to switch from Constant Quantizer(default) to Constant Rate Factor Thanks jbrjake
1504          Currently only used with Constant Quality setting*/
1505                 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultCrf"] > 0 && [fVidQualityMatrix selectedRow] == 2)
1506                 {
1507                 job->crf = 1;
1508                 }
1509                 
1510                 /* Below Sends x264 options to the core library if x264 is selected*/
1511                 /* Lets use this as per Nyx, Thanks Nyx!*/
1512                 job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */
1513                 /* Turbo first pass if two pass and Turbo First pass is selected */
1514                 if( [fVidTwoPassCheck state] == NSOnState && [fVidTurboPassCheck state] == NSOnState )
1515                 {
1516                         /* pass the "Turbo" string to be appended to the existing x264 opts string into a variable for the first pass */
1517                         NSString *firstPassOptStringTurbo = @":ref=1:subme=1:me=dia:analyse=none:trellis=0:no-fast-pskip=0:8x8dct=0:weightb=0";
1518                         /* append the "Turbo" string variable to the existing opts string.
1519              Note: the "Turbo" string must be appended, not prepended to work properly*/
1520                         NSString *firstPassOptStringCombined = [[fAdvancedOptions optionsString] stringByAppendingString:firstPassOptStringTurbo];
1521                         strcpy(job->x264opts, [firstPassOptStringCombined UTF8String]);
1522                 }
1523                 else
1524                 {
1525                         strcpy(job->x264opts, [[fAdvancedOptions optionsString] UTF8String]);
1526                 }
1527         
1528     }
1529
1530     /* Video settings */
1531     if( [fVidRatePopUp indexOfSelectedItem] > 0 )
1532     {
1533         job->vrate      = 27000000;
1534         job->vrate_base = hb_video_rates[[fVidRatePopUp
1535             indexOfSelectedItem]-1].rate;
1536         /* We are not same as source so we set job->cfr to 1 
1537          * to enable constant frame rate since user has specified
1538          * a specific framerate*/
1539         job->cfr = 1;
1540     }
1541     else
1542     {
1543         job->vrate      = title->rate;
1544         job->vrate_base = title->rate_base;
1545         /* We are same as source so we set job->cfr to 0 
1546          * to enable true same as source framerate */
1547         job->cfr = 0;
1548     }
1549
1550     switch( [fVidQualityMatrix selectedRow] )
1551     {
1552         case 0:
1553             /* Target size.
1554                Bitrate should already have been calculated and displayed
1555                in fVidBitrateField, so let's just use it */
1556         case 1:
1557             job->vquality = -1.0;
1558             job->vbitrate = [fVidBitrateField intValue];
1559             break;
1560         case 2:
1561             job->vquality = [fVidQualitySlider floatValue];
1562             job->vbitrate = 0;
1563             break;
1564     }
1565
1566     job->grayscale = ( [fVidGrayscaleCheck state] == NSOnState );
1567
1568     /* Subtitle settings */
1569     job->subtitle = [fSubPopUp indexOfSelectedItem] - 2;
1570
1571     /* Audio tracks and mixdowns */
1572     /* Lets make sure there arent any erroneous audio tracks in the job list, so lets make sure its empty*/
1573     int audiotrack_count = hb_list_count(job->list_audio);
1574     for( int i = 0; i < audiotrack_count;i++)
1575     {
1576         hb_audio_t * temp_audio = (hb_audio_t*) hb_list_item( job->list_audio, 0 );
1577         hb_list_rem(job->list_audio, temp_audio);
1578     }
1579     /* Now lets add our new tracks to the audio list here */
1580     if ([fAudLang1PopUp indexOfSelectedItem] > 0)
1581     {
1582         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
1583         hb_audio_config_init(audio);
1584         audio->in.track = [fAudLang1PopUp indexOfSelectedItem] - 1;
1585         /* We go ahead and assign values to our audio->out.<properties> */
1586         audio->out.track = [fAudLang1PopUp indexOfSelectedItem] - 1;
1587         audio->out.codec = [[fAudTrack1CodecPopUp selectedItem] tag];
1588         audio->out.mixdown = [[fAudTrack1MixPopUp selectedItem] tag];
1589         audio->out.bitrate = [[fAudTrack1BitratePopUp selectedItem] tag];
1590         audio->out.samplerate = [[fAudTrack1RatePopUp selectedItem] tag];
1591         audio->out.dynamic_range_compression = [fAudTrack1DrcField floatValue];
1592         
1593         hb_audio_add( job, audio );
1594         free(audio);
1595     }  
1596     if ([fAudLang2PopUp indexOfSelectedItem] > 0)
1597     {
1598         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
1599         hb_audio_config_init(audio);
1600         audio->in.track = [fAudLang2PopUp indexOfSelectedItem] - 1;
1601         /* We go ahead and assign values to our audio->out.<properties> */
1602         audio->out.track = [fAudLang2PopUp indexOfSelectedItem] - 1;
1603         audio->out.codec = [[fAudTrack2CodecPopUp selectedItem] tag];
1604         audio->out.mixdown = [[fAudTrack2MixPopUp selectedItem] tag];
1605         audio->out.bitrate = [[fAudTrack2BitratePopUp selectedItem] tag];
1606         audio->out.samplerate = [[fAudTrack2RatePopUp selectedItem] tag];
1607         audio->out.dynamic_range_compression = [fAudTrack2DrcField floatValue];
1608         
1609         hb_audio_add( job, audio );
1610         free(audio);
1611         
1612     }
1613     
1614     if ([fAudLang3PopUp indexOfSelectedItem] > 0)
1615     {
1616         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
1617         hb_audio_config_init(audio);
1618         audio->in.track = [fAudLang3PopUp indexOfSelectedItem] - 1;
1619         /* We go ahead and assign values to our audio->out.<properties> */
1620         audio->out.track = [fAudLang3PopUp indexOfSelectedItem] - 1;
1621         audio->out.codec = [[fAudTrack3CodecPopUp selectedItem] tag];
1622         audio->out.mixdown = [[fAudTrack3MixPopUp selectedItem] tag];
1623         audio->out.bitrate = [[fAudTrack3BitratePopUp selectedItem] tag];
1624         audio->out.samplerate = [[fAudTrack3RatePopUp selectedItem] tag];
1625         audio->out.dynamic_range_compression = [fAudTrack3DrcField floatValue];
1626         
1627         hb_audio_add( job, audio );
1628         free(audio);
1629         
1630     }
1631
1632     if ([fAudLang4PopUp indexOfSelectedItem] > 0)
1633     {
1634         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
1635         hb_audio_config_init(audio);
1636         audio->in.track = [fAudLang4PopUp indexOfSelectedItem] - 1;
1637         /* We go ahead and assign values to our audio->out.<properties> */
1638         audio->out.track = [fAudLang4PopUp indexOfSelectedItem] - 1;
1639         audio->out.codec = [[fAudTrack4CodecPopUp selectedItem] tag];
1640         audio->out.mixdown = [[fAudTrack4MixPopUp selectedItem] tag];
1641         audio->out.bitrate = [[fAudTrack4BitratePopUp selectedItem] tag];
1642         audio->out.samplerate = [[fAudTrack4RatePopUp selectedItem] tag];
1643         audio->out.dynamic_range_compression = [fAudTrack4DrcField floatValue];
1644         
1645         hb_audio_add( job, audio );
1646         free(audio);
1647         
1648     }
1649
1650     /* set vfr according to the Picture Window */
1651     if ([fPictureController vfr])
1652     {
1653         job->vfr = 1;
1654     }
1655     else
1656     {
1657         job->vfr = 0;
1658     }
1659     
1660     /* Filters */ 
1661     job->filters = hb_list_init();
1662     
1663     /* Now lets call the filters if applicable.
1664     * The order of the filters is critical
1665     */
1666     
1667         /* Detelecine */
1668     if ([fPictureController detelecine])
1669     {
1670         hb_list_add( job->filters, &hb_filter_detelecine );
1671     }
1672     
1673     /* Decomb */
1674     if ([fPictureController decomb] > 0)
1675     {
1676         /* Run old deinterlacer fd by default */
1677         //fPicSettingDecomb
1678         hb_filter_decomb.settings = (char *) [[fPicSettingDecomb stringValue] UTF8String];
1679         //hb_filter_decomb.settings = "4:10:15:9:10:35:9"; // <-- jbrjakes recommended parameters as of 5/23/08
1680         hb_list_add( job->filters, &hb_filter_decomb );
1681     }
1682
1683     
1684     /* Deinterlace */
1685     if ([fPictureController deinterlace] == 1)
1686     {
1687         /* Run old deinterlacer fd by default */
1688         hb_filter_deinterlace.settings = "-1"; 
1689         hb_list_add( job->filters, &hb_filter_deinterlace );
1690     }
1691     else if ([fPictureController deinterlace] == 2)
1692     {
1693         /* Yadif mode 0 (without spatial deinterlacing.) */
1694         hb_filter_deinterlace.settings = "2"; 
1695         hb_list_add( job->filters, &hb_filter_deinterlace );            
1696     }
1697     else if ([fPictureController deinterlace] == 3)
1698     {
1699         /* Yadif (with spatial deinterlacing) */
1700         hb_filter_deinterlace.settings = "0"; 
1701         hb_list_add( job->filters, &hb_filter_deinterlace );            
1702     }
1703         
1704     /* Denoise */
1705         if ([fPictureController denoise] == 1) // Weak in popup
1706         {
1707                 hb_filter_denoise.settings = "2:1:2:3"; 
1708         hb_list_add( job->filters, &hb_filter_denoise );        
1709         }
1710         else if ([fPictureController denoise] == 2) // Medium in popup
1711         {
1712                 hb_filter_denoise.settings = "3:2:2:3"; 
1713         hb_list_add( job->filters, &hb_filter_denoise );        
1714         }
1715         else if ([fPictureController denoise] == 3) // Strong in popup
1716         {
1717                 hb_filter_denoise.settings = "7:7:5:5"; 
1718         hb_list_add( job->filters, &hb_filter_denoise );        
1719         }
1720     
1721     /* Deblock  (uses pp7 default) */
1722     if ([fPictureController deblock])
1723     {
1724         hb_list_add( job->filters, &hb_filter_deblock );
1725     }
1726
1727 }
1728
1729
1730
1731 /* addToQueue: puts up an alert before ultimately calling doAddToQueue
1732 */
1733 - (IBAction) addToQueue: (id) sender
1734 {
1735         /* We get the destination directory from the destination field here */
1736         NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
1737         /* We check for a valid destination here */
1738         if ([[NSFileManager defaultManager] fileExistsAtPath:destinationDirectory] == 0) 
1739         {
1740                 NSRunAlertPanel(@"Warning!", @"This is not a valid destination directory!", @"OK", nil, nil);
1741         return;
1742         }
1743
1744     /* We check for duplicate name here */
1745         if( [[NSFileManager defaultManager] fileExistsAtPath:
1746             [fDstFile2Field stringValue]] )
1747     {
1748         NSBeginCriticalAlertSheet( NSLocalizedString( @"File already exists", @"" ),
1749             NSLocalizedString( @"Cancel", @"" ), NSLocalizedString( @"Overwrite", @"" ), nil, fWindow, self,
1750             @selector( overwriteAddToQueueAlertDone:returnCode:contextInfo: ),
1751             NULL, NULL, [NSString stringWithFormat:
1752             NSLocalizedString( @"Do you want to overwrite %@?", @"" ),
1753             [fDstFile2Field stringValue]] );
1754         // overwriteAddToQueueAlertDone: will be called when the alert is dismissed.
1755     }
1756
1757     // Warn if another pending job in the queue has the same destination path
1758     else if ( ([fQueueController pendingJobGroupWithDestinationPath:[fDstFile2Field stringValue]] != nil)
1759             || ([[[fQueueController currentJobGroup] destinationPath] isEqualToString: [fDstFile2Field stringValue]]) )
1760     {
1761         NSBeginCriticalAlertSheet( NSLocalizedString( @"Another queued encode has specified the same destination.", @"" ),
1762             NSLocalizedString( @"Cancel", @"" ), NSLocalizedString( @"Overwrite", @"" ), nil, fWindow, self,
1763             @selector( overwriteAddToQueueAlertDone:returnCode:contextInfo: ),
1764             NULL, NULL, [NSString stringWithFormat:
1765             NSLocalizedString( @"Do you want to overwrite %@?", @"" ),
1766             [fDstFile2Field stringValue]] );
1767         // overwriteAddToQueueAlertDone: will be called when the alert is dismissed.
1768     }
1769     
1770     else
1771     {
1772         [self doAddToQueue];
1773     }
1774 }
1775
1776 /* overwriteAddToQueueAlertDone: called from the alert posted by addToQueue that asks
1777    the user if they want to overwrite an exiting movie file.
1778 */
1779 - (void) overwriteAddToQueueAlertDone: (NSWindow *) sheet
1780     returnCode: (int) returnCode contextInfo: (void *) contextInfo
1781 {
1782     if( returnCode == NSAlertAlternateReturn )
1783         [self doAddToQueue];
1784 }
1785
1786 - (void) doAddToQueue
1787 {
1788     hb_list_t  * list  = hb_get_titles( fHandle );
1789     hb_title_t * title = (hb_title_t *) hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
1790     hb_job_t * job = title->job;
1791
1792     // Create a Queue Controller job group. Each job that we submit to libhb will also
1793     // get added to the job group so that the queue can track the jobs.
1794    HBJobGroup * jobGroup = [HBJobGroup jobGroup];
1795     // The job group can maintain meta data that libhb can not...
1796    [jobGroup setPresetName: [fPresetSelectedDisplay stringValue]];
1797
1798     // Job groups require that each job within the group be assigned a unique id so
1799     // that the queue can xref between itself and the private jobs that libhb
1800     // maintains. The ID is composed a group id number and a "sequence" number. libhb
1801     // does not use this id.
1802    static int jobGroupID = 0;
1803    jobGroupID++;
1804     
1805     // A sequence number, starting at zero, is used to identifiy to each pass. This is
1806     // used by the queue UI to determine if a pass if the first pass of an encode.
1807     int sequenceNum = -1;
1808     
1809     [self prepareJob];
1810
1811     /* Destination file */
1812     job->file = [[fDstFile2Field stringValue] UTF8String];
1813
1814     if( [fSubForcedCheck state] == NSOnState )
1815         job->subtitle_force = 1;
1816     else
1817         job->subtitle_force = 0;
1818
1819     /*
1820     * subtitle of -1 is a scan
1821     */
1822     if( job->subtitle == -1 )
1823     {
1824         char *x264opts_tmp;
1825
1826         /*
1827         * When subtitle scan is enabled do a fast pre-scan job
1828         * which will determine which subtitles to enable, if any.
1829         */
1830         job->pass = -1;
1831         x264opts_tmp = job->x264opts;
1832         job->subtitle = -1;
1833
1834         job->x264opts = NULL;
1835
1836         job->indepth_scan = 1;  
1837
1838         job->select_subtitle = (hb_subtitle_t**)malloc(sizeof(hb_subtitle_t*));
1839         *(job->select_subtitle) = NULL;
1840
1841         /*
1842         * Add the pre-scan job
1843         */
1844         job->sequence_id = MakeJobID(jobGroupID, ++sequenceNum);
1845         hb_add( fHandle, job );
1846         [jobGroup addJob:[HBJob jobWithLibhbJob:job]];     // add this pass to the job group
1847
1848         job->x264opts = x264opts_tmp;
1849     }
1850     else
1851         job->select_subtitle = NULL;
1852
1853     /* No subtitle were selected, so reset the subtitle to -1 (which before
1854     * this point meant we were scanning
1855     */
1856     if( job->subtitle == -2 )
1857         job->subtitle = -1;
1858
1859     if( [fVidTwoPassCheck state] == NSOnState )
1860     {
1861         hb_subtitle_t **subtitle_tmp = job->select_subtitle;
1862         job->indepth_scan = 0;
1863
1864         /*
1865          * Do not autoselect subtitles on the first pass of a two pass
1866          */
1867         job->select_subtitle = NULL;
1868         
1869         job->pass = 1;
1870         job->sequence_id = MakeJobID(jobGroupID, ++sequenceNum);
1871         hb_add( fHandle, job );
1872         [jobGroup addJob:[HBJob jobWithLibhbJob:job]];     // add this pass to the job group
1873
1874         job->pass = 2;
1875         job->sequence_id = MakeJobID(jobGroupID, ++sequenceNum);
1876
1877         job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */  
1878         strcpy(job->x264opts, [[fAdvancedOptions optionsString] UTF8String]);
1879
1880         job->select_subtitle = subtitle_tmp;
1881
1882         hb_add( fHandle, job );
1883         [jobGroup addJob:[HBJob jobWithLibhbJob:job]];     // add this pass to the job group
1884     }
1885     else
1886     {
1887         job->indepth_scan = 0;
1888         job->pass = 0;
1889         job->sequence_id = MakeJobID(jobGroupID, ++sequenceNum);
1890         hb_add( fHandle, job );
1891         [jobGroup addJob:[HBJob jobWithLibhbJob:job]];     // add this pass to the job group
1892     }
1893         
1894     NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
1895         [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];
1896         
1897     // Let the queue controller know about the job group
1898     [fQueueController addJobGroup:jobGroup];
1899 }
1900
1901 /* Rip: puts up an alert before ultimately calling doRip
1902 */
1903 - (IBAction) Rip: (id) sender
1904 {
1905     /* Rip or Cancel ? */
1906     hb_state_t s;
1907     hb_get_state2( fHandle, &s );
1908
1909     if(s.state == HB_STATE_WORKING || s.state == HB_STATE_PAUSED)
1910         {
1911         [self Cancel: sender];
1912         return;
1913     }
1914     
1915     // If there are jobs in the queue, then this is a rip the queue
1916     
1917     if (hb_count( fHandle ) > 0)
1918     {
1919         [self doRip];
1920         return;
1921     }
1922
1923     // Before adding jobs to the queue, check for a valid destination.
1924
1925     NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
1926     if ([[NSFileManager defaultManager] fileExistsAtPath:destinationDirectory] == 0) 
1927     {
1928         NSRunAlertPanel(@"Warning!", @"This is not a valid destination directory!", @"OK", nil, nil);
1929         return;
1930     }
1931
1932     /* We check for duplicate name here */
1933     if( [[NSFileManager defaultManager] fileExistsAtPath:[fDstFile2Field stringValue]] )
1934     {
1935         NSBeginCriticalAlertSheet( NSLocalizedString( @"File already exists", @"" ),
1936             NSLocalizedString( @"Cancel", "" ), NSLocalizedString( @"Overwrite", @"" ), nil, fWindow, self,
1937             @selector( overWriteAlertDone:returnCode:contextInfo: ),
1938             NULL, NULL, [NSString stringWithFormat:
1939             NSLocalizedString( @"Do you want to overwrite %@?", @"" ),
1940             [fDstFile2Field stringValue]] );
1941
1942         // overWriteAlertDone: will be called when the alert is dismissed. It will call doRip.
1943     }
1944     else
1945     {
1946         /* if there are no jobs in the queue, then add this one to the queue and rip
1947         otherwise, just rip the queue */
1948         if( hb_count( fHandle ) == 0)
1949         {
1950             [self doAddToQueue];
1951         }
1952
1953         NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
1954         [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];
1955         [self doRip];
1956     }
1957 }
1958
1959 /* overWriteAlertDone: called from the alert posted by Rip: that asks the user if they
1960    want to overwrite an exiting movie file.
1961 */
1962 - (void) overWriteAlertDone: (NSWindow *) sheet
1963     returnCode: (int) returnCode contextInfo: (void *) contextInfo
1964 {
1965     if( returnCode == NSAlertAlternateReturn )
1966     {
1967         /* if there are no jobs in the queue, then add this one to the queue and rip 
1968         otherwise, just rip the queue */
1969         if( hb_count( fHandle ) == 0 )
1970         {
1971             [self doAddToQueue];
1972         }
1973
1974         NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
1975         [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];
1976         [self doRip];
1977     }
1978 }
1979
1980 - (void) remindUserOfSleepOrShutdown
1981 {
1982        if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Put Computer To Sleep"])
1983        {
1984                /*Warn that computer will sleep after encoding*/
1985                int reminduser;
1986                NSBeep();
1987                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);
1988                [NSApp requestUserAttention:NSCriticalRequest];
1989                if ( reminduser == NSAlertAlternateReturn )
1990                {
1991                        [self showPreferencesWindow:nil];
1992                }
1993        }
1994        else if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Shut Down Computer"])
1995        {
1996                /*Warn that computer will shut down after encoding*/
1997                int reminduser;
1998                NSBeep();
1999                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);
2000                [NSApp requestUserAttention:NSCriticalRequest];
2001                if ( reminduser == NSAlertAlternateReturn )
2002                {
2003                        [self showPreferencesWindow:nil];
2004                }
2005        }
2006
2007 }
2008
2009
2010 - (void) doRip
2011 {
2012     /* Let libhb do the job */
2013     hb_start( fHandle );
2014         /*set the fEncodeState State */
2015         fEncodeState = 1;
2016 }
2017
2018
2019
2020
2021 //------------------------------------------------------------------------------------
2022 // Removes all jobs from the queue. Does not cancel the current processing job.
2023 //------------------------------------------------------------------------------------
2024 - (void) doDeleteQueuedJobs
2025 {
2026     hb_job_t * job;
2027     while( ( job = hb_job( fHandle, 0 ) ) )
2028         hb_rem( fHandle, job );
2029 }
2030
2031 //------------------------------------------------------------------------------------
2032 // Cancels and deletes the current job and stops libhb from processing the remaining
2033 // encodes.
2034 //------------------------------------------------------------------------------------
2035 - (void) doCancelCurrentJob
2036 {
2037     // Stop the current job. hb_stop will only cancel the current pass and then set
2038     // its state to HB_STATE_WORKDONE. It also does this asynchronously. So when we
2039     // see the state has changed to HB_STATE_WORKDONE (in updateUI), we'll delete the
2040     // remaining passes of the job and then start the queue back up if there are any
2041     // remaining jobs.
2042      
2043     [fQueueController libhbWillStop];
2044     hb_stop( fHandle );
2045     fEncodeState = 2;   // don't alert at end of processing since this was a cancel
2046     
2047 }
2048
2049 //------------------------------------------------------------------------------------
2050 // Displays an alert asking user if the want to cancel encoding of current job.
2051 // Cancel: returns immediately after posting the alert. Later, when the user
2052 // acknowledges the alert, doCancelCurrentJob is called.
2053 //------------------------------------------------------------------------------------
2054 - (IBAction)Cancel: (id)sender
2055 {
2056     if (!fHandle) return;
2057     
2058     HBJobGroup * jobGroup = [fQueueController currentJobGroup];
2059     if (!jobGroup) return;
2060
2061     NSString * alertTitle = [NSString stringWithFormat:NSLocalizedString(@"Stop encoding %@?", nil),
2062             [jobGroup name]];
2063     
2064     // Which window to attach the sheet to?
2065     NSWindow * docWindow;
2066     if ([sender respondsToSelector: @selector(window)])
2067         docWindow = [sender window];
2068     else
2069         docWindow = fWindow;
2070         
2071     NSBeginCriticalAlertSheet(
2072             alertTitle,
2073             NSLocalizedString(@"Keep Encoding", nil),
2074             nil,
2075             NSLocalizedString(@"Stop Encoding", nil),
2076             docWindow, self,
2077             nil, @selector(didDimissCancelCurrentJob:returnCode:contextInfo:), nil,
2078             NSLocalizedString(@"Your movie will be lost if you don't continue encoding.", nil));
2079     
2080     // didDimissCancelCurrentJob:returnCode:contextInfo: will be called when the dialog is dismissed
2081 }
2082
2083 - (void) didDimissCancelCurrentJob: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo
2084 {
2085     if (returnCode == NSAlertOtherReturn)
2086         [self doCancelCurrentJob];  // <- this also stops libhb
2087 }
2088
2089 - (IBAction) Pause: (id) sender
2090 {
2091     hb_state_t s;
2092     hb_get_state2( fHandle, &s );
2093
2094     if( s.state == HB_STATE_PAUSED )
2095     {
2096         hb_resume( fHandle );
2097     }
2098     else
2099     {
2100         hb_pause( fHandle );
2101     }
2102 }
2103
2104 #pragma mark -
2105 #pragma mark GUI Controls Changed Methods
2106
2107 - (IBAction) titlePopUpChanged: (id) sender
2108 {
2109     hb_list_t  * list  = hb_get_titles( fHandle );
2110     hb_title_t * title = (hb_title_t*)
2111         hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
2112                 
2113                 
2114     /* If Auto Naming is on. We create an output filename of dvd name - title number */
2115     if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultAutoNaming"] > 0)
2116         {
2117                 [fDstFile2Field setStringValue: [NSString stringWithFormat:
2118                         @"%@/%@-%d.%@", [[fDstFile2Field stringValue] stringByDeletingLastPathComponent],
2119                         browsedSourceDisplayName,
2120                           title->index,
2121                         [[fDstFile2Field stringValue] pathExtension]]]; 
2122         }
2123
2124     /* Update chapter popups */
2125     [fSrcChapterStartPopUp removeAllItems];
2126     [fSrcChapterEndPopUp   removeAllItems];
2127     for( int i = 0; i < hb_list_count( title->list_chapter ); i++ )
2128     {
2129         [fSrcChapterStartPopUp addItemWithTitle: [NSString
2130             stringWithFormat: @"%d", i + 1]];
2131         [fSrcChapterEndPopUp addItemWithTitle: [NSString
2132             stringWithFormat: @"%d", i + 1]];
2133     }
2134     [fSrcChapterStartPopUp selectItemAtIndex: 0];
2135     [fSrcChapterEndPopUp   selectItemAtIndex:
2136         hb_list_count( title->list_chapter ) - 1];
2137     [self chapterPopUpChanged:nil];
2138
2139 /* Start Get and set the initial pic size for display */
2140         hb_job_t * job = title->job;
2141         fTitle = title;
2142
2143         /* Pixel Ratio Setting */
2144         if ([[NSUserDefaults standardUserDefaults] boolForKey:@"PixelRatio"])
2145     {
2146                 job->pixel_ratio = 1 ;
2147         }
2148         else
2149         {
2150                 job->pixel_ratio = 0 ;
2151         }
2152         /*Set Source Size Field Here */
2153     [fPicSettingsSrc setStringValue: [NSString stringWithFormat: @"%d x %d", fTitle->width, fTitle->height]];
2154         
2155         /* Set Auto Crop to on upon selecting a new title */
2156     [fPictureController setAutoCrop:YES];
2157     
2158         /* We get the originial output picture width and height and put them
2159         in variables for use with some presets later on */
2160         PicOrigOutputWidth = job->width;
2161         PicOrigOutputHeight = job->height;
2162         AutoCropTop = job->crop[0];
2163         AutoCropBottom = job->crop[1];
2164         AutoCropLeft = job->crop[2];
2165         AutoCropRight = job->crop[3];
2166
2167         /* Run Through encoderPopUpChanged to see if there
2168                 needs to be any pic value modifications based on encoder settings */
2169         //[self encoderPopUpChanged: NULL];
2170         /* END Get and set the initial pic size for display */ 
2171
2172     /* Update subtitle popups */
2173     hb_subtitle_t * subtitle;
2174     [fSubPopUp removeAllItems];
2175     [fSubPopUp addItemWithTitle: @"None"];
2176     [fSubPopUp addItemWithTitle: @"Autoselect"];
2177     for( int i = 0; i < hb_list_count( title->list_subtitle ); i++ )
2178     {
2179         subtitle = (hb_subtitle_t *) hb_list_item( title->list_subtitle, i );
2180
2181         /* We cannot use NSPopUpButton's addItemWithTitle because
2182            it checks for duplicate entries */
2183         [[fSubPopUp menu] addItemWithTitle: [NSString stringWithCString:
2184             subtitle->lang] action: NULL keyEquivalent: @""];
2185     }
2186     [fSubPopUp selectItemAtIndex: 0];
2187
2188         [self subtitleSelectionChanged:nil];
2189
2190     /* Update chapter table */
2191     [fChapterTitlesDelegate resetWithTitle:title];
2192     [fChapterTable reloadData];
2193
2194    /* Lets make sure there arent any erroneous audio tracks in the job list, so lets make sure its empty*/
2195     int audiotrack_count = hb_list_count(job->list_audio);
2196     for( int i = 0; i < audiotrack_count;i++)
2197     {
2198         hb_audio_t * temp_audio = (hb_audio_t*) hb_list_item( job->list_audio, 0 );
2199         hb_list_rem(job->list_audio, temp_audio);
2200     }
2201
2202     /* Update audio popups */
2203     [self addAllAudioTracksToPopUp: fAudLang1PopUp];
2204     [self addAllAudioTracksToPopUp: fAudLang2PopUp];
2205     [self addAllAudioTracksToPopUp: fAudLang3PopUp];
2206     [self addAllAudioTracksToPopUp: fAudLang4PopUp];
2207     /* search for the first instance of our prefs default language for track 1, and set track 2 to "none" */
2208         NSString * audioSearchPrefix = [[NSUserDefaults standardUserDefaults] stringForKey:@"DefaultLanguage"];
2209         [self selectAudioTrackInPopUp: fAudLang1PopUp searchPrefixString: audioSearchPrefix selectIndexIfNotFound: 1];
2210     [self selectAudioTrackInPopUp:fAudLang2PopUp searchPrefixString:nil selectIndexIfNotFound:0];
2211     [self selectAudioTrackInPopUp:fAudLang3PopUp searchPrefixString:nil selectIndexIfNotFound:0];
2212     [self selectAudioTrackInPopUp:fAudLang4PopUp searchPrefixString:nil selectIndexIfNotFound:0];
2213
2214         /* changing the title may have changed the audio channels on offer, */
2215         /* so call audioTrackPopUpChanged for both audio tracks to update the mixdown popups */
2216         [self audioTrackPopUpChanged: fAudLang1PopUp];
2217         [self audioTrackPopUpChanged: fAudLang2PopUp];
2218     [self audioTrackPopUpChanged: fAudLang3PopUp];
2219     [self audioTrackPopUpChanged: fAudLang4PopUp];
2220     
2221     /* We repopulate the Video Framerate popup */
2222
2223     [fVidRatePopUp removeAllItems];
2224     
2225     [fVidRatePopUp addItemWithTitle:@"Same as source"];
2226     
2227         for( int i = 0; i < hb_video_rates_count; i++ )
2228     {
2229         if ([[NSString stringWithCString: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%.3f",23.976]])
2230                 {
2231                         [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@",
2232                                 [NSString stringWithCString: hb_video_rates[i].string], @" (NTSC Film)"]];
2233                 }
2234                 else if ([[NSString stringWithCString: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%d",25]])
2235                 {
2236                         [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@",
2237                                 [NSString stringWithCString: hb_video_rates[i].string], @" (PAL Film/Video)"]];
2238                 }
2239                 else if ([[NSString stringWithCString: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%.2f",29.97]])
2240                 {
2241                         [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@",
2242                                 [NSString stringWithCString: hb_video_rates[i].string], @" (NTSC Video)"]];
2243                 }
2244                 else
2245                 {
2246                         [fVidRatePopUp addItemWithTitle:
2247                                 [NSString stringWithCString: hb_video_rates[i].string]];
2248                 }
2249     }
2250     [fVidRatePopUp selectItemAtIndex: 0];
2251
2252     /* we run the picture size values through calculatePictureSizing to get all picture setting information*/
2253         [self calculatePictureSizing:nil];
2254
2255    /* lets call tableViewSelected to make sure that any preset we have selected is enforced after a title change */
2256         [self selectPreset:nil];
2257 }
2258
2259 - (IBAction) chapterPopUpChanged: (id) sender
2260 {
2261
2262         /* If start chapter popup is greater than end chapter popup,
2263         we set the end chapter popup to the same as start chapter popup */
2264         if ([fSrcChapterStartPopUp indexOfSelectedItem] > [fSrcChapterEndPopUp indexOfSelectedItem])
2265         {
2266                 [fSrcChapterEndPopUp selectItemAtIndex: [fSrcChapterStartPopUp indexOfSelectedItem]];
2267     }
2268
2269                 
2270         hb_list_t  * list  = hb_get_titles( fHandle );
2271     hb_title_t * title = (hb_title_t *)
2272         hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
2273
2274     hb_chapter_t * chapter;
2275     int64_t        duration = 0;
2276     for( int i = [fSrcChapterStartPopUp indexOfSelectedItem];
2277          i <= [fSrcChapterEndPopUp indexOfSelectedItem]; i++ )
2278     {
2279         chapter = (hb_chapter_t *) hb_list_item( title->list_chapter, i );
2280         duration += chapter->duration;
2281     }
2282     
2283     duration /= 90000; /* pts -> seconds */
2284     [fSrcDuration2Field setStringValue: [NSString stringWithFormat:
2285         @"%02lld:%02lld:%02lld", duration / 3600, ( duration / 60 ) % 60,
2286         duration % 60]];
2287
2288     [self calculateBitrate: sender];
2289 }
2290
2291 - (IBAction) formatPopUpChanged: (id) sender
2292 {
2293     NSString * string = [fDstFile2Field stringValue];
2294     int format = [fDstFormatPopUp indexOfSelectedItem];
2295     char * ext = NULL;
2296         /* Initially set the large file (64 bit formatting) output checkbox to hidden */
2297     [fDstMp4LargeFileCheck setHidden: YES];
2298     [fDstMp4HttpOptFileCheck setHidden: YES];
2299     [fDstMp4iPodFileCheck setHidden: YES];
2300     
2301     /* Update the Video Codec PopUp */
2302     /* Note: we now store the video encoder int values from common.c in the tags of each popup for easy retrieval later */
2303     [fVidEncoderPopUp removeAllItems];
2304     NSMenuItem *menuItem;
2305     /* These video encoders are available to all of our current muxers, so lets list them once here */
2306     menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"MPEG-4 (FFmpeg)" action: NULL keyEquivalent: @""];
2307     [menuItem setTag: HB_VCODEC_FFMPEG];
2308     
2309     menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"MPEG-4 (XviD)" action: NULL keyEquivalent: @""];
2310     [menuItem setTag: HB_VCODEC_XVID];
2311     switch( format )
2312     {
2313         case 0:
2314                         /*Get Default MP4 File Extension*/
2315                         if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultMpegName"] > 0)
2316                         {
2317                                 ext = "m4v";
2318                         }
2319                         else
2320                         {
2321                                 ext = "mp4";
2322                         }
2323             /* Add additional video encoders here */
2324             menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"H.264 (x264)" action: NULL keyEquivalent: @""];
2325             [menuItem setTag: HB_VCODEC_X264];
2326             /* We show the mp4 option checkboxes here since we are mp4 */
2327             [fCreateChapterMarkers setEnabled: YES];
2328                         [fDstMp4LargeFileCheck setHidden: NO];
2329                         [fDstMp4HttpOptFileCheck setHidden: NO];
2330             [fDstMp4iPodFileCheck setHidden: NO];
2331             break;
2332             
2333             case 1:
2334             ext = "mkv";
2335             /* Add additional video encoders here */
2336             menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"H.264 (x264)" action: NULL keyEquivalent: @""];
2337             [menuItem setTag: HB_VCODEC_X264];
2338             menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"VP3 (Theora)" action: NULL keyEquivalent: @""];
2339             [menuItem setTag: HB_VCODEC_THEORA];
2340             /* We enable the create chapters checkbox here */
2341                         [fCreateChapterMarkers setEnabled: YES];
2342                         break;
2343             
2344             case 2: 
2345             ext = "avi";
2346             /* Add additional video encoders here */
2347             menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"H.264 (x264)" action: NULL keyEquivalent: @""];
2348             [menuItem setTag: HB_VCODEC_X264];
2349             /* We disable the create chapters checkbox here and make sure it is unchecked*/
2350                         [fCreateChapterMarkers setEnabled: NO];
2351                         [fCreateChapterMarkers setState: NSOffState];
2352                         break;
2353             
2354             case 3:
2355             ext = "ogm";
2356             /* Add additional video encoders here */
2357             menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"VP3 (Theora)" action: NULL keyEquivalent: @""];
2358             [menuItem setTag: HB_VCODEC_THEORA];
2359             /* We disable the create chapters checkbox here and make sure it is unchecked*/
2360                         [fCreateChapterMarkers setEnabled: NO];
2361                         [fCreateChapterMarkers setState: NSOffState];
2362                         break;
2363     }
2364     [fVidEncoderPopUp selectItemAtIndex: 0];
2365     
2366     [self audioAddAudioTrackCodecs: fAudTrack1CodecPopUp];
2367     [self audioAddAudioTrackCodecs: fAudTrack2CodecPopUp];
2368     [self audioAddAudioTrackCodecs: fAudTrack3CodecPopUp];
2369     [self audioAddAudioTrackCodecs: fAudTrack4CodecPopUp];
2370     
2371     /* FIX ME: we need to restore changing the file extension as pwer */
2372     
2373     if( [string characterAtIndex: [string length] - 4] == '.' )
2374         {
2375             [fDstFile2Field setStringValue: [NSString stringWithFormat:
2376                                              @"%@.%s", [string substringToIndex: [string length] - 4],
2377                                              ext]];
2378         }
2379         else
2380         {
2381             [fDstFile2Field setStringValue: [NSString stringWithFormat:
2382                                              @"%@.%s", string, ext]];
2383         }
2384         
2385     if ( SuccessfulScan ) 
2386     {
2387         /* Add/replace to the correct extension */
2388         [self audioTrackPopUpChanged: fAudLang1PopUp];
2389         [self audioTrackPopUpChanged: fAudLang2PopUp];
2390         [self audioTrackPopUpChanged: fAudLang3PopUp];
2391         [self audioTrackPopUpChanged: fAudLang4PopUp];
2392
2393         if ( [fVidEncoderPopUp selectedItem] == nil )
2394         {
2395
2396             [fVidEncoderPopUp selectItemAtIndex:0];
2397             [self videoEncoderPopUpChanged:nil];
2398
2399             /* changing the format may mean that we can / can't offer mono or 6ch, */
2400             /* so call audioTrackPopUpChanged for both audio tracks to update the mixdown popups */
2401
2402
2403
2404             /* We call the method to properly enable/disable turbo 2 pass */
2405             [self twoPassCheckboxChanged: sender];
2406             /* We call method method to change UI to reflect whether a preset is used or not*/
2407         }
2408     }
2409
2410     /* Lets check to see if we want to auto set the .m4v extension for mp4 */
2411     [self autoSetM4vExtension: sender];
2412         [self customSettingUsed: sender];
2413 }
2414
2415
2416
2417     /* if MP4 format and [fDstCodecsPopUp indexOfSelectedItem] > 1 we know that the audio is going to be
2418          * either aac + ac3 passthru, or just ac3 passthru so we need to make sure the output file extension is m4v
2419          * otherwise Quicktime will not play it at all */
2420 - (IBAction) autoSetM4vExtension: (id) sender
2421 {
2422         /*FIX ME: for this to work, we will now have to iterate through the audio list to see if ac3 in an mp4 is chosen 
2423         * for now just comment it out.
2424         */
2425         /*
2426         if ([fDstFormatPopUp indexOfSelectedItem] == 0 && [fDstCodecsPopUp indexOfSelectedItem] > 1)
2427         {
2428             NSString *newpath = [[[fDstFile2Field stringValue] stringByDeletingPathExtension] stringByAppendingPathExtension: @"m4v"];
2429             [fDstFile2Field setStringValue: [NSString stringWithFormat:
2430                                              @"%@", newpath]];
2431         }
2432         */
2433 }
2434 /* Method to determine if we should change the UI
2435 To reflect whether or not a Preset is being used or if
2436 the user is using "Custom" settings by determining the sender*/
2437 - (IBAction) customSettingUsed: (id) sender
2438 {
2439         if ([sender stringValue])
2440         {
2441                 /* Deselect the currently selected Preset if there is one*/
2442                 [fPresetsOutlineView deselectRow:[fPresetsOutlineView selectedRow]];
2443                 /* Change UI to show "Custom" settings are being used */
2444                 [fPresetSelectedDisplay setStringValue: @"Custom"];
2445
2446                 curUserPresetChosenNum = nil;
2447         }
2448 }
2449
2450
2451 #pragma mark -
2452 #pragma mark - Video
2453
2454 - (IBAction) videoEncoderPopUpChanged: (id) sender
2455 {
2456     hb_job_t * job = fTitle->job;
2457     int videoEncoder = [[fVidEncoderPopUp selectedItem] tag];
2458     
2459     [fAdvancedOptions setHidden:YES];
2460     /* If we are using x264 then show the x264 advanced panel*/
2461     if (videoEncoder == HB_VCODEC_X264)
2462     {
2463         [fAdvancedOptions setHidden:NO];
2464         [self autoSetM4vExtension: sender];
2465     }
2466     
2467     /* We need to set loose anamorphic as available depending on whether or not the ffmpeg encoder
2468     is being used as it borks up loose anamorphic .
2469     For convenience lets use the titleOfSelected index. Probably should revisit whether or not we want
2470     to use the index itself but this is easier */
2471     if (videoEncoder == HB_VCODEC_FFMPEG)
2472     {
2473         if (job->pixel_ratio == 2)
2474         {
2475             job->pixel_ratio = 0;
2476         }
2477         [fPictureController setAllowLooseAnamorphic:NO];
2478         /* We set the iPod atom checkbox to disabled and uncheck it as its only for x264 in the mp4
2479          container. Format is taken care of in formatPopUpChanged method by hiding and unchecking
2480          anything other than MP4.
2481          */ 
2482         [fDstMp4iPodFileCheck setEnabled: NO];
2483         [fDstMp4iPodFileCheck setState: NSOffState];
2484     }
2485     else
2486     {
2487         [fPictureController setAllowLooseAnamorphic:YES];
2488         [fDstMp4iPodFileCheck setEnabled: YES];
2489     }
2490     
2491         [self calculatePictureSizing: sender];
2492         [self twoPassCheckboxChanged: sender];
2493 }
2494
2495
2496 - (IBAction) twoPassCheckboxChanged: (id) sender
2497 {
2498         /* check to see if x264 is chosen */
2499         if([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_X264)
2500     {
2501                 if( [fVidTwoPassCheck state] == NSOnState)
2502                 {
2503                         [fVidTurboPassCheck setHidden: NO];
2504                 }
2505                 else
2506                 {
2507                         [fVidTurboPassCheck setHidden: YES];
2508                         [fVidTurboPassCheck setState: NSOffState];
2509                 }
2510                 /* Make sure Two Pass is checked if Turbo is checked */
2511                 if( [fVidTurboPassCheck state] == NSOnState)
2512                 {
2513                         [fVidTwoPassCheck setState: NSOnState];
2514                 }
2515         }
2516         else
2517         {
2518                 [fVidTurboPassCheck setHidden: YES];
2519                 [fVidTurboPassCheck setState: NSOffState];
2520         }
2521         
2522         /* We call method method to change UI to reflect whether a preset is used or not*/
2523         [self customSettingUsed: sender];
2524 }
2525
2526 - (IBAction ) videoFrameRateChanged: (id) sender
2527 {
2528     /* We call method method to calculatePictureSizing to error check detelecine*/
2529     [self calculatePictureSizing: sender];
2530
2531     /* We call method method to change UI to reflect whether a preset is used or not*/
2532         [self customSettingUsed: sender];
2533 }
2534 - (IBAction) videoMatrixChanged: (id) sender;
2535 {
2536     bool target, bitrate, quality;
2537
2538     target = bitrate = quality = false;
2539     if( [fVidQualityMatrix isEnabled] )
2540     {
2541         switch( [fVidQualityMatrix selectedRow] )
2542         {
2543             case 0:
2544                 target = true;
2545                 break;
2546             case 1:
2547                 bitrate = true;
2548                 break;
2549             case 2:
2550                 quality = true;
2551                 break;
2552         }
2553     }
2554     [fVidTargetSizeField  setEnabled: target];
2555     [fVidBitrateField     setEnabled: bitrate];
2556     [fVidQualitySlider    setEnabled: quality];
2557     [fVidTwoPassCheck     setEnabled: !quality &&
2558         [fVidQualityMatrix isEnabled]];
2559     if( quality )
2560     {
2561         [fVidTwoPassCheck setState: NSOffState];
2562                 [fVidTurboPassCheck setHidden: YES];
2563                 [fVidTurboPassCheck setState: NSOffState];
2564     }
2565
2566     [self qualitySliderChanged: sender];
2567     [self calculateBitrate: sender];
2568         [self customSettingUsed: sender];
2569 }
2570
2571 - (IBAction) qualitySliderChanged: (id) sender
2572 {
2573     [fVidConstantCell setTitle: [NSString stringWithFormat:
2574         NSLocalizedString( @"Constant quality: %.0f %%", @"" ), 100.0 *
2575         [fVidQualitySlider floatValue]]];
2576                 [self customSettingUsed: sender];
2577 }
2578
2579 - (void) controlTextDidChange: (NSNotification *) notification
2580 {
2581     [self calculateBitrate:nil];
2582 }
2583
2584 - (IBAction) calculateBitrate: (id) sender
2585 {
2586     if( !fHandle || [fVidQualityMatrix selectedRow] != 0 || !SuccessfulScan )
2587     {
2588         return;
2589     }
2590
2591     hb_list_t  * list  = hb_get_titles( fHandle );
2592     hb_title_t * title = (hb_title_t *) hb_list_item( list,
2593             [fSrcTitlePopUp indexOfSelectedItem] );
2594     hb_job_t * job = title->job;
2595
2596     [self prepareJob];
2597
2598     [fVidBitrateField setIntValue: hb_calc_bitrate( job,
2599             [fVidTargetSizeField intValue] )];
2600 }
2601
2602 #pragma mark -
2603 #pragma mark - Picture
2604
2605 /* lets set the picture size back to the max from right after title scan
2606    Lets use an IBAction here as down the road we could always use a checkbox
2607    in the gui to easily take the user back to max. Remember, the compiler
2608    resolves IBActions down to -(void) during compile anyway */
2609 - (IBAction) revertPictureSizeToMax: (id) sender
2610 {
2611         hb_job_t * job = fTitle->job;
2612         /* We use the output picture width and height
2613      as calculated from libhb right after title is set
2614      in TitlePopUpChanged */
2615         job->width = PicOrigOutputWidth;
2616         job->height = PicOrigOutputHeight;
2617     [fPictureController setAutoCrop:YES];
2618         /* Here we use the auto crop values determined right after scan */
2619         job->crop[0] = AutoCropTop;
2620         job->crop[1] = AutoCropBottom;
2621         job->crop[2] = AutoCropLeft;
2622         job->crop[3] = AutoCropRight;
2623     
2624     
2625     [self calculatePictureSizing: sender];
2626     /* We call method to change UI to reflect whether a preset is used or not*/    
2627     [self customSettingUsed: sender];
2628 }
2629
2630 /**
2631  * Registers changes made in the Picture Settings Window.
2632  */
2633
2634 - (void)pictureSettingsDidChange {
2635         [self calculatePictureSizing:nil];
2636 }
2637
2638 /* Get and Display Current Pic Settings in main window */
2639 - (IBAction) calculatePictureSizing: (id) sender
2640 {
2641         [fPicSettingsOutp setStringValue: [NSString stringWithFormat:@"%d x %d", fTitle->job->width, fTitle->job->height]];
2642         
2643     if (fTitle->job->pixel_ratio == 1)
2644         {
2645         int titlewidth = fTitle->width-fTitle->job->crop[2]-fTitle->job->crop[3];
2646         int arpwidth = fTitle->job->pixel_aspect_width;
2647         int arpheight = fTitle->job->pixel_aspect_height;
2648         int displayparwidth = titlewidth * arpwidth / arpheight;
2649         int displayparheight = fTitle->height-fTitle->job->crop[0]-fTitle->job->crop[1];
2650         [fPicSettingsOutp setStringValue: [NSString stringWithFormat:@"%d x %d", titlewidth, displayparheight]];
2651         [fPicSettingsAnamorphic setStringValue: [NSString stringWithFormat:@"%d x %d Strict", displayparwidth, displayparheight]];
2652         fTitle->job->keep_ratio = 0;
2653         }
2654     else if (fTitle->job->pixel_ratio == 2)
2655     {
2656         hb_job_t * job = fTitle->job;
2657         int output_width, output_height, output_par_width, output_par_height;
2658         hb_set_anamorphic_size(job, &output_width, &output_height, &output_par_width, &output_par_height);
2659         int display_width;
2660         display_width = output_width * output_par_width / output_par_height;
2661
2662         [fPicSettingsOutp setStringValue: [NSString stringWithFormat:@"%d x %d", output_width, output_height]];
2663         [fPicSettingsAnamorphic setStringValue: [NSString stringWithFormat:@"%d x %d Loose", display_width, output_height]];
2664
2665         fTitle->job->keep_ratio = 0;
2666     }
2667         else
2668         {
2669         [fPicSettingsAnamorphic setStringValue:@"Off"];
2670         }
2671
2672         /* Set ON/Off values for the deinterlace/keep aspect ratio according to boolean */
2673         if (fTitle->job->keep_ratio > 0)
2674         {
2675                 [fPicSettingARkeep setStringValue: @"On"];
2676         }
2677         else
2678         {
2679                 [fPicSettingARkeep setStringValue: @"Off"];
2680         }       
2681     
2682     /* Detelecine */
2683     if ([fPictureController detelecine]) {
2684         [fPicSettingDetelecine setStringValue: @"Yes"];
2685     }
2686     else {
2687         [fPicSettingDetelecine setStringValue: @"No"];
2688     }
2689     
2690     /* Decomb */
2691         if ([fPictureController decomb] == 0)
2692         {
2693                 [fPicSettingDecomb setStringValue: @"Off"];
2694         }
2695         else if ([fPictureController decomb] == 1)
2696         {
2697                 [fPicSettingDecomb setStringValue: @"1:2:6:9:80:16:16"];
2698         }
2699     else if ([fPictureController decomb] == 2)
2700     {
2701         [fPicSettingDecomb setStringValue:[[NSUserDefaults standardUserDefaults] stringForKey:@"DecombCustomString"]];
2702     }
2703
2704     /* VFR (Variable Frame Rate) */
2705     if ([fPictureController vfr]) {
2706         /* We change the string of the fps popup to warn that vfr is on Framerate (FPS): */
2707         [fVidRateField setStringValue: @"Framerate (VFR On):"]; 
2708         /* for VFR we select same as source (or title framerate) and disable the popup.
2709         * We know its index 0 as that is determined in titlePopUpChanged */
2710         [fVidRatePopUp selectItemAtIndex: 0];
2711         [fVidRatePopUp setEnabled: NO];  
2712         
2713     }
2714     else {
2715         /* make sure the label for framerate is set to its default */  
2716         [fVidRateField setStringValue: @"Framerate (FPS):"];
2717         [fVidRatePopUp setEnabled: YES];
2718     }
2719     
2720         /* Deinterlace */
2721         if ([fPictureController deinterlace] == 0)
2722         {
2723                 [fPicSettingDeinterlace setStringValue: @"Off"];
2724         }
2725         else if ([fPictureController deinterlace] == 1)
2726         {
2727                 [fPicSettingDeinterlace setStringValue: @"Fast"];
2728         }
2729         else if ([fPictureController deinterlace] == 2)
2730         {
2731                 [fPicSettingDeinterlace setStringValue: @"Slow"];
2732         }
2733         else if ([fPictureController deinterlace] == 3)
2734         {
2735                 [fPicSettingDeinterlace setStringValue: @"Slower"];
2736         }
2737                 
2738     /* Denoise */
2739         if ([fPictureController denoise] == 0)
2740         {
2741                 [fPicSettingDenoise setStringValue: @"Off"];
2742         }
2743         else if ([fPictureController denoise] == 1)
2744         {
2745                 [fPicSettingDenoise setStringValue: @"Weak"];
2746         }
2747         else if ([fPictureController denoise] == 2)
2748         {
2749                 [fPicSettingDenoise setStringValue: @"Medium"];
2750         }
2751         else if ([fPictureController denoise] == 3)
2752         {
2753                 [fPicSettingDenoise setStringValue: @"Strong"];
2754         }
2755     
2756     /* Deblock */
2757     if ([fPictureController deblock]) {
2758         [fPicSettingDeblock setStringValue: @"Yes"];
2759     }
2760     else {
2761         [fPicSettingDeblock setStringValue: @"No"];
2762     }
2763         
2764         if (fTitle->job->pixel_ratio > 0)
2765         {
2766                 [fPicSettingPAR setStringValue: @""];
2767         }
2768         else
2769         {
2770                 [fPicSettingPAR setStringValue: @"Off"];
2771         }
2772         
2773     /* Set the display field for crop as per boolean */
2774         if (![fPictureController autoCrop])
2775         {
2776             [fPicSettingAutoCrop setStringValue: @"Custom"];
2777         }
2778         else
2779         {
2780                 [fPicSettingAutoCrop setStringValue: @"Auto"];
2781         }       
2782         
2783     
2784 }
2785
2786
2787 #pragma mark -
2788 #pragma mark - Audio and Subtitles
2789 - (IBAction) audioCodecsPopUpChanged: (id) sender
2790 {
2791     
2792     NSPopUpButton * audiotrackPopUp;
2793     NSPopUpButton * sampleratePopUp;
2794     NSPopUpButton * bitratePopUp;
2795     NSPopUpButton * audiocodecPopUp;
2796     if (sender == fAudTrack1CodecPopUp)
2797     {
2798         audiotrackPopUp = fAudLang1PopUp;
2799         audiocodecPopUp = fAudTrack1CodecPopUp;
2800         sampleratePopUp = fAudTrack1RatePopUp;
2801         bitratePopUp = fAudTrack1BitratePopUp;
2802     }
2803     else if (sender == fAudTrack2CodecPopUp)
2804     {
2805         audiotrackPopUp = fAudLang2PopUp;
2806         audiocodecPopUp = fAudTrack2CodecPopUp;
2807         sampleratePopUp = fAudTrack2RatePopUp;
2808         bitratePopUp = fAudTrack2BitratePopUp;
2809     }
2810     else if (sender == fAudTrack3CodecPopUp)
2811     {
2812         audiotrackPopUp = fAudLang3PopUp;
2813         audiocodecPopUp = fAudTrack3CodecPopUp;
2814         sampleratePopUp = fAudTrack3RatePopUp;
2815         bitratePopUp = fAudTrack3BitratePopUp;
2816     }
2817     else
2818     {
2819         audiotrackPopUp = fAudLang4PopUp;
2820         audiocodecPopUp = fAudTrack4CodecPopUp;
2821         sampleratePopUp = fAudTrack4RatePopUp;
2822         bitratePopUp = fAudTrack4BitratePopUp;
2823     }
2824         
2825     /* changing the codecs on offer may mean that we can / can't offer mono or 6ch, */
2826         /* so call audioTrackPopUpChanged for both audio tracks to update the mixdown popups */
2827     [self audioTrackPopUpChanged: audiotrackPopUp];
2828     
2829 }
2830
2831 - (IBAction) setEnabledStateOfAudioMixdownControls: (id) sender
2832 {
2833     /* We will be setting the enabled/disabled state of each tracks audio controls based on
2834      * the settings of the source audio for that track. We leave the samplerate and bitrate
2835      * to audiotrackMixdownChanged
2836      */
2837     
2838     /* We will first verify that a lower track number has been selected before enabling each track
2839      * for example, make sure a track is selected for track 1 before enabling track 2, etc.
2840      */
2841     if ([fAudLang1PopUp indexOfSelectedItem] == 0)
2842     {
2843         [fAudLang2PopUp setEnabled: NO];
2844         [fAudLang2PopUp selectItemAtIndex: 0];
2845     }
2846     else
2847     {
2848         [fAudLang2PopUp setEnabled: YES];
2849     }
2850     
2851     if ([fAudLang2PopUp indexOfSelectedItem] == 0)
2852     {
2853         [fAudLang3PopUp setEnabled: NO];
2854         [fAudLang3PopUp selectItemAtIndex: 0];
2855     }
2856     else
2857     {
2858         [fAudLang3PopUp setEnabled: YES];
2859     }
2860     if ([fAudLang3PopUp indexOfSelectedItem] == 0)
2861     {
2862         [fAudLang4PopUp setEnabled: NO];
2863         [fAudLang4PopUp selectItemAtIndex: 0];
2864     }
2865     else
2866     {
2867         [fAudLang4PopUp setEnabled: YES];
2868     }
2869     /* enable/disable the mixdown text and popupbutton for audio track 1 */
2870     [fAudTrack1CodecPopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
2871     [fAudTrack1MixPopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
2872     [fAudTrack1RatePopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
2873     [fAudTrack1BitratePopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
2874     [fAudTrack1DrcSlider setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
2875     [fAudTrack1DrcField setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
2876     if ([fAudLang1PopUp indexOfSelectedItem] == 0)
2877     {
2878         [fAudTrack1CodecPopUp removeAllItems];
2879         [fAudTrack1MixPopUp removeAllItems];
2880         [fAudTrack1RatePopUp removeAllItems];
2881         [fAudTrack1BitratePopUp removeAllItems];
2882         [fAudTrack1DrcSlider setFloatValue: 1.00];
2883         [self audioDRCSliderChanged: fAudTrack1DrcSlider];
2884     }
2885     else if ([[fAudTrack1MixPopUp selectedItem] tag] == HB_ACODEC_AC3)
2886     {
2887         [fAudTrack1RatePopUp setEnabled: NO];
2888         [fAudTrack1BitratePopUp setEnabled: NO];
2889         [fAudTrack1DrcSlider setEnabled: NO];
2890         [fAudTrack1DrcField setEnabled: NO];
2891     }
2892     
2893     /* enable/disable the mixdown text and popupbutton for audio track 2 */
2894     [fAudTrack2CodecPopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
2895     [fAudTrack2MixPopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
2896     [fAudTrack2RatePopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
2897     [fAudTrack2BitratePopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
2898     [fAudTrack2DrcSlider setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
2899     [fAudTrack2DrcField setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
2900     if ([fAudLang2PopUp indexOfSelectedItem] == 0)
2901     {
2902         [fAudTrack2CodecPopUp removeAllItems];
2903         [fAudTrack2MixPopUp removeAllItems];
2904         [fAudTrack2RatePopUp removeAllItems];
2905         [fAudTrack2BitratePopUp removeAllItems];
2906         [fAudTrack2DrcSlider setFloatValue: 1.00];
2907         [self audioDRCSliderChanged: fAudTrack2DrcSlider];
2908     }
2909     else if ([[fAudTrack2MixPopUp selectedItem] tag] == HB_ACODEC_AC3)
2910     {
2911         [fAudTrack2RatePopUp setEnabled: NO];
2912         [fAudTrack2BitratePopUp setEnabled: NO];
2913         [fAudTrack2DrcSlider setEnabled: NO];
2914         [fAudTrack2DrcField setEnabled: NO];
2915     }
2916     
2917     /* enable/disable the mixdown text and popupbutton for audio track 3 */
2918     [fAudTrack3CodecPopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
2919     [fAudTrack3MixPopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
2920     [fAudTrack3RatePopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
2921     [fAudTrack3BitratePopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
2922     [fAudTrack3DrcSlider setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
2923     [fAudTrack3DrcField setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
2924     if ([fAudLang3PopUp indexOfSelectedItem] == 0)
2925     {
2926         [fAudTrack3CodecPopUp removeAllItems];
2927         [fAudTrack3MixPopUp removeAllItems];
2928         [fAudTrack3RatePopUp removeAllItems];
2929         [fAudTrack3BitratePopUp removeAllItems];
2930         [fAudTrack3DrcSlider setFloatValue: 1.00];
2931         [self audioDRCSliderChanged: fAudTrack3DrcSlider];
2932     }
2933     else if ([[fAudTrack3MixPopUp selectedItem] tag] == HB_ACODEC_AC3)
2934     {
2935         [fAudTrack3RatePopUp setEnabled: NO];
2936         [fAudTrack3BitratePopUp setEnabled: NO];
2937         [fAudTrack3DrcSlider setEnabled: NO];
2938         [fAudTrack3DrcField setEnabled: NO];
2939     }
2940     
2941     /* enable/disable the mixdown text and popupbutton for audio track 4 */
2942     [fAudTrack4CodecPopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
2943     [fAudTrack4MixPopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
2944     [fAudTrack4RatePopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
2945     [fAudTrack4BitratePopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
2946     [fAudTrack4DrcSlider setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
2947     [fAudTrack4DrcField setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
2948     if ([fAudLang4PopUp indexOfSelectedItem] == 0)
2949     {
2950         [fAudTrack4CodecPopUp removeAllItems];
2951         [fAudTrack4MixPopUp removeAllItems];
2952         [fAudTrack4RatePopUp removeAllItems];
2953         [fAudTrack4BitratePopUp removeAllItems];
2954         [fAudTrack4DrcSlider setFloatValue: 1.00];
2955         [self audioDRCSliderChanged: fAudTrack4DrcSlider];
2956     }
2957     else if ([[fAudTrack4MixPopUp selectedItem] tag] == HB_ACODEC_AC3)
2958     {
2959         [fAudTrack4RatePopUp setEnabled: NO];
2960         [fAudTrack4BitratePopUp setEnabled: NO];
2961         [fAudTrack4DrcSlider setEnabled: NO];
2962         [fAudTrack4DrcField setEnabled: NO];
2963     }
2964     
2965 }
2966
2967 - (IBAction) addAllAudioTracksToPopUp: (id) sender
2968 {
2969
2970     hb_list_t  * list  = hb_get_titles( fHandle );
2971     hb_title_t * title = (hb_title_t*)
2972         hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
2973
2974         hb_audio_config_t * audio;
2975
2976     [sender removeAllItems];
2977     [sender addItemWithTitle: NSLocalizedString( @"None", @"" )];
2978     for( int i = 0; i < hb_list_count( title->list_audio ); i++ )
2979     {
2980         audio = (hb_audio_config_t *) hb_list_audio_config_item( title->list_audio, i );
2981         [[sender menu] addItemWithTitle:
2982             [NSString stringWithCString: audio->lang.description]
2983             action: NULL keyEquivalent: @""];
2984     }
2985     [sender selectItemAtIndex: 0];
2986
2987 }
2988
2989 - (IBAction) selectAudioTrackInPopUp: (id) sender searchPrefixString: (NSString *) searchPrefixString selectIndexIfNotFound: (int) selectIndexIfNotFound
2990 {
2991
2992     /* this method can be used to find a language, or a language-and-source-format combination, by passing in the appropriate string */
2993     /* e.g. to find the first French track, pass in an NSString * of "Francais" */
2994     /* e.g. to find the first English 5.1 AC3 track, pass in an NSString * of "English (AC3) (5.1 ch)" */
2995     /* if no matching track is found, then selectIndexIfNotFound is used to choose which track to select instead */
2996
2997         if (searchPrefixString)
2998         {
2999
3000         for( int i = 0; i < [sender numberOfItems]; i++ )
3001         {
3002             /* Try to find the desired search string */
3003             if ([[[sender itemAtIndex: i] title] hasPrefix:searchPrefixString])
3004             {
3005                 [sender selectItemAtIndex: i];
3006                 return;
3007             }
3008         }
3009         /* couldn't find the string, so select the requested "search string not found" item */
3010         /* index of 0 means select the "none" item */
3011         /* index of 1 means select the first audio track */
3012         [sender selectItemAtIndex: selectIndexIfNotFound];
3013         }
3014     else
3015     {
3016         /* if no search string is provided, then select the selectIndexIfNotFound item */
3017         [sender selectItemAtIndex: selectIndexIfNotFound];
3018     }
3019
3020 }
3021 - (IBAction) audioAddAudioTrackCodecs: (id)sender
3022 {
3023     int format = [fDstFormatPopUp indexOfSelectedItem];
3024     
3025     /* setup pointers to the appropriate popups for the correct track */
3026     NSPopUpButton * audiocodecPopUp;
3027     NSPopUpButton * audiotrackPopUp;
3028     if (sender == fAudTrack1CodecPopUp)
3029     {
3030         audiotrackPopUp = fAudLang1PopUp;
3031         audiocodecPopUp = fAudTrack1CodecPopUp;
3032     }
3033     else if (sender == fAudTrack2CodecPopUp)
3034     {
3035         audiotrackPopUp = fAudLang2PopUp;
3036         audiocodecPopUp = fAudTrack2CodecPopUp;
3037     }
3038     else if (sender == fAudTrack3CodecPopUp)
3039     {
3040         audiotrackPopUp = fAudLang3PopUp;
3041         audiocodecPopUp = fAudTrack3CodecPopUp;
3042     }
3043     else
3044     {
3045         audiotrackPopUp = fAudLang4PopUp;
3046         audiocodecPopUp = fAudTrack4CodecPopUp;
3047     }
3048     
3049     [audiocodecPopUp removeAllItems];
3050     /* Make sure "None" isnt selected in the source track */
3051     if ([audiotrackPopUp indexOfSelectedItem] > 0)
3052     {
3053         [audiocodecPopUp setEnabled:YES];
3054         NSMenuItem *menuItem;
3055         /* We setup our appropriate popups for codecs and put the int value in the popup tag for easy retrieval */
3056         switch( format )
3057         {
3058             case 0:
3059                 /* MP4 */
3060                 // AAC
3061                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (faac)" action: NULL keyEquivalent: @""];
3062                 [menuItem setTag: HB_ACODEC_FAAC];
3063                 
3064                 // AC3 Passthru
3065                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AC3 Passthru" action: NULL keyEquivalent: @""];
3066                 [menuItem setTag: HB_ACODEC_AC3];
3067                 break;
3068                 
3069             case 1:
3070                 /* MKV */
3071                 // AAC
3072                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (faac)" action: NULL keyEquivalent: @""];
3073                 [menuItem setTag: HB_ACODEC_FAAC];
3074                 // AC3 Passthru
3075                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AC3 Passthru" action: NULL keyEquivalent: @""];
3076                 [menuItem setTag: HB_ACODEC_AC3];
3077                 // MP3
3078                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"MP3 (lame)" action: NULL keyEquivalent: @""];
3079                 [menuItem setTag: HB_ACODEC_LAME];
3080                 // Vorbis
3081                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"Vorbis (vorbis)" action: NULL keyEquivalent: @""];
3082                 [menuItem setTag: HB_ACODEC_VORBIS];
3083                 break;
3084                 
3085             case 2: 
3086                 /* AVI */
3087                 // MP3
3088                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"MP3 (lame)" action: NULL keyEquivalent: @""];
3089                 [menuItem setTag: HB_ACODEC_LAME];
3090                 // AC3 Passthru
3091                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AC3 Passthru" action: NULL keyEquivalent: @""];
3092                 [menuItem setTag: HB_ACODEC_AC3];
3093                 break;
3094                 
3095             case 3:
3096                 /* OGM */
3097                 // Vorbis
3098                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"Vorbis (vorbis)" action: NULL keyEquivalent: @""];
3099                 [menuItem setTag: HB_ACODEC_VORBIS];
3100                 // MP3
3101                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"MP3 (lame)" action: NULL keyEquivalent: @""];
3102                 [menuItem setTag: HB_ACODEC_LAME];
3103                 break;
3104         }
3105         [audiocodecPopUp selectItemAtIndex:0];
3106     }
3107     else
3108     {
3109         [audiocodecPopUp setEnabled:NO];
3110     }
3111 }
3112
3113 - (IBAction) audioTrackPopUpChanged: (id) sender
3114 {
3115     /* utility function to call audioTrackPopUpChanged without passing in a mixdown-to-use */
3116     [self audioTrackPopUpChanged: sender mixdownToUse: 0];
3117 }
3118
3119 - (IBAction) audioTrackPopUpChanged: (id) sender mixdownToUse: (int) mixdownToUse
3120 {
3121     
3122     /* make sure we have a selected title before continuing */
3123     if (fTitle == NULL) return;
3124     /* if the sender is the lanaguage popup and there is nothing in the codec popup, lets call
3125     * audioAddAudioTrackCodecs on the codec popup to populate it properly before moving on
3126     */
3127     if (sender == fAudLang1PopUp && [[fAudTrack1CodecPopUp menu] numberOfItems] == 0)
3128     {
3129         [self audioAddAudioTrackCodecs: fAudTrack1CodecPopUp];
3130     }
3131     if (sender == fAudLang2PopUp && [[fAudTrack2CodecPopUp menu] numberOfItems] == 0)
3132     {
3133         [self audioAddAudioTrackCodecs: fAudTrack2CodecPopUp];
3134     }
3135     if (sender == fAudLang3PopUp && [[fAudTrack3CodecPopUp menu] numberOfItems] == 0)
3136     {
3137         [self audioAddAudioTrackCodecs: fAudTrack3CodecPopUp];
3138     }
3139     if (sender == fAudLang4PopUp && [[fAudTrack4CodecPopUp menu] numberOfItems] == 0)
3140     {
3141         [self audioAddAudioTrackCodecs: fAudTrack4CodecPopUp];
3142     }
3143     
3144     /* Now lets make the sender the appropriate Audio Track popup from this point on */
3145     if (sender == fAudTrack1CodecPopUp || sender == fAudTrack1MixPopUp)
3146     {
3147         sender = fAudLang1PopUp;
3148     }
3149     if (sender == fAudTrack2CodecPopUp || sender == fAudTrack2MixPopUp)
3150     {
3151         sender = fAudLang2PopUp;
3152     }
3153     if (sender == fAudTrack3CodecPopUp || sender == fAudTrack3MixPopUp)
3154     {
3155         sender = fAudLang3PopUp;
3156     }
3157     if (sender == fAudTrack4CodecPopUp || sender == fAudTrack4MixPopUp)
3158     {
3159         sender = fAudLang4PopUp;
3160     }
3161     
3162     /* pointer to this track's mixdown, codec, sample rate and bitrate NSPopUpButton's */
3163     NSPopUpButton * mixdownPopUp;
3164     NSPopUpButton * audiocodecPopUp;
3165     NSPopUpButton * sampleratePopUp;
3166     NSPopUpButton * bitratePopUp;
3167     if (sender == fAudLang1PopUp)
3168     {
3169         mixdownPopUp = fAudTrack1MixPopUp;
3170         audiocodecPopUp = fAudTrack1CodecPopUp;
3171         sampleratePopUp = fAudTrack1RatePopUp;
3172         bitratePopUp = fAudTrack1BitratePopUp;
3173     }
3174     else if (sender == fAudLang2PopUp)
3175     {
3176         mixdownPopUp = fAudTrack2MixPopUp;
3177         audiocodecPopUp = fAudTrack2CodecPopUp;
3178         sampleratePopUp = fAudTrack2RatePopUp;
3179         bitratePopUp = fAudTrack2BitratePopUp;
3180     }
3181     else if (sender == fAudLang3PopUp)
3182     {
3183         mixdownPopUp = fAudTrack3MixPopUp;
3184         audiocodecPopUp = fAudTrack3CodecPopUp;
3185         sampleratePopUp = fAudTrack3RatePopUp;
3186         bitratePopUp = fAudTrack3BitratePopUp;
3187     }
3188     else
3189     {
3190         mixdownPopUp = fAudTrack4MixPopUp;
3191         audiocodecPopUp = fAudTrack4CodecPopUp;
3192         sampleratePopUp = fAudTrack4RatePopUp;
3193         bitratePopUp = fAudTrack4BitratePopUp;
3194     }
3195
3196     /* get the index of the selected audio Track*/
3197     int thisAudioIndex = [sender indexOfSelectedItem] - 1;
3198
3199     /* pointer for the hb_audio_s struct we will use later on */
3200     hb_audio_config_t * audio;
3201
3202     int acodec;
3203     /* check if the audio mixdown controls need their enabled state changing */
3204     [self setEnabledStateOfAudioMixdownControls:nil];
3205
3206     if (thisAudioIndex != -1)
3207     {
3208
3209         /* get the audio */
3210         audio = (hb_audio_config_t *) hb_list_audio_config_item( fTitle->list_audio, thisAudioIndex );// Should "fTitle" be title and be setup ?
3211
3212         /* actually manipulate the proper mixdowns here */
3213         /* delete the previous audio mixdown options */
3214         [mixdownPopUp removeAllItems];
3215
3216         acodec = [[audiocodecPopUp selectedItem] tag];
3217
3218         if (audio != NULL)
3219         {
3220
3221             /* find out if our selected output audio codec supports mono and / or 6ch */
3222             /* we also check for an input codec of AC3 or DCA,
3223              as they are the only libraries able to do the mixdown to mono / conversion to 6-ch */
3224             /* audioCodecsSupportMono and audioCodecsSupport6Ch are the same for now,
3225              but this may change in the future, so they are separated for flexibility */
3226             int audioCodecsSupportMono =
3227                     (audio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)) &&
3228                     (acodec != HB_ACODEC_LAME);
3229             int audioCodecsSupport6Ch =
3230                     (audio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)) &&
3231                     (acodec != HB_ACODEC_LAME);
3232             
3233             /* check for AC-3 passthru */
3234             if (audio->in.codec == HB_ACODEC_AC3 && acodec == HB_ACODEC_AC3)
3235             {
3236                 
3237             NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
3238                  [NSString stringWithCString: "AC3 Passthru"]
3239                                                action: NULL keyEquivalent: @""];
3240              [menuItem setTag: HB_ACODEC_AC3];   
3241             }
3242             else
3243             {
3244                 
3245                 /* add the appropriate audio mixdown menuitems to the popupbutton */
3246                 /* in each case, we set the new menuitem's tag to be the amixdown value for that mixdown,
3247                  so that we can reference the mixdown later */
3248                 
3249                 /* keep a track of the min and max mixdowns we used, so we can select the best match later */
3250                 int minMixdownUsed = 0;
3251                 int maxMixdownUsed = 0;
3252                 
3253                 /* get the input channel layout without any lfe channels */
3254                 int layout = audio->in.channel_layout & HB_INPUT_CH_LAYOUT_DISCRETE_NO_LFE_MASK;
3255                 
3256                 /* do we want to add a mono option? */
3257                 if (audioCodecsSupportMono == 1)
3258                 {
3259                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
3260                                             [NSString stringWithCString: hb_audio_mixdowns[0].human_readable_name]
3261                                                                           action: NULL keyEquivalent: @""];
3262                     [menuItem setTag: hb_audio_mixdowns[0].amixdown];
3263                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[0].amixdown;
3264                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[0].amixdown);
3265                 }
3266                 
3267                 /* do we want to add a stereo option? */
3268                 /* offer stereo if we have a mono source and non-mono-supporting codecs, as otherwise we won't have a mixdown at all */
3269                 /* also offer stereo if we have a stereo-or-better source */
3270                 if ((layout == HB_INPUT_CH_LAYOUT_MONO && audioCodecsSupportMono == 0) || layout >= HB_INPUT_CH_LAYOUT_STEREO)
3271                 {
3272                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
3273                                             [NSString stringWithCString: hb_audio_mixdowns[1].human_readable_name]
3274                                                                           action: NULL keyEquivalent: @""];
3275                     [menuItem setTag: hb_audio_mixdowns[1].amixdown];
3276                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[1].amixdown;
3277                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[1].amixdown);
3278                 }
3279                 
3280                 /* do we want to add a dolby surround (DPL1) option? */
3281                 if (layout == HB_INPUT_CH_LAYOUT_3F1R || layout == HB_INPUT_CH_LAYOUT_3F2R || layout == HB_INPUT_CH_LAYOUT_DOLBY)
3282                 {
3283                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
3284                                             [NSString stringWithCString: hb_audio_mixdowns[2].human_readable_name]
3285                                                                           action: NULL keyEquivalent: @""];
3286                     [menuItem setTag: hb_audio_mixdowns[2].amixdown];
3287                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[2].amixdown;
3288                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[2].amixdown);
3289                 }
3290                 
3291                 /* do we want to add a dolby pro logic 2 (DPL2) option? */
3292                 if (layout == HB_INPUT_CH_LAYOUT_3F2R)
3293                 {
3294                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
3295                                             [NSString stringWithCString: hb_audio_mixdowns[3].human_readable_name]
3296                                                                           action: NULL keyEquivalent: @""];
3297                     [menuItem setTag: hb_audio_mixdowns[3].amixdown];
3298                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[3].amixdown;
3299                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[3].amixdown);
3300                 }
3301                 
3302                 /* do we want to add a 6-channel discrete option? */
3303                 if (audioCodecsSupport6Ch == 1 && layout == HB_INPUT_CH_LAYOUT_3F2R && (audio->in.channel_layout & HB_INPUT_CH_LAYOUT_HAS_LFE))
3304                 {
3305                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
3306                                             [NSString stringWithCString: hb_audio_mixdowns[4].human_readable_name]
3307                                                                           action: NULL keyEquivalent: @""];
3308                     [menuItem setTag: hb_audio_mixdowns[4].amixdown];
3309                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[4].amixdown;
3310                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[4].amixdown);
3311                 }
3312                 
3313                 /* do we want to add an AC-3 passthrough option? */
3314                 if (audio->in.codec == HB_ACODEC_AC3 && acodec == HB_ACODEC_AC3) 
3315                 {
3316                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
3317                                             [NSString stringWithCString: hb_audio_mixdowns[5].human_readable_name]
3318                                                                           action: NULL keyEquivalent: @""];
3319                     [menuItem setTag: HB_ACODEC_AC3];
3320                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[5].amixdown;
3321                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[5].amixdown);
3322                 }
3323                 
3324                 /* auto-select the best mixdown based on our saved mixdown preference */
3325                 
3326                 /* for now, this is hard-coded to a "best" mixdown of HB_AMIXDOWN_DOLBYPLII */
3327                 /* ultimately this should be a prefs option */
3328                 int useMixdown;
3329                 
3330                 /* if we passed in a mixdown to use - in order to load a preset - then try and use it */
3331                 if (mixdownToUse > 0)
3332                 {
3333                     useMixdown = mixdownToUse;
3334                 }
3335                 else
3336                 {
3337                     useMixdown = HB_AMIXDOWN_DOLBYPLII;
3338                 }
3339                 
3340                 /* if useMixdown > maxMixdownUsed, then use maxMixdownUsed */
3341                 if (useMixdown > maxMixdownUsed)
3342                 { 
3343                     useMixdown = maxMixdownUsed;
3344                 }
3345                 
3346                 /* if useMixdown < minMixdownUsed, then use minMixdownUsed */
3347                 if (useMixdown < minMixdownUsed)
3348                 { 
3349                     useMixdown = minMixdownUsed;
3350                 }
3351                 
3352                 /* select the (possibly-amended) preferred mixdown */
3353                 [mixdownPopUp selectItemWithTag: useMixdown];
3354
3355             }
3356             /* In the case of a source track that is not AC3 and the user tries to use AC3 Passthru (which does not work)
3357              * we force the Audio Codec choice back to a workable codec. We use MP3 for avi and aac for all
3358              * other containers.
3359              */
3360             if (audio->in.codec != HB_ACODEC_AC3 && [[audiocodecPopUp selectedItem] tag] == HB_ACODEC_AC3)
3361             {
3362                 /* If we are using the avi container, we select MP3 as there is no aac available*/
3363                 if ([[fDstFormatPopUp selectedItem] tag] == HB_MUX_AVI)
3364                 {
3365                     [audiocodecPopUp selectItemWithTag: HB_ACODEC_LAME];
3366                 }
3367                 else
3368                 {
3369                     [audiocodecPopUp selectItemWithTag: HB_ACODEC_FAAC];
3370                 }
3371             }
3372             /* Setup our samplerate and bitrate popups we will need based on mixdown */
3373             [self audioTrackMixdownChanged: mixdownPopUp];             
3374         }
3375     
3376     }
3377
3378 }
3379 - (IBAction) audioTrackMixdownChanged: (id) sender
3380 {
3381     
3382     int acodec;
3383     /* setup pointers to all of the other audio track controls
3384     * we will need later
3385     */
3386     NSPopUpButton * mixdownPopUp;
3387     NSPopUpButton * sampleratePopUp;
3388     NSPopUpButton * bitratePopUp;
3389     NSPopUpButton * audiocodecPopUp;
3390     NSPopUpButton * audiotrackPopUp;
3391     NSSlider * drcSlider;
3392     NSTextField * drcField;
3393     if (sender == fAudTrack1MixPopUp)
3394     {
3395         audiotrackPopUp = fAudLang1PopUp;
3396         audiocodecPopUp = fAudTrack1CodecPopUp;
3397         mixdownPopUp = fAudTrack1MixPopUp;
3398         sampleratePopUp = fAudTrack1RatePopUp;
3399         bitratePopUp = fAudTrack1BitratePopUp;
3400         drcSlider = fAudTrack1DrcSlider;
3401         drcField = fAudTrack1DrcField;
3402     }
3403     else if (sender == fAudTrack2MixPopUp)
3404     {
3405         audiotrackPopUp = fAudLang2PopUp;
3406         audiocodecPopUp = fAudTrack2CodecPopUp;
3407         mixdownPopUp = fAudTrack2MixPopUp;
3408         sampleratePopUp = fAudTrack2RatePopUp;
3409         bitratePopUp = fAudTrack2BitratePopUp;
3410         drcSlider = fAudTrack2DrcSlider;
3411         drcField = fAudTrack2DrcField;
3412     }
3413     else if (sender == fAudTrack3MixPopUp)
3414     {
3415         audiotrackPopUp = fAudLang3PopUp;
3416         audiocodecPopUp = fAudTrack3CodecPopUp;
3417         mixdownPopUp = fAudTrack3MixPopUp;
3418         sampleratePopUp = fAudTrack3RatePopUp;
3419         bitratePopUp = fAudTrack3BitratePopUp;
3420         drcSlider = fAudTrack3DrcSlider;
3421         drcField = fAudTrack3DrcField;
3422     }
3423     else
3424     {
3425         audiotrackPopUp = fAudLang4PopUp;
3426         audiocodecPopUp = fAudTrack4CodecPopUp;
3427         mixdownPopUp = fAudTrack4MixPopUp;
3428         sampleratePopUp = fAudTrack4RatePopUp;
3429         bitratePopUp = fAudTrack4BitratePopUp;
3430         drcSlider = fAudTrack4DrcSlider;
3431         drcField = fAudTrack4DrcField;
3432     }
3433     acodec = [[audiocodecPopUp selectedItem] tag];
3434     /* storage variable for the min and max bitrate allowed for this codec */
3435     int minbitrate;
3436     int maxbitrate;
3437     
3438     switch( acodec )
3439     {
3440         case HB_ACODEC_FAAC:
3441             /* check if we have a 6ch discrete conversion in either audio track */
3442             if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH)
3443             {
3444                 /* FAAC is happy using our min bitrate of 32 kbps, even for 6ch */
3445                 minbitrate = 32;
3446                 /* If either mixdown popup includes 6-channel discrete, then allow up to 384 kbps */
3447                 maxbitrate = 384;
3448                 break;
3449             }
3450             else
3451             {
3452                 /* FAAC is happy using our min bitrate of 32 kbps for stereo or mono */
3453                 minbitrate = 32;
3454                 /* FAAC won't honour anything more than 160 for stereo, so let's not offer it */
3455                 /* note: haven't dealt with mono separately here, FAAC will just use the max it can */
3456                 maxbitrate = 160;
3457                 break;
3458             }
3459             
3460             case HB_ACODEC_LAME:
3461             /* Lame is happy using our min bitrate of 32 kbps */
3462             minbitrate = 32;
3463             /* Lame won't encode if the bitrate is higher than 320 kbps */
3464             maxbitrate = 320;
3465             break;
3466             
3467             case HB_ACODEC_VORBIS:
3468             if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH)
3469             {
3470                 /* Vorbis causes a crash if we use a bitrate below 192 kbps with 6 channel */
3471                 minbitrate = 192;
3472                 /* If either mixdown popup includes 6-channel discrete, then allow up to 384 kbps */
3473                 maxbitrate = 384;
3474                 break;
3475             }
3476             else
3477             {
3478                 /* Vorbis causes a crash if we use a bitrate below 48 kbps */
3479                 minbitrate = 48;
3480                 /* Vorbis can cope with 384 kbps quite happily, even for stereo */
3481                 maxbitrate = 384;
3482                 break;
3483             }
3484             
3485             default:
3486             /* AC3 passthru disables the bitrate dropdown anyway, so we might as well just use the min and max bitrate */
3487             minbitrate = 32;
3488             maxbitrate = 384;
3489             
3490     }
3491     
3492     /* make sure we have a selected title before continuing */
3493     if (fTitle == NULL) return;
3494     /* get the audio so we can find out what input rates are*/
3495     hb_audio_config_t * audio;
3496     audio = (hb_audio_config_t *) hb_list_audio_config_item( fTitle->list_audio, [audiotrackPopUp indexOfSelectedItem] - 1 );
3497     int inputbitrate = audio->in.bitrate / 1000;
3498     int inputsamplerate = audio->in.samplerate;
3499     
3500     if ([[mixdownPopUp selectedItem] tag] != HB_ACODEC_AC3)
3501     {
3502         [bitratePopUp removeAllItems];
3503         
3504         for( int i = 0; i < hb_audio_bitrates_count; i++ )
3505         {
3506             if (hb_audio_bitrates[i].rate >= minbitrate && hb_audio_bitrates[i].rate <= maxbitrate)
3507             {
3508                 /* add a new menuitem for this bitrate */
3509                 NSMenuItem *menuItem = [[bitratePopUp menu] addItemWithTitle:
3510                                         [NSString stringWithCString: hb_audio_bitrates[i].string]
3511                                                                       action: NULL keyEquivalent: @""];
3512                 /* set its tag to be the actual bitrate as an integer, so we can retrieve it later */
3513                 [menuItem setTag: hb_audio_bitrates[i].rate];
3514             }
3515         }
3516         
3517         /* select the default bitrate (but use 384 for 6-ch AAC) */
3518         if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH)
3519         {
3520             [bitratePopUp selectItemWithTag: 384];
3521         }
3522         else
3523         {
3524             [bitratePopUp selectItemWithTag: hb_audio_bitrates[hb_audio_bitrates_default].rate];
3525         }
3526     }
3527     /* populate and set the sample rate popup */
3528     /* Audio samplerate */
3529     [sampleratePopUp removeAllItems];
3530     /* we create a same as source selection (Auto) so that we can choose to use the input sample rate */
3531     NSMenuItem *menuItem = [[sampleratePopUp menu] addItemWithTitle: @"Auto" action: NULL keyEquivalent: @""];
3532     [menuItem setTag: inputsamplerate];
3533     
3534     for( int i = 0; i < hb_audio_rates_count; i++ )
3535     {
3536         NSMenuItem *menuItem = [[sampleratePopUp menu] addItemWithTitle:
3537                                 [NSString stringWithCString: hb_audio_rates[i].string]
3538                                                                  action: NULL keyEquivalent: @""];
3539         [menuItem setTag: hb_audio_rates[i].rate];
3540     }
3541     /* We use the input sample rate as the default sample rate as downsampling just makes audio worse
3542     * and there is no compelling reason to use anything else as default, though the users default
3543     * preset will likely override any setting chosen here.
3544     */
3545     [sampleratePopUp selectItemWithTag: inputsamplerate];
3546     
3547     
3548     /* Since AC3 Pass Thru uses the input ac3 bitrate and sample rate, we get the input tracks
3549     * bitrate and dispay it in the bitrate popup even though libhb happily ignores any bitrate input from
3550     * the gui. We do this for better user feedback in the audio tab as well as the queue for the most part
3551     */
3552     if ([[mixdownPopUp selectedItem] tag] == HB_ACODEC_AC3)
3553     {
3554         
3555         /* lets also set the bitrate popup to the input bitrate as thats what passthru will use */
3556         [bitratePopUp removeAllItems];
3557         NSMenuItem *menuItem = [[bitratePopUp menu] addItemWithTitle:
3558                                 [NSString stringWithFormat:@"%d", inputbitrate]
3559                                                               action: NULL keyEquivalent: @""];
3560         [menuItem setTag: inputbitrate];
3561         /* For ac3 passthru we disable the sample rate and bitrate popups as well as the drc slider*/
3562         [bitratePopUp setEnabled: NO];
3563         [sampleratePopUp setEnabled: NO];
3564         
3565         [drcSlider setFloatValue: 1.00];
3566         [self audioDRCSliderChanged: drcSlider];
3567         [drcSlider setEnabled: NO];
3568         [drcField setEnabled: NO];
3569     }
3570     else
3571     {
3572         [sampleratePopUp setEnabled: YES];
3573         [bitratePopUp setEnabled: YES];
3574         [drcSlider setEnabled: YES];
3575         [drcField setEnabled: YES];
3576     }
3577     
3578 }
3579
3580 - (IBAction) audioDRCSliderChanged: (id) sender
3581 {
3582     NSSlider * drcSlider;
3583     NSTextField * drcField;
3584     if (sender == fAudTrack1DrcSlider)
3585     {
3586         drcSlider = fAudTrack1DrcSlider;
3587         drcField = fAudTrack1DrcField;
3588     }
3589     else if (sender == fAudTrack2DrcSlider)
3590     {
3591         drcSlider = fAudTrack2DrcSlider;
3592         drcField = fAudTrack2DrcField;
3593     }
3594     else if (sender == fAudTrack3DrcSlider)
3595     {
3596         drcSlider = fAudTrack3DrcSlider;
3597         drcField = fAudTrack3DrcField;
3598     }
3599     else
3600     {
3601         drcSlider = fAudTrack4DrcSlider;
3602         drcField = fAudTrack4DrcField;
3603     }
3604     [drcField setStringValue: [NSString stringWithFormat: @"%.2f", [drcSlider floatValue]]];
3605     /* For now, do not call this until we have an intelligent way to determine audio track selections
3606     * compared to presets
3607     */
3608     //[self customSettingUsed: sender];
3609 }
3610
3611 - (IBAction) subtitleSelectionChanged: (id) sender
3612 {
3613         if ([fSubPopUp indexOfSelectedItem] == 0)
3614         {
3615         [fSubForcedCheck setState: NSOffState];
3616         [fSubForcedCheck setEnabled: NO];       
3617         }
3618         else
3619         {
3620         [fSubForcedCheck setEnabled: YES];      
3621         }
3622         
3623 }
3624
3625
3626
3627
3628 #pragma mark -
3629 #pragma mark Open New Windows
3630
3631 - (IBAction) openHomepage: (id) sender
3632 {
3633     [[NSWorkspace sharedWorkspace] openURL: [NSURL
3634         URLWithString:@"http://handbrake.fr/"]];
3635 }
3636
3637 - (IBAction) openForums: (id) sender
3638 {
3639     [[NSWorkspace sharedWorkspace] openURL: [NSURL
3640         URLWithString:@"http://handbrake.fr/forum/"]];
3641 }
3642 - (IBAction) openUserGuide: (id) sender
3643 {
3644     [[NSWorkspace sharedWorkspace] openURL: [NSURL
3645         URLWithString:@"http://handbrake.fr/trac/wiki/HandBrakeGuide"]];
3646 }
3647
3648 /**
3649  * Shows debug output window.
3650  */
3651 - (IBAction)showDebugOutputPanel:(id)sender
3652 {
3653     [outputPanel showOutputPanel:sender];
3654 }
3655
3656 /**
3657  * Shows preferences window.
3658  */
3659 - (IBAction) showPreferencesWindow: (id) sender
3660 {
3661     NSWindow * window = [fPreferencesController window];
3662     if (![window isVisible])
3663         [window center];
3664
3665     [window makeKeyAndOrderFront: nil];
3666 }
3667
3668 /**
3669  * Shows queue window.
3670  */
3671 - (IBAction) showQueueWindow:(id)sender
3672 {
3673     [fQueueController showQueueWindow:sender];
3674 }
3675
3676
3677 - (IBAction) toggleDrawer:(id)sender {
3678     [fPresetDrawer toggle:self];
3679 }
3680
3681 /**
3682  * Shows Picture Settings Window.
3683  */
3684
3685 - (IBAction) showPicturePanel: (id) sender
3686 {
3687         hb_list_t  * list  = hb_get_titles( fHandle );
3688     hb_title_t * title = (hb_title_t *) hb_list_item( list,
3689             [fSrcTitlePopUp indexOfSelectedItem] );
3690     [fPictureController showPanelInWindow:fWindow forTitle:title];
3691 }
3692
3693 #pragma mark -
3694 #pragma mark Preset Outline View Methods
3695 #pragma mark - Required
3696 /* These are required by the NSOutlineView Datasource Delegate */
3697 /* We use this to deterimine children of an item */
3698 - (id)outlineView:(NSOutlineView *)fPresetsOutlineView child:(NSInteger)index ofItem:(id)item
3699 {
3700 if (item == nil)
3701         return [UserPresets objectAtIndex:index];
3702     
3703     // We are only one level deep, so we can't be asked about children
3704     NSAssert (NO, @"Presets View outlineView:child:ofItem: currently can't handle nested items.");
3705     return nil;
3706 }
3707 /* We use this to determine if an item should be expandable */
3708 - (BOOL)outlineView:(NSOutlineView *)fPresetsOutlineView isItemExpandable:(id)item
3709 {
3710
3711     /* For now, we maintain one level, so set to no
3712     * when nested, we set to yes for any preset "folders"
3713     */
3714     return NO;
3715
3716 }
3717 /* used to specify the number of levels to show for each item */
3718 - (int)outlineView:(NSOutlineView *)fPresetsOutlineView numberOfChildrenOfItem:(id)item
3719 {
3720     /* currently use no levels to test outline view viability */
3721     if (item == nil)
3722         return [UserPresets count];
3723     else
3724         return 0;
3725 }
3726 /* Used to tell the outline view which information is to be displayed per item */
3727 - (id)outlineView:(NSOutlineView *)fPresetsOutlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
3728 {
3729         /* We have two columns right now, icon and PresetName */
3730         
3731     if ([[tableColumn identifier] isEqualToString:@"PresetName"])
3732     {
3733         return [item objectForKey:@"PresetName"];
3734     }
3735     else
3736     {
3737         return @"something";
3738     }
3739 }
3740
3741 #pragma mark - Added Functionality (optional)
3742 /* Use to customize the font and display characteristics of the title cell */
3743 - (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
3744 {
3745     if ([[tableColumn identifier] isEqualToString:@"PresetName"])
3746     {
3747         NSDictionary *userPresetDict = item;
3748         NSFont *txtFont;
3749         NSColor *fontColor;
3750         NSColor *shadowColor;
3751         txtFont = [NSFont systemFontOfSize: [NSFont smallSystemFontSize]];
3752         /*check to see if its a selected row */
3753         if ([fPresetsOutlineView selectedRow] == [fPresetsOutlineView rowForItem:item])
3754         {
3755             
3756             fontColor = [NSColor blackColor];
3757             shadowColor = [NSColor colorWithDeviceRed:(127.0/255.0) green:(140.0/255.0) blue:(160.0/255.0) alpha:1.0];
3758         }
3759         else
3760         {
3761             if ([[userPresetDict objectForKey:@"Type"] intValue] == 0)
3762             {
3763                 fontColor = [NSColor blueColor];
3764             }
3765             else // User created preset, use a black font
3766             {
3767                 fontColor = [NSColor blackColor];
3768             }
3769             shadowColor = nil;
3770         }
3771         /* We use Bold Text for the HB Default */
3772         if ([[userPresetDict objectForKey:@"Default"] intValue] == 1)// 1 is HB default
3773         {
3774             txtFont = [NSFont boldSystemFontOfSize: [NSFont smallSystemFontSize]];
3775         }
3776         /* We use Bold Text for the User Specified Default */
3777         if ([[userPresetDict objectForKey:@"Default"] intValue] == 2)// 2 is User default
3778         {
3779             txtFont = [NSFont boldSystemFontOfSize: [NSFont smallSystemFontSize]];
3780         }
3781         
3782         
3783         [cell setTextColor:fontColor];
3784         [cell setFont:txtFont];
3785         
3786     }
3787 }
3788
3789 /* We use this to edit the name field in the outline view */
3790 - (void)outlineView:(NSOutlineView *)outlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
3791 {
3792     if ([[tableColumn identifier] isEqualToString:@"PresetName"])
3793     {
3794         id theRecord;
3795         
3796         theRecord = item;
3797         [theRecord setObject:object forKey:@"PresetName"];
3798         
3799         [self sortPresets];
3800         
3801         [fPresetsOutlineView reloadData];
3802         /* We save all of the preset data here */
3803         [self savePreset];
3804     }
3805 }
3806 /* We use this to provide tooltips for the items in the presets outline view */
3807 - (NSString *)outlineView:(NSOutlineView *)fPresetsOutlineView toolTipForCell:(NSCell *)cell rect:(NSRectPointer)rect tableColumn:(NSTableColumn *)tc item:(id)item mouseLocation:(NSPoint)mouseLocation
3808 {
3809     //if ([[tc identifier] isEqualToString:@"PresetName"])
3810     //{
3811         /* initialize the tooltip contents variable */
3812         NSString *loc_tip;
3813         /* if there is a description for the preset, we show it in the tooltip */
3814         if ([item objectForKey:@"PresetDescription"])
3815         {
3816             loc_tip = [item objectForKey:@"PresetDescription"];
3817             return (loc_tip);
3818         }
3819         else
3820         {
3821             loc_tip = @"No description available";
3822         }
3823         return (loc_tip);
3824     //}
3825 }
3826
3827 #pragma mark -
3828 #pragma mark Preset Outline View Methods (dragging related)
3829
3830
3831 - (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard
3832 {
3833         // Dragging is only allowed for custom presets.
3834         if ([[[UserPresets objectAtIndex:[fPresetsOutlineView selectedRow]] objectForKey:@"Type"] intValue] == 0) // 0 is built in preset
3835     {
3836         return NO;
3837     }
3838     // Don't retain since this is just holding temporaral drag information, and it is
3839     //only used during a drag!  We could put this in the pboard actually.
3840     fDraggedNodes = items;
3841     // Provide data for our custom type, and simple NSStrings.
3842     [pboard declareTypes:[NSArray arrayWithObjects: DragDropSimplePboardType, nil] owner:self];
3843     
3844     // the actual data doesn't matter since DragDropSimplePboardType drags aren't recognized by anyone but us!.
3845     [pboard setData:[NSData data] forType:DragDropSimplePboardType]; 
3846     
3847     return YES;
3848 }
3849
3850 - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(int)index
3851 {
3852         // Don't allow dropping ONTO an item since they can't really contain any children.
3853     
3854     BOOL isOnDropTypeProposal = index == NSOutlineViewDropOnItemIndex;
3855     if (isOnDropTypeProposal)
3856         return NSDragOperationNone;
3857     
3858     
3859         // Don't allow dropping INTO an item since they can't really contain any children as of yet.
3860         
3861     if (item != nil)
3862         {
3863                 index = [fPresetsOutlineView rowForItem: item] + 1;
3864                 item = nil;
3865         }
3866     
3867     // Don't allow dropping into the Built In Presets.
3868     if (index < presetCurrentBuiltInCount)
3869     {
3870         return NSDragOperationNone;
3871         index = MAX (index, presetCurrentBuiltInCount);
3872         }
3873         
3874     [outlineView setDropItem:item dropChildIndex:index];
3875     return NSDragOperationGeneric;
3876 }
3877
3878
3879
3880 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(int)index
3881 {
3882     NSMutableIndexSet *moveItems = [NSMutableIndexSet indexSet];
3883     
3884     id obj;
3885     NSEnumerator *enumerator = [fDraggedNodes objectEnumerator];
3886     while (obj = [enumerator nextObject])
3887     {
3888         [moveItems addIndex:[UserPresets indexOfObject:obj]];
3889     }
3890     // Successful drop, lets rearrange the view and save it all
3891     [self moveObjectsInPresetsArray:UserPresets fromIndexes:moveItems toIndex: index];
3892     [fPresetsOutlineView reloadData];
3893     [self savePreset];
3894     return YES;
3895 }
3896
3897 - (void)moveObjectsInPresetsArray:(NSMutableArray *)array fromIndexes:(NSIndexSet *)indexSet toIndex:(unsigned)insertIndex
3898 {
3899     unsigned index = [indexSet lastIndex];
3900     unsigned aboveInsertIndexCount = 0;
3901     
3902     while (index != NSNotFound)
3903     {
3904         unsigned removeIndex;
3905         
3906         if (index >= insertIndex)
3907         {
3908             removeIndex = index + aboveInsertIndexCount;
3909             aboveInsertIndexCount++;
3910         }
3911         else
3912         {
3913             removeIndex = index;
3914             insertIndex--;
3915         }
3916         
3917         id object = [[array objectAtIndex:removeIndex] retain];
3918         [array removeObjectAtIndex:removeIndex];
3919         [array insertObject:object atIndex:insertIndex];
3920         [object release];
3921         
3922         index = [indexSet indexLessThanIndex:index];
3923     }
3924 }
3925
3926
3927
3928 #pragma mark - Functional Preset NSOutlineView Methods
3929
3930 - (IBAction)selectPreset:(id)sender
3931 {
3932
3933     if ([fPresetsOutlineView selectedRow] >= 0)
3934     {
3935         chosenPreset = [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]];
3936         /* we set the preset display field in main window here */
3937         [fPresetSelectedDisplay setStringValue:[chosenPreset objectForKey:@"PresetName"]];
3938         if ([[chosenPreset objectForKey:@"Default"] intValue] == 1)
3939         {
3940             [fPresetSelectedDisplay setStringValue:[NSString stringWithFormat:@"%@ (Default)", [chosenPreset objectForKey:@"PresetName"]]];
3941         }
3942         else
3943         {
3944             [fPresetSelectedDisplay setStringValue:[chosenPreset objectForKey:@"PresetName"]];
3945         }
3946         /* File Format */
3947         [fDstFormatPopUp selectItemWithTitle:[chosenPreset objectForKey:@"FileFormat"]];
3948         [self formatPopUpChanged:nil];
3949
3950         /* Chapter Markers*/
3951         [fCreateChapterMarkers setState:[[chosenPreset objectForKey:@"ChapterMarkers"] intValue]];
3952         /* Allow Mpeg4 64 bit formatting +4GB file sizes */
3953         [fDstMp4LargeFileCheck setState:[[chosenPreset objectForKey:@"Mp4LargeFile"] intValue]];
3954         /* Mux mp4 with http optimization */
3955         [fDstMp4HttpOptFileCheck setState:[[chosenPreset objectForKey:@"Mp4HttpOptimize"] intValue]];
3956
3957         /* Video encoder */
3958         /* We set the advanced opt string here if applicable*/
3959         [fAdvancedOptions setOptions:[chosenPreset objectForKey:@"x264Option"]];
3960         /* We use a conditional to account for the new x264 encoder dropdown as well as presets made using legacy x264 settings*/
3961         if ([[chosenPreset objectForKey:@"VideoEncoder"] isEqualToString:@"x264 (h.264 Main)"] ||
3962             [[chosenPreset objectForKey:@"VideoEncoder"] isEqualToString:@"x264 (h.264 iPod)"] ||
3963             [[chosenPreset objectForKey:@"VideoEncoder"] isEqualToString:@"x264"])
3964         {
3965             [fVidEncoderPopUp selectItemWithTitle:@"H.264 (x264)"];
3966             /* special case for legacy preset to check the new fDstMp4HttpOptFileCheck checkbox to set the ipod atom */
3967             if ([[chosenPreset objectForKey:@"VideoEncoder"] isEqualToString:@"x264 (h.264 iPod)"])
3968             {
3969                 [fDstMp4iPodFileCheck setState:NSOnState];
3970                 /* We also need to add "level=30:" to the advanced opts string to set the correct level for the iPod when
3971                  encountering a legacy preset as it used to be handled separately from the opt string*/
3972                 [fAdvancedOptions setOptions:[@"level=30:" stringByAppendingString:[fAdvancedOptions optionsString]]];
3973             }
3974             else
3975             {
3976                 [fDstMp4iPodFileCheck setState:NSOffState];
3977             }
3978         }
3979         else if ([[chosenPreset objectForKey:@"VideoEncoder"] isEqualToString:@"FFmpeg"])
3980         {
3981             [fVidEncoderPopUp selectItemWithTitle:@"MPEG-4 (FFmpeg)"];
3982         }
3983         else if ([[chosenPreset objectForKey:@"VideoEncoder"] isEqualToString:@"XviD"])
3984         {
3985             [fVidEncoderPopUp selectItemWithTitle:@"MPEG-4 (XviD)"];
3986         }
3987         else
3988         {
3989             [fVidEncoderPopUp selectItemWithTitle:[chosenPreset objectForKey:@"VideoEncoder"]];
3990         }
3991
3992         /* Lets run through the following functions to get variables set there */
3993         [self videoEncoderPopUpChanged:nil];
3994         /* Set the state of ipod compatible with Mp4iPodCompatible. Only for x264*/
3995         [fDstMp4iPodFileCheck setState:[[chosenPreset objectForKey:@"Mp4iPodCompatible"] intValue]];
3996         [self calculateBitrate:nil];
3997
3998         /* Video quality */
3999         [fVidQualityMatrix selectCellAtRow:[[chosenPreset objectForKey:@"VideoQualityType"] intValue] column:0];
4000
4001         [fVidTargetSizeField setStringValue:[chosenPreset objectForKey:@"VideoTargetSize"]];
4002         [fVidBitrateField setStringValue:[chosenPreset objectForKey:@"VideoAvgBitrate"]];
4003         [fVidQualitySlider setFloatValue:[[chosenPreset objectForKey:@"VideoQualitySlider"] floatValue]];
4004
4005         [self videoMatrixChanged:nil];
4006
4007         /* Video framerate */
4008         /* For video preset video framerate, we want to make sure that Same as source does not conflict with the
4009          detected framerate in the fVidRatePopUp so we use index 0*/
4010         if ([[chosenPreset objectForKey:@"VideoFramerate"] isEqualToString:@"Same as source"])
4011         {
4012             [fVidRatePopUp selectItemAtIndex: 0];
4013         }
4014         else
4015         {
4016             [fVidRatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"VideoFramerate"]];
4017         }
4018
4019         /* GrayScale */
4020         [fVidGrayscaleCheck setState:[[chosenPreset objectForKey:@"VideoGrayScale"] intValue]];
4021
4022         /* 2 Pass Encoding */
4023         [fVidTwoPassCheck setState:[[chosenPreset objectForKey:@"VideoTwoPass"] intValue]];
4024         [self twoPassCheckboxChanged:nil];
4025         /* Turbo 1st pass for 2 Pass Encoding */
4026         [fVidTurboPassCheck setState:[[chosenPreset objectForKey:@"VideoTurboTwoPass"] intValue]];
4027
4028         /*Audio*/
4029         if ([chosenPreset objectForKey:@"FileCodecs"])
4030         {
4031             /* We need to handle the audio codec popup by determining what was chosen from the deprecated Codecs PopUp for past presets*/
4032             if ([[chosenPreset objectForKey:@"FileCodecs"] isEqualToString: @"AVC/H.264 Video / AAC + AC3 Audio"])
4033             {
4034                 /* We need to address setting languages etc. here in the new multi track audio panel */
4035                 /* Track One set here */
4036                 /*for track one though a track should be selected but lets check here anyway and use track one if its not.*/
4037                 if ([fAudLang1PopUp indexOfSelectedItem] == 0)
4038                 {
4039                     [fAudLang1PopUp selectItemAtIndex: 1];
4040                     [self audioTrackPopUpChanged: fAudLang1PopUp];
4041                 }
4042                 [fAudTrack1CodecPopUp selectItemWithTitle: @"AAC (faac)"];
4043                 [self audioTrackPopUpChanged: fAudTrack1CodecPopUp];
4044                 /* Track Two, set source same as track one */
4045                 [fAudLang2PopUp selectItemAtIndex: [fAudLang1PopUp indexOfSelectedItem]];
4046                 [self audioTrackPopUpChanged: fAudLang2PopUp];
4047                 [fAudTrack2CodecPopUp selectItemWithTitle: @"AC3 Passthru"];
4048                 [self audioTrackPopUpChanged: fAudTrack2CodecPopUp];
4049             }
4050             else if ([[chosenPreset objectForKey:@"FileCodecs"] isEqualToString:@"MPEG-4 Video / AAC Audio"] ||
4051                      [[chosenPreset objectForKey:@"FileCodecs"] isEqualToString:@"AVC/H.264 Video / AAC Audio"])
4052             {
4053                 if ([fAudLang1PopUp indexOfSelectedItem] > 0)
4054                 {
4055                     [fAudTrack1CodecPopUp selectItemWithTitle: @"AAC (faac)"];
4056                     [self audioTrackPopUpChanged: fAudTrack1CodecPopUp];
4057                 }
4058                 if ([fAudLang2PopUp indexOfSelectedItem] > 0)
4059                 {
4060                     [fAudTrack2CodecPopUp selectItemWithTitle: @"AAC (faac)"];
4061                     [self audioTrackPopUpChanged: fAudTrack2CodecPopUp];
4062                 }
4063                 if ([fAudLang3PopUp indexOfSelectedItem] > 0)
4064                 {
4065                     [fAudTrack3CodecPopUp selectItemWithTitle: @"AAC (faac)"];
4066                     [self audioTrackPopUpChanged: fAudTrack3CodecPopUp];
4067                 }
4068                 if ([fAudLang4PopUp indexOfSelectedItem] > 0)
4069                 {
4070                     [fAudTrack4CodecPopUp selectItemWithTitle: @"AAC (faac)"];
4071                     [self audioTrackPopUpChanged: fAudTrack4CodecPopUp];
4072                 }
4073             }
4074             else if ([[chosenPreset objectForKey:@"FileCodecs"] isEqualToString:@"MPEG-4 Video / AC-3 Audio"] ||
4075                      [[chosenPreset objectForKey:@"FileCodecs"] isEqualToString:@"AVC/H.264 Video / AC-3 Audio"])
4076             {
4077                 if ([fAudLang1PopUp indexOfSelectedItem] > 0)
4078                 {
4079                     [fAudTrack1CodecPopUp selectItemWithTitle: @"AC3 Passthru"];
4080                     [self audioTrackPopUpChanged: fAudTrack1CodecPopUp];
4081                 }
4082                 if ([fAudLang2PopUp indexOfSelectedItem] > 0)
4083                 {
4084                     [fAudTrack2CodecPopUp selectItemWithTitle: @"AC3 Passthru"];
4085                     [self audioTrackPopUpChanged: fAudTrack2CodecPopUp];
4086                 }
4087                 if ([fAudLang3PopUp indexOfSelectedItem] > 0)
4088                 {
4089                     [fAudTrack3CodecPopUp selectItemWithTitle: @"AC3 Passthru"];
4090                     [self audioTrackPopUpChanged: fAudTrack3CodecPopUp];
4091                 }
4092                 if ([fAudLang4PopUp indexOfSelectedItem] > 0)
4093                 {
4094                     [fAudTrack4CodecPopUp selectItemWithTitle: @"AC3 Passthru"];
4095                     [self audioTrackPopUpChanged: fAudTrack4CodecPopUp];
4096                 }
4097             }
4098             else if ([[chosenPreset objectForKey:@"FileCodecs"] isEqualToString:@"MPEG-4 Video / MP3 Audio"] ||
4099                      [[chosenPreset objectForKey:@"FileCodecs"] isEqualToString:@"AVC/H.264 Video / MP3 Audio"])
4100             {
4101                 if ([fAudLang1PopUp indexOfSelectedItem] > 0)
4102                 {
4103                     [fAudTrack1CodecPopUp selectItemWithTitle: @"MP3 (lame)"];
4104                     [self audioTrackPopUpChanged: fAudTrack1CodecPopUp];
4105                 }
4106                 if ([fAudLang2PopUp indexOfSelectedItem] > 0)
4107                 {
4108                     [fAudTrack2CodecPopUp selectItemWithTitle: @"MP3 (lame)"];
4109                     [self audioTrackPopUpChanged: fAudTrack2CodecPopUp];
4110                 }
4111                 if ([fAudLang3PopUp indexOfSelectedItem] > 0)
4112                 {
4113                     [fAudTrack3CodecPopUp selectItemWithTitle: @"MP3 (lame)"];
4114                     [self audioTrackPopUpChanged: fAudTrack3CodecPopUp];
4115                 }
4116                 if ([fAudLang4PopUp indexOfSelectedItem] > 0)
4117                 {
4118                     [fAudTrack4CodecPopUp selectItemWithTitle: @"MP3 (lame)"];
4119                     [self audioTrackPopUpChanged: fAudTrack4CodecPopUp];
4120                 }
4121             }
4122             else if ([[chosenPreset objectForKey:@"FileCodecs"] isEqualToString:@"MPEG-4 Video / Vorbis Audio"])
4123             {
4124                 if ([fAudLang1PopUp indexOfSelectedItem] > 0)
4125                 {
4126                     [fAudTrack1CodecPopUp selectItemWithTitle: @"Vorbis (vorbis)"];
4127                     [self audioTrackPopUpChanged: fAudTrack1CodecPopUp];
4128                 }
4129                 if ([fAudLang2PopUp indexOfSelectedItem] > 0)
4130                 {
4131                     [fAudTrack2CodecPopUp selectItemWithTitle: @"Vorbis (vorbis)"];
4132                     [self audioTrackPopUpChanged: fAudTrack2CodecPopUp];
4133                 }
4134                 if ([fAudLang3PopUp indexOfSelectedItem] > 0)
4135                 {
4136                     [fAudTrack3CodecPopUp selectItemWithTitle: @"Vorbis (vorbis)"];
4137                     [self audioTrackPopUpChanged: fAudTrack3CodecPopUp];
4138                 }
4139                 if ([fAudLang4PopUp indexOfSelectedItem] > 0)
4140                 {
4141                     [fAudTrack4CodecPopUp selectItemWithTitle: @"Vorbis (vorbis)"];
4142                     [self audioTrackPopUpChanged: fAudTrack4CodecPopUp];
4143                 }
4144             }
4145             /* We detect here if we have the old audio sample rate and if so we apply samplerate and bitrate to the existing four tracks if chosen
4146             * UNLESS the CodecPopUp is AC3 in which case the preset values are ignored in favor of rates set in audioTrackMixdownChanged*/
4147             if ([chosenPreset objectForKey:@"AudioSampleRate"])
4148             {
4149                 if ([fAudLang1PopUp indexOfSelectedItem] > 0 && [fAudTrack1CodecPopUp titleOfSelectedItem] != @"AC3 Passthru")
4150                 {
4151                     [fAudTrack1RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"AudioSampleRate"]];
4152                     [fAudTrack1BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"AudioBitRate"]];
4153                 }
4154                 if ([fAudLang2PopUp indexOfSelectedItem] > 0 && [fAudTrack2CodecPopUp titleOfSelectedItem] != @"AC3 Passthru")
4155                 {
4156                     [fAudTrack2RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"AudioSampleRate"]];
4157                     [fAudTrack2BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"AudioBitRate"]];
4158                 }
4159                 if ([fAudLang3PopUp indexOfSelectedItem] > 0 && [fAudTrack3CodecPopUp titleOfSelectedItem] != @"AC3 Passthru")
4160                 {
4161                     [fAudTrack3RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"AudioSampleRate"]];
4162                     [fAudTrack3BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"AudioBitRate"]];
4163                 }
4164                 if ([fAudLang4PopUp indexOfSelectedItem] > 0 && [fAudTrack4CodecPopUp titleOfSelectedItem] != @"AC3 Passthru")
4165                 {
4166                     [fAudTrack4RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"AudioSampleRate"]];
4167                     [fAudTrack4BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"AudioBitRate"]];
4168                 }
4169             }
4170             /* We detect here if we have the old DRC Slider and if so we apply it to the existing four tracks if chosen */
4171             if ([chosenPreset objectForKey:@"AudioDRCSlider"])
4172             {
4173                 if ([fAudLang1PopUp indexOfSelectedItem] > 0)
4174                 {
4175                     [fAudTrack1DrcSlider setFloatValue:[[chosenPreset objectForKey:@"AudioDRCSlider"] floatValue]];
4176                     [self audioDRCSliderChanged: fAudTrack1DrcSlider];
4177                 }
4178                 if ([fAudLang2PopUp indexOfSelectedItem] > 0)
4179                 {
4180                     [fAudTrack2DrcSlider setFloatValue:[[chosenPreset objectForKey:@"AudioDRCSlider"] floatValue]];
4181                     [self audioDRCSliderChanged: fAudTrack2DrcSlider];
4182                 }
4183                 if ([fAudLang3PopUp indexOfSelectedItem] > 0)
4184                 {
4185                     [fAudTrack3DrcSlider setFloatValue:[[chosenPreset objectForKey:@"AudioDRCSlider"] floatValue]];
4186                     [self audioDRCSliderChanged: fAudTrack3DrcSlider];
4187                 }
4188                 if ([fAudLang4PopUp indexOfSelectedItem] > 0)
4189                 {
4190                     [fAudTrack4DrcSlider setFloatValue:[[chosenPreset objectForKey:@"AudioDRCSlider"] floatValue]];
4191                     [self audioDRCSliderChanged: fAudTrack4DrcSlider];
4192                 }
4193             }
4194         }
4195         else // since there was no codecs key in the preset we know we can use new multi-audio track presets
4196         {
4197             if ([chosenPreset objectForKey:@"Audio1Track"] > 0)
4198             {
4199                 if ([fAudLang1PopUp indexOfSelectedItem] == 0)
4200                 {
4201                     [fAudLang1PopUp selectItemAtIndex: 1];
4202                 }
4203                 [self audioTrackPopUpChanged: fAudLang1PopUp];
4204                 [fAudTrack1CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Encoder"]];
4205                 [self audioTrackPopUpChanged: fAudTrack1CodecPopUp];
4206                 [fAudTrack1MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Mixdown"]];
4207                 /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
4208                  * mixdown*/
4209                 if  ([fAudTrack1MixPopUp selectedItem] == nil)
4210                 {
4211                     [self audioTrackPopUpChanged: fAudTrack1CodecPopUp];
4212                 }
4213                 [fAudTrack1RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Samplerate"]];
4214                 /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
4215                 if (![[chosenPreset objectForKey:@"Audio1Encoder"] isEqualToString:@"AC3 Passthru"])
4216                 {
4217                     [fAudTrack1BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Bitrate"]];
4218                 }
4219                 [fAudTrack1DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio1TrackDRCSlider"] floatValue]];
4220                 [self audioDRCSliderChanged: fAudTrack1DrcSlider];
4221             }
4222             if ([chosenPreset objectForKey:@"Audio2Track"] > 0)
4223             {
4224                 if ([fAudLang2PopUp indexOfSelectedItem] == 0)
4225                 {
4226                     [fAudLang2PopUp selectItemAtIndex: 1];
4227                 }
4228                 [self audioTrackPopUpChanged: fAudLang2PopUp];
4229                 [fAudTrack2CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Encoder"]];
4230                 [self audioTrackPopUpChanged: fAudTrack2CodecPopUp];
4231                 [fAudTrack2MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Mixdown"]];
4232                 /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
4233                  * mixdown*/
4234                 if  ([fAudTrack2MixPopUp selectedItem] == nil)
4235                 {
4236                     [self audioTrackPopUpChanged: fAudTrack2CodecPopUp];
4237                 }
4238                 [fAudTrack2RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Samplerate"]];
4239                 /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
4240                 if (![[chosenPreset objectForKey:@"Audio2Encoder"] isEqualToString:@"AC3 Passthru"])
4241                 {
4242                     [fAudTrack2BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Bitrate"]];
4243                 }
4244                 [fAudTrack2DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio2TrackDRCSlider"] floatValue]];
4245                 [self audioDRCSliderChanged: fAudTrack2DrcSlider];
4246             }
4247             if ([chosenPreset objectForKey:@"Audio3Track"] > 0)
4248             {
4249                 if ([fAudLang3PopUp indexOfSelectedItem] == 0)
4250                 {
4251                     [fAudLang3PopUp selectItemAtIndex: 1];
4252                 }
4253                 [self audioTrackPopUpChanged: fAudLang3PopUp];
4254                 [fAudTrack3CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Encoder"]];
4255                 [self audioTrackPopUpChanged: fAudTrack3CodecPopUp];
4256                 [fAudTrack3MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Mixdown"]];
4257                 /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
4258                  * mixdown*/
4259                 if  ([fAudTrack3MixPopUp selectedItem] == nil)
4260                 {
4261                     [self audioTrackPopUpChanged: fAudTrack3CodecPopUp];
4262                 }
4263                 [fAudTrack3RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Samplerate"]];
4264                 /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
4265                 if (![[chosenPreset objectForKey:@"Audio3Encoder"] isEqualToString: @"AC3 Passthru"])
4266                 {
4267                     [fAudTrack3BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Bitrate"]];
4268                 }
4269                 [fAudTrack3DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio3TrackDRCSlider"] floatValue]];
4270                 [self audioDRCSliderChanged: fAudTrack3DrcSlider];
4271             }
4272             if ([chosenPreset objectForKey:@"Audio4Track"] > 0)
4273             {
4274                 if ([fAudLang4PopUp indexOfSelectedItem] == 0)
4275                 {
4276                     [fAudLang4PopUp selectItemAtIndex: 1];
4277                 }
4278                 [self audioTrackPopUpChanged: fAudLang4PopUp];
4279                 [fAudTrack4CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Encoder"]];
4280                 [self audioTrackPopUpChanged: fAudTrack4CodecPopUp];
4281                 [fAudTrack4MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Mixdown"]];
4282                 /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
4283                  * mixdown*/
4284                 if  ([fAudTrack4MixPopUp selectedItem] == nil)
4285                 {
4286                     [self audioTrackPopUpChanged: fAudTrack4CodecPopUp];
4287                 }
4288                 [fAudTrack4RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Samplerate"]];
4289                 /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
4290                 if (![[chosenPreset objectForKey:@"Audio4Encoder"] isEqualToString:@"AC3 Passthru"])
4291                 {
4292                     [fAudTrack4BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Bitrate"]];
4293                 }
4294                 [fAudTrack4DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio4TrackDRCSlider"] floatValue]];
4295                 [self audioDRCSliderChanged: fAudTrack4DrcSlider];
4296             }
4297
4298
4299         }
4300
4301         /* We now cleanup any extra audio tracks that may be previously set if we need to, we do it here so we don't have to
4302          * duplicate any code for legacy presets.*/
4303         /* First we handle the legacy Codecs crazy AVC/H.264 Video / AAC + AC3 Audio atv hybrid */
4304         if ([chosenPreset objectForKey:@"FileCodecs"] && [[chosenPreset objectForKey:@"FileCodecs"] isEqualToString:@"AVC/H.264 Video / AAC + AC3 Audio"])
4305         {
4306             [fAudLang3PopUp selectItemAtIndex: 0];
4307             [self audioTrackPopUpChanged: fAudLang3PopUp];
4308             [fAudLang4PopUp selectItemAtIndex: 0];
4309             [self audioTrackPopUpChanged: fAudLang4PopUp];
4310         }
4311         else
4312         {
4313             if (![chosenPreset objectForKey:@"Audio2Track"] || [chosenPreset objectForKey:@"Audio2Track"] == 0)
4314             {
4315                 [fAudLang2PopUp selectItemAtIndex: 0];
4316                 [self audioTrackPopUpChanged: fAudLang2PopUp];
4317             }
4318             if (![chosenPreset objectForKey:@"Audio3Track"] || [chosenPreset objectForKey:@"Audio3Track"] > 0)
4319             {
4320                 [fAudLang3PopUp selectItemAtIndex: 0];
4321                 [self audioTrackPopUpChanged: fAudLang3PopUp];
4322             }
4323             if (![chosenPreset objectForKey:@"Audio4Track"] || [chosenPreset objectForKey:@"Audio4Track"] > 0)
4324             {
4325                 [fAudLang4PopUp selectItemAtIndex: 0];
4326                 [self audioTrackPopUpChanged: fAudLang4PopUp];
4327             }
4328         }
4329
4330         /*Subtitles*/
4331         [fSubPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Subtitles"]];
4332         /* Forced Subtitles */
4333         [fSubForcedCheck setState:[[chosenPreset objectForKey:@"SubtitlesForced"] intValue]];
4334
4335         /* Picture Settings */
4336         /* Note: objectForKey:@"UsesPictureSettings" now refers to picture size, this encompasses:
4337          * height, width, keep ar, anamorphic and crop settings.
4338          * picture filters are now handled separately.
4339          * We will be able to actually change the key names for legacy preset keys when preset file
4340          * update code is done. But for now, lets hang onto the old legacy key name for backwards compatibility.
4341          */
4342         /* Check to see if the objectForKey:@"UsesPictureSettings is greater than 0, as 0 means use picture sizing "None" 
4343          * and the preset completely ignores any picture sizing values in the preset.
4344          */
4345         if ([[chosenPreset objectForKey:@"UsesPictureSettings"]  intValue] > 0)
4346         {
4347             hb_job_t * job = fTitle->job;
4348             /* Check to see if the objectForKey:@"UsesPictureSettings is 2 which is "Use Max for the source */
4349             if ([[chosenPreset objectForKey:@"UsesPictureSettings"]  intValue] == 2 || [[chosenPreset objectForKey:@"UsesMaxPictureSettings"]  intValue] == 1)
4350             {
4351                 /* Use Max Picture settings for whatever the dvd is.*/
4352                 [self revertPictureSizeToMax:nil];
4353                 job->keep_ratio = [[chosenPreset objectForKey:@"PictureKeepRatio"]  intValue];
4354                 if (job->keep_ratio == 1)
4355                 {
4356                     hb_fix_aspect( job, HB_KEEP_WIDTH );
4357                     if( job->height > fTitle->height )
4358                     {
4359                         job->height = fTitle->height;
4360                         hb_fix_aspect( job, HB_KEEP_HEIGHT );
4361                     }
4362                 }
4363                 job->pixel_ratio = [[chosenPreset objectForKey:@"PicturePAR"]  intValue];
4364             }
4365             else // /* If not 0 or 2 we assume objectForKey:@"UsesPictureSettings is 1 which is "Use picture sizing from when the preset was set" */
4366             {
4367                 /* we check to make sure the presets width/height does not exceed the sources width/height */
4368                 if (fTitle->width < [[chosenPreset objectForKey:@"PictureWidth"]  intValue] || fTitle->height < [[chosenPreset objectForKey:@"PictureHeight"]  intValue])
4369                 {
4370                     /* if so, then we use the sources height and width to avoid scaling up */
4371                     job->width = fTitle->width;
4372                     job->height = fTitle->height;
4373                 }
4374                 else // source width/height is >= the preset height/width
4375                 {
4376                     /* we can go ahead and use the presets values for height and width */
4377                     job->width = [[chosenPreset objectForKey:@"PictureWidth"]  intValue];
4378                     job->height = [[chosenPreset objectForKey:@"PictureHeight"]  intValue];
4379                 }
4380                 job->keep_ratio = [[chosenPreset objectForKey:@"PictureKeepRatio"]  intValue];
4381                 if (job->keep_ratio == 1)
4382                 {
4383                     hb_fix_aspect( job, HB_KEEP_WIDTH );
4384                     if( job->height > fTitle->height )
4385                     {
4386                         job->height = fTitle->height;
4387                         hb_fix_aspect( job, HB_KEEP_HEIGHT );
4388                     }
4389                 }
4390                 job->pixel_ratio = [[chosenPreset objectForKey:@"PicturePAR"]  intValue];
4391                 
4392                 
4393                 /* If Cropping is set to custom, then recall all four crop values from
4394                  when the preset was created and apply them */
4395                 if ([[chosenPreset objectForKey:@"PictureAutoCrop"]  intValue] == 0)
4396                 {
4397                     [fPictureController setAutoCrop:NO];
4398                     
4399                     /* Here we use the custom crop values saved at the time the preset was saved */
4400                     job->crop[0] = [[chosenPreset objectForKey:@"PictureTopCrop"]  intValue];
4401                     job->crop[1] = [[chosenPreset objectForKey:@"PictureBottomCrop"]  intValue];
4402                     job->crop[2] = [[chosenPreset objectForKey:@"PictureLeftCrop"]  intValue];
4403                     job->crop[3] = [[chosenPreset objectForKey:@"PictureRightCrop"]  intValue];
4404                     
4405                 }
4406                 else /* if auto crop has been saved in preset, set to auto and use post scan auto crop */
4407                 {
4408                     [fPictureController setAutoCrop:YES];
4409                     /* Here we use the auto crop values determined right after scan */
4410                     job->crop[0] = AutoCropTop;
4411                     job->crop[1] = AutoCropBottom;
4412                     job->crop[2] = AutoCropLeft;
4413                     job->crop[3] = AutoCropRight;
4414                     
4415                 }
4416                 /* If the preset has no objectForKey:@"UsesPictureFilters", then we know it is a legacy preset
4417                  * and handle the filters here as before.
4418                  * NOTE: This should be removed when the update presets code is done as we can be assured that legacy
4419                  * presets are updated to work properly with new keys.
4420                  */
4421                 if (![chosenPreset objectForKey:@"UsesPictureFilters"])
4422                 {
4423                     /* Filters */
4424                     /* Deinterlace */
4425                     if ([chosenPreset objectForKey:@"PictureDeinterlace"])
4426                     {
4427                         /* We check to see if the preset used the past fourth "Slowest" deinterlaceing and set that to "Slower
4428                          * since we no longer have a fourth "Slowest" deinterlacing due to the mcdeint bug */
4429                         if ([[chosenPreset objectForKey:@"PictureDeinterlace"] intValue] == 4)
4430                         {
4431                             [fPictureController setDeinterlace:3];
4432                         }
4433                         else
4434                         {
4435                             
4436                             [fPictureController setDeinterlace:[[chosenPreset objectForKey:@"PictureDeinterlace"] intValue]];
4437                         }
4438                     }
4439                     else
4440                     {
4441                         [fPictureController setDeinterlace:0];
4442                     }
4443                     /* VFR */
4444                     if ([[chosenPreset objectForKey:@"VFR"] intValue] == 1)
4445                     {
4446                         [fPictureController setVFR:[[chosenPreset objectForKey:@"VFR"] intValue]];
4447                     }
4448                     else
4449                     {
4450                         [fPictureController setVFR:0];
4451                     }
4452                     /* Detelecine */
4453                     if ([[chosenPreset objectForKey:@"PictureDetelecine"] intValue] == 1)
4454                     {
4455                         [fPictureController setDetelecine:[[chosenPreset objectForKey:@"PictureDetelecine"] intValue]];
4456                     }
4457                     else
4458                     {
4459                         [fPictureController setDetelecine:0];
4460                     }
4461                     /* Denoise */
4462                     if ([chosenPreset objectForKey:@"PictureDenoise"])
4463                     {
4464                         [fPictureController setDenoise:[[chosenPreset objectForKey:@"PictureDenoise"] intValue]];
4465                     }
4466                     else
4467                     {
4468                         [fPictureController setDenoise:0];
4469                     }   
4470                     /* Deblock */
4471                     if ([[chosenPreset objectForKey:@"PictureDeblock"] intValue] == 1)
4472                     {
4473                         [fPictureController setDeblock:[[chosenPreset objectForKey:@"PictureDeblock"] intValue]];
4474                     }
4475                     else
4476                     {
4477                         [fPictureController setDeblock:0];
4478                     }
4479
4480                     [self calculatePictureSizing:nil];
4481                 }
4482
4483             }
4484
4485
4486         }
4487         /* If the preset has an objectForKey:@"UsesPictureFilters", then we know it is a newer style filters preset
4488          * and handle the filters here depending on whether or not the preset specifies applying the filter.
4489          */
4490         if ([chosenPreset objectForKey:@"UsesPictureFilters"] && [[chosenPreset objectForKey:@"UsesPictureFilters"]  intValue] > 0)
4491         {
4492             /* Filters */
4493             /* Deinterlace */
4494             if ([chosenPreset objectForKey:@"PictureDeinterlace"])
4495             {
4496                 /* We check to see if the preset used the past fourth "Slowest" deinterlaceing and set that to "Slower
4497                  * since we no longer have a fourth "Slowest" deinterlacing due to the mcdeint bug */
4498                 if ([[chosenPreset objectForKey:@"PictureDeinterlace"] intValue] == 4)
4499                 {
4500                     [fPictureController setDeinterlace:3];
4501                 }
4502                 else
4503                 {
4504                     [fPictureController setDeinterlace:[[chosenPreset objectForKey:@"PictureDeinterlace"] intValue]];
4505                 }
4506             }
4507             else
4508             {
4509                 [fPictureController setDeinterlace:0];
4510             }
4511             /* VFR */
4512             if ([[chosenPreset objectForKey:@"VFR"] intValue] == 1)
4513             {
4514                 [fPictureController setVFR:[[chosenPreset objectForKey:@"VFR"] intValue]];
4515             }
4516             else
4517             {
4518                 [fPictureController setVFR:0];
4519             }
4520             /* Detelecine */
4521             if ([[chosenPreset objectForKey:@"PictureDetelecine"] intValue] == 1)
4522             {
4523                 [fPictureController setDetelecine:[[chosenPreset objectForKey:@"PictureDetelecine"] intValue]];
4524             }
4525             else
4526             {
4527                 [fPictureController setDetelecine:0];
4528             }
4529             /* Denoise */
4530             if ([chosenPreset objectForKey:@"PictureDenoise"])
4531             {
4532                 [fPictureController setDenoise:[[chosenPreset objectForKey:@"PictureDenoise"] intValue]];
4533             }
4534             else
4535             {
4536                 [fPictureController setDenoise:0];
4537             }   
4538             /* Deblock */
4539             if ([[chosenPreset objectForKey:@"PictureDeblock"] intValue] == 1)
4540             {
4541                 [fPictureController setDeblock:[[chosenPreset objectForKey:@"PictureDeblock"] intValue]];
4542             }
4543             else
4544             {
4545                 [fPictureController setDeblock:0];
4546             }
4547             /* Decomb */
4548             /* Even though we currently allow for a custom setting for decomb, ultimately it will only have Off and
4549              * Default so we just pay attention to anything greater than 0 as 1 (Default). 0 is Off. */
4550             if ([[chosenPreset objectForKey:@"PictureDecomb"] intValue] > 0)
4551             {
4552                 [fPictureController setDecomb:1];
4553             }
4554             else
4555             {
4556                 [fPictureController setDecomb:0];
4557             }
4558         }
4559         [self calculatePictureSizing:nil];
4560     }
4561 }
4562
4563
4564 #pragma mark -
4565 #pragma mark Manage Presets
4566
4567 - (void) loadPresets {
4568         /* We declare the default NSFileManager into fileManager */
4569         NSFileManager * fileManager = [NSFileManager defaultManager];
4570         /*We define the location of the user presets file */
4571     UserPresetsFile = @"~/Library/Application Support/HandBrake/UserPresets.plist";
4572         UserPresetsFile = [[UserPresetsFile stringByExpandingTildeInPath]retain];
4573     /* We check for the presets.plist */
4574         if ([fileManager fileExistsAtPath:UserPresetsFile] == 0)
4575         {
4576                 [fileManager createFileAtPath:UserPresetsFile contents:nil attributes:nil];
4577         }
4578
4579         UserPresets = [[NSMutableArray alloc] initWithContentsOfFile:UserPresetsFile];
4580         if (nil == UserPresets)
4581         {
4582                 UserPresets = [[NSMutableArray alloc] init];
4583                 [self addFactoryPresets:nil];
4584         }
4585         [fPresetsOutlineView reloadData];
4586 }
4587
4588
4589 - (IBAction) showAddPresetPanel: (id) sender
4590 {
4591     /* Deselect the currently selected Preset if there is one*/
4592     [fPresetsOutlineView deselectRow:[fPresetsOutlineView selectedRow]];
4593
4594     /* Populate the preset picture settings popup here */
4595     [fPresetNewPicSettingsPopUp removeAllItems];
4596     [fPresetNewPicSettingsPopUp addItemWithTitle:@"None"];
4597     [fPresetNewPicSettingsPopUp addItemWithTitle:@"Current"];
4598     [fPresetNewPicSettingsPopUp addItemWithTitle:@"Source Maximum (post source scan)"];
4599     [fPresetNewPicSettingsPopUp selectItemAtIndex: 0];  
4600     /* Uncheck the preset use filters checkbox */
4601     [fPresetNewPicFiltersCheck setState:NSOffState];
4602     /* Erase info from the input fields*/
4603         [fPresetNewName setStringValue: @""];
4604         [fPresetNewDesc setStringValue: @""];
4605         /* Show the panel */
4606         [NSApp beginSheet:fAddPresetPanel modalForWindow:fWindow modalDelegate:nil didEndSelector:NULL contextInfo:NULL];
4607 }
4608
4609 - (IBAction) closeAddPresetPanel: (id) sender
4610 {
4611     [NSApp endSheet: fAddPresetPanel];
4612     [fAddPresetPanel orderOut: self];
4613 }
4614
4615 - (IBAction)addUserPreset:(id)sender
4616 {
4617     if (![[fPresetNewName stringValue] length])
4618             NSRunAlertPanel(@"Warning!", @"You need to insert a name for the preset.", @"OK", nil , nil);
4619     else
4620     {
4621         /* Here we create a custom user preset */
4622         [UserPresets addObject:[self createPreset]];
4623         [self addPreset];
4624
4625         [self closeAddPresetPanel:nil];
4626     }
4627 }
4628 - (void)addPreset
4629 {
4630
4631         
4632         /* We Reload the New Table data for presets */
4633     [fPresetsOutlineView reloadData];
4634    /* We save all of the preset data here */
4635     [self savePreset];
4636 }
4637
4638 - (void)sortPresets
4639 {
4640
4641         
4642         /* We Sort the Presets By Factory or Custom */
4643         NSSortDescriptor * presetTypeDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"Type" 
4644                                                     ascending:YES] autorelease];
4645         /* We Sort the Presets Alphabetically by name  We do not use this now as we have drag and drop*/
4646         /*
4647     NSSortDescriptor * presetNameDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"PresetName" 
4648                                                     ascending:YES selector:@selector(caseInsensitiveCompare:)] autorelease];
4649         //NSArray *sortDescriptors=[NSArray arrayWithObjects:presetTypeDescriptor,presetNameDescriptor,nil];
4650     
4651     */
4652     /* Since we can drag and drop our custom presets, lets just sort by type and not name */
4653     NSArray *sortDescriptors=[NSArray arrayWithObjects:presetTypeDescriptor,nil];
4654         NSArray *sortedArray=[UserPresets sortedArrayUsingDescriptors:sortDescriptors];
4655         [UserPresets setArray:sortedArray];
4656         
4657
4658 }
4659
4660 - (IBAction)insertPreset:(id)sender
4661 {
4662     int index = [fPresetsOutlineView selectedRow];
4663     [UserPresets insertObject:[self createPreset] atIndex:index];
4664     [fPresetsOutlineView reloadData];
4665     [self savePreset];
4666 }
4667
4668 - (NSDictionary *)createPreset
4669 {
4670     NSMutableDictionary *preset = [[NSMutableDictionary alloc] init];
4671         /* Get the New Preset Name from the field in the AddPresetPanel */
4672     [preset setObject:[fPresetNewName stringValue] forKey:@"PresetName"];
4673         /*Set whether or not this is a user preset or factory 0 is factory, 1 is user*/
4674         [preset setObject:[NSNumber numberWithInt:1] forKey:@"Type"];
4675         /*Set whether or not this is default, at creation set to 0*/
4676         [preset setObject:[NSNumber numberWithInt:0] forKey:@"Default"];
4677         /*Get the whether or not to apply pic Size and Cropping (includes Anamorphic)*/
4678         [preset setObject:[NSNumber numberWithInt:[fPresetNewPicSettingsPopUp indexOfSelectedItem]] forKey:@"UsesPictureSettings"];
4679     /* Get whether or not to use the current Picture Filter settings for the preset */
4680     [preset setObject:[NSNumber numberWithInt:[fPresetNewPicFiltersCheck state]] forKey:@"UsesPictureFilters"];
4681  
4682     /* Get New Preset Description from the field in the AddPresetPanel*/
4683         [preset setObject:[fPresetNewDesc stringValue] forKey:@"PresetDescription"];
4684         /* File Format */
4685     [preset setObject:[fDstFormatPopUp titleOfSelectedItem] forKey:@"FileFormat"];
4686         /* Chapter Markers fCreateChapterMarkers*/
4687         [preset setObject:[NSNumber numberWithInt:[fCreateChapterMarkers state]] forKey:@"ChapterMarkers"];
4688         /* Allow Mpeg4 64 bit formatting +4GB file sizes */
4689         [preset setObject:[NSNumber numberWithInt:[fDstMp4LargeFileCheck state]] forKey:@"Mp4LargeFile"];
4690     /* Mux mp4 with http optimization */
4691     [preset setObject:[NSNumber numberWithInt:[fDstMp4HttpOptFileCheck state]] forKey:@"Mp4HttpOptimize"];
4692     /* Add iPod uuid atom */
4693     [preset setObject:[NSNumber numberWithInt:[fDstMp4iPodFileCheck state]] forKey:@"Mp4iPodCompatible"];
4694
4695     /* Codecs */
4696         /* Video encoder */
4697         [preset setObject:[fVidEncoderPopUp titleOfSelectedItem] forKey:@"VideoEncoder"];
4698         /* x264 Option String */
4699         [preset setObject:[fAdvancedOptions optionsString] forKey:@"x264Option"];
4700
4701         [preset setObject:[NSNumber numberWithInt:[fVidQualityMatrix selectedRow]] forKey:@"VideoQualityType"];
4702         [preset setObject:[fVidTargetSizeField stringValue] forKey:@"VideoTargetSize"];
4703         [preset setObject:[fVidBitrateField stringValue] forKey:@"VideoAvgBitrate"];
4704         [preset setObject:[NSNumber numberWithFloat:[fVidQualitySlider floatValue]] forKey:@"VideoQualitySlider"];
4705
4706         /* Video framerate */
4707     if ([fVidRatePopUp indexOfSelectedItem] == 0) // Same as source is selected
4708         {
4709         [preset setObject:@"Same as source" forKey:@"VideoFramerate"];
4710     }
4711     else // we can record the actual titleOfSelectedItem
4712     {
4713     [preset setObject:[fVidRatePopUp titleOfSelectedItem] forKey:@"VideoFramerate"];
4714     }
4715         /* GrayScale */
4716         [preset setObject:[NSNumber numberWithInt:[fVidGrayscaleCheck state]] forKey:@"VideoGrayScale"];
4717         /* 2 Pass Encoding */
4718         [preset setObject:[NSNumber numberWithInt:[fVidTwoPassCheck state]] forKey:@"VideoTwoPass"];
4719         /* Turbo 2 pass Encoding fVidTurboPassCheck*/
4720         [preset setObject:[NSNumber numberWithInt:[fVidTurboPassCheck state]] forKey:@"VideoTurboTwoPass"];
4721         /*Picture Settings*/
4722         hb_job_t * job = fTitle->job;
4723         /* Picture Sizing */
4724         /* Use Max Picture settings for whatever the dvd is.*/
4725         [preset setObject:[NSNumber numberWithInt:0] forKey:@"UsesMaxPictureSettings"];
4726         [preset setObject:[NSNumber numberWithInt:fTitle->job->width] forKey:@"PictureWidth"];
4727         [preset setObject:[NSNumber numberWithInt:fTitle->job->height] forKey:@"PictureHeight"];
4728         [preset setObject:[NSNumber numberWithInt:fTitle->job->keep_ratio] forKey:@"PictureKeepRatio"];
4729         [preset setObject:[NSNumber numberWithInt:fTitle->job->pixel_ratio] forKey:@"PicturePAR"];
4730     
4731     /* Set crop settings here */
4732         [preset setObject:[NSNumber numberWithInt:[fPictureController autoCrop]] forKey:@"PictureAutoCrop"];
4733     [preset setObject:[NSNumber numberWithInt:job->crop[0]] forKey:@"PictureTopCrop"];
4734     [preset setObject:[NSNumber numberWithInt:job->crop[1]] forKey:@"PictureBottomCrop"];
4735         [preset setObject:[NSNumber numberWithInt:job->crop[2]] forKey:@"PictureLeftCrop"];
4736         [preset setObject:[NSNumber numberWithInt:job->crop[3]] forKey:@"PictureRightCrop"];
4737     
4738     /* Picture Filters */
4739     [preset setObject:[NSNumber numberWithInt:[fPictureController deinterlace]] forKey:@"PictureDeinterlace"];
4740         [preset setObject:[NSNumber numberWithInt:[fPictureController detelecine]] forKey:@"PictureDetelecine"];
4741     [preset setObject:[NSNumber numberWithInt:[fPictureController vfr]] forKey:@"VFR"];
4742         [preset setObject:[NSNumber numberWithInt:[fPictureController denoise]] forKey:@"PictureDenoise"];
4743     [preset setObject:[NSNumber numberWithInt:[fPictureController deblock]] forKey:@"PictureDeblock"]; 
4744     [preset setObject:[NSNumber numberWithInt:[fPictureController decomb]] forKey:@"PictureDecomb"];
4745     
4746     
4747     /*Audio*/
4748     if ([fAudLang1PopUp indexOfSelectedItem] > 0)
4749     {
4750         [preset setObject:[NSNumber numberWithInt:[fAudLang1PopUp indexOfSelectedItem]] forKey:@"Audio1Track"];
4751         [preset setObject:[fAudLang1PopUp titleOfSelectedItem] forKey:@"Audio1TrackDescription"];
4752         [preset setObject:[fAudTrack1CodecPopUp titleOfSelectedItem] forKey:@"Audio1Encoder"];
4753         [preset setObject:[fAudTrack1MixPopUp titleOfSelectedItem] forKey:@"Audio1Mixdown"];
4754         [preset setObject:[fAudTrack1RatePopUp titleOfSelectedItem] forKey:@"Audio1Samplerate"];
4755         [preset setObject:[fAudTrack1BitratePopUp titleOfSelectedItem] forKey:@"Audio1Bitrate"];
4756         [preset setObject:[NSNumber numberWithFloat:[fAudTrack1DrcSlider floatValue]] forKey:@"Audio1TrackDRCSlider"];
4757     }
4758     if ([fAudLang2PopUp indexOfSelectedItem] > 0)
4759     {
4760         [preset setObject:[NSNumber numberWithInt:[fAudLang2PopUp indexOfSelectedItem]] forKey:@"Audio2Track"];
4761         [preset setObject:[fAudLang2PopUp titleOfSelectedItem] forKey:@"Audio2TrackDescription"];
4762         [preset setObject:[fAudTrack2CodecPopUp titleOfSelectedItem] forKey:@"Audio2Encoder"];
4763         [preset setObject:[fAudTrack2MixPopUp titleOfSelectedItem] forKey:@"Audio2Mixdown"];
4764         [preset setObject:[fAudTrack2RatePopUp titleOfSelectedItem] forKey:@"Audio2Samplerate"];
4765         [preset setObject:[fAudTrack2BitratePopUp titleOfSelectedItem] forKey:@"Audio2Bitrate"];
4766         [preset setObject:[NSNumber numberWithFloat:[fAudTrack2DrcSlider floatValue]] forKey:@"Audio2TrackDRCSlider"];
4767     }
4768     if ([fAudLang3PopUp indexOfSelectedItem] > 0)
4769     {
4770         [preset setObject:[NSNumber numberWithInt:[fAudLang3PopUp indexOfSelectedItem]] forKey:@"Audio3Track"];
4771         [preset setObject:[fAudLang3PopUp titleOfSelectedItem] forKey:@"Audio3TrackDescription"];
4772         [preset setObject:[fAudTrack3CodecPopUp titleOfSelectedItem] forKey:@"Audio3Encoder"];
4773         [preset setObject:[fAudTrack3MixPopUp titleOfSelectedItem] forKey:@"Audio3Mixdown"];
4774         [preset setObject:[fAudTrack3RatePopUp titleOfSelectedItem] forKey:@"Audio3Samplerate"];
4775         [preset setObject:[fAudTrack3BitratePopUp titleOfSelectedItem] forKey:@"Audio3Bitrate"];
4776         [preset setObject:[NSNumber numberWithFloat:[fAudTrack3DrcSlider floatValue]] forKey:@"Audio3TrackDRCSlider"];
4777     }
4778     if ([fAudLang4PopUp indexOfSelectedItem] > 0)
4779     {
4780         [preset setObject:[NSNumber numberWithInt:[fAudLang4PopUp indexOfSelectedItem]] forKey:@"Audio4Track"];
4781         [preset setObject:[fAudLang4PopUp titleOfSelectedItem] forKey:@"Audio4TrackDescription"];
4782         [preset setObject:[fAudTrack4CodecPopUp titleOfSelectedItem] forKey:@"Audio4Encoder"];
4783         [preset setObject:[fAudTrack4MixPopUp titleOfSelectedItem] forKey:@"Audio4Mixdown"];
4784         [preset setObject:[fAudTrack4RatePopUp titleOfSelectedItem] forKey:@"Audio4Samplerate"];
4785         [preset setObject:[fAudTrack4BitratePopUp titleOfSelectedItem] forKey:@"Audio4Bitrate"];
4786         [preset setObject:[NSNumber numberWithFloat:[fAudTrack4DrcSlider floatValue]] forKey:@"Audio4TrackDRCSlider"];
4787     }
4788     
4789         /* Subtitles*/
4790         [preset setObject:[fSubPopUp titleOfSelectedItem] forKey:@"Subtitles"];
4791     /* Forced Subtitles */
4792         [preset setObject:[NSNumber numberWithInt:[fSubForcedCheck state]] forKey:@"SubtitlesForced"];
4793     
4794     [preset autorelease];
4795     return preset;
4796
4797 }
4798
4799 - (void)savePreset
4800 {
4801     [UserPresets writeToFile:UserPresetsFile atomically:YES];
4802         /* We get the default preset in case it changed */
4803         [self getDefaultPresets:nil];
4804
4805 }
4806
4807 - (IBAction)deletePreset:(id)sender
4808 {
4809     int status;
4810     NSEnumerator *enumerator;
4811     NSNumber *index;
4812     NSMutableArray *tempArray;
4813     id tempObject;
4814     
4815     if ( [fPresetsOutlineView numberOfSelectedRows] == 0 )
4816         return;
4817     /* Alert user before deleting preset */
4818         /* Comment out for now, tie to user pref eventually */
4819
4820     //NSBeep();
4821     status = NSRunAlertPanel(@"Warning!", @"Are you sure that you want to delete the selected preset?", @"OK", @"Cancel", nil);
4822     
4823     if ( status == NSAlertDefaultReturn ) {
4824         enumerator = [fPresetsOutlineView selectedRowEnumerator];
4825         tempArray = [NSMutableArray array];
4826         
4827         while ( (index = [enumerator nextObject]) ) {
4828             tempObject = [UserPresets objectAtIndex:[index intValue]];
4829             [tempArray addObject:tempObject];
4830         }
4831         
4832         [UserPresets removeObjectsInArray:tempArray];
4833         [fPresetsOutlineView reloadData];
4834         [self savePreset];   
4835     }
4836 }
4837
4838 #pragma mark -
4839 #pragma mark Manage Default Preset
4840
4841 - (IBAction)getDefaultPresets:(id)sender
4842 {
4843         int i = 0;
4844     presetCurrentBuiltInCount = 0;
4845     NSEnumerator *enumerator = [UserPresets objectEnumerator];
4846         id tempObject;
4847         while (tempObject = [enumerator nextObject])
4848         {
4849                 NSDictionary *thisPresetDict = tempObject;
4850                 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 1) // 1 is HB default
4851                 {
4852                         presetHbDefault = i;    
4853                 }
4854                 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 2) // 2 is User specified default
4855                 {
4856                         presetUserDefault = i;  
4857                 }
4858         if ([[thisPresetDict objectForKey:@"Type"] intValue] == 0) // Type 0 is a built in preset               
4859         {
4860                         presetCurrentBuiltInCount++; // <--increment the current number of built in presets     
4861                 }
4862                 i++;
4863         }
4864 }
4865
4866 - (IBAction)setDefaultPreset:(id)sender
4867 {
4868     int i = 0;
4869     NSEnumerator *enumerator = [UserPresets objectEnumerator];
4870         id tempObject;
4871         /* First make sure the old user specified default preset is removed */
4872         while (tempObject = [enumerator nextObject])
4873         {
4874                 /* make sure we are not removing the default HB preset */
4875                 if ([[[UserPresets objectAtIndex:i] objectForKey:@"Default"] intValue] != 1) // 1 is HB default
4876                 {
4877                         [[UserPresets objectAtIndex:i] setObject:[NSNumber numberWithInt:0] forKey:@"Default"];
4878                 }
4879                 i++;
4880         }
4881         /* Second, go ahead and set the appropriate user specfied preset */
4882         /* we get the chosen preset from the UserPresets array */
4883         if ([[[UserPresets objectAtIndex:[fPresetsOutlineView selectedRow]] objectForKey:@"Default"] intValue] != 1) // 1 is HB default
4884         {
4885                 [[UserPresets objectAtIndex:[fPresetsOutlineView selectedRow]] setObject:[NSNumber numberWithInt:2] forKey:@"Default"];
4886         }
4887         /*FIX ME: I think we now need to use the items not rows in NSOutlineView */
4888     presetUserDefault = [fPresetsOutlineView selectedRow];
4889         
4890         /* We save all of the preset data here */
4891     [self savePreset];
4892         /* We Reload the New Table data for presets */
4893     [fPresetsOutlineView reloadData];
4894 }
4895
4896 - (IBAction)selectDefaultPreset:(id)sender
4897 {
4898         /* if there is a user specified default, we use it */
4899         if (presetUserDefault)
4900         {
4901         [fPresetsOutlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:presetUserDefault] byExtendingSelection:NO];
4902         [self selectPreset:nil];
4903         }
4904         else if (presetHbDefault) //else we use the built in default presetHbDefault
4905         {
4906         [fPresetsOutlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:presetHbDefault] byExtendingSelection:NO];
4907         [self selectPreset:nil];
4908         }
4909 }
4910
4911
4912 #pragma mark -
4913 #pragma mark Manage Built In Presets
4914
4915
4916 - (IBAction)deleteFactoryPresets:(id)sender
4917 {
4918     //int status;
4919     NSEnumerator *enumerator = [UserPresets objectEnumerator];
4920         id tempObject;
4921     
4922         //NSNumber *index;
4923     NSMutableArray *tempArray;
4924
4925
4926         tempArray = [NSMutableArray array];
4927         /* we look here to see if the preset is we move on to the next one */
4928         while ( tempObject = [enumerator nextObject] )  
4929                 {
4930                         /* if the preset is "Factory" then we put it in the array of
4931                         presets to delete */
4932                         if ([[tempObject objectForKey:@"Type"] intValue] == 0)
4933                         {
4934                                 [tempArray addObject:tempObject];
4935                         }
4936         }
4937         
4938         [UserPresets removeObjectsInArray:tempArray];
4939         [fPresetsOutlineView reloadData];
4940         [self savePreset];   
4941
4942 }
4943
4944    /* We use this method to recreate new, updated factory
4945    presets */
4946 - (IBAction)addFactoryPresets:(id)sender
4947 {
4948    
4949    /* First, we delete any existing built in presets */
4950     [self deleteFactoryPresets: sender];
4951     /* Then we generate new built in presets programmatically with fPresetsBuiltin
4952     * which is all setup in HBPresets.h and  HBPresets.m*/
4953     [fPresetsBuiltin generateBuiltinPresets:UserPresets];
4954     [self sortPresets];
4955     [self addPreset];
4956     
4957 }
4958
4959
4960
4961
4962
4963 @end
4964
4965 /*******************************
4966  * Subclass of the HBPresetsOutlineView *
4967  *******************************/
4968
4969 @implementation HBPresetsOutlineView
4970 - (NSImage *)dragImageForRowsWithIndexes:(NSIndexSet *)dragRows tableColumns:(NSArray *)tableColumns event:(NSEvent*)dragEvent offset:(NSPointPointer)dragImageOffset
4971 {
4972     fIsDragging = YES;
4973
4974     // By default, NSTableView only drags an image of the first column. Change this to
4975     // drag an image of the queue's icon and PresetName columns.
4976     NSArray * cols = [NSArray arrayWithObjects: [self tableColumnWithIdentifier:@"icon"], [self tableColumnWithIdentifier:@"PresetName"], nil];
4977     return [super dragImageForRowsWithIndexes:dragRows tableColumns:cols event:dragEvent offset:dragImageOffset];
4978 }
4979
4980
4981
4982 - (void) mouseDown:(NSEvent *)theEvent
4983 {
4984     [super mouseDown:theEvent];
4985         fIsDragging = NO;
4986 }
4987
4988
4989
4990 - (BOOL) isDragging;
4991 {
4992     return fIsDragging;
4993 }
4994 @end
4995
4996
4997