OSDN Git Service

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