OSDN Git Service

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