OSDN Git Service

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