OSDN Git Service

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