OSDN Git Service

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