OSDN Git Service

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