OSDN Git Service

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