OSDN Git Service

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