OSDN Git Service

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