OSDN Git Service

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