OSDN Git Service

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