OSDN Git Service

86674adeaf407e3dd8f22cb06fe1abadc95502e2
[handbrake-jp/handbrake-jp-git.git] / macosx / Controller.m
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 #include <dlfcn.h>
8 #import "Controller.h"
9 #import "HBOutputPanelController.h"
10 #import "HBPreferencesController.h"
11 #import "HBDVDDetector.h"
12 #import "HBPresets.h"
13 #import "HBPreviewController.h"
14
15 #define DragDropSimplePboardType        @"MyCustomOutlineViewPboardType"
16
17 /* We setup the toolbar values here ShowPreviewIdentifier */
18 static NSString *        ToggleDrawerIdentifier             = @"Toggle Drawer Item Identifier";
19 static NSString *        StartEncodingIdentifier            = @"Start Encoding Item Identifier";
20 static NSString *        PauseEncodingIdentifier            = @"Pause Encoding Item Identifier";
21 static NSString *        ShowQueueIdentifier                = @"Show Queue Item Identifier";
22 static NSString *        AddToQueueIdentifier               = @"Add to Queue Item Identifier";
23 static NSString *        ShowPictureIdentifier             = @"Show Picture Window Item Identifier";
24 static NSString *        ShowPreviewIdentifier             = @"Show Preview Window Item Identifier";
25 static NSString *        ShowActivityIdentifier             = @"Debug Output Item Identifier";
26 static NSString *        ChooseSourceIdentifier             = @"Choose Source Item Identifier";
27
28
29 /*******************************
30  * HBController implementation *
31  *******************************/
32 @implementation HBController
33
34 - (id)init
35 {
36     self = [super init];
37     if( !self )
38     {
39         return nil;
40     }
41
42     /* replace bundled app icon with one which is 32/64-bit savvy */
43 #if defined( __LP64__ )
44     fApplicationIcon = [[NSImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForImageResource:@"HandBrake-64.icns"]];
45 #else
46     fApplicationIcon = [[NSImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForImageResource:@"HandBrake.icns"]];
47 #endif
48     if( fApplicationIcon != nil )
49         [NSApp setApplicationIconImage:fApplicationIcon];
50     
51     [HBPreferencesController registerUserDefaults];
52     fHandle = NULL;
53     fQueueEncodeLibhb = NULL;
54     /* Check for check for the app support directory here as
55      * outputPanel needs it right away, as may other future methods
56      */
57     NSString *libraryDir = [NSSearchPathForDirectoriesInDomains( NSLibraryDirectory,
58                                                                 NSUserDomainMask,
59                                                                 YES ) objectAtIndex:0];
60     AppSupportDirectory = [[libraryDir stringByAppendingPathComponent:@"Application Support"]
61                            stringByAppendingPathComponent:@"HandBrake"];
62     if( ![[NSFileManager defaultManager] fileExistsAtPath:AppSupportDirectory] )
63     {
64         [[NSFileManager defaultManager] createDirectoryAtPath:AppSupportDirectory
65                                                    attributes:nil];
66     }
67     /* Check for and create the App Support Preview directory if necessary */
68     NSString *PreviewDirectory = [AppSupportDirectory stringByAppendingPathComponent:@"Previews"];
69     if( ![[NSFileManager defaultManager] fileExistsAtPath:PreviewDirectory] )
70     {
71         [[NSFileManager defaultManager] createDirectoryAtPath:PreviewDirectory
72                                                    attributes:nil];
73     }                                                            
74     outputPanel = [[HBOutputPanelController alloc] init];
75     fPictureController = [[PictureController alloc] init];
76     fQueueController = [[HBQueueController alloc] init];
77     fAdvancedOptions = [[HBAdvancedController alloc] init];
78     /* we init the HBPresets class which currently is only used
79      * for updating built in presets, may move more functionality
80      * there in the future
81      */
82     fPresetsBuiltin = [[HBPresets alloc] init];
83     fPreferencesController = [[HBPreferencesController alloc] init];
84     /* Lets report the HandBrake version number here to the activity log and text log file */
85     NSString *versionStringFull = [[NSString stringWithFormat: @"Handbrake Version: %@", [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"]] stringByAppendingString: [NSString stringWithFormat: @" (%@)", [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"]]];
86     [self writeToActivityLog: "%s", [versionStringFull UTF8String]];
87     
88     return self;
89 }
90
91
92 - (void) applicationDidFinishLaunching: (NSNotification *) notification
93 {
94     /* Init libhb with check for updates libhb style set to "0" so its ignored and lets sparkle take care of it */
95     int loggingLevel = [[[NSUserDefaults standardUserDefaults] objectForKey:@"LoggingLevel"] intValue];
96     fHandle = hb_init(loggingLevel, 0);
97     /* Optional dvd nav UseDvdNav*/
98     hb_dvd_set_dvdnav([[[NSUserDefaults standardUserDefaults] objectForKey:@"UseDvdNav"] boolValue]);
99     /* Init a separate instance of libhb for user scanning and setting up jobs */
100     fQueueEncodeLibhb = hb_init(loggingLevel, 0);
101     
102         // Set the Growl Delegate
103     [GrowlApplicationBridge setGrowlDelegate: self];
104     /* Init others controllers */
105     [fPictureController SetHandle: fHandle];
106     [fPictureController   setHBController: self];
107     
108     [fQueueController   setHandle: fQueueEncodeLibhb];
109     [fQueueController   setHBController: self];
110
111     fChapterTitlesDelegate = [[ChapterTitles alloc] init];
112     [fChapterTable setDataSource:fChapterTitlesDelegate];
113     [fChapterTable setDelegate:fChapterTitlesDelegate];
114     
115     /* setup the subtitles delegate and connections to table */
116     fSubtitlesDelegate = [[HBSubtitles alloc] init];
117     [fSubtitlesTable setDataSource:fSubtitlesDelegate];
118     [fSubtitlesTable setDelegate:fSubtitlesDelegate];
119     [fSubtitlesTable setRowHeight:25.0];
120     
121     [fPresetsOutlineView setAutosaveName:@"Presets View"];
122     [fPresetsOutlineView setAutosaveExpandedItems:YES];
123     
124     dockIconProgress = 0;
125     
126     /* Init QueueFile .plist */
127     [self loadQueueFile];
128     /* Run hbInstances to get any info on other instances as well as set the
129      * pid number for this instance in the case of multi-instance encoding. */ 
130     hbInstanceNum = [self hbInstances];
131     
132     /* Call UpdateUI every 1/2 sec */
133     
134     [[NSRunLoop currentRunLoop] addTimer:[NSTimer
135                                           scheduledTimerWithTimeInterval:0.5 
136                                           target:self
137                                           selector:@selector(updateUI:) 
138                                           userInfo:nil repeats:YES]
139                                           forMode:NSDefaultRunLoopMode];
140                              
141
142     // Open debug output window now if it was visible when HB was closed
143     if ([[NSUserDefaults standardUserDefaults] boolForKey:@"OutputPanelIsOpen"])
144         [self showDebugOutputPanel:nil];
145
146     // Open queue window now if it was visible when HB was closed
147     if ([[NSUserDefaults standardUserDefaults] boolForKey:@"QueueWindowIsOpen"])
148         [self showQueueWindow:nil];
149
150         [self openMainWindow:nil];
151     
152     /* We have to set the bool to tell hb what to do after a scan
153      * Initially we set it to NO until we start processing the queue
154      */
155      applyQueueToScan = NO;
156     
157     /* Now we re-check the queue array to see if there are
158      * any remaining encodes to be done in it and ask the
159      * user if they want to reload the queue */
160     if ([QueueFileArray count] > 0)
161         {
162         /* run  getQueueStats to see whats in the queue file */
163         [self getQueueStats];
164         /* this results in these values
165          * fEncodingQueueItem = 0;
166          * fPendingCount = 0;
167          * fCompletedCount = 0;
168          * fCanceledCount = 0;
169          * fWorkingCount = 0;
170          */
171         
172         /*On Screen Notification*/
173         NSString * alertTitle;
174         
175         /* We check to see if there is already another instance of hb running.
176          * Note: hbInstances == 1 means we are the only instance of HandBrake.app
177          */
178         if (hbInstanceNum > 1)
179         {
180             alertTitle = [NSString stringWithFormat:
181                           NSLocalizedString(@"There is already an instance of HandBrake running.", @"")];
182             NSBeginCriticalAlertSheet(
183                                       alertTitle,
184                                       NSLocalizedString(@"Reload Queue", nil),
185                                       nil,
186                                       nil,
187                                       fWindow, self,
188                                       nil, @selector(didDimissReloadQueue:returnCode:contextInfo:), nil,
189                                       NSLocalizedString(@" HandBrake will now load up the existing queue.", nil));    
190         }
191         else
192         {
193             if (fWorkingCount > 0 || fPendingCount > 0)
194             {
195                 if (fWorkingCount > 0)
196                 {
197                     alertTitle = [NSString stringWithFormat:
198                                   NSLocalizedString(@"HandBrake Has Detected %d Previously Encoding Item(s) and %d Pending Item(s) In Your Queue.", @""),
199                                   fWorkingCount,fPendingCount];
200                 }
201                 else
202                 {
203                     alertTitle = [NSString stringWithFormat:
204                                   NSLocalizedString(@"HandBrake Has Detected %d Pending Item(s) In Your Queue.", @""),
205                                   fPendingCount];
206                 }
207                 
208                 NSBeginCriticalAlertSheet(
209                                           alertTitle,
210                                           NSLocalizedString(@"Reload Queue", nil),
211                                           nil,
212                                           NSLocalizedString(@"Empty Queue", nil),
213                                           fWindow, self,
214                                           nil, @selector(didDimissReloadQueue:returnCode:contextInfo:), nil,
215                                           NSLocalizedString(@" Do you want to reload them ?", nil));
216             }
217             else
218             {
219                 /* Since we addressed any pending or previously encoding items above, we go ahead and make sure the queue
220                  * is empty of any finished items or cancelled items */
221                 [self clearQueueAllItems];
222                 /* We show whichever open source window specified in LaunchSourceBehavior preference key */
223                 if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"LaunchSourceBehavior"] isEqualToString: @"Open Source"])
224                 {
225                     [self browseSources:nil];
226                 }
227                 
228                 if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"LaunchSourceBehavior"] isEqualToString: @"Open Source (Title Specific)"])
229                 {
230                     [self browseSources:(id)fOpenSourceTitleMMenu];
231                 }
232             }
233             
234         }
235     }
236     else
237     {
238         /* We show whichever open source window specified in LaunchSourceBehavior preference key */
239         if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"LaunchSourceBehavior"] isEqualToString: @"Open Source"])
240         {
241             [self browseSources:nil];
242         }
243         
244         if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"LaunchSourceBehavior"] isEqualToString: @"Open Source (Title Specific)"])
245         {
246             [self browseSources:(id)fOpenSourceTitleMMenu];
247         }
248     }
249     currentQueueEncodeNameString = @"";
250 }
251
252 #pragma mark -
253 #pragma mark Multiple Instances
254
255 /* hbInstances checks to see if other instances of HB are running and also sets the pid for this instance for multi-instance queue encoding */
256  
257  /* Note for now since we are in early phases of multi-instance I have put in quite a bit of logging. Can be removed as we see fit. */
258 - (int) hbInstances
259 {
260     /* check to see if another instance of HandBrake.app is running */
261     NSArray *runningAppDictionaries = [[NSWorkspace sharedWorkspace] launchedApplications];
262     NSDictionary *runningAppsDictionary;
263     int hbInstances = 0;
264     NSString * thisInstanceAppPath = [[NSBundle mainBundle] bundlePath];
265     NSString * runningInstanceAppPath;
266     int runningInstancePidNum;
267     [self writeToActivityLog: "hbInstances path to this instance: %s", [thisInstanceAppPath UTF8String]];
268     for (runningAppsDictionary in runningAppDictionaries)
269         {
270         if ([[runningAppsDictionary valueForKey:@"NSApplicationName"] isEqualToString:@"HandBrake"])
271                 {
272             /*Report the path to each active instances app path */
273             runningInstancePidNum = [[runningAppsDictionary valueForKey:@"NSApplicationProcessIdentifier"] intValue];
274             runningInstanceAppPath = [runningAppsDictionary valueForKey:@"NSApplicationPath"];
275             [self writeToActivityLog: "hbInstance found instance pidnum:%d at path: %s", runningInstancePidNum, [runningInstanceAppPath UTF8String]];
276             /* see if this is us by comparing the app path */
277             if ([runningInstanceAppPath isEqualToString: thisInstanceAppPath])
278             {
279                 /* If so this is our pidnum */
280                 [self writeToActivityLog: "hbInstance MATCH FOUND, our pidnum is:%d", runningInstancePidNum];
281                 /* Get the PID number for this hb instance, used in multi instance encoding */
282                 pidNum = runningInstancePidNum;
283                 /* Report this pid to the activity log */
284                 [self writeToActivityLog: "Pid for this instance:%d", pidNum];
285                 /* Tell fQueueController what our pidNum is */
286                 [fQueueController setPidNum:pidNum];
287             }
288             hbInstances++;
289         }
290         }
291     return hbInstances;
292 }
293
294 #pragma mark -
295
296 - (void) didDimissReloadQueue: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo
297 {
298     
299     [self writeToActivityLog: "didDimissReloadQueue number of hb instances:%d", hbInstanceNum];
300     if (returnCode == NSAlertOtherReturn)
301     {
302         [self writeToActivityLog: "didDimissReloadQueue NSAlertOtherReturn Chosen"];
303         [self clearQueueAllItems];
304         
305         /* We show whichever open source window specified in LaunchSourceBehavior preference key */
306         if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"LaunchSourceBehavior"] isEqualToString: @"Open Source"])
307         {
308             [self browseSources:nil];
309         }
310         
311         if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"LaunchSourceBehavior"] isEqualToString: @"Open Source (Title Specific)"])
312         {
313             [self browseSources:(id)fOpenSourceTitleMMenu];
314         }
315     }
316     else
317     {
318         [self writeToActivityLog: "didDimissReloadQueue First Button Chosen"];
319         if (hbInstanceNum == 1)
320         {
321             
322             [self setQueueEncodingItemsAsPending];
323         }
324         [self showQueueWindow:NULL];
325     }
326 }
327
328 - (NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication *) app
329 {
330
331     
332     hb_state_t s;
333     hb_get_state( fQueueEncodeLibhb, &s );
334     
335     if ( s.state != HB_STATE_IDLE )
336     {
337         int result = NSRunCriticalAlertPanel(
338                                              NSLocalizedString(@"Are you sure you want to quit HandBrake?", nil),
339                                              NSLocalizedString(@"If you quit HandBrake your current encode will be reloaded into your queue at next launch. Do you want to quit anyway?", nil),
340                                              NSLocalizedString(@"Quit", nil), NSLocalizedString(@"Don't Quit", nil), nil, @"A movie" );
341         
342         if (result == NSAlertDefaultReturn)
343         {
344             return NSTerminateNow;
345         }
346         else
347             return NSTerminateCancel;
348     }
349     
350     // Warn if items still in the queue
351     else if ( fPendingCount > 0 )
352     {
353         int result = NSRunCriticalAlertPanel(
354                                              NSLocalizedString(@"Are you sure you want to quit HandBrake?", nil),
355                                              NSLocalizedString(@"There are pending encodes in your queue. Do you want to quit anyway?",nil),
356                                              NSLocalizedString(@"Quit", nil), NSLocalizedString(@"Don't Quit", nil), nil);
357         
358         if ( result == NSAlertDefaultReturn )
359             return NSTerminateNow;
360         else
361             return NSTerminateCancel;
362     }
363     
364     return NSTerminateNow;
365 }
366
367 - (void)applicationWillTerminate:(NSNotification *)aNotification
368 {
369     [currentQueueEncodeNameString release];
370     [browsedSourceDisplayName release];
371     [outputPanel release];
372         [fQueueController release];
373     [fPreviewController release];
374     [fPictureController release];
375     [fApplicationIcon release];
376
377     hb_close(&fHandle);
378     hb_close(&fQueueEncodeLibhb);
379     hb_global_close();
380
381 }
382
383
384 - (void) awakeFromNib
385 {
386     [fWindow center];
387     [fWindow setExcludedFromWindowsMenu:NO];
388     
389     [fAdvancedOptions setView:fAdvancedView];
390     
391     /* lets setup our presets drawer for drag and drop here */
392     [fPresetsOutlineView registerForDraggedTypes: [NSArray arrayWithObject:DragDropSimplePboardType] ];
393     [fPresetsOutlineView setDraggingSourceOperationMask:NSDragOperationEvery forLocal:YES];
394     [fPresetsOutlineView setVerticalMotionCanBeginDrag: YES];
395     
396     /* Initialize currentScanCount so HB can use it to
397      evaluate successive scans */
398         currentScanCount = 0;
399     
400     
401     /* Init UserPresets .plist */
402         [self loadPresets];
403         
404     fRipIndicatorShown = NO;  // initially out of view in the nib
405     
406     /* For 64 bit builds, the threaded animation in the progress
407      * indicators conflicts with the animation in the advanced tab
408      * for reasons not completely clear. jbrjake found a note in the
409      * 10.5 dev notes regarding this possiblility. It was also noted
410      * that unless specified, setUsesThreadedAnimation defaults to true.
411      * So, at least for now we set the indicator animation to NO for
412      * both the scan and regular progress indicators for both 32 and 64 bit
413      * as it test out fine on both and there is no reason our progress indicators
414      * should require their own thread.
415      */
416
417     [fScanIndicator setUsesThreadedAnimation:NO];
418     [fRipIndicator setUsesThreadedAnimation:NO];
419   
420     
421     
422         /* Show/Dont Show Presets drawer upon launch based
423      on user preference DefaultPresetsDrawerShow*/
424         if( [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultPresetsDrawerShow"] > 0 )
425         {
426         [fPresetDrawer setDelegate:self];
427         NSSize drawerSize = NSSizeFromString( [[NSUserDefaults standardUserDefaults] 
428                                                stringForKey:@"Drawer Size"] );
429         if( drawerSize.width )
430             [fPresetDrawer setContentSize: drawerSize];
431                 [fPresetDrawer open];
432         }
433     
434     /* Initially set the dvd angle widgets to hidden (dvdnav only) */
435     [fSrcAngleLabel setHidden:YES];
436     [fSrcAnglePopUp setHidden:YES];
437     
438     /* Setup the start / stop popup */
439     [fEncodeStartStopPopUp removeAllItems];
440     [fEncodeStartStopPopUp addItemWithTitle: @"Chapters"];
441     [fEncodeStartStopPopUp addItemWithTitle: @"Seconds"];
442     [fEncodeStartStopPopUp addItemWithTitle: @"Frames"];
443     /* Align the start / stop widgets with the chapter popups */
444     [fSrcTimeStartEncodingField setFrameOrigin:[fSrcChapterStartPopUp frame].origin];
445     [fSrcTimeEndEncodingField setFrameOrigin:[fSrcChapterEndPopUp frame].origin];
446     
447     [fSrcFrameStartEncodingField setFrameOrigin:[fSrcChapterStartPopUp frame].origin];
448     [fSrcFrameEndEncodingField setFrameOrigin:[fSrcChapterEndPopUp frame].origin];
449     
450     /* Destination box*/
451     NSMenuItem *menuItem;
452     [fDstFormatPopUp removeAllItems];
453     // MP4 file
454     menuItem = [[fDstFormatPopUp menu] addItemWithTitle:@"MP4 file" action: NULL keyEquivalent: @""];
455     [menuItem setTag: HB_MUX_MP4];
456         // MKV file
457     menuItem = [[fDstFormatPopUp menu] addItemWithTitle:@"MKV file" action: NULL keyEquivalent: @""];
458     [menuItem setTag: HB_MUX_MKV];
459     
460     [fDstFormatPopUp selectItemAtIndex: 0];
461     
462     [self formatPopUpChanged:nil];
463     
464         /* We enable the create chapters checkbox here since we are .mp4 */
465         [fCreateChapterMarkers setEnabled: YES];
466         if ([fDstFormatPopUp indexOfSelectedItem] == 0 && [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultChapterMarkers"] > 0)
467         {
468                 [fCreateChapterMarkers setState: NSOnState];
469         }
470     
471     
472     
473     
474     [fDstFile2Field setStringValue: [NSString stringWithFormat:
475                                      @"%@/Desktop/Movie.mp4", NSHomeDirectory()]];
476     
477     /* Video encoder */
478     [fVidEncoderPopUp removeAllItems];
479     [fVidEncoderPopUp addItemWithTitle: @"FFmpeg"];
480     
481     
482     /* Video quality */
483     [fVidTargetSizeField setIntValue: 700];
484         [fVidBitrateField    setIntValue: 1000];
485     
486     [fVidQualityMatrix   selectCell: fVidBitrateCell];
487     [self videoMatrixChanged:nil];
488     
489     /* Video framerate */
490     [fVidRatePopUp removeAllItems];
491         [fVidRatePopUp addItemWithTitle: NSLocalizedString( @"Same as source", @"" )];
492     for( int i = 0; i < hb_video_rates_count; i++ )
493     {
494         if ([[NSString stringWithUTF8String: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%.3f",23.976]])
495                 {
496                         [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@",
497                                              [NSString stringWithUTF8String: hb_video_rates[i].string], @" (NTSC Film)"]];
498                 }
499                 else if ([[NSString stringWithUTF8String: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%d",25]])
500                 {
501                         [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@",
502                                              [NSString stringWithUTF8String: hb_video_rates[i].string], @" (PAL Film/Video)"]];
503                 }
504                 else if ([[NSString stringWithUTF8String: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%.2f",29.97]])
505                 {
506                         [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@",
507                                              [NSString stringWithUTF8String: hb_video_rates[i].string], @" (NTSC Video)"]];
508                 }
509                 else
510                 {
511                         [fVidRatePopUp addItemWithTitle:
512              [NSString stringWithUTF8String: hb_video_rates[i].string]];
513                 }
514     }
515     [fVidRatePopUp selectItemAtIndex: 0];
516         
517         /* Set Auto Crop to On at launch */
518     [fPictureController setAutoCrop:YES];
519         
520         /* Audio bitrate */
521     [fAudTrack1BitratePopUp removeAllItems];
522     for( int i = 0; i < hb_audio_bitrates_count; i++ )
523     {
524         [fAudTrack1BitratePopUp addItemWithTitle:
525          [NSString stringWithUTF8String: hb_audio_bitrates[i].string]];
526         
527     }
528     [fAudTrack1BitratePopUp selectItemAtIndex: hb_audio_bitrates_default];
529         
530     /* Audio samplerate */
531     [fAudTrack1RatePopUp removeAllItems];
532     for( int i = 0; i < hb_audio_rates_count; i++ )
533     {
534         [fAudTrack1RatePopUp addItemWithTitle:
535          [NSString stringWithUTF8String: hb_audio_rates[i].string]];
536     }
537     [fAudTrack1RatePopUp selectItemAtIndex: hb_audio_rates_default];
538         
539     /* Bottom */
540     [fStatusField setStringValue: @""];
541     
542     [self enableUI: NO];
543         [self setupToolbar];
544     
545         /* We disable the Turbo 1st pass checkbox since we are not x264 */
546         [fVidTurboPassCheck setEnabled: NO];
547         [fVidTurboPassCheck setState: NSOffState];
548     
549     
550         /* lets get our default prefs here */
551         [self getDefaultPresets:nil];
552         /* lets initialize the current successful scancount here to 0 */
553         currentSuccessfulScanCount = 0;
554 }
555
556 - (void) enableUI: (bool) b
557 {
558     NSControl * controls[] =
559     { fSrcTitleField, fSrcTitlePopUp,
560         fSrcChapterField, fSrcChapterStartPopUp, fSrcChapterToField,
561         fSrcChapterEndPopUp, fSrcDuration1Field, fSrcDuration2Field,
562         fDstFormatField, fDstFormatPopUp, fDstFile1Field, fDstFile2Field,
563         fDstBrowseButton, fVidRateField, fVidRatePopUp,fVidEncoderField, fVidEncoderPopUp, fVidQualityField,
564         fPictureSizeField,fPictureCroppingField, fVideoFiltersField,fVidQualityMatrix, fSubField, fSubPopUp,
565         fAudSourceLabel, fAudCodecLabel, fAudMixdownLabel, fAudSamplerateLabel, fAudBitrateLabel,
566         fAudTrack1Label, fAudTrack2Label, fAudTrack3Label, fAudTrack4Label,
567         fAudLang1PopUp, fAudLang2PopUp, fAudLang3PopUp, fAudLang4PopUp,
568         fAudTrack1CodecPopUp, fAudTrack2CodecPopUp, fAudTrack3CodecPopUp, fAudTrack4CodecPopUp,
569         fAudTrack1MixPopUp, fAudTrack2MixPopUp, fAudTrack3MixPopUp, fAudTrack4MixPopUp,
570         fAudTrack1RatePopUp, fAudTrack2RatePopUp, fAudTrack3RatePopUp, fAudTrack4RatePopUp,
571         fAudTrack1BitratePopUp, fAudTrack2BitratePopUp, fAudTrack3BitratePopUp, fAudTrack4BitratePopUp,
572         fAudDrcLabel, fAudTrack1DrcSlider, fAudTrack1DrcField, fAudTrack2DrcSlider,
573         fAudTrack2DrcField, fAudTrack3DrcSlider, fAudTrack3DrcField, fAudTrack4DrcSlider,fAudTrack4DrcField,
574         fQueueStatus,fPresetsAdd,fPresetsDelete,fSrcAngleLabel,fSrcAnglePopUp,
575                 fCreateChapterMarkers,fVidTurboPassCheck,fDstMp4LargeFileCheck,fSubForcedCheck,fPresetsOutlineView,
576         fAudDrcLabel,fDstMp4HttpOptFileCheck,fDstMp4iPodFileCheck,fVidQualityRFField,fVidQualityRFLabel,
577         fEncodeStartStopPopUp,fSrcTimeStartEncodingField,fSrcTimeEndEncodingField,fSrcFrameStartEncodingField,
578         fSrcFrameEndEncodingField, fLoadChaptersButton, fSaveChaptersButton, fFrameratePfrCheck};
579     
580     for( unsigned i = 0;
581         i < sizeof( controls ) / sizeof( NSControl * ); i++ )
582     {
583         if( [[controls[i] className] isEqualToString: @"NSTextField"] )
584         {
585             NSTextField * tf = (NSTextField *) controls[i];
586             if( ![tf isBezeled] )
587             {
588                 [tf setTextColor: b ? [NSColor controlTextColor] :
589                  [NSColor disabledControlTextColor]];
590                 continue;
591             }
592         }
593         [controls[i] setEnabled: b];
594         
595     }
596     
597         if (b) 
598     {
599         
600         /* if we're enabling the interface, check if the audio mixdown controls need to be enabled or not */
601         /* these will have been enabled by the mass control enablement above anyway, so we're sense-checking it here */
602         [self setEnabledStateOfAudioMixdownControls:nil];
603         /* we also call calculatePictureSizing here to sense check if we already have vfr selected */
604         [self calculatePictureSizing:nil];
605         /* Also enable the preview window hud controls */
606         [fPictureController enablePreviewHudControls];
607     }
608     else 
609     {
610         
611                 [fPresetsOutlineView setEnabled: NO];
612         [fPictureController disablePreviewHudControls];
613     }
614     
615     [self videoMatrixChanged:nil];
616     [fAdvancedOptions enableUI:b];
617 }
618
619
620 /***********************************************************************
621  * UpdateDockIcon
622  ***********************************************************************
623  * Shows a progression bar on the dock icon, filled according to
624  * 'progress' (0.0 <= progress <= 1.0).
625  * Called with progress < 0.0 or progress > 1.0, restores the original
626  * icon.
627  **********************************************************************/
628 - (void) UpdateDockIcon: (float) progress
629 {
630     NSData * tiff;
631     NSBitmapImageRep * bmp;
632     uint32_t * pen;
633     uint32_t black = htonl( 0x000000FF );
634     uint32_t red   = htonl( 0xFF0000FF );
635     uint32_t white = htonl( 0xFFFFFFFF );
636     int row_start, row_end;
637     int i, j;
638
639     if( progress < 0.0 || progress > 1.0 )
640     {
641         [NSApp setApplicationIconImage: fApplicationIcon];
642         return;
643     }
644
645     /* Get it in a raw bitmap form */
646     tiff = [fApplicationIcon TIFFRepresentationUsingCompression:
647             NSTIFFCompressionNone factor: 1.0];
648     bmp = [NSBitmapImageRep imageRepWithData: tiff];
649     
650     /* Draw the progression bar */
651     /* It's pretty simple (ugly?) now, but I'm no designer */
652
653     row_start = 3 * (int) [bmp size].height / 4;
654     row_end   = 7 * (int) [bmp size].height / 8;
655
656     for( i = row_start; i < row_start + 2; i++ )
657     {
658         pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
659         for( j = 0; j < (int) [bmp size].width; j++ )
660         {
661             pen[j] = black;
662         }
663     }
664     for( i = row_start + 2; i < row_end - 2; i++ )
665     {
666         pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
667         pen[0] = black;
668         pen[1] = black;
669         for( j = 2; j < (int) [bmp size].width - 2; j++ )
670         {
671             if( j < 2 + (int) ( ( [bmp size].width - 4.0 ) * progress ) )
672             {
673                 pen[j] = red;
674             }
675             else
676             {
677                 pen[j] = white;
678             }
679         }
680         pen[j]   = black;
681         pen[j+1] = black;
682     }
683     for( i = row_end - 2; i < row_end; i++ )
684     {
685         pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
686         for( j = 0; j < (int) [bmp size].width; j++ )
687         {
688             pen[j] = black;
689         }
690     }
691
692     /* Now update the dock icon */
693     tiff = [bmp TIFFRepresentationUsingCompression:
694             NSTIFFCompressionNone factor: 1.0];
695     NSImage* icon = [[NSImage alloc] initWithData: tiff];
696     [NSApp setApplicationIconImage: icon];
697     [icon release];
698 }
699
700 - (void) updateUI: (NSTimer *) timer
701 {
702     
703     /* Update UI for fHandle (user scanning instance of libhb ) */
704     
705     hb_list_t  * list;
706     list = hb_get_titles( fHandle );
707     /* check to see if there has been a new scan done
708      this bypasses the constraints of HB_STATE_WORKING
709      not allowing setting a newly scanned source */
710         int checkScanCount = hb_get_scancount( fHandle );
711         if( checkScanCount > currentScanCount )
712         {
713                 currentScanCount = checkScanCount;
714         [fScanIndicator setIndeterminate: NO];
715         [fScanIndicator setDoubleValue: 0.0];
716         [fScanIndicator setHidden: YES];
717                 [self showNewScan:nil];
718         }
719     
720     hb_state_t s;
721     hb_get_state( fHandle, &s );
722     
723     switch( s.state )
724     {
725         case HB_STATE_IDLE:
726             break;
727 #define p s.param.scanning
728         case HB_STATE_SCANNING:
729                 {
730             [fSrcDVD2Field setStringValue: [NSString stringWithFormat:
731                                             NSLocalizedString( @"Scanning title %d of %d...", @"" ),
732                                             p.title_cur, p.title_count]];
733             [fScanIndicator setHidden: NO];
734             [fScanIndicator setDoubleValue: 100.0 * ((double)( p.title_cur - 1 ) / p.title_count)];
735             break;
736                 }
737 #undef p
738             
739 #define p s.param.scandone
740         case HB_STATE_SCANDONE:
741         {
742             [fScanIndicator setIndeterminate: NO];
743             [fScanIndicator setDoubleValue: 0.0];
744             [fScanIndicator setHidden: YES];
745                         [self writeToActivityLog:"ScanDone state received from fHandle"];
746             [self showNewScan:nil];
747             [[fWindow toolbar] validateVisibleItems];
748             
749                         break;
750         }
751 #undef p
752             
753 #define p s.param.working
754         case HB_STATE_WORKING:
755         {
756             
757             break;
758         }
759 #undef p
760             
761 #define p s.param.muxing
762         case HB_STATE_MUXING:
763         {
764             
765             break;
766         }
767 #undef p
768             
769         case HB_STATE_PAUSED:
770             break;
771             
772         case HB_STATE_WORKDONE:
773         {
774             break;
775         }
776     }
777     
778     
779     /* Update UI for fQueueEncodeLibhb */
780     // hb_list_t  * list;
781     // list = hb_get_titles( fQueueEncodeLibhb ); //fQueueEncodeLibhb
782     /* check to see if there has been a new scan done
783      this bypasses the constraints of HB_STATE_WORKING
784      not allowing setting a newly scanned source */
785         
786     checkScanCount = hb_get_scancount( fQueueEncodeLibhb );
787         if( checkScanCount > currentScanCount )
788         {
789                 currentScanCount = checkScanCount;
790         }
791     
792     //hb_state_t s;
793     hb_get_state( fQueueEncodeLibhb, &s );
794     
795     switch( s.state )
796     {
797         case HB_STATE_IDLE:
798             break;
799 #define p s.param.scanning
800         case HB_STATE_SCANNING:
801                 {
802             [fStatusField setStringValue: [NSString stringWithFormat:
803                                            NSLocalizedString( @"Queue Scanning title %d of %d...", @"" ),
804                                            p.title_cur, p.title_count]];
805             
806             /* Set the status string in fQueueController as well */                               
807             [fQueueController setQueueStatusString: [NSString stringWithFormat:
808                                                      NSLocalizedString( @"Queue Scanning title %d of %d...", @"" ),
809                                                      p.title_cur, p.title_count]];
810             break;
811                 }
812 #undef p
813             
814 #define p s.param.scandone
815         case HB_STATE_SCANDONE:
816         {
817                         [self writeToActivityLog:"ScanDone state received from fQueueEncodeLibhb"];
818             [self processNewQueueEncode];
819             [[fWindow toolbar] validateVisibleItems];
820             
821                         break;
822         }
823 #undef p
824             
825             
826 #define p s.param.working
827             
828         case HB_STATE_SEARCHING:
829                 {
830             NSMutableString * string;
831             NSString * pass_desc;
832             
833             /* Update text field */
834             pass_desc = @"";
835             //string = [NSMutableString stringWithFormat: NSLocalizedString( @"Searching for start point: pass %d %@ of %d, %.2f %%", @"" ), p.job_cur, pass_desc, p.job_count, 100.0 * p.progress];
836             /* For now, do not announce "pass x of x for the search phase ... */
837             string = [NSMutableString stringWithFormat: NSLocalizedString( @"Searching for start point ... :  %.2f %%", @"" ), 100.0 * p.progress];
838             
839                         if( p.seconds > -1 )
840             {
841                 [string appendFormat:
842                  NSLocalizedString( @" (ETA %02dh%02dm%02ds)", @"" ), p.hours, p.minutes, p.seconds];
843             }
844             
845             [fStatusField setStringValue: string];
846             /* Set the status string in fQueueController as well */
847             [fQueueController setQueueStatusString: string];
848             /* Update slider */
849             CGFloat progress_total = ( p.progress + p.job_cur - 1 ) / p.job_count;
850             [fRipIndicator setIndeterminate: NO];
851             [fRipIndicator setDoubleValue:100.0 * progress_total];
852             
853             // If progress bar hasn't been revealed at the bottom of the window, do
854             // that now. This code used to be in doRip. I moved it to here to handle
855             // the case where hb_start is called by HBQueueController and not from
856             // HBController.
857             if( !fRipIndicatorShown )
858             {
859                 NSRect frame = [fWindow frame];
860                 if( frame.size.width <= 591 )
861                     frame.size.width = 591;
862                 frame.size.height += 36;
863                 frame.origin.y -= 36;
864                 [fWindow setFrame:frame display:YES animate:YES];
865                 fRipIndicatorShown = YES;
866                 
867             }
868             
869             /* Update dock icon */
870             /* Note not done yet */
871             break;  
872         }
873             
874             
875         case HB_STATE_WORKING:
876         {
877             NSMutableString * string;
878             NSString * pass_desc;
879                         /* Update text field */
880             if (p.job_cur == 1 && p.job_count > 1)
881             {
882                 if ([[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SubtitleList"] && [[[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex]objectForKey:@"SubtitleList"] objectAtIndex:0] objectForKey:@"subtitleSourceTrackNum"] intValue] == 1)
883                 {
884                     pass_desc = @"(subtitle scan)";   
885                 }
886                 else
887                 {
888                     pass_desc = @"";
889                 }
890             }
891             else
892             {
893                 pass_desc = @"";
894             }
895             
896                         string = [NSMutableString stringWithFormat: NSLocalizedString( @"Encoding: pass %d %@ of %d, %.2f %%", @"" ), p.job_cur, pass_desc, p.job_count, 100.0 * p.progress];
897             
898                         if( p.seconds > -1 )
899             {
900                 [string appendFormat:
901                  NSLocalizedString( @" (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)", @"" ),
902                  p.rate_cur, p.rate_avg, p.hours, p.minutes, p.seconds];
903             }
904             
905             [fStatusField setStringValue: string];
906             /* Set the status string in fQueueController as well but add currentQueueEncodeNameString to delineate
907              * which encode is being worked on by this instance in a multiple instance situation
908              */
909             NSString * queueStatusString = [NSString stringWithFormat:@"%@ -> %@",string,currentQueueEncodeNameString];
910             [fQueueController setQueueStatusString:queueStatusString];
911             
912             /* Update slider */
913             CGFloat progress_total = ( p.progress + p.job_cur - 1 ) / p.job_count;
914             [fRipIndicator setIndeterminate: NO];
915             [fRipIndicator setDoubleValue:100.0 * progress_total];
916             
917             // If progress bar hasn't been revealed at the bottom of the window, do
918             // that now. This code used to be in doRip. I moved it to here to handle
919             // the case where hb_start is called by HBQueueController and not from
920             // HBController.
921             if( !fRipIndicatorShown )
922             {
923                 NSRect frame = [fWindow frame];
924                 if( frame.size.width <= 591 )
925                     frame.size.width = 591;
926                 frame.size.height += 36;
927                 frame.origin.y -= 36;
928                 [fWindow setFrame:frame display:YES animate:YES];
929                 fRipIndicatorShown = YES;
930                 
931             }
932             
933             /* Update dock icon */
934             if( dockIconProgress < 100.0 * progress_total )
935             {
936                 [self UpdateDockIcon: progress_total];
937                 dockIconProgress += 5;
938             }
939             
940             break;
941         }
942 #undef p
943             
944 #define p s.param.muxing
945         case HB_STATE_MUXING:
946         {
947             /* Update text field */
948             [fStatusField setStringValue: NSLocalizedString( @"Muxing...", @"" )];
949             /* Set the status string in fQueueController as well */
950             [fQueueController setQueueStatusString: NSLocalizedString( @"Muxing...", @"" )];
951             /* Update slider */
952             [fRipIndicator setIndeterminate: YES];
953             [fRipIndicator startAnimation: nil];
954             
955             /* Update dock icon */
956             [self UpdateDockIcon: 1.0];
957             
958                         break;
959         }
960 #undef p
961             
962         case HB_STATE_PAUSED:
963                     [fStatusField setStringValue: NSLocalizedString( @"Paused", @"" )];
964             [fQueueController setQueueStatusString: NSLocalizedString( @"Paused", @"" )];
965             
966                         break;
967             
968         case HB_STATE_WORKDONE:
969         {
970             // HB_STATE_WORKDONE happpens as a result of libhb finishing all its jobs
971             // or someone calling hb_stop. In the latter case, hb_stop does not clear
972             // out the remaining passes/jobs in the queue. We'll do that here.
973             
974             // Delete all remaining jobs of this encode.
975             [fStatusField setStringValue: NSLocalizedString( @"Encode Finished.", @"" )];
976             /* Set the status string in fQueueController as well */
977             [fQueueController setQueueStatusString: NSLocalizedString( @"Encode Finished.", @"" )];
978             [fRipIndicator setIndeterminate: NO];
979             [fRipIndicator stopAnimation: nil];
980             [fRipIndicator setDoubleValue: 0.0];
981             [[fWindow toolbar] validateVisibleItems];
982             
983             /* Restore dock icon */
984             [self UpdateDockIcon: -1.0];
985             dockIconProgress = 0;
986             
987             if( fRipIndicatorShown )
988             {
989                 NSRect frame = [fWindow frame];
990                 if( frame.size.width <= 591 )
991                                     frame.size.width = 591;
992                 frame.size.height += -36;
993                 frame.origin.y -= -36;
994                 [fWindow setFrame:frame display:YES animate:YES];
995                                 fRipIndicatorShown = NO;
996                         }
997             /* Since we are done with this encode, tell output to stop writing to the
998              * individual encode log
999              */
1000                         [outputPanel endEncodeLog];
1001             /* Check to see if the encode state has not been cancelled
1002              to determine if we should check for encode done notifications */
1003                         if( fEncodeState != 2 )
1004             {
1005                 NSString *pathOfFinishedEncode;
1006                 /* Get the output file name for the finished encode */
1007                 pathOfFinishedEncode = [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"DestinationPath"];
1008                 
1009                 /* Both the Growl Alert and Sending to MetaX can be done as encodes roll off the queue */
1010                 if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Growl Notification"] || 
1011                     [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window And Growl"])
1012                 {
1013                     /* If Play System Alert has been selected in Preferences */
1014                     if( [[NSUserDefaults standardUserDefaults] boolForKey:@"AlertWhenDoneSound"] == YES )
1015                     {
1016                         NSBeep();
1017                     }
1018                     [self showGrowlDoneNotification:pathOfFinishedEncode];
1019                 }
1020                 
1021                 /* Send to MetaX */
1022                 [self sendToMetaX:pathOfFinishedEncode];
1023                 
1024                 /* since we have successfully completed an encode, we increment the queue counter */
1025                 [self incrementQueueItemDone:currentQueueEncodeIndex]; 
1026
1027             }
1028             
1029             break;
1030         }
1031     }
1032     
1033     /* Since we can use multiple instance off of the same queue file it is imperative that we keep the QueueFileArray updated off of the QueueFile.plist
1034      * so we go ahead and do it in this existing timer as opposed to using a new one */
1035     
1036     NSMutableArray * tempQueueArray = [[NSMutableArray alloc] initWithContentsOfFile:QueueFile];
1037     [QueueFileArray setArray:tempQueueArray];
1038     [tempQueueArray release]; 
1039     /* Send Fresh QueueFileArray to fQueueController to update queue window */
1040     [fQueueController setQueueArray: QueueFileArray];
1041     [self getQueueStats];
1042     
1043 }
1044
1045 /* We use this to write messages to stderr from the macgui which show up in the activity window and log*/
1046 - (void) writeToActivityLog:(const char *) format, ...
1047 {
1048     va_list args;
1049     va_start(args, format);
1050     if (format != nil)
1051     {
1052         char str[1024];
1053         vsnprintf( str, 1024, format, args );
1054
1055         time_t _now = time( NULL );
1056         struct tm * now  = localtime( &_now );
1057         fprintf(stderr, "[%02d:%02d:%02d] macgui: %s\n", now->tm_hour, now->tm_min, now->tm_sec, str );
1058     }
1059     va_end(args);
1060 }
1061
1062 #pragma mark -
1063 #pragma mark Toolbar
1064 // ============================================================
1065 // NSToolbar Related Methods
1066 // ============================================================
1067
1068 - (void) setupToolbar {
1069     NSToolbar *toolbar = [[[NSToolbar alloc] initWithIdentifier: @"HandBrake Toolbar"] autorelease];
1070
1071     [toolbar setAllowsUserCustomization: YES];
1072     [toolbar setAutosavesConfiguration: YES];
1073     [toolbar setDisplayMode: NSToolbarDisplayModeIconAndLabel];
1074
1075     [toolbar setDelegate: self];
1076
1077     [fWindow setToolbar: toolbar];
1078 }
1079
1080 - (NSToolbarItem *) toolbar: (NSToolbar *)toolbar itemForItemIdentifier:
1081     (NSString *) itemIdent willBeInsertedIntoToolbar:(BOOL) willBeInserted {
1082     NSToolbarItem * item = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdent] autorelease];
1083
1084     if ([itemIdent isEqualToString: ToggleDrawerIdentifier])
1085     {
1086         [item setLabel: @"Toggle Presets"];
1087         [item setPaletteLabel: @"Toggler Presets"];
1088         [item setToolTip: @"Open/Close Preset Drawer"];
1089         [item setImage: [NSImage imageNamed: @"Drawer"]];
1090         [item setTarget: self];
1091         [item setAction: @selector(toggleDrawer:)];
1092         [item setAutovalidates: NO];
1093     }
1094     else if ([itemIdent isEqualToString: StartEncodingIdentifier])
1095     {
1096         [item setLabel: @"Start"];
1097         [item setPaletteLabel: @"Start Encoding"];
1098         [item setToolTip: @"Start Encoding"];
1099         [item setImage: [NSImage imageNamed: @"Play"]];
1100         [item setTarget: self];
1101         [item setAction: @selector(Rip:)];
1102     }
1103     else if ([itemIdent isEqualToString: ShowQueueIdentifier])
1104     {
1105         [item setLabel: @"Show Queue"];
1106         [item setPaletteLabel: @"Show Queue"];
1107         [item setToolTip: @"Show Queue"];
1108         [item setImage: [NSImage imageNamed: @"Queue"]];
1109         [item setTarget: self];
1110         [item setAction: @selector(showQueueWindow:)];
1111         [item setAutovalidates: NO];
1112     }
1113     else if ([itemIdent isEqualToString: AddToQueueIdentifier])
1114     {
1115         [item setLabel: @"Add to Queue"];
1116         [item setPaletteLabel: @"Add to Queue"];
1117         [item setToolTip: @"Add to Queue"];
1118         [item setImage: [NSImage imageNamed: @"AddToQueue"]];
1119         [item setTarget: self];
1120         [item setAction: @selector(addToQueue:)];
1121     }
1122     else if ([itemIdent isEqualToString: PauseEncodingIdentifier])
1123     {
1124         [item setLabel: @"Pause"];
1125         [item setPaletteLabel: @"Pause Encoding"];
1126         [item setToolTip: @"Pause Encoding"];
1127         [item setImage: [NSImage imageNamed: @"Pause"]];
1128         [item setTarget: self];
1129         [item setAction: @selector(Pause:)];
1130     }
1131     else if ([itemIdent isEqualToString: ShowPictureIdentifier])
1132     {
1133         [item setLabel: @"Picture Settings"];
1134         [item setPaletteLabel: @"Show Picture Settings"];
1135         [item setToolTip: @"Show Picture Settings"];
1136         [item setImage: [NSImage imageNamed: @"pref-picture"]];
1137         [item setTarget: self];
1138         [item setAction: @selector(showPicturePanel:)];
1139     }
1140     else if ([itemIdent isEqualToString: ShowPreviewIdentifier])
1141     {
1142         [item setLabel: @"Preview Window"];
1143         [item setPaletteLabel: @"Show Preview"];
1144         [item setToolTip: @"Show Preview"];
1145         //[item setImage: [NSImage imageNamed: @"pref-picture"]];
1146         [item setImage: [NSImage imageNamed: @"Brushed_Window"]];
1147         [item setTarget: self];
1148         [item setAction: @selector(showPreviewWindow:)];
1149     }
1150     else if ([itemIdent isEqualToString: ShowActivityIdentifier]) 
1151     {
1152         [item setLabel: @"Activity Window"];
1153         [item setPaletteLabel: @"Show Activity Window"];
1154         [item setToolTip: @"Show Activity Window"];
1155         [item setImage: [NSImage imageNamed: @"ActivityWindow"]];
1156         [item setTarget: self];
1157         [item setAction: @selector(showDebugOutputPanel:)];
1158         [item setAutovalidates: NO];
1159     }
1160     else if ([itemIdent isEqualToString: ChooseSourceIdentifier])
1161     {
1162         [item setLabel: @"Source"];
1163         [item setPaletteLabel: @"Source"];
1164         [item setToolTip: @"Choose Video Source"];
1165         [item setImage: [NSImage imageNamed: @"Source"]];
1166         [item setTarget: self];
1167         [item setAction: @selector(browseSources:)];
1168     }
1169     else
1170     {
1171         return nil;
1172     }
1173
1174     return item;
1175 }
1176
1177 - (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar
1178 {
1179     return [NSArray arrayWithObjects: ChooseSourceIdentifier, NSToolbarSeparatorItemIdentifier, StartEncodingIdentifier,
1180         PauseEncodingIdentifier, AddToQueueIdentifier, ShowQueueIdentifier, NSToolbarFlexibleSpaceItemIdentifier, 
1181                 NSToolbarSpaceItemIdentifier, ShowPictureIdentifier, ShowPreviewIdentifier, ShowActivityIdentifier, ToggleDrawerIdentifier, nil];
1182 }
1183
1184 - (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) toolbar
1185 {
1186     return [NSArray arrayWithObjects:  StartEncodingIdentifier, PauseEncodingIdentifier, AddToQueueIdentifier,
1187         ChooseSourceIdentifier, ShowQueueIdentifier, ShowPictureIdentifier, ShowPreviewIdentifier, ShowActivityIdentifier, ToggleDrawerIdentifier,
1188         NSToolbarCustomizeToolbarItemIdentifier, NSToolbarFlexibleSpaceItemIdentifier,
1189         NSToolbarSpaceItemIdentifier, NSToolbarSeparatorItemIdentifier, nil];
1190 }
1191
1192 - (BOOL) validateToolbarItem: (NSToolbarItem *) toolbarItem
1193 {
1194     NSString * ident = [toolbarItem itemIdentifier];
1195         
1196     if (fHandle)
1197     {
1198         hb_state_t s;
1199         
1200         hb_get_state( fHandle, &s );
1201         if (s.state == HB_STATE_SCANNING)
1202         {
1203             
1204             if ([ident isEqualToString: ChooseSourceIdentifier])
1205             {
1206                 [toolbarItem setImage: [NSImage imageNamed: @"Stop"]];
1207                 [toolbarItem setLabel: @"Cancel Scan"];
1208                 [toolbarItem setPaletteLabel: @"Cancel Scanning"];
1209                 [toolbarItem setToolTip: @"Cancel Scanning Source"];
1210                 return YES;
1211             }
1212             
1213             if ([ident isEqualToString: StartEncodingIdentifier] || [ident isEqualToString: AddToQueueIdentifier])
1214                 return NO;
1215         }
1216         else
1217         {
1218             if ([ident isEqualToString: ChooseSourceIdentifier])
1219             {
1220                 [toolbarItem setImage: [NSImage imageNamed: @"Source"]];
1221                 [toolbarItem setLabel: @"Source"];
1222                 [toolbarItem setPaletteLabel: @"Source"];
1223                 [toolbarItem setToolTip: @"Choose Video Source"];
1224                 return YES;
1225             }
1226         }
1227
1228         hb_get_state2( fQueueEncodeLibhb, &s );
1229         
1230         if (s.state == HB_STATE_WORKING || s.state == HB_STATE_SEARCHING || s.state == HB_STATE_MUXING)
1231         {
1232             if ([ident isEqualToString: StartEncodingIdentifier])
1233             {
1234                 [toolbarItem setImage: [NSImage imageNamed: @"Stop"]];
1235                 [toolbarItem setLabel: @"Stop"];
1236                 [toolbarItem setPaletteLabel: @"Stop"];
1237                 [toolbarItem setToolTip: @"Stop Encoding"];
1238                 return YES;
1239             }
1240             if ([ident isEqualToString: PauseEncodingIdentifier])
1241             {
1242                 [toolbarItem setImage: [NSImage imageNamed: @"Pause"]];
1243                 [toolbarItem setLabel: @"Pause"];
1244                 [toolbarItem setPaletteLabel: @"Pause Encoding"];
1245                 [toolbarItem setToolTip: @"Pause Encoding"];
1246                 return YES;
1247             }
1248             if (SuccessfulScan)
1249             {
1250                 if ([ident isEqualToString: AddToQueueIdentifier])
1251                     return YES;
1252                 if ([ident isEqualToString: ShowPictureIdentifier])
1253                     return YES;
1254                 if ([ident isEqualToString: ShowPreviewIdentifier])
1255                     return YES;
1256             }
1257         }
1258         else if (s.state == HB_STATE_PAUSED)
1259         {
1260             if ([ident isEqualToString: PauseEncodingIdentifier])
1261             {
1262                 [toolbarItem setImage: [NSImage imageNamed: @"Play"]];
1263                 [toolbarItem setLabel: @"Resume"];
1264                 [toolbarItem setPaletteLabel: @"Resume Encoding"];
1265                 [toolbarItem setToolTip: @"Resume Encoding"];
1266                 return YES;
1267             }
1268             if ([ident isEqualToString: StartEncodingIdentifier])
1269                 return YES;
1270             if ([ident isEqualToString: AddToQueueIdentifier])
1271                 return YES;
1272             if ([ident isEqualToString: ShowPictureIdentifier])
1273                 return YES;
1274             if ([ident isEqualToString: ShowPreviewIdentifier])
1275                 return YES;
1276         }
1277         else if (s.state == HB_STATE_SCANNING)
1278             return NO;
1279         else if (s.state == HB_STATE_WORKDONE || s.state == HB_STATE_SCANDONE || SuccessfulScan)
1280         {
1281             if ([ident isEqualToString: StartEncodingIdentifier])
1282             {
1283                 [toolbarItem setImage: [NSImage imageNamed: @"Play"]];
1284                 if (hb_count(fHandle) > 0)
1285                     [toolbarItem setLabel: @"Start Queue"];
1286                 else
1287                     [toolbarItem setLabel: @"Start"];
1288                 [toolbarItem setPaletteLabel: @"Start Encoding"];
1289                 [toolbarItem setToolTip: @"Start Encoding"];
1290                 return YES;
1291             }
1292             if ([ident isEqualToString: AddToQueueIdentifier])
1293                 return YES;
1294             if ([ident isEqualToString: ShowPictureIdentifier])
1295                 return YES;
1296             if ([ident isEqualToString: ShowPreviewIdentifier])
1297                 return YES;
1298         }
1299
1300     }
1301     /* If there are any pending queue items, make sure the start/stop button is active */
1302     if ([ident isEqualToString: StartEncodingIdentifier] && fPendingCount > 0)
1303         return YES;
1304     if ([ident isEqualToString: ShowQueueIdentifier])
1305         return YES;
1306     if ([ident isEqualToString: ToggleDrawerIdentifier])
1307         return YES;
1308     if ([ident isEqualToString: ChooseSourceIdentifier])
1309         return YES;
1310     if ([ident isEqualToString: ShowActivityIdentifier])
1311         return YES;
1312     
1313     return NO;
1314 }
1315
1316 - (BOOL) validateMenuItem: (NSMenuItem *) menuItem
1317 {
1318     SEL action = [menuItem action];
1319     
1320     hb_state_t s;
1321     hb_get_state2( fHandle, &s );
1322     
1323     if (fHandle)
1324     {
1325         if (action == @selector(addToQueue:) || action == @selector(showPicturePanel:) || action == @selector(showAddPresetPanel:))
1326             return SuccessfulScan && [fWindow attachedSheet] == nil;
1327         
1328         if (action == @selector(browseSources:))
1329         {
1330             if (s.state == HB_STATE_SCANNING)
1331                 return NO;
1332             else
1333                 return [fWindow attachedSheet] == nil;
1334         }
1335         if (action == @selector(selectDefaultPreset:))
1336             return [fPresetsOutlineView selectedRow] >= 0 && [fWindow attachedSheet] == nil;
1337         if (action == @selector(Pause:))
1338         {
1339             if (s.state == HB_STATE_WORKING)
1340             {
1341                 if(![[menuItem title] isEqualToString:@"Pause Encoding"])
1342                     [menuItem setTitle:@"Pause Encoding"];
1343                 return YES;
1344             }
1345             else if (s.state == HB_STATE_PAUSED)
1346             {
1347                 if(![[menuItem title] isEqualToString:@"Resume Encoding"])
1348                     [menuItem setTitle:@"Resume Encoding"];
1349                 return YES;
1350             }
1351             else
1352                 return NO;
1353         }
1354         if (action == @selector(Rip:))
1355         {
1356             if (s.state == HB_STATE_WORKING || s.state == HB_STATE_MUXING || s.state == HB_STATE_PAUSED)
1357             {
1358                 if(![[menuItem title] isEqualToString:@"Stop Encoding"])
1359                     [menuItem setTitle:@"Stop Encoding"];
1360                 return YES;
1361             }
1362             else if (SuccessfulScan)
1363             {
1364                 if(![[menuItem title] isEqualToString:@"Start Encoding"])
1365                     [menuItem setTitle:@"Start Encoding"];
1366                 return [fWindow attachedSheet] == nil;
1367             }
1368             else
1369                 return NO;
1370         }
1371     }
1372     if( action == @selector(setDefaultPreset:) )
1373     {
1374         return [fPresetsOutlineView selectedRow] != -1;
1375     }
1376
1377     return YES;
1378 }
1379
1380 #pragma mark -
1381 #pragma mark Encode Done Actions
1382 // register a test notification and make
1383 // it enabled by default
1384 #define SERVICE_NAME @"Encode Done"
1385 - (NSDictionary *)registrationDictionaryForGrowl 
1386
1387     NSDictionary *registrationDictionary = [NSDictionary dictionaryWithObjectsAndKeys: 
1388     [NSArray arrayWithObjects:SERVICE_NAME,nil], GROWL_NOTIFICATIONS_ALL, 
1389     [NSArray arrayWithObjects:SERVICE_NAME,nil], GROWL_NOTIFICATIONS_DEFAULT, 
1390     nil]; 
1391
1392     return registrationDictionary; 
1393
1394
1395 -(void)showGrowlDoneNotification:(NSString *) filePath
1396 {
1397     /* This end of encode action is called as each encode rolls off of the queue */
1398     /* Setup the Growl stuff ... */
1399     NSString * finishedEncode = filePath;
1400     /* strip off the path to just show the file name */
1401     finishedEncode = [finishedEncode lastPathComponent];
1402     NSString * growlMssg = [NSString stringWithFormat: @"your HandBrake encode %@ is done!",finishedEncode];
1403     [GrowlApplicationBridge 
1404      notifyWithTitle:@"Put down that cocktail..." 
1405      description:growlMssg 
1406      notificationName:SERVICE_NAME
1407      iconData:nil 
1408      priority:0 
1409      isSticky:1 
1410      clickContext:nil];
1411 }
1412 -(void)sendToMetaX:(NSString *) filePath
1413 {
1414     /* This end of encode action is called as each encode rolls off of the queue */
1415     if([[NSUserDefaults standardUserDefaults] boolForKey: @"sendToMetaX"] == YES)
1416     {
1417         NSString *sendToApp = [[NSUserDefaults standardUserDefaults] objectForKey: @"SendCompletedEncodeToApp"];
1418         if (![sendToApp isEqualToString:@"None"])
1419         {
1420             [self writeToActivityLog: "trying to send encode to: %s", [sendToApp UTF8String]];
1421             NSAppleScript *myScript = [[NSAppleScript alloc] initWithSource: [NSString stringWithFormat: @"%@%@%@%@%@", @"tell application \"",sendToApp,@"\" to open (POSIX file \"", filePath, @"\")"]];
1422             [myScript executeAndReturnError: nil];
1423             [myScript release];
1424         }
1425         
1426     }
1427 }
1428
1429 - (void) queueCompletedAlerts
1430 {
1431     /* If Play System Alert has been selected in Preferences */
1432     if( [[NSUserDefaults standardUserDefaults] boolForKey:@"AlertWhenDoneSound"] == YES )
1433     {
1434         NSBeep();
1435     }
1436     
1437     /* If Alert Window or Window and Growl has been selected */
1438     if( [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window"] ||
1439        [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window And Growl"] )
1440     {
1441         /*On Screen Notification*/
1442         int status;
1443         status = NSRunAlertPanel(@"Put down that cocktail...",@"Your HandBrake queue is done!", @"OK", nil, nil);
1444         [NSApp requestUserAttention:NSCriticalRequest];
1445     }
1446     
1447     /* If sleep has been selected */
1448     if( [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Put Computer To Sleep"] )
1449     {
1450         /* Sleep */
1451         NSDictionary* errorDict;
1452         NSAppleEventDescriptor* returnDescriptor = nil;
1453         NSAppleScript* scriptObject = [[NSAppleScript alloc] initWithSource:
1454                                        @"tell application \"Finder\" to sleep"];
1455         returnDescriptor = [scriptObject executeAndReturnError: &errorDict];
1456         [scriptObject release];
1457     }
1458     /* If Shutdown has been selected */
1459     if( [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Shut Down Computer"] )
1460     {
1461         /* Shut Down */
1462         NSDictionary* errorDict;
1463         NSAppleEventDescriptor* returnDescriptor = nil;
1464         NSAppleScript* scriptObject = [[NSAppleScript alloc] initWithSource:
1465                                        @"tell application \"Finder\" to shut down"];
1466         returnDescriptor = [scriptObject executeAndReturnError: &errorDict];
1467         [scriptObject release];
1468     }
1469 }
1470
1471 #pragma mark -
1472 #pragma mark Get New Source
1473
1474 /*Opens the source browse window, called from Open Source widgets */
1475 - (IBAction) browseSources: (id) sender
1476 {
1477     
1478     hb_state_t s;
1479     hb_get_state( fHandle, &s );
1480     if (s.state == HB_STATE_SCANNING)
1481     {
1482         [self cancelScanning:nil];
1483         return;
1484     }
1485     
1486     
1487     NSOpenPanel * panel;
1488         
1489     panel = [NSOpenPanel openPanel];
1490     [panel setAllowsMultipleSelection: NO];
1491     [panel setCanChooseFiles: YES];
1492     [panel setCanChooseDirectories: YES ];
1493     NSString * sourceDirectory;
1494         if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastSourceDirectory"])
1495         {
1496                 sourceDirectory = [[NSUserDefaults standardUserDefaults] stringForKey:@"LastSourceDirectory"];
1497         }
1498         else
1499         {
1500                 sourceDirectory = @"~/Desktop";
1501                 sourceDirectory = [sourceDirectory stringByExpandingTildeInPath];
1502         }
1503     /* we open up the browse sources sheet here and call for browseSourcesDone after the sheet is closed
1504         * to evaluate whether we want to specify a title, we pass the sender in the contextInfo variable
1505         */
1506     [panel beginSheetForDirectory: sourceDirectory file: nil types: nil
1507                    modalForWindow: fWindow modalDelegate: self
1508                    didEndSelector: @selector( browseSourcesDone:returnCode:contextInfo: )
1509                       contextInfo: sender]; 
1510 }
1511
1512 - (void) browseSourcesDone: (NSOpenPanel *) sheet
1513                 returnCode: (int) returnCode contextInfo: (void *) contextInfo
1514 {
1515     /* we convert the sender content of contextInfo back into a variable called sender
1516      * mostly just for consistency for evaluation later
1517      */
1518     id sender = (id)contextInfo;
1519     /* User selected a file to open */
1520         if( returnCode == NSOKButton )
1521     {
1522             /* Free display name allocated previously by this code */
1523         [browsedSourceDisplayName release];
1524        
1525         NSString *scanPath = [[sheet filenames] objectAtIndex: 0];
1526         /* we set the last searched source directory in the prefs here */
1527         NSString *sourceDirectory = [scanPath stringByDeletingLastPathComponent];
1528         [[NSUserDefaults standardUserDefaults] setObject:sourceDirectory forKey:@"LastSourceDirectory"];
1529         /* we order out sheet, which is the browse window as we need to open
1530          * the title selection sheet right away
1531          */
1532         [sheet orderOut: self];
1533         
1534         if (sender == fOpenSourceTitleMMenu || [[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask)
1535         {
1536             /* We put the chosen source path in the source display text field for the
1537              * source title selection sheet in which the user specifies the specific title to be
1538              * scanned  as well as the short source name in fSrcDsplyNameTitleScan just for display
1539              * purposes in the title panel
1540              */
1541             /* Full Path */
1542             [fScanSrcTitlePathField setStringValue:scanPath];
1543             NSString *displayTitlescanSourceName;
1544
1545             if ([[scanPath lastPathComponent] isEqualToString: @"VIDEO_TS"])
1546             {
1547                 /* If VIDEO_TS Folder is chosen, choose its parent folder for the source display name
1548                  we have to use the title->path value so we get the proper name of the volume if a physical dvd is the source*/
1549                 displayTitlescanSourceName = [[scanPath stringByDeletingLastPathComponent] lastPathComponent];
1550             }
1551             else
1552             {
1553                 /* if not the VIDEO_TS Folder, we can assume the chosen folder is the source name */
1554                 displayTitlescanSourceName = [scanPath lastPathComponent];
1555             }
1556             /* we set the source display name in the title selection dialogue */
1557             [fSrcDsplyNameTitleScan setStringValue:displayTitlescanSourceName];
1558             /* we set the attempted scans display name for main window to displayTitlescanSourceName*/
1559             browsedSourceDisplayName = [displayTitlescanSourceName retain];
1560             /* We show the actual sheet where the user specifies the title to be scanned
1561              * as we are going to do a title specific scan
1562              */
1563             [self showSourceTitleScanPanel:nil];
1564         }
1565         else
1566         {
1567             /* We are just doing a standard full source scan, so we specify "0" to libhb */
1568             NSString *path = [[sheet filenames] objectAtIndex: 0];
1569             
1570             /* We check to see if the chosen file at path is a package */
1571             if ([[NSWorkspace sharedWorkspace] isFilePackageAtPath:path])
1572             {
1573                 [self writeToActivityLog: "trying to open a package at: %s", [path UTF8String]];
1574                 /* We check to see if this is an .eyetv package */
1575                 if ([[path pathExtension] isEqualToString: @"eyetv"])
1576                 {
1577                     [self writeToActivityLog:"trying to open eyetv package"];
1578                     /* We're looking at an EyeTV package - try to open its enclosed
1579                      .mpg media file */
1580                      browsedSourceDisplayName = [[[path stringByDeletingPathExtension] lastPathComponent] retain];
1581                     NSString *mpgname;
1582                     int n = [[path stringByAppendingString: @"/"]
1583                              completePathIntoString: &mpgname caseSensitive: YES
1584                              matchesIntoArray: nil
1585                              filterTypes: [NSArray arrayWithObject: @"mpg"]];
1586                     if (n > 0)
1587                     {
1588                         /* Found an mpeg inside the eyetv package, make it our scan path 
1589                         and call performScan on the enclosed mpeg */
1590                         path = mpgname;
1591                         [self writeToActivityLog:"found mpeg in eyetv package"];
1592                         [self performScan:path scanTitleNum:0];
1593                     }
1594                     else
1595                     {
1596                         /* We did not find an mpeg file in our package, so we do not call performScan */
1597                         [self writeToActivityLog:"no valid mpeg in eyetv package"];
1598                     }
1599                 }
1600                 /* We check to see if this is a .dvdmedia package */
1601                 else if ([[path pathExtension] isEqualToString: @"dvdmedia"])
1602                 {
1603                     /* path IS a package - but dvdmedia packages can be treaded like normal directories */
1604                     browsedSourceDisplayName = [[[path stringByDeletingPathExtension] lastPathComponent] retain];
1605                     [self writeToActivityLog:"trying to open dvdmedia package"];
1606                     [self performScan:path scanTitleNum:0];
1607                 }
1608                 else
1609                 {
1610                     /* The package is not an eyetv package, so we do not call performScan */
1611                     [self writeToActivityLog:"unable to open package"];
1612                 }
1613             }
1614             else // path is not a package, so we treat it as a dvd parent folder or VIDEO_TS folder
1615             {
1616                 /* path is not a package, so we call perform scan directly on our file */
1617                 if ([[path lastPathComponent] isEqualToString: @"VIDEO_TS"])
1618                 {
1619                     [self writeToActivityLog:"trying to open video_ts folder (video_ts folder chosen)"];
1620                     /* If VIDEO_TS Folder is chosen, choose its parent folder for the source display name*/
1621                     browsedSourceDisplayName = [[[path stringByDeletingLastPathComponent] lastPathComponent] retain];
1622                 }
1623                 else
1624                 {
1625                     [self writeToActivityLog:"trying to open video_ts folder (parent directory chosen)"];
1626                     /* if not the VIDEO_TS Folder, we can assume the chosen folder is the source name */
1627                     /* make sure we remove any path extension as this can also be an '.mpg' file */
1628                     browsedSourceDisplayName = [[path lastPathComponent] retain];
1629                 }
1630                 applyQueueToScan = NO;
1631                 [self performScan:path scanTitleNum:0];
1632             }
1633
1634         }
1635
1636     }
1637 }
1638
1639 - (IBAction)showAboutPanel:(id)sender
1640 {
1641     NSMutableDictionary* d = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
1642         fApplicationIcon, @"ApplicationIcon",
1643         nil ];
1644     [NSApp orderFrontStandardAboutPanelWithOptions:d];
1645     [d release];
1646 }
1647
1648 /* Here we open the title selection sheet where we can specify an exact title to be scanned */
1649 - (IBAction) showSourceTitleScanPanel: (id) sender
1650 {
1651     /* We default the title number to be scanned to "0" which results in a full source scan, unless the
1652     * user changes it
1653     */
1654     [fScanSrcTitleNumField setStringValue: @"0"];
1655         /* Show the panel */
1656         [NSApp beginSheet:fScanSrcTitlePanel modalForWindow:fWindow modalDelegate:nil didEndSelector:NULL contextInfo:NULL];
1657 }
1658
1659 - (IBAction) closeSourceTitleScanPanel: (id) sender
1660 {
1661     [NSApp endSheet: fScanSrcTitlePanel];
1662     [fScanSrcTitlePanel orderOut: self];
1663     
1664     if(sender == fScanSrcTitleOpenButton)
1665     {
1666         /* We setup the scan status in the main window to indicate a source title scan */
1667         [fSrcDVD2Field setStringValue: @"Opening a new source title ..."];
1668                 [fScanIndicator setHidden: NO];
1669         [fScanIndicator setIndeterminate: YES];
1670         [fScanIndicator startAnimation: nil];
1671                 
1672         /* We use the performScan method to actually perform the specified scan passing the path and the title
1673          * to be scanned
1674          */
1675         applyQueueToScan = NO;
1676         [self performScan:[fScanSrcTitlePathField stringValue] scanTitleNum:[fScanSrcTitleNumField intValue]];
1677     }
1678 }
1679
1680 /* Here we actually tell hb_scan to perform the source scan, using the path to source and title number*/
1681 - (void) performScan:(NSString *) scanPath scanTitleNum: (int) scanTitleNum
1682 {
1683     
1684     /* use a bool to determine whether or not we can decrypt using vlc */
1685     BOOL cancelScanDecrypt = 0;
1686     BOOL vlcFound = 0;
1687     NSString *path = scanPath;
1688     HBDVDDetector *detector = [HBDVDDetector detectorForPath:path];
1689     
1690     // Notify ChapterTitles that there's no title
1691     [fChapterTitlesDelegate resetWithTitle:nil];
1692     [fChapterTable reloadData];
1693     
1694     // Notify Subtitles that there's no title
1695     [fSubtitlesDelegate resetWithTitle:nil];
1696     [fSubtitlesTable reloadData];
1697     
1698     [self enableUI: NO];
1699     
1700     if( [detector isVideoDVD] )
1701     {
1702         // The chosen path was actually on a DVD, so use the raw block
1703         // device path instead.
1704         path = [detector devicePath];
1705         [self writeToActivityLog: "trying to open a physical dvd at: %s", [scanPath UTF8String]];
1706         
1707         /* lets check for vlc here to make sure we have a dylib available to use for decrypting */
1708         void *dvdcss = dlopen("libdvdcss.2.dylib", RTLD_LAZY);
1709         if (dvdcss == NULL) 
1710         {
1711             /*compatible vlc not found, so we set the bool to cancel scanning to 1 */
1712             cancelScanDecrypt = 1;
1713             [self writeToActivityLog: "VLC app not found for decrypting physical dvd"];
1714             int status;
1715             status = NSRunAlertPanel(@"HandBrake could not find VLC or your VLC is incompatible (Note: 32 bit vlc is not compatible with 64 bit HandBrake and vice-versa).",@"Please download and install VLC media player if you wish to read encrypted DVDs.", @"Get VLC", @"Cancel Scan", @"Attempt Scan Anyway");
1716             [NSApp requestUserAttention:NSCriticalRequest];
1717             
1718             if (status == NSAlertDefaultReturn)
1719             {
1720                 /* User chose to go download vlc (as they rightfully should) so we send them to the vlc site */
1721                 [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://www.videolan.org/vlc/download-macosx.html"]];
1722             }
1723             else if (status == NSAlertAlternateReturn)
1724             {
1725                 /* User chose to cancel the scan */
1726                 [self writeToActivityLog: "cannot open physical dvd , scan cancelled"];
1727             }
1728             else
1729             {
1730                 /* 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 */
1731                 cancelScanDecrypt = 0;
1732                 [self writeToActivityLog: "user overrode vlc warning -trying to open physical dvd without decryption"];
1733             }
1734             
1735         }
1736         else
1737         {
1738             /* VLC was found in /Applications so all is well, we can carry on using vlc's libdvdcss.dylib for decrypting if needed */
1739             [self writeToActivityLog: "VLC app found for decrypting physical dvd"];
1740             vlcFound = 1;
1741             dlclose(dvdcss);
1742         }
1743     }
1744     
1745     if (cancelScanDecrypt == 0)
1746     {
1747         /* we actually pass the scan off to libhb here */
1748         /* If there is no title number passed to scan, we use "0"
1749          * which causes the default behavior of a full source scan
1750          */
1751         if (!scanTitleNum)
1752         {
1753             scanTitleNum = 0;
1754         }
1755         if (scanTitleNum > 0)
1756         {
1757             [self writeToActivityLog: "scanning specifically for title: %d", scanTitleNum];
1758         }
1759         /* We use our advance pref to determine how many previews to scan */
1760         int hb_num_previews = [[[NSUserDefaults standardUserDefaults] objectForKey:@"PreviewsNumber"] intValue];
1761         /* set title to NULL */
1762         fTitle = NULL;
1763         hb_scan( fHandle, [path UTF8String], scanTitleNum, hb_num_previews, 1 );
1764         [fSrcDVD2Field setStringValue:@"Scanning new source ..."];
1765     }
1766 }
1767
1768 - (IBAction) cancelScanning:(id)sender
1769 {
1770     hb_scan_stop(fHandle);
1771 }
1772
1773 - (IBAction) showNewScan:(id)sender
1774 {
1775     hb_list_t  * list;
1776         hb_title_t * title;
1777         int feature_title=0; // Used to store the main feature title
1778
1779         list = hb_get_titles( fHandle );
1780         
1781         if( !hb_list_count( list ) )
1782         {
1783             /* We display a message if a valid dvd source was not chosen */
1784             [fSrcDVD2Field setStringValue: @"No Valid Source Found"];
1785             SuccessfulScan = NO;
1786             
1787             // Notify ChapterTitles that there's no title
1788             [fSubtitlesDelegate resetWithTitle:nil];
1789             [fSubtitlesTable reloadData];
1790             
1791             // Notify Subtitles that there's no title
1792             [fChapterTitlesDelegate resetWithTitle:nil];
1793             [fChapterTable reloadData];
1794         }
1795         else
1796         {
1797             if (applyQueueToScan == YES)
1798             {
1799                 /* we are a rescan of an existing queue item and need to apply the queued settings to the scan */
1800                 [self writeToActivityLog: "showNewScan: This is a queued item rescan"];
1801                 
1802             }
1803             else if (applyQueueToScan == NO)
1804             {
1805                 [self writeToActivityLog: "showNewScan: This is a new source item scan"];
1806             }
1807             else
1808             {
1809                 [self writeToActivityLog: "showNewScan: cannot grok scan status"];
1810             }
1811             
1812               /* We increment the successful scancount here by one,
1813              which we use at the end of this function to tell the gui
1814              if this is the first successful scan since launch and whether
1815              or not we should set all settings to the defaults */
1816             currentSuccessfulScanCount++;
1817             
1818             [[fWindow toolbar] validateVisibleItems];
1819             
1820             [fSrcTitlePopUp removeAllItems];
1821             for( int i = 0; i < hb_list_count( list ); i++ )
1822             {
1823                 title = (hb_title_t *) hb_list_item( list, i );
1824                 
1825                 currentSource = [NSString stringWithUTF8String: title->name];
1826                 /*Set DVD Name at top of window with the browsedSourceDisplayName grokked right before -performScan */
1827                 if (!browsedSourceDisplayName)
1828                 {
1829                     browsedSourceDisplayName = @"NoNameDetected";
1830                 }
1831                 [fSrcDVD2Field setStringValue:browsedSourceDisplayName];
1832                 
1833                 /* If its a queue rescan for edit, get the queue item output path */
1834                 /* if not, its a new source scan. */
1835                 /* Check to see if the last destination has been set,use if so, if not, use Desktop */
1836                 if (applyQueueToScan == YES)
1837                 {
1838                     [fDstFile2Field setStringValue: [NSString stringWithFormat:@"%@", [[QueueFileArray objectAtIndex:fqueueEditRescanItemNum] objectForKey:@"DestinationPath"]]];
1839                 }
1840                 else if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastDestinationDirectory"])
1841                 {
1842                     [fDstFile2Field setStringValue: [NSString stringWithFormat:
1843                                                      @"%@/%@.mp4", [[NSUserDefaults standardUserDefaults] stringForKey:@"LastDestinationDirectory"],[browsedSourceDisplayName stringByDeletingPathExtension]]];
1844                 }
1845                 else
1846                 {
1847                     [fDstFile2Field setStringValue: [NSString stringWithFormat:
1848                                                      @"%@/Desktop/%@.mp4", NSHomeDirectory(),[browsedSourceDisplayName stringByDeletingPathExtension]]];
1849                 }
1850                 
1851                 /* See if this is the main feature according to libhb */
1852                 if (title->index == title->job->feature)
1853                 {
1854                     feature_title = i;
1855                 }
1856                 
1857                 [fSrcTitlePopUp addItemWithTitle: [NSString
1858                                                    stringWithFormat: @"%s %d - %02dh%02dm%02ds",
1859                                                    title->name,title->index, title->hours, title->minutes,
1860                                                    title->seconds]];
1861             }
1862             
1863             /* if we are a stream, select the first title */
1864             if (title->type == HB_STREAM_TYPE)
1865             {
1866                 [fSrcTitlePopUp selectItemAtIndex: 0];
1867             }
1868             else
1869             {
1870                 /* if not then select the main feature title */
1871                 [fSrcTitlePopUp selectItemAtIndex: feature_title];
1872             }
1873             [self titlePopUpChanged:nil];
1874             
1875             SuccessfulScan = YES;
1876             [self enableUI: YES];
1877
1878             /* if its the initial successful scan after awakeFromNib */
1879             if (currentSuccessfulScanCount == 1)
1880             {
1881                 [self encodeStartStopPopUpChanged:nil];
1882                 
1883                 [self selectDefaultPreset:nil];
1884                 
1885                 // Open preview window now if it was visible when HB was closed
1886                 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"PreviewWindowIsOpen"])
1887                     [self showPreviewWindow:nil];
1888                 
1889                 // Open picture sizing window now if it was visible when HB was closed
1890                 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"PictureSizeWindowIsOpen"])
1891                     [self showPicturePanel:nil];
1892                 
1893             }
1894             if (applyQueueToScan == YES)
1895             {
1896                 /* we are a rescan of an existing queue item and need to apply the queued settings to the scan */
1897                 [self writeToActivityLog: "showNewScan: calling applyQueueSettingsToMainWindow"];
1898                 [self applyQueueSettingsToMainWindow:nil];
1899                 
1900             }
1901
1902             
1903         }
1904
1905 }
1906
1907
1908 #pragma mark -
1909 #pragma mark New Output Destination
1910
1911 - (IBAction) browseFile: (id) sender
1912 {
1913     /* Open a panel to let the user choose and update the text field */
1914     NSSavePanel * panel = [NSSavePanel savePanel];
1915         /* We get the current file name and path from the destination field here */
1916         [panel beginSheetForDirectory: [[fDstFile2Field stringValue] stringByDeletingLastPathComponent] file: [[fDstFile2Field stringValue] lastPathComponent]
1917                                    modalForWindow: fWindow modalDelegate: self
1918                                    didEndSelector: @selector( browseFileDone:returnCode:contextInfo: )
1919                                           contextInfo: NULL];
1920 }
1921
1922 - (void) browseFileDone: (NSSavePanel *) sheet
1923              returnCode: (int) returnCode contextInfo: (void *) contextInfo
1924 {
1925     if( returnCode == NSOKButton )
1926     {
1927         [fDstFile2Field setStringValue: [sheet filename]];
1928         /* Save this path to the prefs so that on next browse destination window it opens there */
1929         NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
1930         [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];   
1931     }
1932 }
1933
1934
1935 #pragma mark -
1936 #pragma mark Main Window Control
1937
1938 - (IBAction) openMainWindow: (id) sender
1939 {
1940     [fWindow  makeKeyAndOrderFront:nil];
1941 }
1942
1943 - (BOOL) windowShouldClose: (id) sender
1944 {
1945     return YES;
1946 }
1947
1948 - (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag
1949 {
1950     if( !flag ) {
1951         [fWindow  makeKeyAndOrderFront:nil];
1952                 
1953         return YES;
1954     }
1955     
1956     return NO;
1957 }
1958
1959 - (NSSize) drawerWillResizeContents:(NSDrawer *) drawer toSize:(NSSize) contentSize {
1960         [[NSUserDefaults standardUserDefaults] setObject:NSStringFromSize( contentSize ) forKey:@"Drawer Size"];
1961         return contentSize;
1962 }
1963
1964 #pragma mark -
1965 #pragma mark Queue File
1966
1967 - (void) loadQueueFile {
1968         /* We declare the default NSFileManager into fileManager */
1969         NSFileManager * fileManager = [NSFileManager defaultManager];
1970         /*We define the location of the user presets file */
1971     QueueFile = @"~/Library/Application Support/HandBrake/Queue.plist";
1972         QueueFile = [[QueueFile stringByExpandingTildeInPath]retain];
1973     /* We check for the Queue.plist */
1974         if ([fileManager fileExistsAtPath:QueueFile] == 0)
1975         {
1976                 [fileManager createFileAtPath:QueueFile contents:nil attributes:nil];
1977         }
1978     
1979         QueueFileArray = [[NSMutableArray alloc] initWithContentsOfFile:QueueFile];
1980         /* lets check to see if there is anything in the queue file .plist */
1981     if (nil == QueueFileArray)
1982         {
1983         /* if not, then lets initialize an empty array */
1984                 QueueFileArray = [[NSMutableArray alloc] init];
1985     }
1986     else
1987     {
1988         /* ONLY clear out encoded items if we are single instance */
1989         if (hbInstanceNum == 1)
1990         {
1991             [self clearQueueEncodedItems];
1992         }
1993     }
1994 }
1995
1996 - (void)addQueueFileItem
1997 {
1998     [QueueFileArray addObject:[self createQueueFileItem]];
1999     [self saveQueueFileItem];
2000     
2001 }
2002
2003 - (void) removeQueueFileItem:(int) queueItemToRemove
2004 {
2005     [QueueFileArray removeObjectAtIndex:queueItemToRemove];
2006     [self saveQueueFileItem];
2007
2008 }
2009
2010 - (void)saveQueueFileItem
2011 {
2012     [QueueFileArray writeToFile:QueueFile atomically:YES];
2013     [fQueueController setQueueArray: QueueFileArray];
2014     [self getQueueStats];
2015 }
2016
2017 - (void)getQueueStats
2018 {
2019 /* lets get the stats on the status of the queue array */
2020
2021 fEncodingQueueItem = 0;
2022 fPendingCount = 0;
2023 fCompletedCount = 0;
2024 fCanceledCount = 0;
2025 fWorkingCount = 0;
2026
2027     /* We use a number system to set the encode status of the queue item
2028      * in controller.mm
2029      * 0 == already encoded
2030      * 1 == is being encoded
2031      * 2 == is yet to be encoded
2032      * 3 == cancelled
2033      */
2034
2035         int i = 0;
2036     NSEnumerator *enumerator = [QueueFileArray objectEnumerator];
2037         id tempObject;
2038         while (tempObject = [enumerator nextObject])
2039         {
2040                 NSDictionary *thisQueueDict = tempObject;
2041                 if ([[thisQueueDict objectForKey:@"Status"] intValue] == 0) // Completed
2042                 {
2043                         fCompletedCount++;      
2044                 }
2045                 if ([[thisQueueDict objectForKey:@"Status"] intValue] == 1) // being encoded
2046                 {
2047                         fWorkingCount++;
2048             fEncodingQueueItem = i;
2049             /* check to see if we are the instance doing this encoding */
2050             if ([thisQueueDict objectForKey:@"EncodingPID"] && [[thisQueueDict objectForKey:@"EncodingPID"] intValue] == pidNum)
2051             {
2052                 currentQueueEncodeIndex = i;
2053             }
2054                 
2055                 }
2056         if ([[thisQueueDict objectForKey:@"Status"] intValue] == 2) // pending          
2057         {
2058                         fPendingCount++;
2059                 }
2060         if ([[thisQueueDict objectForKey:@"Status"] intValue] == 3) // cancelled                
2061         {
2062                         fCanceledCount++;
2063                 }
2064                 i++;
2065         }
2066
2067     /* Set the queue status field in the main window */
2068     NSMutableString * string;
2069     if (fPendingCount == 1)
2070     {
2071         string = [NSMutableString stringWithFormat: NSLocalizedString( @"%d encode pending in the queue", @"" ), fPendingCount];
2072     }
2073     else
2074     {
2075         string = [NSMutableString stringWithFormat: NSLocalizedString( @"%d encode(s) pending in the queue", @"" ), fPendingCount];
2076     }
2077     [fQueueStatus setStringValue:string];
2078 }
2079
2080 /* Used to get the next pending queue item index and return it if found */
2081 - (int)getNextPendingQueueIndex
2082 {
2083     /* initialize nextPendingIndex to -1, this value tells incrementQueueItemDone that there are no pending items in the queue */
2084     int nextPendingIndex = -1;
2085         BOOL nextPendingFound = NO;
2086     NSEnumerator *enumerator = [QueueFileArray objectEnumerator];
2087         id tempObject;
2088     int i = 0;
2089         while (tempObject = [enumerator nextObject])
2090         {
2091                 NSDictionary *thisQueueDict = tempObject;
2092         if ([[thisQueueDict objectForKey:@"Status"] intValue] == 2 && nextPendingFound == NO) // pending                
2093         {
2094                         nextPendingFound = YES;
2095             nextPendingIndex = [QueueFileArray indexOfObject: tempObject];
2096             [self writeToActivityLog: "getNextPendingQueueIndex next pending encod index is:%d", nextPendingIndex];
2097                 }
2098         i++;
2099         }
2100     return nextPendingIndex;
2101 }
2102
2103
2104 /* This method will set any item marked as encoding back to pending
2105  * currently used right after a queue reload
2106  */
2107 - (void) setQueueEncodingItemsAsPending
2108 {
2109     NSEnumerator *enumerator = [QueueFileArray objectEnumerator];
2110         id tempObject;
2111     NSMutableArray *tempArray;
2112     tempArray = [NSMutableArray array];
2113     /* we look here to see if the preset is we move on to the next one */
2114     while ( tempObject = [enumerator nextObject] )  
2115     {
2116         /* We want to keep any queue item that is pending or was previously being encoded */
2117         if ([[tempObject objectForKey:@"Status"] intValue] == 1 || [[tempObject objectForKey:@"Status"] intValue] == 2)
2118         {
2119             /* If the queue item is marked as "encoding" (1)
2120              * then change its status back to pending (2) which effectively
2121              * puts it back into the queue to be encoded
2122              */
2123             if ([[tempObject objectForKey:@"Status"] intValue] == 1)
2124             {
2125                 [tempObject setObject:[NSNumber numberWithInt: 2] forKey:@"Status"];
2126             }
2127             [tempArray addObject:tempObject];
2128         }
2129     }
2130     
2131     [QueueFileArray setArray:tempArray];
2132     [self saveQueueFileItem];
2133 }
2134
2135
2136 /* This method will clear the queue of any encodes that are not still pending
2137  * this includes both successfully completed encodes as well as cancelled encodes */
2138 - (void) clearQueueEncodedItems
2139 {
2140     NSEnumerator *enumerator = [QueueFileArray objectEnumerator];
2141         id tempObject;
2142     NSMutableArray *tempArray;
2143     tempArray = [NSMutableArray array];
2144     /* we look here to see if the preset is we move on to the next one */
2145     while ( tempObject = [enumerator nextObject] )  
2146     {
2147         /* If the queue item is either completed (0) or cancelled (3) from the
2148          * last session, then we put it in tempArray to be deleted from QueueFileArray.
2149          * NOTE: this means we retain pending (2) and also an item that is marked as
2150          * still encoding (1). If the queue has an item that is still marked as encoding
2151          * from a previous session, we can conlude that HB was either shutdown, or crashed
2152          * during the encodes so we keep it and tell the user in the "Load Queue Alert"
2153          */
2154         if ([[tempObject objectForKey:@"Status"] intValue] == 0 || [[tempObject objectForKey:@"Status"] intValue] == 3)
2155         {
2156             [tempArray addObject:tempObject];
2157         }
2158     }
2159     
2160     [QueueFileArray removeObjectsInArray:tempArray];
2161     [self saveQueueFileItem];
2162 }
2163
2164 /* This method will clear the queue of all encodes. effectively creating an empty queue */
2165 - (void) clearQueueAllItems
2166 {
2167     NSEnumerator *enumerator = [QueueFileArray objectEnumerator];
2168         id tempObject;
2169     NSMutableArray *tempArray;
2170     tempArray = [NSMutableArray array];
2171     /* we look here to see if the preset is we move on to the next one */
2172     while ( tempObject = [enumerator nextObject] )  
2173     {
2174         [tempArray addObject:tempObject];
2175     }
2176     
2177     [QueueFileArray removeObjectsInArray:tempArray];
2178     [self saveQueueFileItem];
2179 }
2180
2181 /* This method will duplicate prepareJob however into the
2182  * queue .plist instead of into the job structure so it can
2183  * be recalled later */
2184 - (NSDictionary *)createQueueFileItem
2185 {
2186     NSMutableDictionary *queueFileJob = [[NSMutableDictionary alloc] init];
2187     
2188        hb_list_t  * list  = hb_get_titles( fHandle );
2189     hb_title_t * title = (hb_title_t *) hb_list_item( list,
2190             [fSrcTitlePopUp indexOfSelectedItem] );
2191     hb_job_t * job = title->job;
2192     
2193     
2194     
2195     /* We use a number system to set the encode status of the queue item
2196      * 0 == already encoded
2197      * 1 == is being encoded
2198      * 2 == is yet to be encoded
2199      * 3 == cancelled
2200      */
2201     [queueFileJob setObject:[NSNumber numberWithInt:2] forKey:@"Status"];
2202     /* Source and Destination Information */
2203     
2204     [queueFileJob setObject:[NSString stringWithUTF8String: title->path] forKey:@"SourcePath"];
2205     [queueFileJob setObject:[fSrcDVD2Field stringValue] forKey:@"SourceName"];
2206     [queueFileJob setObject:[NSNumber numberWithInt:title->index] forKey:@"TitleNumber"];
2207     [queueFileJob setObject:[NSNumber numberWithInt:[fSrcAnglePopUp indexOfSelectedItem] + 1] forKey:@"TitleAngle"];
2208     
2209     /* Determine and set a variable to tell hb what start and stop times to use ... chapters vs seconds */
2210     if( [fEncodeStartStopPopUp indexOfSelectedItem] == 0 )
2211     {
2212         [queueFileJob setObject:[NSNumber numberWithInt:0] forKey:@"fEncodeStartStop"];    
2213     }
2214     else if ([fEncodeStartStopPopUp indexOfSelectedItem] == 1)
2215     {
2216         [queueFileJob setObject:[NSNumber numberWithInt:1] forKey:@"fEncodeStartStop"];   
2217     }
2218     else if ([fEncodeStartStopPopUp indexOfSelectedItem] == 2)
2219     {
2220         [queueFileJob setObject:[NSNumber numberWithInt:2] forKey:@"fEncodeStartStop"];
2221     }
2222     /* Chapter encode info */
2223     [queueFileJob setObject:[NSNumber numberWithInt:[fSrcChapterStartPopUp indexOfSelectedItem] + 1] forKey:@"ChapterStart"];
2224     [queueFileJob setObject:[NSNumber numberWithInt:[fSrcChapterEndPopUp indexOfSelectedItem] + 1] forKey:@"ChapterEnd"];
2225     /* Time (pts) encode info */
2226     [queueFileJob setObject:[NSNumber numberWithInt:[fSrcTimeStartEncodingField intValue]] forKey:@"StartSeconds"];
2227     [queueFileJob setObject:[NSNumber numberWithInt:[fSrcTimeEndEncodingField intValue] - [fSrcTimeStartEncodingField intValue]] forKey:@"StopSeconds"];
2228     /* Frame number encode info */
2229     [queueFileJob setObject:[NSNumber numberWithInt:[fSrcFrameStartEncodingField intValue]] forKey:@"StartFrame"];
2230     [queueFileJob setObject:[NSNumber numberWithInt:[fSrcFrameEndEncodingField intValue] - [fSrcFrameStartEncodingField intValue]] forKey:@"StopFrame"];
2231     
2232     
2233     /* The number of seek points equals the number of seconds announced in the title as that is our current granularity */
2234     int title_duration_seconds = (title->hours * 3600) + (title->minutes * 60) + (title->seconds);
2235     [queueFileJob setObject:[NSNumber numberWithInt:title_duration_seconds] forKey:@"SourceTotalSeconds"];
2236     
2237     [queueFileJob setObject:[fDstFile2Field stringValue] forKey:@"DestinationPath"];
2238     
2239     /* Lets get the preset info if there is any */
2240     [queueFileJob setObject:[fPresetSelectedDisplay stringValue] forKey:@"PresetName"];
2241     [queueFileJob setObject:[NSNumber numberWithInt:[fPresetsOutlineView selectedRow]] forKey:@"PresetIndexNum"];
2242     
2243     [queueFileJob setObject:[fDstFormatPopUp titleOfSelectedItem] forKey:@"FileFormat"];
2244     /* Chapter Markers*/
2245     /* If we have only one chapter or a title without chapters, set chapter markers to off */
2246     if ([fSrcChapterStartPopUp indexOfSelectedItem] ==  [fSrcChapterEndPopUp indexOfSelectedItem])
2247     {
2248         [queueFileJob setObject:[NSNumber numberWithInt:0] forKey:@"ChapterMarkers"];
2249     }
2250     else
2251     {
2252         [queueFileJob setObject:[NSNumber numberWithInt:[fCreateChapterMarkers state]] forKey:@"ChapterMarkers"];
2253     }
2254         
2255     /* We need to get the list of chapter names to put into an array and store 
2256      * in our queue, so they can be reapplied in prepareJob when this queue
2257      * item comes up if Chapter Markers is set to on.
2258      */
2259      int i;
2260      NSMutableArray *ChapterNamesArray = [[NSMutableArray alloc] init];
2261      int chaptercount = hb_list_count( fTitle->list_chapter );
2262      for( i = 0; i < chaptercount; i++ )
2263     {
2264         hb_chapter_t *chapter = (hb_chapter_t *) hb_list_item( fTitle->list_chapter, i );
2265         if( chapter != NULL )
2266         {
2267           [ChapterNamesArray addObject:[NSString stringWithCString:chapter->title encoding:NSUTF8StringEncoding]];
2268         }
2269     }
2270     [queueFileJob setObject:[NSMutableArray arrayWithArray: ChapterNamesArray] forKey:@"ChapterNames"];
2271     [ChapterNamesArray autorelease];
2272     
2273     /* Allow Mpeg4 64 bit formatting +4GB file sizes */
2274         [queueFileJob setObject:[NSNumber numberWithInt:[fDstMp4LargeFileCheck state]] forKey:@"Mp4LargeFile"];
2275     /* Mux mp4 with http optimization */
2276     [queueFileJob setObject:[NSNumber numberWithInt:[fDstMp4HttpOptFileCheck state]] forKey:@"Mp4HttpOptimize"];
2277     /* Add iPod uuid atom */
2278     [queueFileJob setObject:[NSNumber numberWithInt:[fDstMp4iPodFileCheck state]] forKey:@"Mp4iPodCompatible"];
2279     
2280     /* Codecs */
2281         /* Video encoder */
2282         [queueFileJob setObject:[fVidEncoderPopUp titleOfSelectedItem] forKey:@"VideoEncoder"];
2283         /* x264 Option String */
2284         [queueFileJob setObject:[fAdvancedOptions optionsString] forKey:@"x264Option"];
2285
2286         [queueFileJob setObject:[NSNumber numberWithInt:[fVidQualityMatrix selectedRow]] forKey:@"VideoQualityType"];
2287         [queueFileJob setObject:[fVidTargetSizeField stringValue] forKey:@"VideoTargetSize"];
2288         [queueFileJob setObject:[fVidBitrateField stringValue] forKey:@"VideoAvgBitrate"];
2289         [queueFileJob setObject:[NSNumber numberWithFloat:[fVidQualityRFField floatValue]] forKey:@"VideoQualitySlider"];
2290     /* Framerate */
2291     [queueFileJob setObject:[fVidRatePopUp titleOfSelectedItem] forKey:@"VideoFramerate"];
2292     [queueFileJob setObject:[NSNumber numberWithInt:[fFrameratePfrCheck state]] forKey:@"VideoFrameratePFR"];
2293     
2294         /* 2 Pass Encoding */
2295         [queueFileJob setObject:[NSNumber numberWithInt:[fVidTwoPassCheck state]] forKey:@"VideoTwoPass"];
2296         /* Turbo 2 pass Encoding fVidTurboPassCheck*/
2297         [queueFileJob setObject:[NSNumber numberWithInt:[fVidTurboPassCheck state]] forKey:@"VideoTurboTwoPass"];
2298     
2299         /* Picture Sizing */
2300         /* Use Max Picture settings for whatever the dvd is.*/
2301         [queueFileJob setObject:[NSNumber numberWithInt:0] forKey:@"UsesMaxPictureSettings"];
2302         [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->width] forKey:@"PictureWidth"];
2303         [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->height] forKey:@"PictureHeight"];
2304         [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->keep_ratio] forKey:@"PictureKeepRatio"];
2305         [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->anamorphic.mode] forKey:@"PicturePAR"];
2306     [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->modulus] forKey:@"PictureModulus"];
2307     /* if we are custom anamorphic, store the exact storage, par and display dims */
2308     if (fTitle->job->anamorphic.mode == 3)
2309     {
2310         [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->modulus] forKey:@"PicturePARModulus"];
2311         
2312         [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->width] forKey:@"PicturePARStorageWidth"];
2313         [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->height] forKey:@"PicturePARStorageHeight"];
2314         
2315         [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->anamorphic.par_width] forKey:@"PicturePARPixelWidth"];
2316         [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->anamorphic.par_height] forKey:@"PicturePARPixelHeight"];
2317         
2318         [queueFileJob setObject:[NSNumber numberWithFloat:fTitle->job->anamorphic.dar_width] forKey:@"PicturePARDisplayWidth"];
2319         [queueFileJob setObject:[NSNumber numberWithFloat:fTitle->job->anamorphic.dar_height] forKey:@"PicturePARDisplayHeight"];
2320
2321     }
2322     NSString * pictureSummary;
2323     pictureSummary = [fPictureSizeField stringValue];
2324     [queueFileJob setObject:pictureSummary forKey:@"PictureSizingSummary"];                 
2325     /* Set crop settings here */
2326         [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController autoCrop]] forKey:@"PictureAutoCrop"];
2327     [queueFileJob setObject:[NSNumber numberWithInt:job->crop[0]] forKey:@"PictureTopCrop"];
2328     [queueFileJob setObject:[NSNumber numberWithInt:job->crop[1]] forKey:@"PictureBottomCrop"];
2329         [queueFileJob setObject:[NSNumber numberWithInt:job->crop[2]] forKey:@"PictureLeftCrop"];
2330         [queueFileJob setObject:[NSNumber numberWithInt:job->crop[3]] forKey:@"PictureRightCrop"];
2331     
2332     /* Picture Filters */
2333     [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController detelecine]] forKey:@"PictureDetelecine"];
2334     [queueFileJob setObject:[fPictureController detelecineCustomString] forKey:@"PictureDetelecineCustom"];
2335     
2336     [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController useDecomb]] forKey:@"PictureDecombDeinterlace"];
2337     [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController decomb]] forKey:@"PictureDecomb"];
2338     [queueFileJob setObject:[fPictureController decombCustomString] forKey:@"PictureDecombCustom"];
2339     
2340     [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController deinterlace]] forKey:@"PictureDeinterlace"];
2341     [queueFileJob setObject:[fPictureController deinterlaceCustomString] forKey:@"PictureDeinterlaceCustom"];
2342     
2343     [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController denoise]] forKey:@"PictureDenoise"];
2344     [queueFileJob setObject:[fPictureController denoiseCustomString] forKey:@"PictureDenoiseCustom"];
2345     
2346     [queueFileJob setObject:[NSString stringWithFormat:@"%d",[fPictureController deblock]] forKey:@"PictureDeblock"];
2347     
2348     [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController grayscale]] forKey:@"VideoGrayScale"];
2349     
2350     /*Audio*/
2351     if ([fAudLang1PopUp indexOfSelectedItem] > 0)
2352     {
2353         [queueFileJob setObject:[NSNumber numberWithInt:[fAudLang1PopUp indexOfSelectedItem]] forKey:@"Audio1Track"];
2354         [queueFileJob setObject:[fAudLang1PopUp titleOfSelectedItem] forKey:@"Audio1TrackDescription"];
2355         [queueFileJob setObject:[fAudTrack1CodecPopUp titleOfSelectedItem] forKey:@"Audio1Encoder"];
2356         [queueFileJob setObject:[fAudTrack1MixPopUp titleOfSelectedItem] forKey:@"Audio1Mixdown"];
2357         [queueFileJob setObject:[fAudTrack1RatePopUp titleOfSelectedItem] forKey:@"Audio1Samplerate"];
2358         [queueFileJob setObject:[fAudTrack1BitratePopUp titleOfSelectedItem] forKey:@"Audio1Bitrate"];
2359         [queueFileJob setObject:[NSNumber numberWithFloat:[fAudTrack1DrcSlider floatValue]] forKey:@"Audio1TrackDRCSlider"];
2360     }
2361     if ([fAudLang2PopUp indexOfSelectedItem] > 0)
2362     {
2363         [queueFileJob setObject:[NSNumber numberWithInt:[fAudLang2PopUp indexOfSelectedItem]] forKey:@"Audio2Track"];
2364         [queueFileJob setObject:[fAudLang2PopUp titleOfSelectedItem] forKey:@"Audio2TrackDescription"];
2365         [queueFileJob setObject:[fAudTrack2CodecPopUp titleOfSelectedItem] forKey:@"Audio2Encoder"];
2366         [queueFileJob setObject:[fAudTrack2MixPopUp titleOfSelectedItem] forKey:@"Audio2Mixdown"];
2367         [queueFileJob setObject:[fAudTrack2RatePopUp titleOfSelectedItem] forKey:@"Audio2Samplerate"];
2368         [queueFileJob setObject:[fAudTrack2BitratePopUp titleOfSelectedItem] forKey:@"Audio2Bitrate"];
2369         [queueFileJob setObject:[NSNumber numberWithFloat:[fAudTrack2DrcSlider floatValue]] forKey:@"Audio2TrackDRCSlider"];
2370     }
2371     if ([fAudLang3PopUp indexOfSelectedItem] > 0)
2372     {
2373         [queueFileJob setObject:[NSNumber numberWithInt:[fAudLang3PopUp indexOfSelectedItem]] forKey:@"Audio3Track"];
2374         [queueFileJob setObject:[fAudLang3PopUp titleOfSelectedItem] forKey:@"Audio3TrackDescription"];
2375         [queueFileJob setObject:[fAudTrack3CodecPopUp titleOfSelectedItem] forKey:@"Audio3Encoder"];
2376         [queueFileJob setObject:[fAudTrack3MixPopUp titleOfSelectedItem] forKey:@"Audio3Mixdown"];
2377         [queueFileJob setObject:[fAudTrack3RatePopUp titleOfSelectedItem] forKey:@"Audio3Samplerate"];
2378         [queueFileJob setObject:[fAudTrack3BitratePopUp titleOfSelectedItem] forKey:@"Audio3Bitrate"];
2379         [queueFileJob setObject:[NSNumber numberWithFloat:[fAudTrack3DrcSlider floatValue]] forKey:@"Audio3TrackDRCSlider"];
2380     }
2381     if ([fAudLang4PopUp indexOfSelectedItem] > 0)
2382     {
2383         [queueFileJob setObject:[NSNumber numberWithInt:[fAudLang4PopUp indexOfSelectedItem]] forKey:@"Audio4Track"];
2384         [queueFileJob setObject:[fAudLang4PopUp titleOfSelectedItem] forKey:@"Audio4TrackDescription"];
2385         [queueFileJob setObject:[fAudTrack4CodecPopUp titleOfSelectedItem] forKey:@"Audio4Encoder"];
2386         [queueFileJob setObject:[fAudTrack4MixPopUp titleOfSelectedItem] forKey:@"Audio4Mixdown"];
2387         [queueFileJob setObject:[fAudTrack4RatePopUp titleOfSelectedItem] forKey:@"Audio4Samplerate"];
2388         [queueFileJob setObject:[fAudTrack4BitratePopUp titleOfSelectedItem] forKey:@"Audio4Bitrate"];
2389         [queueFileJob setObject:[NSNumber numberWithFloat:[fAudTrack4DrcSlider floatValue]] forKey:@"Audio4TrackDRCSlider"];
2390     }
2391     
2392         /* Subtitles*/
2393     NSMutableArray *subtitlesArray = [[NSMutableArray alloc] initWithArray:[fSubtitlesDelegate getSubtitleArray] copyItems:YES];
2394     [queueFileJob setObject:[NSArray arrayWithArray: subtitlesArray] forKey:@"SubtitleList"];
2395     [subtitlesArray autorelease];
2396
2397     /* Now we go ahead and set the "job->values in the plist for passing right to fQueueEncodeLibhb */
2398      
2399     [queueFileJob setObject:[NSNumber numberWithInt:[fSrcChapterStartPopUp indexOfSelectedItem] + 1] forKey:@"JobChapterStart"];
2400     
2401     [queueFileJob setObject:[NSNumber numberWithInt:[fSrcChapterEndPopUp indexOfSelectedItem] + 1] forKey:@"JobChapterEnd"];
2402     
2403     
2404     [queueFileJob setObject:[NSNumber numberWithInt:[[fDstFormatPopUp selectedItem] tag]] forKey:@"JobFileFormatMux"];
2405     
2406     /* Codecs */
2407         /* Video encoder */
2408         [queueFileJob setObject:[NSNumber numberWithInt:[[fVidEncoderPopUp selectedItem] tag]] forKey:@"JobVideoEncoderVcodec"];
2409         
2410     /* Framerate */
2411     [queueFileJob setObject:[NSNumber numberWithInt:[fVidRatePopUp indexOfSelectedItem]] forKey:@"JobIndexVideoFramerate"];
2412     [queueFileJob setObject:[NSNumber numberWithInt:title->rate] forKey:@"JobVrate"];
2413     [queueFileJob setObject:[NSNumber numberWithInt:title->rate_base] forKey:@"JobVrateBase"];
2414         
2415     /* Picture Sizing */
2416         /* Use Max Picture settings for whatever the dvd is.*/
2417         [queueFileJob setObject:[NSNumber numberWithInt:0] forKey:@"UsesMaxPictureSettings"];
2418         [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->width] forKey:@"PictureWidth"];
2419         [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->height] forKey:@"PictureHeight"];
2420         [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->keep_ratio] forKey:@"PictureKeepRatio"];
2421         [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->anamorphic.mode] forKey:@"PicturePAR"];
2422     
2423     /* Set crop settings here */
2424         [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController autoCrop]] forKey:@"PictureAutoCrop"];
2425     [queueFileJob setObject:[NSNumber numberWithInt:job->crop[0]] forKey:@"PictureTopCrop"];
2426     [queueFileJob setObject:[NSNumber numberWithInt:job->crop[1]] forKey:@"PictureBottomCrop"];
2427         [queueFileJob setObject:[NSNumber numberWithInt:job->crop[2]] forKey:@"PictureLeftCrop"];
2428         [queueFileJob setObject:[NSNumber numberWithInt:job->crop[3]] forKey:@"PictureRightCrop"];
2429     
2430     
2431     /*Audio*/
2432     if ([fAudLang1PopUp indexOfSelectedItem] > 0)
2433     {
2434         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack1CodecPopUp selectedItem] tag]] forKey:@"JobAudio1Encoder"];
2435         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack1MixPopUp selectedItem] tag]] forKey:@"JobAudio1Mixdown"];
2436         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack1RatePopUp selectedItem] tag]] forKey:@"JobAudio1Samplerate"];
2437         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack1BitratePopUp selectedItem] tag]] forKey:@"JobAudio1Bitrate"];
2438      }
2439     if ([fAudLang2PopUp indexOfSelectedItem] > 0)
2440     {
2441         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack2CodecPopUp selectedItem] tag]] forKey:@"JobAudio2Encoder"];
2442         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack2MixPopUp selectedItem] tag]] forKey:@"JobAudio2Mixdown"];
2443         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack2RatePopUp selectedItem] tag]] forKey:@"JobAudio2Samplerate"];
2444         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack2BitratePopUp selectedItem] tag]] forKey:@"JobAudio2Bitrate"];
2445     }
2446     if ([fAudLang3PopUp indexOfSelectedItem] > 0)
2447     {
2448         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack3CodecPopUp selectedItem] tag]] forKey:@"JobAudio3Encoder"];
2449         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack3MixPopUp selectedItem] tag]] forKey:@"JobAudio3Mixdown"];
2450         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack3RatePopUp selectedItem] tag]] forKey:@"JobAudio3Samplerate"];
2451         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack3BitratePopUp selectedItem] tag]] forKey:@"JobAudio3Bitrate"];
2452     }
2453     if ([fAudLang4PopUp indexOfSelectedItem] > 0)
2454     {
2455         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack4CodecPopUp selectedItem] tag]] forKey:@"JobAudio4Encoder"];
2456         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack4MixPopUp selectedItem] tag]] forKey:@"JobAudio4Mixdown"];
2457         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack4RatePopUp selectedItem] tag]] forKey:@"JobAudio4Samplerate"];
2458         [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack4BitratePopUp selectedItem] tag]] forKey:@"JobAudio4Bitrate"];
2459     }
2460
2461     /* we need to auto relase the queueFileJob and return it */
2462     [queueFileJob autorelease];
2463     return queueFileJob;
2464
2465 }
2466
2467 /* this is actually called from the queue controller to modify the queue array and return it back to the queue controller */
2468 - (void)moveObjectsInQueueArray:(NSMutableArray *)array fromIndexes:(NSIndexSet *)indexSet toIndex:(NSUInteger)insertIndex
2469 {
2470     NSUInteger index = [indexSet lastIndex];
2471     NSUInteger aboveInsertIndexCount = 0;
2472     
2473     
2474     NSUInteger removeIndex;
2475         
2476     if (index >= insertIndex)
2477     {
2478         removeIndex = index + aboveInsertIndexCount;
2479         aboveInsertIndexCount++;
2480     }
2481     else
2482     {
2483         removeIndex = index;
2484         insertIndex--;
2485     }
2486
2487     id object = [[QueueFileArray objectAtIndex:removeIndex] retain];
2488     [QueueFileArray removeObjectAtIndex:removeIndex];
2489     [QueueFileArray insertObject:object atIndex:insertIndex];
2490     [object release];
2491         
2492     index = [indexSet indexLessThanIndex:index];
2493
2494    /* We save all of the Queue data here 
2495     * and it also gets sent back to the queue controller*/
2496     [self saveQueueFileItem]; 
2497     
2498 }
2499
2500
2501 #pragma mark -
2502 #pragma mark Queue Job Processing
2503
2504 - (void) incrementQueueItemDone:(int) queueItemDoneIndexNum
2505 {
2506     /* Mark the encode just finished as done (status 0)*/
2507     [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] setObject:[NSNumber numberWithInt:0] forKey:@"Status"];
2508         
2509     /* We save all of the Queue data here */
2510     [self saveQueueFileItem];
2511
2512     /* Since we have now marked a queue item as done
2513      * we can go ahead and increment currentQueueEncodeIndex 
2514      * so that if there is anything left in the queue we can
2515      * go ahead and move to the next item if we want to */
2516     int queueItems = [QueueFileArray count];
2517     /* Check to see if there are any more pending items in the queue */
2518     int newQueueItemIndex = [self getNextPendingQueueIndex];
2519     /* If we still have more pending items in our queue, lets go to the next one */
2520     if (newQueueItemIndex >= 0 && newQueueItemIndex < queueItems)
2521     {
2522         /*Set our currentQueueEncodeIndex now to the newly found Pending encode as we own it */
2523         currentQueueEncodeIndex = newQueueItemIndex;
2524         /* now we mark the queue item as Status = 1 ( being encoded ) so another instance can not come along and try to scan it while we are scanning */
2525         [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] setObject:[NSNumber numberWithInt:1] forKey:@"Status"];
2526         [self writeToActivityLog: "incrementQueueItemDone new pending items found: %d", currentQueueEncodeIndex];
2527         [self saveQueueFileItem];
2528         /* now we can go ahead and scan the new pending queue item */
2529         [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]];
2530
2531     }
2532     else
2533     {
2534         [self writeToActivityLog: "incrementQueueItemDone there are no more pending encodes"];
2535         /*Since there are no more items to encode, go to queueCompletedAlerts for user specified alerts after queue completed*/
2536         [self queueCompletedAlerts];
2537     }
2538 }
2539
2540 /* Here we actually tell hb_scan to perform the source scan, using the path to source and title number*/
2541 - (void) performNewQueueScan:(NSString *) scanPath scanTitleNum: (int) scanTitleNum
2542 {
2543     /* Tell HB to output a new activity log file for this encode */
2544     [outputPanel startEncodeLog:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"DestinationPath"]];
2545     
2546     /* We now flag the queue item as being owned by this instance of HB using the PID */
2547     [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] setObject:[NSNumber numberWithInt:pidNum] forKey:@"EncodingPID"];
2548     /* Get the currentQueueEncodeNameString from the queue item to display in the status field */
2549     currentQueueEncodeNameString = [[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"DestinationPath"] lastPathComponent]retain];
2550     /* We save all of the Queue data here */
2551     [self saveQueueFileItem];
2552     
2553     /* use a bool to determine whether or not we can decrypt using vlc */
2554     BOOL cancelScanDecrypt = 0;
2555     /* set the bool so that showNewScan knows to apply the appropriate queue
2556      * settings as this is a queue rescan
2557      */
2558     NSString *path = scanPath;
2559     HBDVDDetector *detector = [HBDVDDetector detectorForPath:path];
2560     
2561     if( [detector isVideoDVD] )
2562     {
2563         // The chosen path was actually on a DVD, so use the raw block
2564         // device path instead.
2565         path = [detector devicePath];
2566         [self writeToActivityLog: "trying to open a physical dvd at: %s", [scanPath UTF8String]];
2567
2568         /* lets check for vlc here to make sure we have a dylib available to use for decrypting */
2569         void *dvdcss = dlopen("libdvdcss.2.dylib", RTLD_LAZY);
2570         if (dvdcss == NULL) 
2571         {
2572             /*vlc not found in /Applications so we set the bool to cancel scanning to 1 */
2573             cancelScanDecrypt = 1;
2574             [self writeToActivityLog: "VLC app not found for decrypting physical dvd"];
2575             int status;
2576             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");
2577             [NSApp requestUserAttention:NSCriticalRequest];
2578             
2579             if (status == NSAlertDefaultReturn)
2580             {
2581                 /* User chose to go download vlc (as they rightfully should) so we send them to the vlc site */
2582                 [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://www.videolan.org/"]];
2583             }
2584             else if (status == NSAlertAlternateReturn)
2585             {
2586             /* User chose to cancel the scan */
2587             [self writeToActivityLog: "cannot open physical dvd , scan cancelled"];
2588             }
2589             else
2590             {
2591             /* 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 */
2592             cancelScanDecrypt = 0;
2593             [self writeToActivityLog: "user overrode vlc warning -trying to open physical dvd without decryption"];
2594             }
2595
2596         }
2597         else
2598         {
2599             /* VLC was found in /Applications so all is well, we can carry on using vlc's libdvdcss.dylib for decrypting if needed */
2600             dlclose(dvdcss);
2601             [self writeToActivityLog: "VLC app found for decrypting physical dvd"];
2602         }
2603     }
2604
2605     if (cancelScanDecrypt == 0)
2606     {
2607         /* we actually pass the scan off to libhb here */
2608         /* If there is no title number passed to scan, we use "0"
2609          * which causes the default behavior of a full source scan
2610          */
2611         if (!scanTitleNum)
2612         {
2613             scanTitleNum = 0;
2614         }
2615         if (scanTitleNum > 0)
2616         {
2617             [self writeToActivityLog: "scanning specifically for title: %d", scanTitleNum];
2618         }
2619         
2620          /* We use our advance pref to determine how many previews to scan */
2621         int hb_num_previews = [[[NSUserDefaults standardUserDefaults] objectForKey:@"PreviewsNumber"] intValue];
2622         hb_scan( fQueueEncodeLibhb, [path UTF8String], scanTitleNum, hb_num_previews, 0 );
2623     }
2624 }
2625
2626 /* This assumes that we have re-scanned and loaded up a new queue item to send to libhb as fQueueEncodeLibhb */
2627 - (void) processNewQueueEncode
2628 {
2629     hb_list_t  * list  = hb_get_titles( fQueueEncodeLibhb );
2630     hb_title_t * title = (hb_title_t *) hb_list_item( list,0 ); // is always zero since now its a single title scan
2631     hb_job_t * job = title->job;
2632     
2633     if( !hb_list_count( list ) )
2634     {
2635         [self writeToActivityLog: "processNewQueueEncode WARNING nothing found in the title list"];
2636     }
2637     
2638     NSMutableDictionary * queueToApply = [QueueFileArray objectAtIndex:currentQueueEncodeIndex];
2639     [self writeToActivityLog: "Preset: %s", [[queueToApply objectForKey:@"PresetName"] UTF8String]];
2640     [self writeToActivityLog: "processNewQueueEncode number of passes expected is: %d", ([[queueToApply objectForKey:@"VideoTwoPass"] intValue] + 1)];
2641     job->file = [[queueToApply objectForKey:@"DestinationPath"] UTF8String];
2642     [self prepareJob];
2643     
2644     /*
2645      * If scanning we need to do some extra setup of the job.
2646      */
2647     if( job->indepth_scan == 1 )
2648     {
2649         char *x264opts_tmp;
2650         
2651         /*
2652          * When subtitle scan is enabled do a fast pre-scan job
2653          * which will determine which subtitles to enable, if any.
2654          */
2655         job->pass = -1;
2656         x264opts_tmp = job->x264opts;
2657         
2658         job->x264opts = NULL;
2659         
2660         job->indepth_scan = 1;  
2661
2662         
2663         /*
2664          * Add the pre-scan job
2665          */
2666         hb_add( fQueueEncodeLibhb, job );
2667         job->x264opts = x264opts_tmp;
2668     }
2669
2670     
2671     if( [[queueToApply objectForKey:@"VideoTwoPass"] intValue] == 1 )
2672     {
2673         job->indepth_scan = 0;
2674         
2675
2676         
2677         job->pass = 1;
2678         
2679         hb_add( fQueueEncodeLibhb, job );
2680         
2681         job->pass = 2;
2682         
2683         job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */  
2684         strcpy(job->x264opts, [[queueToApply objectForKey:@"x264Option"] UTF8String]);
2685         
2686         hb_add( fQueueEncodeLibhb, job );
2687         
2688     }
2689     else
2690     {
2691         job->indepth_scan = 0;
2692         job->pass = 0;
2693         
2694         hb_add( fQueueEncodeLibhb, job );
2695     }
2696         
2697     NSString *destinationDirectory = [[queueToApply objectForKey:@"DestinationPath"] stringByDeletingLastPathComponent];
2698         [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];
2699         /* Lets mark our new encode as 1 or "Encoding" */
2700     [queueToApply setObject:[NSNumber numberWithInt:1] forKey:@"Status"];
2701     [self saveQueueFileItem];
2702     
2703     /* we need to clean up the subtitle tracks after the job(s) have been set  */
2704     int num_subtitle_tracks = hb_list_count(job->list_subtitle);
2705     int ii;
2706     for(ii = 0; ii < num_subtitle_tracks; ii++)
2707     {
2708         hb_subtitle_t * subtitle;
2709         subtitle = (hb_subtitle_t *)hb_list_item(job->list_subtitle, 0);
2710         
2711
2712         hb_list_rem(job->list_subtitle, subtitle);
2713         free(subtitle);
2714     }
2715     
2716     /* We should be all setup so let 'er rip */   
2717     [self doRip];
2718 }
2719
2720 #pragma mark -
2721 #pragma mark Queue Item Editing
2722
2723 /* Rescans the chosen queue item back into the main window */
2724 - (void)rescanQueueItemToMainWindow:(NSString *) scanPath scanTitleNum: (int) scanTitleNum selectedQueueItem: (int) selectedQueueItem
2725 {
2726     fqueueEditRescanItemNum = selectedQueueItem;
2727     [self writeToActivityLog: "rescanQueueItemToMainWindow: Re-scanning queue item at index:%d",fqueueEditRescanItemNum];
2728     applyQueueToScan = YES;
2729     /* Set the browsedSourceDisplayName for showNewScan */
2730     browsedSourceDisplayName = [[QueueFileArray objectAtIndex:fqueueEditRescanItemNum] objectForKey:@"SourceName"];
2731     [self performScan:scanPath scanTitleNum:scanTitleNum];
2732 }
2733
2734
2735 /* We use this method after a queue item rescan for edit.
2736  * it largely mirrors -selectPreset in terms of structure.
2737  * Assumes that a queue item has been reloaded into the main window.
2738  */
2739 - (IBAction)applyQueueSettingsToMainWindow:(id)sender
2740 {
2741     NSMutableDictionary * queueToApply = [QueueFileArray objectAtIndex:fqueueEditRescanItemNum];
2742     hb_job_t * job = fTitle->job;
2743     if (queueToApply)
2744     {
2745         [self writeToActivityLog: "applyQueueSettingsToMainWindow: queue item found"];
2746     }
2747     /* Set title number and chapters */
2748     /* since the queue only scans a single title, its already been selected in showNewScan
2749        so do not try to reset it here. However if we do decide to do full source scans on
2750        a queue edit rescan, we would need it. So leaving in for now but commenting out. */
2751     //[fSrcTitlePopUp selectItemAtIndex: [[queueToApply objectForKey:@"TitleNumber"] intValue] - 1];
2752     
2753     [fSrcChapterStartPopUp selectItemAtIndex: [[queueToApply objectForKey:@"ChapterStart"] intValue] - 1];
2754     [fSrcChapterEndPopUp selectItemAtIndex: [[queueToApply objectForKey:@"ChapterEnd"] intValue] - 1];
2755     
2756     /* File Format */
2757     [fDstFormatPopUp selectItemWithTitle:[queueToApply objectForKey:@"FileFormat"]];
2758     [self formatPopUpChanged:nil];
2759     
2760     /* Chapter Markers*/
2761     [fCreateChapterMarkers setState:[[queueToApply objectForKey:@"ChapterMarkers"] intValue]];
2762     /* Allow Mpeg4 64 bit formatting +4GB file sizes */
2763     [fDstMp4LargeFileCheck setState:[[queueToApply objectForKey:@"Mp4LargeFile"] intValue]];
2764     /* Mux mp4 with http optimization */
2765     [fDstMp4HttpOptFileCheck setState:[[queueToApply objectForKey:@"Mp4HttpOptimize"] intValue]];
2766     
2767     /* Video encoder */
2768     /* We set the advanced opt string here if applicable*/
2769     [fVidEncoderPopUp selectItemWithTitle:[queueToApply objectForKey:@"VideoEncoder"]];
2770     [fAdvancedOptions setOptions:[queueToApply objectForKey:@"x264Option"]];
2771     
2772     /* Lets run through the following functions to get variables set there */
2773     [self videoEncoderPopUpChanged:nil];
2774     /* Set the state of ipod compatible with Mp4iPodCompatible. Only for x264*/
2775     [fDstMp4iPodFileCheck setState:[[queueToApply objectForKey:@"Mp4iPodCompatible"] intValue]];
2776     [self calculateBitrate:nil];
2777     
2778     /* Video quality */
2779     [fVidQualityMatrix selectCellAtRow:[[queueToApply objectForKey:@"VideoQualityType"] intValue] column:0];
2780     
2781     [fVidTargetSizeField setStringValue:[queueToApply objectForKey:@"VideoTargetSize"]];
2782     [fVidBitrateField setStringValue:[queueToApply objectForKey:@"VideoAvgBitrate"]];
2783     /* Since we are now using RF Values for the slider, we detect if the preset uses an old quality float.
2784      * So, check to see if the quality value is less than 1.0 which should indicate the old ".062" type
2785      * quality preset. Caveat: in the case of x264, where the RF scale starts at 0, it would misinterpret
2786      * a preset that uses 0.0 - 0.99 for RF as an old style preset. Not sure how to get around that one yet,
2787      * though it should be a corner case since it would pretty much be a preset for lossless encoding. */
2788     if ([[queueToApply objectForKey:@"VideoQualitySlider"] floatValue] < 1.0)
2789     {
2790         /* For the quality slider we need to convert the old percent's to the new rf scales */
2791         float rf =  (([fVidQualitySlider maxValue] - [fVidQualitySlider minValue]) * [[queueToApply objectForKey:@"VideoQualitySlider"] floatValue]);
2792         [fVidQualitySlider setFloatValue:rf];
2793         
2794     }
2795     else
2796     {
2797         /* Since theora's qp value goes up from left to right, we can just set the slider float value */
2798         if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_THEORA)
2799         {
2800             [fVidQualitySlider setFloatValue:[[queueToApply objectForKey:@"VideoQualitySlider"] floatValue]];
2801         }
2802         else
2803         {
2804             /* since ffmpeg and x264 use an "inverted" slider (lower qp/rf values indicate a higher quality) we invert the value on the slider */
2805             [fVidQualitySlider setFloatValue:([fVidQualitySlider maxValue] + [fVidQualitySlider minValue]) - [[queueToApply objectForKey:@"VideoQualitySlider"] floatValue]];
2806         }
2807     }
2808     
2809     [self videoMatrixChanged:nil];
2810         
2811     /* Video framerate */
2812     /* For video preset video framerate, we want to make sure that Same as source does not conflict with the
2813      detected framerate in the fVidRatePopUp so we use index 0*/
2814     if ([[queueToApply objectForKey:@"VideoFramerate"] isEqualToString:@"Same as source"])
2815     {
2816         [fVidRatePopUp selectItemAtIndex: 0];
2817     }
2818     else
2819     {
2820         [fVidRatePopUp selectItemWithTitle:[queueToApply objectForKey:@"VideoFramerate"]];
2821     }
2822     
2823     /* 2 Pass Encoding */
2824     [fVidTwoPassCheck setState:[[queueToApply objectForKey:@"VideoTwoPass"] intValue]];
2825     [self twoPassCheckboxChanged:nil];
2826     /* Turbo 1st pass for 2 Pass Encoding */
2827     [fVidTurboPassCheck setState:[[queueToApply objectForKey:@"VideoTurboTwoPass"] intValue]];
2828     
2829     /*Audio*/
2830     
2831     
2832     /* Now lets add our new tracks to the audio list here */
2833     if ([[queueToApply objectForKey:@"Audio1Track"] intValue] > 0)
2834     {
2835         [fAudLang1PopUp selectItemAtIndex: [[queueToApply objectForKey:@"Audio1Track"] intValue]];
2836         [self audioTrackPopUpChanged: fAudLang1PopUp];
2837         [fAudTrack1CodecPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio1Encoder"]];
2838         [self audioTrackPopUpChanged: fAudTrack1CodecPopUp];
2839         
2840         [fAudTrack1MixPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio1Mixdown"]];
2841         
2842         [fAudTrack1RatePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio1Samplerate"]];
2843         [fAudTrack1BitratePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio1Bitrate"]];
2844         
2845         [fAudTrack1DrcSlider setFloatValue:[[queueToApply objectForKey:@"Audio1TrackDRCSlider"] floatValue]];
2846         [self audioDRCSliderChanged: fAudTrack1DrcSlider];
2847     }
2848     else
2849     {
2850         [fAudLang1PopUp selectItemAtIndex: 0];
2851         [self audioTrackPopUpChanged: fAudLang1PopUp];
2852     }
2853     if ([[queueToApply objectForKey:@"Audio2Track"] intValue] > 0)
2854     {
2855         [fAudLang2PopUp selectItemAtIndex: [[queueToApply objectForKey:@"Audio2Track"] intValue]];
2856         [self audioTrackPopUpChanged: fAudLang2PopUp];
2857         [fAudTrack2CodecPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio2Encoder"]];
2858         [self audioTrackPopUpChanged: fAudTrack2CodecPopUp];
2859         
2860         [fAudTrack2MixPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio2Mixdown"]];
2861         
2862         [fAudTrack2RatePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio2Samplerate"]];
2863         [fAudTrack2BitratePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio2Bitrate"]];
2864         
2865         [fAudTrack2DrcSlider setFloatValue:[[queueToApply objectForKey:@"Audio2TrackDRCSlider"] floatValue]];
2866         [self audioDRCSliderChanged: fAudTrack2DrcSlider];
2867     }
2868     else
2869     {
2870         [fAudLang2PopUp selectItemAtIndex: 0];
2871         [self audioTrackPopUpChanged: fAudLang2PopUp];
2872     }
2873     if ([[queueToApply objectForKey:@"Audio3Track"] intValue] > 0)
2874     {
2875         [fAudLang3PopUp selectItemAtIndex: [[queueToApply objectForKey:@"Audio3Track"] intValue]];
2876         [self audioTrackPopUpChanged: fAudLang3PopUp];
2877         [fAudTrack3CodecPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio3Encoder"]];
2878         [self audioTrackPopUpChanged: fAudTrack3CodecPopUp];
2879         
2880         [fAudTrack3MixPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio3Mixdown"]];
2881         
2882         [fAudTrack3RatePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio3Samplerate"]];
2883         [fAudTrack3BitratePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio3Bitrate"]];
2884         
2885         [fAudTrack3DrcSlider setFloatValue:[[queueToApply objectForKey:@"Audio3TrackDRCSlider"] floatValue]];
2886         [self audioDRCSliderChanged: fAudTrack3DrcSlider];
2887     }
2888     else
2889     {
2890         [fAudLang3PopUp selectItemAtIndex: 0];
2891         [self audioTrackPopUpChanged: fAudLang3PopUp];
2892     }
2893     if ([[queueToApply objectForKey:@"Audio4Track"] intValue] > 0)
2894     {
2895         [fAudLang4PopUp selectItemAtIndex: [[queueToApply objectForKey:@"Audio4Track"] intValue]];
2896         [self audioTrackPopUpChanged: fAudLang4PopUp];
2897         [fAudTrack4CodecPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio4Encoder"]];
2898         [self audioTrackPopUpChanged: fAudTrack4CodecPopUp];
2899         
2900         [fAudTrack4MixPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio4Mixdown"]];
2901         
2902         [fAudTrack4RatePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio4Samplerate"]];
2903         [fAudTrack4BitratePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio4Bitrate"]];
2904         
2905         [fAudTrack4DrcSlider setFloatValue:[[queueToApply objectForKey:@"Audio4TrackDRCSlider"] floatValue]];
2906         [self audioDRCSliderChanged: fAudTrack4DrcSlider];
2907     }
2908     else
2909     {
2910         [fAudLang4PopUp selectItemAtIndex: 0];
2911         [self audioTrackPopUpChanged: fAudLang4PopUp];
2912     }
2913     
2914     /*Subtitles*/
2915     /* Crashy crashy right now, working on it */
2916     [fSubtitlesDelegate setNewSubtitles:[queueToApply objectForKey:@"SubtitleList"]];
2917     [fSubtitlesTable reloadData];  
2918     /* Picture Settings */
2919     
2920     /* If Cropping is set to custom, then recall all four crop values from
2921      when the preset was created and apply them */
2922     if ([[queueToApply objectForKey:@"PictureAutoCrop"]  intValue] == 0)
2923     {
2924         [fPictureController setAutoCrop:NO];
2925         
2926         /* Here we use the custom crop values saved at the time the preset was saved */
2927         job->crop[0] = [[queueToApply objectForKey:@"PictureTopCrop"]  intValue];
2928         job->crop[1] = [[queueToApply objectForKey:@"PictureBottomCrop"]  intValue];
2929         job->crop[2] = [[queueToApply objectForKey:@"PictureLeftCrop"]  intValue];
2930         job->crop[3] = [[queueToApply objectForKey:@"PictureRightCrop"]  intValue];
2931         
2932     }
2933     else /* if auto crop has been saved in preset, set to auto and use post scan auto crop */
2934     {
2935         [fPictureController setAutoCrop:YES];
2936         /* Here we use the auto crop values determined right after scan */
2937         job->crop[0] = AutoCropTop;
2938         job->crop[1] = AutoCropBottom;
2939         job->crop[2] = AutoCropLeft;
2940         job->crop[3] = AutoCropRight;
2941         
2942     }
2943     
2944     job->modulus = [[queueToApply objectForKey:@"PictureModulus"]  intValue];
2945     
2946     /* we check to make sure the presets width/height does not exceed the sources width/height */
2947     if (fTitle->width < [[queueToApply objectForKey:@"PictureWidth"]  intValue] || fTitle->height < [[queueToApply objectForKey:@"PictureHeight"]  intValue])
2948     {
2949         /* if so, then we use the sources height and width to avoid scaling up */
2950         //job->width = fTitle->width;
2951         //job->height = fTitle->height;
2952         [self revertPictureSizeToMax:nil];
2953     }
2954     else // source width/height is >= the preset height/width
2955     {
2956         /* we can go ahead and use the presets values for height and width */
2957         job->width = [[queueToApply objectForKey:@"PictureWidth"]  intValue];
2958         job->height = [[queueToApply objectForKey:@"PictureHeight"]  intValue];
2959     }
2960     job->keep_ratio = [[queueToApply objectForKey:@"PictureKeepRatio"]  intValue];
2961     if (job->keep_ratio == 1)
2962     {
2963         hb_fix_aspect( job, HB_KEEP_WIDTH );
2964         if( job->height > fTitle->height )
2965         {
2966             job->height = fTitle->height;
2967             hb_fix_aspect( job, HB_KEEP_HEIGHT );
2968         }
2969     }
2970     job->anamorphic.mode = [[queueToApply objectForKey:@"PicturePAR"]  intValue];
2971     job->modulus = [[queueToApply objectForKey:@"PictureModulus"]  intValue];
2972     
2973     /* Filters */
2974     
2975     /* We only allow *either* Decomb or Deinterlace. So check for the PictureDecombDeinterlace key.
2976      * also, older presets may not have this key, in which case we also check to see if that preset had  PictureDecomb
2977      * specified, in which case we use decomb and ignore any possible Deinterlace settings as using both was less than
2978      * sane.
2979      */
2980     [fPictureController setUseDecomb:1];
2981     [fPictureController setDecomb:0];
2982     [fPictureController setDeinterlace:0];
2983     if ([[queueToApply objectForKey:@"PictureDecombDeinterlace"] intValue] == 1 || [[queueToApply objectForKey:@"PictureDecomb"] intValue] > 0)
2984     {
2985         /* we are using decomb */
2986         /* Decomb */
2987         if ([[queueToApply objectForKey:@"PictureDecomb"] intValue] > 0)
2988         {
2989             [fPictureController setDecomb:[[queueToApply objectForKey:@"PictureDecomb"] intValue]];
2990             
2991             /* if we are using "Custom" in the decomb setting, also set the custom string*/
2992             if ([[queueToApply objectForKey:@"PictureDecomb"] intValue] == 1)
2993             {
2994                 [fPictureController setDecombCustomString:[queueToApply objectForKey:@"PictureDecombCustom"]];    
2995             }
2996         }
2997     }
2998     else
2999     {
3000         /* We are using Deinterlace */
3001         /* Deinterlace */
3002         if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] > 0)
3003         {
3004             [fPictureController setUseDecomb:0];
3005             [fPictureController setDeinterlace:[[queueToApply objectForKey:@"PictureDeinterlace"] intValue]];
3006             /* if we are using "Custom" in the deinterlace setting, also set the custom string*/
3007             if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 1)
3008             {
3009                 [fPictureController setDeinterlaceCustomString:[queueToApply objectForKey:@"PictureDeinterlaceCustom"]];    
3010             }
3011         }
3012     }
3013     
3014     
3015     /* Detelecine */
3016     if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] > 0)
3017     {
3018         [fPictureController setDetelecine:[[queueToApply objectForKey:@"PictureDetelecine"] intValue]];
3019         /* if we are using "Custom" in the detelecine setting, also set the custom string*/
3020         if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] == 1)
3021         {
3022             [fPictureController setDetelecineCustomString:[queueToApply objectForKey:@"PictureDetelecineCustom"]];    
3023         }
3024     }
3025     else
3026     {
3027         [fPictureController setDetelecine:0];
3028     }
3029     
3030     /* Denoise */
3031     if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] > 0)
3032     {
3033         [fPictureController setDenoise:[[queueToApply objectForKey:@"PictureDenoise"] intValue]];
3034         /* if we are using "Custom" in the denoise setting, also set the custom string*/
3035         if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 1)
3036         {
3037             [fPictureController setDenoiseCustomString:[queueToApply objectForKey:@"PictureDenoiseCustom"]];    
3038         }
3039     }
3040     else
3041     {
3042         [fPictureController setDenoise:0];
3043     }   
3044     
3045     /* Deblock */
3046     if ([[queueToApply objectForKey:@"PictureDeblock"] intValue] == 1)
3047     {
3048         /* if its a one, then its the old on/off deblock, set on to 5*/
3049         [fPictureController setDeblock:5];
3050     }
3051     else
3052     {
3053         /* use the settings intValue */
3054         [fPictureController setDeblock:[[queueToApply objectForKey:@"PictureDeblock"] intValue]];
3055     }
3056     
3057     if ([[queueToApply objectForKey:@"VideoGrayScale"] intValue] == 1)
3058     {
3059         [fPictureController setGrayscale:1];
3060     }
3061     else
3062     {
3063         [fPictureController setGrayscale:0];
3064     }
3065     
3066     /* we call SetTitle: in fPictureController so we get an instant update in the Picture Settings window */
3067     [fPictureController SetTitle:fTitle];
3068     [self calculatePictureSizing:nil];
3069     
3070     /* somehow we need to figure out a way to tie the queue item to a preset if it used one */
3071     //[queueFileJob setObject:[fPresetSelectedDisplay stringValue] forKey:@"PresetName"];
3072     //[queueFileJob setObject:[NSNumber numberWithInt:[fPresetsOutlineView selectedRow]] forKey:@"PresetIndexNum"];
3073     if ([queueToApply objectForKey:@"PresetIndexNum"]) // This item used a preset so insert that info
3074         {
3075                 /* Deselect the currently selected Preset if there is one*/
3076         //[fPresetsOutlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:[[queueToApply objectForKey:@"PresetIndexNum"] intValue]] byExtendingSelection:NO];
3077         //[self selectPreset:nil];
3078                 
3079         //[fPresetsOutlineView selectRow:[[queueToApply objectForKey:@"PresetIndexNum"] intValue]];
3080                 /* Change UI to show "Custom" settings are being used */
3081                 //[fPresetSelectedDisplay setStringValue: [[queueToApply objectForKey:@"PresetName"] stringValue]];
3082         
3083                 curUserPresetChosenNum = nil;
3084         }
3085     else
3086     {
3087         /* Deselect the currently selected Preset if there is one*/
3088                 [fPresetsOutlineView deselectRow:[fPresetsOutlineView selectedRow]];
3089                 /* Change UI to show "Custom" settings are being used */
3090                 [fPresetSelectedDisplay setStringValue: @"Custom"];
3091         
3092                 //curUserPresetChosenNum = nil;
3093     }
3094     
3095     /* We need to set this bool back to NO, in case the user wants to do a scan */
3096     //applyQueueToScan = NO;
3097     
3098     /* Not that source is loaded and settings applied, delete the queue item from the queue */
3099     [self writeToActivityLog: "applyQueueSettingsToMainWindow: deleting queue item:%d",fqueueEditRescanItemNum];
3100     [self removeQueueFileItem:fqueueEditRescanItemNum];
3101 }
3102
3103
3104
3105 #pragma mark -
3106 #pragma mark Live Preview
3107 /* Note,this is much like prepareJob, but directly sets the job vars so Picture Preview
3108  * can encode to its temp preview directory and playback. This is *not* used for any actual user
3109  * encodes
3110  */
3111 - (void) prepareJobForPreview
3112 {
3113     hb_list_t  * list  = hb_get_titles( fHandle );
3114     hb_title_t * title = (hb_title_t *) hb_list_item( list,
3115             [fSrcTitlePopUp indexOfSelectedItem] );
3116     hb_job_t * job = title->job;
3117     hb_audio_config_t * audio;
3118     /* set job->angle for libdvdnav */
3119     job->angle = [fSrcAnglePopUp indexOfSelectedItem] + 1;
3120     /* Chapter selection */
3121     job->chapter_start = [fSrcChapterStartPopUp indexOfSelectedItem] + 1;
3122     job->chapter_end   = [fSrcChapterEndPopUp   indexOfSelectedItem] + 1;
3123         
3124     /* Format (Muxer) and Video Encoder */
3125     job->mux = [[fDstFormatPopUp selectedItem] tag];
3126     job->vcodec = [[fVidEncoderPopUp selectedItem] tag];
3127
3128     job->chapter_markers = 0;
3129     
3130         if( job->vcodec & HB_VCODEC_X264 )
3131     {
3132                 
3133                 /* Below Sends x264 options to the core library if x264 is selected*/
3134                 /* Lets use this as per Nyx, Thanks Nyx!*/
3135                 job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */
3136                 /* For previews we ignore the turbo option for the first pass of two since we only use 1 pass */
3137                 strcpy(job->x264opts, [[fAdvancedOptions optionsString] UTF8String]);
3138
3139         
3140     }
3141
3142     /* Video settings */
3143    /* Set vfr to 0 as it's only on if using same as source in the framerate popup
3144      * and detelecine is on, so we handle that in the logic below
3145      */
3146     job->vfr = 0;
3147     if( [fVidRatePopUp indexOfSelectedItem] > 0 )
3148     {
3149         /* a specific framerate has been chosen */
3150         job->vrate      = 27000000;
3151         job->vrate_base = hb_video_rates[[fVidRatePopUp indexOfSelectedItem]-1].rate;
3152         /* We are not same as source so we set job->cfr to 1 
3153          * to enable constant frame rate since user has specified
3154          * a specific framerate*/
3155         if ([fFrameratePfrCheck state] == 1)
3156         {
3157             job->cfr = 2;
3158         }
3159         else
3160         {
3161             job->cfr = 1;
3162         }
3163     }
3164     else
3165     {
3166         /* We are same as source (variable) */
3167         job->vrate      = title->rate;
3168         job->vrate_base = title->rate_base;
3169         /* We are same as source so we set job->cfr to 0 
3170          * to enable true same as source framerate */
3171         job->cfr = 0;
3172         /* If we are same as source and we have detelecine on, we need to turn on
3173          * job->vfr
3174          */
3175         if ([fPictureController detelecine] == 1)
3176         {
3177             job->vfr = 1;
3178         }
3179     }
3180
3181     switch( [fVidQualityMatrix selectedRow] )
3182     {
3183         case 0:
3184             /* Target size.
3185                Bitrate should already have been calculated and displayed
3186                in fVidBitrateField, so let's just use it */
3187         case 1:
3188             job->vquality = -1.0;
3189             job->vbitrate = [fVidBitrateField intValue];
3190             break;
3191         case 2:
3192             job->vquality = [fVidQualityRFField floatValue];
3193             job->vbitrate = 0;
3194             break;
3195     }
3196
3197     /* Subtitle settings */
3198     NSMutableArray *subtitlesArray = [[NSMutableArray alloc] initWithArray:[fSubtitlesDelegate getSubtitleArray] copyItems:YES];
3199     
3200     
3201 int subtitle = nil;
3202 int force;
3203 int burned;
3204 int def;
3205 bool one_burned = FALSE;
3206
3207     int i = 0;
3208     NSEnumerator *enumerator = [subtitlesArray objectEnumerator];
3209     id tempObject;
3210     while (tempObject = [enumerator nextObject])
3211     {
3212         
3213         subtitle = [[tempObject objectForKey:@"subtitleSourceTrackNum"] intValue];
3214         force = [[tempObject objectForKey:@"subtitleTrackForced"] intValue];
3215         burned = [[tempObject objectForKey:@"subtitleTrackBurned"] intValue];
3216         def = [[tempObject objectForKey:@"subtitleTrackDefault"] intValue];
3217         
3218         /* since the subtitleSourceTrackNum 0 is "None" in our array of the subtitle popups,
3219          * we want to ignore it for display as well as encoding.
3220          */
3221         if (subtitle > 0)
3222         {
3223             /* if i is 0, then we are in the first item of the subtitles which we need to 
3224              * check for the "Foreign Audio Search" which would be subtitleSourceTrackNum of 1
3225              * bearing in mind that for all tracks subtitleSourceTrackNum of 0 is None.
3226              */
3227             
3228             /* if we are on the first track and using "Foreign Audio Search" */ 
3229             if (i == 0 && subtitle == 1)
3230             {
3231                 /* NOTE: Currently foreign language search is borked for preview.
3232                  * Commented out but left in for initial commit. */
3233                 
3234                 
3235                 [self writeToActivityLog: "Foreign Language Search: %d", 1];
3236                 
3237                 job->indepth_scan = 1;
3238                 
3239                 if (burned != 1)
3240                 {
3241                     job->select_subtitle_config.dest = PASSTHRUSUB;
3242                 }
3243                 else
3244                 {
3245                     job->select_subtitle_config.dest = RENDERSUB;
3246                 }
3247                 
3248                 job->select_subtitle_config.force = force;
3249                 job->select_subtitle_config.default_track = def;
3250             }
3251             else
3252             {
3253                 
3254                 /* for the actual source tracks, we must subtract the non source entries so 
3255                  * that the menu index matches the source subtitle_list index for convenience */
3256                 if (i == 0)
3257                 {
3258                     /* for the first track, the source tracks start at menu index 2 ( None is 0,
3259                      * Foreign Language Search is 1) so subtract 2 */
3260                     subtitle = subtitle - 2;
3261                 }
3262                 else
3263                 {
3264                     /* for all other tracks, the source tracks start at menu index 1 (None is 0)
3265                      * so subtract 1. */
3266                     
3267                     subtitle = subtitle - 1;
3268                 }
3269                 
3270                 /* We are setting a source subtitle so access the source subtitle info */  
3271                 hb_subtitle_t * subt;
3272                 
3273                 subt = (hb_subtitle_t *)hb_list_item(title->list_subtitle, subtitle);
3274                 
3275                 /* if we are getting the subtitles from an external srt file */
3276                 if ([[tempObject objectForKey:@"subtitleSourceTrackType"] isEqualToString:@"SRT"])
3277                 {
3278                     hb_subtitle_config_t sub_config;
3279                     
3280                     sub_config.offset = [[tempObject objectForKey:@"subtitleTrackSrtOffset"] intValue];
3281                     
3282                     /* we need to srncpy file path and char code */
3283                     strncpy(sub_config.src_filename, [[tempObject objectForKey:@"subtitleSourceSrtFilePath"] UTF8String], 128);
3284                     strncpy(sub_config.src_codeset, [[tempObject objectForKey:@"subtitleTrackSrtCharCode"] UTF8String], 40);
3285                     
3286                     sub_config.force = 0;
3287                     sub_config.dest = PASSTHRUSUB;
3288                     sub_config.default_track = def;
3289                     
3290                     hb_srt_add( job, &sub_config, [[tempObject objectForKey:@"subtitleTrackSrtLanguageIso3"] UTF8String]);
3291                 }
3292                 
3293                 if (subt != NULL)
3294                 {
3295                     hb_subtitle_config_t sub_config = subt->config;
3296                     
3297                     if ( !burned && subt->format == PICTURESUB )
3298                     {
3299                         sub_config.dest = PASSTHRUSUB;
3300                     }
3301                     else if ( burned && subt->format == PICTURESUB )
3302                     {
3303                         // Only allow one subtitle to be burned into the video
3304                         if (one_burned)
3305                             continue;
3306                         one_burned = TRUE;
3307                     }
3308                     sub_config.force = force;
3309                     sub_config.default_track = def;
3310                     hb_subtitle_add( job, &sub_config, subtitle );
3311                 }   
3312                 
3313             }
3314         }
3315         i++;
3316     }
3317    
3318     
3319     
3320 [subtitlesArray autorelease];    
3321     
3322     
3323     /* Audio tracks and mixdowns */
3324     /* Lets make sure there arent any erroneous audio tracks in the job list, so lets make sure its empty*/
3325     int audiotrack_count = hb_list_count(job->list_audio);
3326     for( int i = 0; i < audiotrack_count;i++)
3327     {
3328         hb_audio_t * temp_audio = (hb_audio_t*) hb_list_item( job->list_audio, 0 );
3329         hb_list_rem(job->list_audio, temp_audio);
3330     }
3331     /* Now lets add our new tracks to the audio list here */
3332     if ([fAudLang1PopUp indexOfSelectedItem] > 0)
3333     {
3334         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3335         hb_audio_config_init(audio);
3336         audio->in.track = [fAudLang1PopUp indexOfSelectedItem] - 1;
3337         /* We go ahead and assign values to our audio->out.<properties> */
3338         audio->out.track = [fAudLang1PopUp indexOfSelectedItem] - 1;
3339         audio->out.codec = [[fAudTrack1CodecPopUp selectedItem] tag];
3340         audio->out.mixdown = [[fAudTrack1MixPopUp selectedItem] tag];
3341         audio->out.bitrate = [[fAudTrack1BitratePopUp selectedItem] tag];
3342         audio->out.samplerate = [[fAudTrack1RatePopUp selectedItem] tag];
3343         audio->out.dynamic_range_compression = [fAudTrack1DrcField floatValue];
3344         
3345         hb_audio_add( job, audio );
3346         free(audio);
3347     }  
3348     if ([fAudLang2PopUp indexOfSelectedItem] > 0)
3349     {
3350         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3351         hb_audio_config_init(audio);
3352         audio->in.track = [fAudLang2PopUp indexOfSelectedItem] - 1;
3353         /* We go ahead and assign values to our audio->out.<properties> */
3354         audio->out.track = [fAudLang2PopUp indexOfSelectedItem] - 1;
3355         audio->out.codec = [[fAudTrack2CodecPopUp selectedItem] tag];
3356         audio->out.mixdown = [[fAudTrack2MixPopUp selectedItem] tag];
3357         audio->out.bitrate = [[fAudTrack2BitratePopUp selectedItem] tag];
3358         audio->out.samplerate = [[fAudTrack2RatePopUp selectedItem] tag];
3359         audio->out.dynamic_range_compression = [fAudTrack2DrcField floatValue];
3360         
3361         hb_audio_add( job, audio );
3362         free(audio);
3363         
3364     }
3365     
3366     if ([fAudLang3PopUp indexOfSelectedItem] > 0)
3367     {
3368         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3369         hb_audio_config_init(audio);
3370         audio->in.track = [fAudLang3PopUp indexOfSelectedItem] - 1;
3371         /* We go ahead and assign values to our audio->out.<properties> */
3372         audio->out.track = [fAudLang3PopUp indexOfSelectedItem] - 1;
3373         audio->out.codec = [[fAudTrack3CodecPopUp selectedItem] tag];
3374         audio->out.mixdown = [[fAudTrack3MixPopUp selectedItem] tag];
3375         audio->out.bitrate = [[fAudTrack3BitratePopUp selectedItem] tag];
3376         audio->out.samplerate = [[fAudTrack3RatePopUp selectedItem] tag];
3377         audio->out.dynamic_range_compression = [fAudTrack3DrcField floatValue];
3378         
3379         hb_audio_add( job, audio );
3380         free(audio);
3381         
3382     }
3383
3384     if ([fAudLang4PopUp indexOfSelectedItem] > 0)
3385     {
3386         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3387         hb_audio_config_init(audio);
3388         audio->in.track = [fAudLang4PopUp indexOfSelectedItem] - 1;
3389         /* We go ahead and assign values to our audio->out.<properties> */
3390         audio->out.track = [fAudLang4PopUp indexOfSelectedItem] - 1;
3391         audio->out.codec = [[fAudTrack4CodecPopUp selectedItem] tag];
3392         audio->out.mixdown = [[fAudTrack4MixPopUp selectedItem] tag];
3393         audio->out.bitrate = [[fAudTrack4BitratePopUp selectedItem] tag];
3394         audio->out.samplerate = [[fAudTrack4RatePopUp selectedItem] tag];
3395         audio->out.dynamic_range_compression = [fAudTrack4DrcField floatValue];
3396         
3397         hb_audio_add( job, audio );
3398         free(audio);
3399         
3400     }
3401
3402     
3403     
3404     /* Filters */
3405     
3406     /* Though Grayscale is not really a filter, per se
3407      * we put it here since its in the filters panel
3408      */
3409      
3410     if ([fPictureController grayscale])
3411     {
3412         job->grayscale = 1;
3413     }
3414     else
3415     {
3416         job->grayscale = 0;
3417     }
3418     
3419     /* Initialize the filters list */
3420     job->filters = hb_list_init();
3421     
3422     /* Now lets call the filters if applicable.
3423     * The order of the filters is critical
3424     */
3425     
3426         /* Detelecine */
3427     hb_filter_detelecine.settings = NULL;
3428     if ([fPictureController detelecine] == 1)
3429     {
3430         /* use a custom detelecine string */
3431         hb_filter_detelecine.settings = (char *) [[fPictureController detelecineCustomString] UTF8String];
3432         hb_list_add( job->filters, &hb_filter_detelecine );
3433     }
3434     if ([fPictureController detelecine] == 2)
3435     {
3436         /* Default */
3437         hb_list_add( job->filters, &hb_filter_detelecine );
3438     }
3439     
3440     
3441     
3442     if ([fPictureController useDecomb] == 1)
3443     {
3444         /* Decomb */
3445         /* we add the custom string if present */
3446         hb_filter_decomb.settings = NULL;
3447         if ([fPictureController decomb] == 1)
3448         {
3449             /* use a custom decomb string */
3450             hb_filter_decomb.settings = (char *) [[fPictureController decombCustomString] UTF8String];
3451             hb_list_add( job->filters, &hb_filter_decomb );
3452         }
3453         if ([fPictureController decomb] == 2)
3454         {
3455             /* Run old deinterlacer fd by default */
3456             //hb_filter_decomb.settings = (char *) [[fPicSettingDecomb stringValue] UTF8String];
3457             hb_list_add( job->filters, &hb_filter_decomb );
3458         }
3459     }
3460     else
3461     {
3462         
3463         /* Deinterlace */
3464         if ([fPictureController deinterlace] == 1)
3465         {
3466             /* we add the custom string if present */
3467             hb_filter_deinterlace.settings = (char *) [[fPictureController deinterlaceCustomString] UTF8String];
3468             hb_list_add( job->filters, &hb_filter_deinterlace );            
3469         }
3470         else if ([fPictureController deinterlace] == 2)
3471         {
3472             /* Run old deinterlacer fd by default */
3473             hb_filter_deinterlace.settings = "-1"; 
3474             hb_list_add( job->filters, &hb_filter_deinterlace );
3475         }
3476         else if ([fPictureController deinterlace] == 3)
3477         {
3478             /* Yadif mode 0 (without spatial deinterlacing.) */
3479             hb_filter_deinterlace.settings = "2"; 
3480             hb_list_add( job->filters, &hb_filter_deinterlace );            
3481         }
3482         else if ([fPictureController deinterlace] == 4)
3483         {
3484             /* Yadif (with spatial deinterlacing) */
3485             hb_filter_deinterlace.settings = "0"; 
3486             hb_list_add( job->filters, &hb_filter_deinterlace );            
3487         }
3488         
3489         }
3490     
3491     /* Denoise */
3492         if ([fPictureController denoise] == 1) // custom in popup
3493         {
3494                 /* we add the custom string if present */
3495         hb_filter_denoise.settings = (char *) [[fPictureController denoiseCustomString] UTF8String]; 
3496         hb_list_add( job->filters, &hb_filter_denoise );        
3497         }
3498     else if ([fPictureController denoise] == 2) // Weak in popup
3499         {
3500                 hb_filter_denoise.settings = "2:1:2:3"; 
3501         hb_list_add( job->filters, &hb_filter_denoise );        
3502         }
3503         else if ([fPictureController denoise] == 3) // Medium in popup
3504         {
3505                 hb_filter_denoise.settings = "3:2:2:3"; 
3506         hb_list_add( job->filters, &hb_filter_denoise );        
3507         }
3508         else if ([fPictureController denoise] == 4) // Strong in popup
3509         {
3510                 hb_filter_denoise.settings = "7:7:5:5"; 
3511         hb_list_add( job->filters, &hb_filter_denoise );        
3512         }
3513     
3514     
3515     /* Deblock  (uses pp7 default) */
3516     /* NOTE: even though there is a valid deblock setting of 0 for the filter, for 
3517      * the macgui's purposes a value of 0 actually means to not even use the filter
3518      * current hb_filter_deblock.settings valid ranges are from 5 - 15 
3519      */
3520     if ([fPictureController deblock] != 0)
3521     {
3522         NSString *deblockStringValue = [NSString stringWithFormat: @"%d",[fPictureController deblock]];
3523         hb_filter_deblock.settings = (char *) [deblockStringValue UTF8String];
3524         hb_list_add( job->filters, &hb_filter_deblock );
3525     }
3526
3527 }
3528
3529
3530 #pragma mark -
3531 #pragma mark Job Handling
3532
3533
3534 - (void) prepareJob
3535 {
3536     
3537     NSMutableDictionary * queueToApply = [QueueFileArray objectAtIndex:currentQueueEncodeIndex];
3538     hb_list_t  * list  = hb_get_titles( fQueueEncodeLibhb );
3539     hb_title_t * title = (hb_title_t *) hb_list_item( list,0 ); // is always zero since now its a single title scan
3540     hb_job_t * job = title->job;
3541     hb_audio_config_t * audio;
3542     /* Title Angle for dvdnav */
3543     job->angle = [[queueToApply objectForKey:@"TitleAngle"] intValue];
3544     
3545     if([[queueToApply objectForKey:@"fEncodeStartStop"] intValue] == 0)
3546     {
3547         /* Chapter selection */
3548         [self writeToActivityLog: "Start / Stop set to chapters"];
3549         job->chapter_start = [[queueToApply objectForKey:@"JobChapterStart"] intValue];
3550         job->chapter_end   = [[queueToApply objectForKey:@"JobChapterEnd"] intValue];
3551     }
3552     else if ([[queueToApply objectForKey:@"fEncodeStartStop"] intValue] == 1)
3553     {
3554         /* we are pts based start / stop */
3555         [self writeToActivityLog: "Start / Stop set to seconds ..."];
3556         
3557         /* Point A to Point B. Time to time in seconds.*/
3558         /* get the start seconds from the start seconds field */
3559         int start_seconds = [[queueToApply objectForKey:@"StartSeconds"] intValue];
3560         job->pts_to_start = start_seconds * 90000LL;
3561         /* Stop seconds is actually the duration of encode, so subtract the end seconds from the start seconds */
3562         int stop_seconds = [[queueToApply objectForKey:@"StopSeconds"] intValue];
3563         job->pts_to_stop = stop_seconds * 90000LL;
3564         
3565     }
3566     else if ([[queueToApply objectForKey:@"fEncodeStartStop"] intValue] == 2)
3567     {
3568         /* we are frame based start / stop */
3569         [self writeToActivityLog: "Start / Stop set to frames ..."];
3570         
3571         /* Point A to Point B. Frame to frame */
3572         /* get the start frame from the start frame field */
3573         int start_frame = [[queueToApply objectForKey:@"StartFrame"] intValue];
3574         job->frame_to_start = start_frame;
3575         /* get the frame to stop on from the end frame field */
3576         int stop_frame = [[queueToApply objectForKey:@"StopFrame"] intValue];
3577         job->frame_to_stop = stop_frame;
3578         
3579     }
3580
3581         
3582         
3583     
3584     /* Format (Muxer) and Video Encoder */
3585     job->mux = [[queueToApply objectForKey:@"JobFileFormatMux"] intValue];
3586     job->vcodec = [[queueToApply objectForKey:@"JobVideoEncoderVcodec"] intValue];
3587     
3588     
3589     /* If mpeg-4, then set mpeg-4 specific options like chapters and > 4gb file sizes */
3590     if( [[queueToApply objectForKey:@"Mp4LargeFile"] intValue] == 1)
3591     {
3592         job->largeFileSize = 1;
3593     }
3594     else
3595     {
3596         job->largeFileSize = 0;
3597     }
3598     /* We set http optimized mp4 here */
3599     if( [[queueToApply objectForKey:@"Mp4HttpOptimize"] intValue] == 1 )
3600     {
3601         job->mp4_optimize = 1;
3602     }
3603     else
3604     {
3605         job->mp4_optimize = 0;
3606     }
3607
3608         
3609     /* We set the chapter marker extraction here based on the format being
3610      mpeg4 or mkv and the checkbox being checked */
3611     if ([[queueToApply objectForKey:@"ChapterMarkers"] intValue] == 1)
3612     {
3613         job->chapter_markers = 1;
3614         
3615         /* now lets get our saved chapter names out the array in the queue file
3616          * and insert them back into the title chapter list. We have it here,
3617          * because unless we are inserting chapter markers there is no need to
3618          * spend the overhead of iterating through the chapter names array imo
3619          * Also, note that if for some reason we don't apply chapter names, the
3620          * chapters just come out 001, 002, etc. etc.
3621          */
3622          
3623         NSMutableArray *ChapterNamesArray = [queueToApply objectForKey:@"ChapterNames"];
3624         int i = 0;
3625         NSEnumerator *enumerator = [ChapterNamesArray objectEnumerator];
3626         id tempObject;
3627         while (tempObject = [enumerator nextObject])
3628         {
3629             hb_chapter_t *chapter = (hb_chapter_t *) hb_list_item( title->list_chapter, i );
3630             if( chapter != NULL )
3631             {
3632                 strncpy( chapter->title, [tempObject UTF8String], 1023);
3633                 chapter->title[1023] = '\0';
3634             }
3635             i++;
3636         }
3637     }
3638     else
3639     {
3640         job->chapter_markers = 0;
3641     }
3642     
3643     if( job->vcodec & HB_VCODEC_X264 )
3644     {
3645                 if ([[queueToApply objectForKey:@"Mp4iPodCompatible"] intValue] == 1)
3646             {
3647             job->ipod_atom = 1;
3648                 }
3649         else
3650         {
3651             job->ipod_atom = 0;
3652         }
3653                 
3654                 
3655                 /* Below Sends x264 options to the core library if x264 is selected*/
3656                 /* Lets use this as per Nyx, Thanks Nyx!*/
3657                 job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */
3658                 /* Turbo first pass if two pass and Turbo First pass is selected */
3659                 if( [[queueToApply objectForKey:@"VideoTwoPass"] intValue] == 1 && [[queueToApply objectForKey:@"VideoTurboTwoPass"] intValue] == 1 )
3660                 {
3661                         /* pass the "Turbo" string to be appended to the existing x264 opts string into a variable for the first pass */
3662                         NSString *firstPassOptStringTurbo = @":ref=1:subme=2:me=dia:analyse=none:trellis=0:no-fast-pskip=0:8x8dct=0:weightb=0";
3663                         /* append the "Turbo" string variable to the existing opts string.
3664              Note: the "Turbo" string must be appended, not prepended to work properly*/
3665                         NSString *firstPassOptStringCombined = [[queueToApply objectForKey:@"x264Option"] stringByAppendingString:firstPassOptStringTurbo];
3666                         strcpy(job->x264opts, [firstPassOptStringCombined UTF8String]);
3667                 }
3668                 else
3669                 {
3670                         strcpy(job->x264opts, [[queueToApply objectForKey:@"x264Option"] UTF8String]);
3671                 }
3672         
3673     }
3674     
3675     
3676     /* Picture Size Settings */
3677     job->width = [[queueToApply objectForKey:@"PictureWidth"]  intValue];
3678     job->height = [[queueToApply objectForKey:@"PictureHeight"]  intValue];
3679     
3680     job->keep_ratio = [[queueToApply objectForKey:@"PictureKeepRatio"]  intValue];
3681     job->anamorphic.mode = [[queueToApply objectForKey:@"PicturePAR"]  intValue];
3682     job->modulus = [[queueToApply objectForKey:@"PictureModulus"] intValue];
3683     if ([[queueToApply objectForKey:@"PicturePAR"]  intValue] == 3)
3684     {
3685         /* insert our custom values here for capuj */
3686         job->width = [[queueToApply objectForKey:@"PicturePARStorageWidth"]  intValue];
3687         job->height = [[queueToApply objectForKey:@"PicturePARStorageHeight"]  intValue];
3688         
3689         job->modulus = [[queueToApply objectForKey:@"PicturePARModulus"] intValue];
3690         
3691         job->anamorphic.par_width = [[queueToApply objectForKey:@"PicturePARPixelWidth"]  intValue];
3692         job->anamorphic.par_height = [[queueToApply objectForKey:@"PicturePARPixelHeight"]  intValue];
3693         
3694         job->anamorphic.dar_width = [[queueToApply objectForKey:@"PicturePARDisplayWidth"]  floatValue];
3695         job->anamorphic.dar_height = [[queueToApply objectForKey:@"PicturePARDisplayHeight"]  floatValue];
3696     }
3697     
3698     /* Here we use the crop values saved at the time the preset was saved */
3699     job->crop[0] = [[queueToApply objectForKey:@"PictureTopCrop"]  intValue];
3700     job->crop[1] = [[queueToApply objectForKey:@"PictureBottomCrop"]  intValue];
3701     job->crop[2] = [[queueToApply objectForKey:@"PictureLeftCrop"]  intValue];
3702     job->crop[3] = [[queueToApply objectForKey:@"PictureRightCrop"]  intValue];
3703     
3704     /* Video settings */
3705     /* Framerate */
3706     
3707     /* Set vfr to 0 as it's only on if using same as source in the framerate popup
3708      * and detelecine is on, so we handle that in the logic below
3709      */
3710     job->vfr = 0;
3711     if( [[queueToApply objectForKey:@"JobIndexVideoFramerate"] intValue] > 0 )
3712     {
3713         /* a specific framerate has been chosen */
3714         job->vrate      = 27000000;
3715         job->vrate_base = hb_video_rates[[[queueToApply objectForKey:@"JobIndexVideoFramerate"] intValue]-1].rate;
3716         /* We are not same as source so we set job->cfr to 1 
3717          * to enable constant frame rate since user has specified
3718          * a specific framerate*/
3719         
3720         if ([[queueToApply objectForKey:@"VideoFrameratePFR"] intValue] == 1)
3721         {
3722             job->cfr = 2;
3723         }
3724         else
3725         {
3726             job->cfr = 1;
3727         }
3728     }
3729     else
3730     {
3731         /* We are same as source (variable) */
3732         job->vrate      = [[queueToApply objectForKey:@"JobVrate"] intValue];
3733         job->vrate_base = [[queueToApply objectForKey:@"JobVrateBase"] intValue];
3734         /* We are same as source so we set job->cfr to 0 
3735          * to enable true same as source framerate */
3736         job->cfr = 0;
3737         /* If we are same as source and we have detelecine on, we need to turn on
3738          * job->vfr
3739          */
3740         if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] == 1)
3741         {
3742             job->vfr = 1;
3743         }
3744     }
3745     
3746     if ( [[queueToApply objectForKey:@"VideoQualityType"] intValue] != 2 )
3747     {
3748         /* Target size.
3749          Bitrate should already have been calculated and displayed
3750          in fVidBitrateField, so let's just use it same as abr*/
3751         job->vquality = -1.0;
3752         job->vbitrate = [[queueToApply objectForKey:@"VideoAvgBitrate"] intValue];
3753     }
3754     if ( [[queueToApply objectForKey:@"VideoQualityType"] intValue] == 2 )
3755     {
3756         job->vquality = [[queueToApply objectForKey:@"VideoQualitySlider"] floatValue];
3757         job->vbitrate = 0;
3758         
3759     }
3760     
3761     job->grayscale = [[queueToApply objectForKey:@"VideoGrayScale"] intValue];
3762     
3763
3764
3765 #pragma mark -
3766 #pragma mark Process Subtitles to libhb
3767
3768 /* Map the settings in the dictionaries for the SubtitleList array to match title->list_subtitle
3769  * which means that we need to account for the offset of non source language settings in from
3770  * the NSPopUpCell menu. For all of the objects in the SubtitleList array this means 0 is "None"
3771  * from the popup menu, additionally the first track has "Foreign Audio Search" at 1. So we use
3772  * an int to offset the index number for the objectForKey:@"subtitleSourceTrackNum" to map that
3773  * to the source tracks position in title->list_subtitle.
3774  */
3775
3776 int subtitle = nil;
3777 int force;
3778 int burned;
3779 int def;
3780 bool one_burned = FALSE;
3781
3782     int i = 0;
3783     NSEnumerator *enumerator = [[queueToApply objectForKey:@"SubtitleList"] objectEnumerator];
3784     id tempObject;
3785     while (tempObject = [enumerator nextObject])
3786     {
3787         
3788         subtitle = [[tempObject objectForKey:@"subtitleSourceTrackNum"] intValue];
3789         force = [[tempObject objectForKey:@"subtitleTrackForced"] intValue];
3790         burned = [[tempObject objectForKey:@"subtitleTrackBurned"] intValue];
3791         def = [[tempObject objectForKey:@"subtitleTrackDefault"] intValue];
3792         
3793         /* since the subtitleSourceTrackNum 0 is "None" in our array of the subtitle popups,
3794          * we want to ignore it for display as well as encoding.
3795          */
3796         if (subtitle > 0)
3797         {
3798             /* if i is 0, then we are in the first item of the subtitles which we need to 
3799              * check for the "Foreign Audio Search" which would be subtitleSourceTrackNum of 1
3800              * bearing in mind that for all tracks subtitleSourceTrackNum of 0 is None.
3801              */
3802             
3803             /* if we are on the first track and using "Foreign Audio Search" */ 
3804             if (i == 0 && subtitle == 1)
3805             {
3806                 [self writeToActivityLog: "Foreign Language Search: %d", 1];
3807                 
3808                 job->indepth_scan = 1;
3809                 
3810                 if (burned != 1)
3811                 {
3812                     job->select_subtitle_config.dest = PASSTHRUSUB;
3813                 }
3814                 else
3815                 {
3816                     job->select_subtitle_config.dest = RENDERSUB;
3817                 }
3818                 
3819                 job->select_subtitle_config.force = force;
3820                 job->select_subtitle_config.default_track = def;
3821             }
3822             else
3823             {
3824                 
3825                 /* for the actual source tracks, we must subtract the non source entries so 
3826                  * that the menu index matches the source subtitle_list index for convenience */
3827                 if (i == 0)
3828                 {
3829                     /* for the first track, the source tracks start at menu index 2 ( None is 0,
3830                      * Foreign Language Search is 1) so subtract 2 */
3831                     subtitle = subtitle - 2;
3832                 }
3833                 else
3834                 {
3835                     /* for all other tracks, the source tracks start at menu index 1 (None is 0)
3836                      * so subtract 1. */
3837                     
3838                     subtitle = subtitle - 1;
3839                 }
3840                 
3841                 /* We are setting a source subtitle so access the source subtitle info */  
3842                 hb_subtitle_t * subt;
3843                 
3844                 subt = (hb_subtitle_t *)hb_list_item(title->list_subtitle, subtitle);
3845                 
3846                 /* if we are getting the subtitles from an external srt file */
3847                 if ([[tempObject objectForKey:@"subtitleSourceTrackType"] isEqualToString:@"SRT"])
3848                 {
3849                     hb_subtitle_config_t sub_config;
3850                     
3851                     sub_config.offset = [[tempObject objectForKey:@"subtitleTrackSrtOffset"] intValue];
3852                     
3853                     /* we need to srncpy file name and codeset */
3854                     strncpy(sub_config.src_filename, [[tempObject objectForKey:@"subtitleSourceSrtFilePath"] UTF8String], 128);
3855                     strncpy(sub_config.src_codeset, [[tempObject objectForKey:@"subtitleTrackSrtCharCode"] UTF8String], 40);
3856                     
3857                     sub_config.force = 0;
3858                     sub_config.dest = PASSTHRUSUB;
3859                     sub_config.default_track = def;
3860                     
3861                     hb_srt_add( job, &sub_config, [[tempObject objectForKey:@"subtitleTrackSrtLanguageIso3"] UTF8String]);
3862                 }
3863                 
3864                 
3865                 if (subt != NULL)
3866                 {
3867                     hb_subtitle_config_t sub_config = subt->config;
3868                     
3869                     if ( !burned && subt->format == PICTURESUB )
3870                     {
3871                         sub_config.dest = PASSTHRUSUB;
3872                     }
3873                     else if ( burned && subt->format == PICTURESUB )
3874                     {
3875                         // Only allow one subtitle to be burned into the video
3876                         if (one_burned)
3877                             continue;
3878                         one_burned = TRUE;
3879                     }
3880                     sub_config.force = force;
3881                     sub_config.default_track = def;
3882                     hb_subtitle_add( job, &sub_config, subtitle );
3883                 }   
3884                 
3885             }
3886         }
3887         i++;
3888     }
3889
3890 #pragma mark -
3891
3892    
3893     /* Audio tracks and mixdowns */
3894     /* Lets make sure there arent any erroneous audio tracks in the job list, so lets make sure its empty*/
3895     int audiotrack_count = hb_list_count(job->list_audio);
3896     for( int i = 0; i < audiotrack_count;i++)
3897     {
3898         hb_audio_t * temp_audio = (hb_audio_t*) hb_list_item( job->list_audio, 0 );
3899         hb_list_rem(job->list_audio, temp_audio);
3900     }
3901     /* Now lets add our new tracks to the audio list here */
3902     if ([[queueToApply objectForKey:@"Audio1Track"] intValue] > 0)
3903     {
3904         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3905         hb_audio_config_init(audio);
3906         audio->in.track = [[queueToApply objectForKey:@"Audio1Track"] intValue] - 1;
3907         /* We go ahead and assign values to our audio->out.<properties> */
3908         audio->out.track = [[queueToApply objectForKey:@"Audio1Track"] intValue] - 1;
3909         audio->out.codec = [[queueToApply objectForKey:@"JobAudio1Encoder"] intValue];
3910         audio->out.mixdown = [[queueToApply objectForKey:@"JobAudio1Mixdown"] intValue];
3911         audio->out.bitrate = [[queueToApply objectForKey:@"JobAudio1Bitrate"] intValue];
3912         audio->out.samplerate = [[queueToApply objectForKey:@"JobAudio1Samplerate"] intValue];
3913         audio->out.dynamic_range_compression = [[queueToApply objectForKey:@"Audio1TrackDRCSlider"] floatValue];
3914         
3915         hb_audio_add( job, audio );
3916         free(audio);
3917     }  
3918     if ([[queueToApply objectForKey:@"Audio2Track"] intValue] > 0)
3919     {
3920         
3921         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3922         hb_audio_config_init(audio);
3923         audio->in.track = [[queueToApply objectForKey:@"Audio2Track"] intValue] - 1;
3924         [self writeToActivityLog: "prepareJob audiotrack 2 is: %d", audio->in.track];
3925         /* We go ahead and assign values to our audio->out.<properties> */
3926         audio->out.track = [[queueToApply objectForKey:@"Audio2Track"] intValue] - 1;
3927         audio->out.codec = [[queueToApply objectForKey:@"JobAudio2Encoder"] intValue];
3928         audio->out.mixdown = [[queueToApply objectForKey:@"JobAudio2Mixdown"] intValue];
3929         audio->out.bitrate = [[queueToApply objectForKey:@"JobAudio2Bitrate"] intValue];
3930         audio->out.samplerate = [[queueToApply objectForKey:@"JobAudio2Samplerate"] intValue];
3931         audio->out.dynamic_range_compression = [[queueToApply objectForKey:@"Audio2TrackDRCSlider"] floatValue];
3932         
3933         hb_audio_add( job, audio );
3934         free(audio);
3935     }
3936     
3937     if ([[queueToApply objectForKey:@"Audio3Track"] intValue] > 0)
3938     {
3939         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3940         hb_audio_config_init(audio);
3941         audio->in.track = [[queueToApply objectForKey:@"Audio3Track"] intValue] - 1;
3942         /* We go ahead and assign values to our audio->out.<properties> */
3943         audio->out.track = [[queueToApply objectForKey:@"Audio3Track"] intValue] - 1;
3944         audio->out.codec = [[queueToApply objectForKey:@"JobAudio3Encoder"] intValue];
3945         audio->out.mixdown = [[queueToApply objectForKey:@"JobAudio3Mixdown"] intValue];
3946         audio->out.bitrate = [[queueToApply objectForKey:@"JobAudio3Bitrate"] intValue];
3947         audio->out.samplerate = [[queueToApply objectForKey:@"JobAudio3Samplerate"] intValue];
3948         audio->out.dynamic_range_compression = [[queueToApply objectForKey:@"Audio3TrackDRCSlider"] floatValue];
3949         
3950         hb_audio_add( job, audio );
3951         free(audio);        
3952     }
3953     
3954     if ([[queueToApply objectForKey:@"Audio4Track"] intValue] > 0)
3955     {
3956         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
3957         hb_audio_config_init(audio);
3958         audio->in.track = [[queueToApply objectForKey:@"Audio4Track"] intValue] - 1;
3959         /* We go ahead and assign values to our audio->out.<properties> */
3960         audio->out.track = [[queueToApply objectForKey:@"Audio4Track"] intValue] - 1;
3961         audio->out.codec = [[queueToApply objectForKey:@"JobAudio4Encoder"] intValue];
3962         audio->out.mixdown = [[queueToApply objectForKey:@"JobAudio4Mixdown"] intValue];
3963         audio->out.bitrate = [[queueToApply objectForKey:@"JobAudio4Bitrate"] intValue];
3964         audio->out.samplerate = [[queueToApply objectForKey:@"JobAudio4Samplerate"] intValue];
3965         audio->out.dynamic_range_compression = [[queueToApply objectForKey:@"Audio4TrackDRCSlider"] floatValue];
3966         
3967         hb_audio_add( job, audio );
3968         
3969
3970     }
3971     
3972     /* Filters */ 
3973     job->filters = hb_list_init();
3974     
3975     /* Now lets call the filters if applicable.
3976      * The order of the filters is critical
3977      */
3978     /* Detelecine */
3979     hb_filter_detelecine.settings = NULL;
3980     if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] == 1)
3981     {
3982         /* use a custom detelecine string */
3983         hb_filter_detelecine.settings = (char *) [[queueToApply objectForKey:@"PictureDetelecineCustom"] UTF8String];
3984         hb_list_add( job->filters, &hb_filter_detelecine );
3985     }
3986     if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] == 2)
3987     {
3988         /* Use libhb's default values */
3989         hb_list_add( job->filters, &hb_filter_detelecine );
3990     }
3991     
3992     if ([[queueToApply objectForKey:@"PictureDecombDeinterlace"] intValue] == 1)
3993     {
3994         /* Decomb */
3995         /* we add the custom string if present */
3996         hb_filter_decomb.settings = NULL;
3997         if ([[queueToApply objectForKey:@"PictureDecomb"] intValue] == 1)
3998         {
3999             /* use a custom decomb string */
4000             hb_filter_decomb.settings = (char *) [[queueToApply objectForKey:@"PictureDecombCustom"] UTF8String];
4001             hb_list_add( job->filters, &hb_filter_decomb );
4002         }
4003         if ([[queueToApply objectForKey:@"PictureDecomb"] intValue] == 2)
4004         {
4005             /* Use libhb default */
4006             hb_list_add( job->filters, &hb_filter_decomb );
4007         }
4008         
4009     }
4010     else
4011     {
4012         
4013         /* Deinterlace */
4014         if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 1)
4015         {
4016             /* we add the custom string if present */
4017             hb_filter_deinterlace.settings = (char *) [[queueToApply objectForKey:@"PictureDeinterlaceCustom"] UTF8String];
4018             hb_list_add( job->filters, &hb_filter_deinterlace );            
4019         }
4020         else if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 2)
4021         {
4022             /* Run old deinterlacer fd by default */
4023             hb_filter_deinterlace.settings = "-1"; 
4024             hb_list_add( job->filters, &hb_filter_deinterlace );
4025         }
4026         else if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 3)
4027         {
4028             /* Yadif mode 0 (without spatial deinterlacing.) */
4029             hb_filter_deinterlace.settings = "2"; 
4030             hb_list_add( job->filters, &hb_filter_deinterlace );            
4031         }
4032         else if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 4)
4033         {
4034             /* Yadif (with spatial deinterlacing) */
4035             hb_filter_deinterlace.settings = "0"; 
4036             hb_list_add( job->filters, &hb_filter_deinterlace );            
4037         }
4038         
4039         
4040     }
4041     /* Denoise */
4042         if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 1) // Custom in popup
4043         {
4044                 /* we add the custom string if present */
4045         hb_filter_denoise.settings = (char *) [[queueToApply objectForKey:@"PictureDenoiseCustom"] UTF8String];
4046         hb_list_add( job->filters, &hb_filter_denoise );        
4047         }
4048     else if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 2) // Weak in popup
4049         {
4050                 hb_filter_denoise.settings = "2:1:2:3"; 
4051         hb_list_add( job->filters, &hb_filter_denoise );        
4052         }
4053         else if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 3) // Medium in popup
4054         {
4055                 hb_filter_denoise.settings = "3:2:2:3"; 
4056         hb_list_add( job->filters, &hb_filter_denoise );        
4057         }
4058         else if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 4) // Strong in popup
4059         {
4060                 hb_filter_denoise.settings = "7:7:5:5"; 
4061         hb_list_add( job->filters, &hb_filter_denoise );        
4062         }
4063     
4064     
4065     /* Deblock  (uses pp7 default) */
4066     /* NOTE: even though there is a valid deblock setting of 0 for the filter, for 
4067      * the macgui's purposes a value of 0 actually means to not even use the filter
4068      * current hb_filter_deblock.settings valid ranges are from 5 - 15 
4069      */
4070     if ([[queueToApply objectForKey:@"PictureDeblock"] intValue] != 0)
4071     {
4072         hb_filter_deblock.settings = (char *) [[queueToApply objectForKey:@"PictureDeblock"] UTF8String];
4073         hb_list_add( job->filters, &hb_filter_deblock );
4074     }
4075 [self writeToActivityLog: "prepareJob exiting"];    
4076 }
4077
4078
4079
4080 /* addToQueue: puts up an alert before ultimately calling doAddToQueue
4081  */
4082 - (IBAction) addToQueue: (id) sender
4083 {
4084         /* We get the destination directory from the destination field here */
4085         NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
4086         /* We check for a valid destination here */
4087         if ([[NSFileManager defaultManager] fileExistsAtPath:destinationDirectory] == 0) 
4088         {
4089                 NSRunAlertPanel(@"Warning!", @"This is not a valid destination directory!", @"OK", nil, nil);
4090         return;
4091         }
4092     
4093     BOOL fileExists;
4094     fileExists = NO;
4095     
4096     BOOL fileExistsInQueue;
4097     fileExistsInQueue = NO;
4098     
4099     /* We check for and existing file here */
4100     if([[NSFileManager defaultManager] fileExistsAtPath: [fDstFile2Field stringValue]])
4101     {
4102         fileExists = YES;
4103     }
4104     
4105     /* We now run through the queue and make sure we are not overwriting an exisiting queue item */
4106     int i = 0;
4107     NSEnumerator *enumerator = [QueueFileArray objectEnumerator];
4108         id tempObject;
4109         while (tempObject = [enumerator nextObject])
4110         {
4111                 NSDictionary *thisQueueDict = tempObject;
4112                 if ([[thisQueueDict objectForKey:@"DestinationPath"] isEqualToString: [fDstFile2Field stringValue]])
4113                 {
4114                         fileExistsInQueue = YES;        
4115                 }
4116         i++;
4117         }
4118     
4119     
4120         if(fileExists == YES)
4121     {
4122         NSBeginCriticalAlertSheet( NSLocalizedString( @"File already exists.", @"" ),
4123                                   NSLocalizedString( @"Cancel", @"" ), NSLocalizedString( @"Overwrite", @"" ), nil, fWindow, self,
4124                                   @selector( overwriteAddToQueueAlertDone:returnCode:contextInfo: ),
4125                                   NULL, NULL, [NSString stringWithFormat:
4126                                                NSLocalizedString( @"Do you want to overwrite %@?", @"" ),
4127                                                [fDstFile2Field stringValue]] );
4128     }
4129     else if (fileExistsInQueue == YES)
4130     {
4131         NSBeginCriticalAlertSheet( NSLocalizedString( @"There is already a queue item for this destination.", @"" ),
4132                                   NSLocalizedString( @"Cancel", @"" ), NSLocalizedString( @"Overwrite", @"" ), nil, fWindow, self,
4133                                   @selector( overwriteAddToQueueAlertDone:returnCode:contextInfo: ),
4134                                   NULL, NULL, [NSString stringWithFormat:
4135                                                NSLocalizedString( @"Do you want to overwrite %@?", @"" ),
4136                                                [fDstFile2Field stringValue]] );
4137     }
4138     else
4139     {
4140         [self doAddToQueue];
4141     }
4142 }
4143
4144 /* overwriteAddToQueueAlertDone: called from the alert posted by addToQueue that asks
4145    the user if they want to overwrite an exiting movie file.
4146 */
4147 - (void) overwriteAddToQueueAlertDone: (NSWindow *) sheet
4148     returnCode: (int) returnCode contextInfo: (void *) contextInfo
4149 {
4150     if( returnCode == NSAlertAlternateReturn )
4151         [self doAddToQueue];
4152 }
4153
4154 - (void) doAddToQueue
4155 {
4156     [self addQueueFileItem ];
4157 }
4158
4159
4160
4161 /* Rip: puts up an alert before ultimately calling doRip
4162 */
4163 - (IBAction) Rip: (id) sender
4164 {
4165     [self writeToActivityLog: "Rip: Pending queue count is %d", fPendingCount];
4166     /* Rip or Cancel ? */
4167     hb_state_t s;
4168     hb_get_state2( fQueueEncodeLibhb, &s );
4169     
4170     if(s.state == HB_STATE_WORKING || s.state == HB_STATE_PAUSED)
4171         {
4172         [self Cancel: sender];
4173         return;
4174     }
4175     
4176     /* We check to see if we need to warn the user that the computer will go to sleep
4177                  or shut down when encoding is finished */
4178                 [self remindUserOfSleepOrShutdown];
4179     
4180     // If there are pending jobs in the queue, then this is a rip the queue
4181     if (fPendingCount > 0)
4182     {
4183         currentQueueEncodeIndex = [self getNextPendingQueueIndex];
4184         /* here lets start the queue with the first pending item */
4185         [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]]; 
4186         
4187         return;
4188     }
4189     
4190     // Before adding jobs to the queue, check for a valid destination.
4191     
4192     NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
4193     if ([[NSFileManager defaultManager] fileExistsAtPath:destinationDirectory] == 0) 
4194     {
4195         NSRunAlertPanel(@"Warning!", @"This is not a valid destination directory!", @"OK", nil, nil);
4196         return;
4197     }
4198     
4199     /* We check for duplicate name here */
4200     if( [[NSFileManager defaultManager] fileExistsAtPath:[fDstFile2Field stringValue]] )
4201     {
4202         NSBeginCriticalAlertSheet( NSLocalizedString( @"File already exists", @"" ),
4203                                   NSLocalizedString( @"Cancel", "" ), NSLocalizedString( @"Overwrite", @"" ), nil, fWindow, self,
4204                                   @selector( overWriteAlertDone:returnCode:contextInfo: ),
4205                                   NULL, NULL, [NSString stringWithFormat:
4206                                                NSLocalizedString( @"Do you want to overwrite %@?", @"" ),
4207                                                [fDstFile2Field stringValue]] );
4208         
4209         // overWriteAlertDone: will be called when the alert is dismissed. It will call doRip.
4210     }
4211     else
4212     {
4213         /* if there are no pending jobs in the queue, then add this one to the queue and rip
4214          otherwise, just rip the queue */
4215         if(fPendingCount == 0)
4216         {
4217             [self doAddToQueue];
4218         }
4219         
4220         /* go right to processing the new queue encode */
4221         currentQueueEncodeIndex = [self getNextPendingQueueIndex];
4222         [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]]; 
4223         
4224     }
4225 }
4226
4227 /* overWriteAlertDone: called from the alert posted by Rip: that asks the user if they
4228    want to overwrite an exiting movie file.
4229 */
4230 - (void) overWriteAlertDone: (NSWindow *) sheet
4231     returnCode: (int) returnCode contextInfo: (void *) contextInfo
4232 {
4233     if( returnCode == NSAlertAlternateReturn )
4234     {
4235         /* if there are no jobs in the queue, then add this one to the queue and rip 
4236         otherwise, just rip the queue */
4237         if( fPendingCount == 0 )
4238         {
4239             [self doAddToQueue];
4240         }
4241
4242         NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
4243         [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];
4244         [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]]; 
4245       
4246     }
4247 }
4248
4249 - (void) remindUserOfSleepOrShutdown
4250 {
4251        if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Put Computer To Sleep"])
4252        {
4253                /*Warn that computer will sleep after encoding*/
4254                int reminduser;
4255                NSBeep();
4256                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);
4257                [NSApp requestUserAttention:NSCriticalRequest];
4258                if ( reminduser == NSAlertAlternateReturn )
4259                {
4260                        [self showPreferencesWindow:nil];
4261                }
4262        }
4263        else if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Shut Down Computer"])
4264        {
4265                /*Warn that computer will shut down after encoding*/
4266                int reminduser;
4267                NSBeep();
4268                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);
4269                [NSApp requestUserAttention:NSCriticalRequest];
4270                if ( reminduser == NSAlertAlternateReturn )
4271                {
4272                        [self showPreferencesWindow:nil];
4273                }
4274        }
4275
4276 }
4277
4278
4279 - (void) doRip
4280 {
4281     /* Let libhb do the job */
4282     hb_start( fQueueEncodeLibhb );
4283     /*set the fEncodeState State */
4284         fEncodeState = 1;
4285 }
4286
4287
4288 //------------------------------------------------------------------------------------
4289 // Displays an alert asking user if the want to cancel encoding of current job.
4290 // Cancel: returns immediately after posting the alert. Later, when the user
4291 // acknowledges the alert, doCancelCurrentJob is called.
4292 //------------------------------------------------------------------------------------
4293 - (IBAction)Cancel: (id)sender
4294 {
4295     if (!fQueueController) return;
4296     
4297   hb_pause( fQueueEncodeLibhb );
4298     NSString * alertTitle = [NSString stringWithFormat:NSLocalizedString(@"You are currently encoding. What would you like to do ?", nil)];
4299    
4300     // Which window to attach the sheet to?
4301     NSWindow * docWindow;
4302     if ([sender respondsToSelector: @selector(window)])
4303         docWindow = [sender window];
4304     else
4305         docWindow = fWindow;
4306         
4307     NSBeginCriticalAlertSheet(
4308             alertTitle,
4309             NSLocalizedString(@"Continue Encoding", nil),
4310             NSLocalizedString(@"Cancel Current and Stop", nil),
4311             NSLocalizedString(@"Cancel Current and Continue", nil),
4312             docWindow, self,
4313             nil, @selector(didDimissCancel:returnCode:contextInfo:), nil,
4314             NSLocalizedString(@"Your encode will be cancelled if you don't continue encoding.", nil));
4315     
4316     // didDimissCancelCurrentJob:returnCode:contextInfo: will be called when the dialog is dismissed
4317 }
4318
4319 - (void) didDimissCancel: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo
4320 {
4321    hb_resume( fQueueEncodeLibhb );
4322      if (returnCode == NSAlertOtherReturn)
4323     {
4324         [self doCancelCurrentJob];  // <- this also stops libhb
4325     }
4326     if (returnCode == NSAlertAlternateReturn)
4327     {
4328     [self doCancelCurrentJobAndStop];
4329     }
4330 }
4331
4332 //------------------------------------------------------------------------------------
4333 // Cancels and deletes the current job and stops libhb from processing the remaining
4334 // encodes.
4335 //------------------------------------------------------------------------------------
4336 - (void) doCancelCurrentJob
4337 {
4338     // Stop the current job. hb_stop will only cancel the current pass and then set
4339     // its state to HB_STATE_WORKDONE. It also does this asynchronously. So when we
4340     // see the state has changed to HB_STATE_WORKDONE (in updateUI), we'll delete the
4341     // remaining passes of the job and then start the queue back up if there are any
4342     // remaining jobs.
4343      
4344     
4345     hb_stop( fQueueEncodeLibhb );
4346     
4347     // Delete all remaining jobs since libhb doesn't do this on its own.
4348             hb_job_t * job;
4349             while( ( job = hb_job(fQueueEncodeLibhb, 0) ) )
4350                 hb_rem( fQueueEncodeLibhb, job );
4351                 
4352     fEncodeState = 2;   // don't alert at end of processing since this was a cancel
4353     
4354     // now that we've stopped the currently encoding job, lets mark it as cancelled
4355     [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] setObject:[NSNumber numberWithInt:3] forKey:@"Status"];
4356     // and as always, save it in the queue .plist...
4357     /* We save all of the Queue data here */
4358     [self saveQueueFileItem];
4359     
4360     // ... and see if there are more items left in our queue
4361     int queueItems = [QueueFileArray count];
4362     /* If we still have more items in our queue, lets go to the next one */
4363     /* Check to see if there are any more pending items in the queue */
4364     int newQueueItemIndex = [self getNextPendingQueueIndex];
4365     /* If we still have more pending items in our queue, lets go to the next one */
4366     if (newQueueItemIndex >= 0 && newQueueItemIndex < queueItems)
4367     {
4368         /*Set our currentQueueEncodeIndex now to the newly found Pending encode as we own it */
4369         currentQueueEncodeIndex = newQueueItemIndex;
4370         /* now we mark the queue item as Status = 1 ( being encoded ) so another instance can not come along and try to scan it while we are scanning */
4371         [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] setObject:[NSNumber numberWithInt:1] forKey:@"Status"];
4372         [self writeToActivityLog: "incrementQueueItemDone new pending items found: %d", currentQueueEncodeIndex];
4373         [self saveQueueFileItem];
4374         /* now we can go ahead and scan the new pending queue item */
4375         [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]];
4376
4377     }
4378     else
4379     {
4380         [self writeToActivityLog: "incrementQueueItemDone there are no more pending encodes"];
4381     }
4382 }
4383
4384 - (void) doCancelCurrentJobAndStop
4385 {
4386     hb_stop( fQueueEncodeLibhb );
4387     
4388     // Delete all remaining jobs since libhb doesn't do this on its own.
4389             hb_job_t * job;
4390             while( ( job = hb_job(fQueueEncodeLibhb, 0) ) )
4391                 hb_rem( fQueueEncodeLibhb, job );
4392                 
4393                 
4394     fEncodeState = 2;   // don't alert at end of processing since this was a cancel
4395     
4396     // now that we've stopped the currently encoding job, lets mark it as cancelled
4397     [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] setObject:[NSNumber numberWithInt:3] forKey:@"Status"];
4398     // and as always, save it in the queue .plist...
4399     /* We save all of the Queue data here */
4400     [self saveQueueFileItem];
4401     // so now lets move to 
4402     currentQueueEncodeIndex++ ;
4403     [self writeToActivityLog: "cancelling current job and stopping the queue"];
4404 }
4405 - (IBAction) Pause: (id) sender
4406 {
4407     hb_state_t s;
4408     hb_get_state2( fQueueEncodeLibhb, &s );
4409
4410     if( s.state == HB_STATE_PAUSED )
4411     {
4412         hb_resume( fQueueEncodeLibhb );
4413     }
4414     else
4415     {
4416         hb_pause( fQueueEncodeLibhb );
4417     }
4418 }
4419
4420 #pragma mark -
4421 #pragma mark GUI Controls Changed Methods
4422
4423 - (IBAction) titlePopUpChanged: (id) sender
4424 {
4425     hb_list_t  * list  = hb_get_titles( fHandle );
4426     hb_title_t * title = (hb_title_t*)
4427         hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
4428
4429     /* If we are a stream type and a batch scan, grok the output file name from title->name upon title change */
4430     if (title->type == HB_STREAM_TYPE && hb_list_count( list ) > 1 )
4431     {
4432         /* we set the default name according to the new title->name */
4433         [fDstFile2Field setStringValue: [NSString stringWithFormat:
4434                                          @"%@/%@.%@", [[fDstFile2Field stringValue] stringByDeletingLastPathComponent],
4435                                          [NSString stringWithUTF8String: title->name],
4436                                          [[fDstFile2Field stringValue] pathExtension]]];
4437         
4438         /* Change the source to read out the parent folder also */
4439         [fSrcDVD2Field setStringValue:[NSString stringWithFormat:@"%@/%@", browsedSourceDisplayName,[NSString stringWithUTF8String: title->name]]];
4440     }
4441     
4442     /* For point a to point b pts encoding, set the start and end fields to 0 and the title duration in seconds respectively */
4443     int duration = (title->hours * 3600) + (title->minutes * 60) + (title->seconds);
4444     [fSrcTimeStartEncodingField setStringValue: [NSString stringWithFormat: @"%d", 0]];
4445     [fSrcTimeEndEncodingField setStringValue: [NSString stringWithFormat: @"%d", duration]];
4446     /* For point a to point b frame encoding, set the start and end fields to 0 and the title duration * announced fps in seconds respectively */
4447     [fSrcFrameStartEncodingField setStringValue: [NSString stringWithFormat: @"%d", 1]];
4448     //[fSrcFrameEndEncodingField setStringValue: [NSString stringWithFormat: @"%d", ((title->hours * 3600) + (title->minutes * 60) + (title->seconds)) * 24]];
4449     [fSrcFrameEndEncodingField setStringValue: [NSString stringWithFormat: @"%d", duration * (title->rate / title->rate_base)]];    
4450     
4451     /* If Auto Naming is on. We create an output filename of dvd name - title number */
4452     if( [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultAutoNaming"] > 0 && ( hb_list_count( list ) > 1 ) )
4453         {
4454                 [fDstFile2Field setStringValue: [NSString stringWithFormat:
4455                         @"%@/%@-%d.%@", [[fDstFile2Field stringValue] stringByDeletingLastPathComponent],
4456                         [browsedSourceDisplayName stringByDeletingPathExtension],
4457             title->index,
4458                         [[fDstFile2Field stringValue] pathExtension]]]; 
4459         }
4460     /* Update encode start / stop variables */
4461      
4462     
4463     
4464     /* Update chapter popups */
4465     [fSrcChapterStartPopUp removeAllItems];
4466     [fSrcChapterEndPopUp   removeAllItems];
4467     for( int i = 0; i < hb_list_count( title->list_chapter ); i++ )
4468     {
4469         [fSrcChapterStartPopUp addItemWithTitle: [NSString
4470             stringWithFormat: @"%d", i + 1]];
4471         [fSrcChapterEndPopUp addItemWithTitle: [NSString
4472             stringWithFormat: @"%d", i + 1]];
4473     }
4474
4475     [fSrcChapterStartPopUp selectItemAtIndex: 0];
4476     [fSrcChapterEndPopUp   selectItemAtIndex:
4477         hb_list_count( title->list_chapter ) - 1];
4478     [self chapterPopUpChanged:nil];
4479     
4480     /* if using dvd nav, show the angle widget */
4481     if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"UseDvdNav"] boolValue])
4482     {
4483         [fSrcAngleLabel setHidden:NO];
4484         [fSrcAnglePopUp setHidden:NO];
4485         
4486         [fSrcAnglePopUp removeAllItems];
4487         for( int i = 0; i < title->angle_count; i++ )
4488         {
4489             [fSrcAnglePopUp addItemWithTitle: [NSString stringWithFormat: @"%d", i + 1]];
4490         }
4491         [fSrcAnglePopUp selectItemAtIndex: 0];
4492     }
4493     else
4494     {
4495         [fSrcAngleLabel setHidden:YES];
4496         [fSrcAnglePopUp setHidden:YES];
4497     }
4498     
4499     /* Start Get and set the initial pic size for display */
4500         hb_job_t * job = title->job;
4501         fTitle = title;
4502     
4503     /* Set Auto Crop to on upon selecting a new title  */
4504     [fPictureController setAutoCrop:YES];
4505     
4506         /* We get the originial output picture width and height and put them
4507         in variables for use with some presets later on */
4508         PicOrigOutputWidth = job->width;
4509         PicOrigOutputHeight = job->height;
4510         AutoCropTop = job->crop[0];
4511         AutoCropBottom = job->crop[1];
4512         AutoCropLeft = job->crop[2];
4513         AutoCropRight = job->crop[3];
4514
4515         /* Reset the new title in fPictureController &&  fPreviewController*/
4516     [fPictureController SetTitle:title];
4517
4518         
4519     /* Update Subtitle Table */
4520     [fSubtitlesDelegate resetWithTitle:title];
4521     [fSubtitlesTable reloadData];
4522     
4523
4524     /* Update chapter table */
4525     [fChapterTitlesDelegate resetWithTitle:title];
4526     [fChapterTable reloadData];
4527
4528    /* Lets make sure there arent any erroneous audio tracks in the job list, so lets make sure its empty*/
4529     int audiotrack_count = hb_list_count(job->list_audio);
4530     for( int i = 0; i < audiotrack_count;i++)
4531     {
4532         hb_audio_t * temp_audio = (hb_audio_t*) hb_list_item( job->list_audio, 0 );
4533         hb_list_rem(job->list_audio, temp_audio);
4534     }
4535
4536     /* Update audio popups */
4537     [self addAllAudioTracksToPopUp: fAudLang1PopUp];
4538     [self addAllAudioTracksToPopUp: fAudLang2PopUp];
4539     [self addAllAudioTracksToPopUp: fAudLang3PopUp];
4540     [self addAllAudioTracksToPopUp: fAudLang4PopUp];
4541     /* search for the first instance of our prefs default language for track 1, and set track 2 to "none" */
4542         NSString * audioSearchPrefix = [[NSUserDefaults standardUserDefaults] stringForKey:@"DefaultLanguage"];
4543         [self selectAudioTrackInPopUp: fAudLang1PopUp searchPrefixString: audioSearchPrefix selectIndexIfNotFound: 1];
4544     [self selectAudioTrackInPopUp:fAudLang2PopUp searchPrefixString:nil selectIndexIfNotFound:0];
4545     [self selectAudioTrackInPopUp:fAudLang3PopUp searchPrefixString:nil selectIndexIfNotFound:0];
4546     [self selectAudioTrackInPopUp:fAudLang4PopUp searchPrefixString:nil selectIndexIfNotFound:0];
4547
4548         /* changing the title may have changed the audio channels on offer, */
4549         /* so call audioTrackPopUpChanged for both audio tracks to update the mixdown popups */
4550         [self audioTrackPopUpChanged: fAudLang1PopUp];
4551         [self audioTrackPopUpChanged: fAudLang2PopUp];
4552     [self audioTrackPopUpChanged: fAudLang3PopUp];
4553     [self audioTrackPopUpChanged: fAudLang4PopUp];
4554
4555     [fVidRatePopUp selectItemAtIndex: 0];
4556
4557     /* we run the picture size values through calculatePictureSizing to get all picture setting information*/
4558         [self calculatePictureSizing:nil];
4559
4560    /* lets call tableViewSelected to make sure that any preset we have selected is enforced after a title change */
4561     [self selectPreset:nil];
4562 }
4563
4564 - (IBAction) encodeStartStopPopUpChanged: (id) sender;
4565 {
4566     if( [fEncodeStartStopPopUp isEnabled] )
4567     {
4568         /* We are chapters */
4569         if( [fEncodeStartStopPopUp indexOfSelectedItem] == 0 )
4570         {
4571             [fSrcChapterStartPopUp  setHidden: NO];
4572             [fSrcChapterEndPopUp  setHidden: NO];
4573             
4574             [fSrcTimeStartEncodingField  setHidden: YES];
4575             [fSrcTimeEndEncodingField  setHidden: YES];
4576             
4577             [fSrcFrameStartEncodingField  setHidden: YES];
4578             [fSrcFrameEndEncodingField  setHidden: YES];
4579             
4580                [self chapterPopUpChanged:nil];   
4581         }
4582         /* We are time based (seconds) */
4583         else if ([fEncodeStartStopPopUp indexOfSelectedItem] == 1)
4584         {
4585             [fSrcChapterStartPopUp  setHidden: YES];
4586             [fSrcChapterEndPopUp  setHidden: YES];
4587             
4588             [fSrcTimeStartEncodingField  setHidden: NO];
4589             [fSrcTimeEndEncodingField  setHidden: NO];
4590             
4591             [fSrcFrameStartEncodingField  setHidden: YES];
4592             [fSrcFrameEndEncodingField  setHidden: YES];
4593             
4594             [self startEndSecValueChanged:nil];
4595         }
4596         /* We are frame based */
4597         else if ([fEncodeStartStopPopUp indexOfSelectedItem] == 2)
4598         {
4599             [fSrcChapterStartPopUp  setHidden: YES];
4600             [fSrcChapterEndPopUp  setHidden: YES];
4601             
4602             [fSrcTimeStartEncodingField  setHidden: YES];
4603             [fSrcTimeEndEncodingField  setHidden: YES];
4604             
4605             [fSrcFrameStartEncodingField  setHidden: NO];
4606             [fSrcFrameEndEncodingField  setHidden: NO];
4607             
4608             [self startEndFrameValueChanged:nil];
4609         }
4610     }
4611 }
4612
4613 - (IBAction) chapterPopUpChanged: (id) sender
4614 {
4615
4616         /* If start chapter popup is greater than end chapter popup,
4617         we set the end chapter popup to the same as start chapter popup */
4618         if ([fSrcChapterStartPopUp indexOfSelectedItem] > [fSrcChapterEndPopUp indexOfSelectedItem])
4619         {
4620                 [fSrcChapterEndPopUp selectItemAtIndex: [fSrcChapterStartPopUp indexOfSelectedItem]];
4621     }
4622
4623                 
4624         hb_list_t  * list  = hb_get_titles( fHandle );
4625     hb_title_t * title = (hb_title_t *)
4626         hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
4627
4628     hb_chapter_t * chapter;
4629     int64_t        duration = 0;
4630     for( int i = [fSrcChapterStartPopUp indexOfSelectedItem];
4631          i <= [fSrcChapterEndPopUp indexOfSelectedItem]; i++ )
4632     {
4633         chapter = (hb_chapter_t *) hb_list_item( title->list_chapter, i );
4634         duration += chapter->duration;
4635     }
4636     
4637     duration /= 90000; /* pts -> seconds */
4638     [fSrcDuration2Field setStringValue: [NSString stringWithFormat:
4639         @"%02lld:%02lld:%02lld", duration / 3600, ( duration / 60 ) % 60,
4640         duration % 60]];
4641     
4642     [self calculateBitrate: sender];
4643     
4644     if ( [fSrcChapterStartPopUp indexOfSelectedItem] ==  [fSrcChapterEndPopUp indexOfSelectedItem] )
4645     {
4646     /* Disable chapter markers for any source with less than two chapters as it makes no sense. */
4647     [fCreateChapterMarkers setEnabled: NO];
4648     [fCreateChapterMarkers setState: NSOffState];
4649     }
4650     else
4651     {
4652     [fCreateChapterMarkers setEnabled: YES];
4653     }
4654 }
4655
4656 - (IBAction) startEndSecValueChanged: (id) sender
4657 {
4658
4659         int duration = [fSrcTimeEndEncodingField intValue] - [fSrcTimeStartEncodingField intValue];
4660     [fSrcDuration2Field setStringValue: [NSString stringWithFormat:
4661         @"%02d:%02d:%02d", duration / 3600, ( duration / 60 ) % 60,
4662         duration % 60]];
4663     
4664     //[self calculateBitrate: sender];
4665     
4666 }
4667
4668 - (IBAction) startEndFrameValueChanged: (id) sender
4669 {
4670     hb_list_t  * list  = hb_get_titles( fHandle );
4671     hb_title_t * title = (hb_title_t*)
4672     hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
4673     
4674     int duration = ([fSrcFrameEndEncodingField intValue] - [fSrcFrameStartEncodingField intValue]) / (title->rate / title->rate_base);
4675     [fSrcDuration2Field setStringValue: [NSString stringWithFormat:
4676                                          @"%02d:%02d:%02d", duration / 3600, ( duration / 60 ) % 60,
4677                                          duration % 60]];
4678     
4679     //[self calculateBitrate: sender];
4680 }
4681
4682
4683 - (IBAction) formatPopUpChanged: (id) sender
4684 {
4685     NSString * string = [fDstFile2Field stringValue];
4686     int format = [fDstFormatPopUp indexOfSelectedItem];
4687     char * ext = NULL;
4688         /* Initially set the large file (64 bit formatting) output checkbox to hidden */
4689     [fDstMp4LargeFileCheck setHidden: YES];
4690     [fDstMp4HttpOptFileCheck setHidden: YES];
4691     [fDstMp4iPodFileCheck setHidden: YES];
4692     
4693     /* Update the Video Codec PopUp */
4694     /* lets get the tag of the currently selected item first so we might reset it later */
4695     int selectedVidEncoderTag;
4696     selectedVidEncoderTag = [[fVidEncoderPopUp selectedItem] tag];
4697     
4698     /* Note: we now store the video encoder int values from common.c in the tags of each popup for easy retrieval later */
4699     [fVidEncoderPopUp removeAllItems];
4700     NSMenuItem *menuItem;
4701     /* These video encoders are available to all of our current muxers, so lets list them once here */
4702     menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"MPEG-4 (FFmpeg)" action: NULL keyEquivalent: @""];
4703     [menuItem setTag: HB_VCODEC_FFMPEG];
4704     
4705     switch( format )
4706     {
4707         case 0:
4708                         /*Get Default MP4 File Extension*/
4709                         if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultMpegName"] > 0)
4710                         {
4711                                 ext = "m4v";
4712                         }
4713                         else
4714                         {
4715                                 ext = "mp4";
4716                         }
4717             /* Add additional video encoders here */
4718             menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"H.264 (x264)" action: NULL keyEquivalent: @""];
4719             [menuItem setTag: HB_VCODEC_X264];
4720             /* We show the mp4 option checkboxes here since we are mp4 */
4721             [fCreateChapterMarkers setEnabled: YES];
4722                         [fDstMp4LargeFileCheck setHidden: NO];
4723                         [fDstMp4HttpOptFileCheck setHidden: NO];
4724             [fDstMp4iPodFileCheck setHidden: NO];
4725             break;
4726             
4727             case 1:
4728             ext = "mkv";
4729             /* Add additional video encoders here */
4730             menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"H.264 (x264)" action: NULL keyEquivalent: @""];
4731             [menuItem setTag: HB_VCODEC_X264];
4732             menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"VP3 (Theora)" action: NULL keyEquivalent: @""];
4733             [menuItem setTag: HB_VCODEC_THEORA];
4734             /* We enable the create chapters checkbox here */
4735                         [fCreateChapterMarkers setEnabled: YES];
4736                         break;
4737             
4738
4739     }
4740     /* tell fSubtitlesDelegate we have a new video container */
4741     
4742     [fSubtitlesDelegate containerChanged:[[fDstFormatPopUp selectedItem] tag]];
4743     [fSubtitlesTable reloadData];
4744     /* if we have a previously selected vid encoder tag, then try to select it */
4745     if (selectedVidEncoderTag)
4746     {
4747         [fVidEncoderPopUp selectItemWithTag: selectedVidEncoderTag];
4748     }
4749     else
4750     {
4751         [fVidEncoderPopUp selectItemAtIndex: 0];
4752     }
4753
4754     [self audioAddAudioTrackCodecs: fAudTrack1CodecPopUp];
4755     [self audioAddAudioTrackCodecs: fAudTrack2CodecPopUp];
4756     [self audioAddAudioTrackCodecs: fAudTrack3CodecPopUp];
4757     [self audioAddAudioTrackCodecs: fAudTrack4CodecPopUp];
4758
4759     if( format == 0 )
4760         [self autoSetM4vExtension: sender];
4761     else
4762         [fDstFile2Field setStringValue: [NSString stringWithFormat:@"%@.%s", [string stringByDeletingPathExtension], ext]];
4763
4764     if( SuccessfulScan )
4765     {
4766         /* Add/replace to the correct extension */
4767         [self audioTrackPopUpChanged: fAudLang1PopUp];
4768         [self audioTrackPopUpChanged: fAudLang2PopUp];
4769         [self audioTrackPopUpChanged: fAudLang3PopUp];
4770         [self audioTrackPopUpChanged: fAudLang4PopUp];
4771
4772         if( [fVidEncoderPopUp selectedItem] == nil )
4773         {
4774
4775             [fVidEncoderPopUp selectItemAtIndex:0];
4776             [self videoEncoderPopUpChanged:nil];
4777
4778             /* changing the format may mean that we can / can't offer mono or 6ch, */
4779             /* so call audioTrackPopUpChanged for both audio tracks to update the mixdown popups */
4780
4781             /* We call the method to properly enable/disable turbo 2 pass */
4782             [self twoPassCheckboxChanged: sender];
4783             /* We call method method to change UI to reflect whether a preset is used or not*/
4784         }
4785     }
4786         [self customSettingUsed: sender];
4787 }
4788
4789 - (IBAction) autoSetM4vExtension: (id) sender
4790 {
4791     if ( [fDstFormatPopUp indexOfSelectedItem] )
4792         return;
4793
4794     NSString * extension = @"mp4";
4795
4796     if( [[fAudTrack1CodecPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack2CodecPopUp selectedItem] tag] == HB_ACODEC_AC3 ||
4797                                                         [[fAudTrack3CodecPopUp selectedItem] tag] == HB_ACODEC_AC3 ||
4798                                                         [[fAudTrack4CodecPopUp selectedItem] tag] == HB_ACODEC_AC3 ||
4799                                                         [fCreateChapterMarkers state] == NSOnState ||
4800                                                         [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultMpegName"] > 0 )
4801     {
4802         extension = @"m4v";
4803     }
4804
4805     if( [extension isEqualTo: [[fDstFile2Field stringValue] pathExtension]] )
4806         return;
4807     else
4808         [fDstFile2Field setStringValue: [NSString stringWithFormat:@"%@.%@",
4809                                     [[fDstFile2Field stringValue] stringByDeletingPathExtension], extension]];
4810 }
4811
4812 /* Method to determine if we should change the UI
4813 To reflect whether or not a Preset is being used or if
4814 the user is using "Custom" settings by determining the sender*/
4815 - (IBAction) customSettingUsed: (id) sender
4816 {
4817         if ([sender stringValue])
4818         {
4819                 /* Deselect the currently selected Preset if there is one*/
4820                 [fPresetsOutlineView deselectRow:[fPresetsOutlineView selectedRow]];
4821                 /* Change UI to show "Custom" settings are being used */
4822                 [fPresetSelectedDisplay setStringValue: @"Custom"];
4823
4824                 curUserPresetChosenNum = nil;
4825         }
4826 [self calculateBitrate:nil];
4827 }
4828
4829
4830 #pragma mark -
4831 #pragma mark - Video
4832
4833 - (IBAction) videoEncoderPopUpChanged: (id) sender
4834 {
4835     hb_job_t * job = fTitle->job;
4836     int videoEncoder = [[fVidEncoderPopUp selectedItem] tag];
4837     
4838     [fAdvancedOptions setHidden:YES];
4839     /* If we are using x264 then show the x264 advanced panel*/
4840     if (videoEncoder == HB_VCODEC_X264)
4841     {
4842         [fAdvancedOptions setHidden:NO];
4843         [self autoSetM4vExtension: sender];
4844     }
4845
4846     if (videoEncoder == HB_VCODEC_FFMPEG)
4847     {
4848         /* We set the iPod atom checkbox to disabled and uncheck it as its only for x264 in the mp4
4849          container. Format is taken care of in formatPopUpChanged method by hiding and unchecking
4850          anything other than MP4.
4851          */ 
4852         [fDstMp4iPodFileCheck setEnabled: NO];
4853         [fDstMp4iPodFileCheck setState: NSOffState];
4854     }
4855     else
4856     {
4857         [fDstMp4iPodFileCheck setEnabled: YES];
4858     }
4859     [self setupQualitySlider];
4860         [self calculatePictureSizing: sender];
4861         [self twoPassCheckboxChanged: sender];
4862 }
4863
4864
4865 - (IBAction) twoPassCheckboxChanged: (id) sender
4866 {
4867         /* check to see if x264 is chosen */
4868         if([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_X264)
4869     {
4870                 if( [fVidTwoPassCheck state] == NSOnState)
4871                 {
4872                         [fVidTurboPassCheck setHidden: NO];
4873                 }
4874                 else
4875                 {
4876                         [fVidTurboPassCheck setHidden: YES];
4877                         [fVidTurboPassCheck setState: NSOffState];
4878                 }
4879                 /* Make sure Two Pass is checked if Turbo is checked */
4880                 if( [fVidTurboPassCheck state] == NSOnState)
4881                 {
4882                         [fVidTwoPassCheck setState: NSOnState];
4883                 }
4884         }
4885         else
4886         {
4887                 [fVidTurboPassCheck setHidden: YES];
4888                 [fVidTurboPassCheck setState: NSOffState];
4889         }
4890         
4891         /* We call method method to change UI to reflect whether a preset is used or not*/
4892         [self customSettingUsed: sender];
4893 }
4894
4895 - (IBAction ) videoFrameRateChanged: (id) sender
4896 {
4897     /* Hide and set the PFR Checkbox to OFF if we are set to Same as Source */
4898     if ([fVidRatePopUp indexOfSelectedItem] == 0)
4899     {
4900         [fFrameratePfrCheck setHidden:YES];
4901         [fFrameratePfrCheck setState:0];
4902     }
4903     else
4904     {
4905         [fFrameratePfrCheck setHidden:NO];
4906     }
4907     
4908     /* We call method method to calculatePictureSizing to error check detelecine*/
4909     [self calculatePictureSizing: sender];
4910
4911     /* We call method method to change UI to reflect whether a preset is used or not*/
4912         [self customSettingUsed: sender];
4913 }
4914 - (IBAction) videoMatrixChanged: (id) sender;
4915 {
4916     bool target, bitrate, quality;
4917
4918     target = bitrate = quality = false;
4919     if( [fVidQualityMatrix isEnabled] )
4920     {
4921         switch( [fVidQualityMatrix selectedRow] )
4922         {
4923             case 0:
4924                 target = true;
4925                 break;
4926             case 1:
4927                 bitrate = true;
4928                 break;
4929             case 2:
4930                 quality = true;
4931                 break;
4932         }
4933     }
4934     [fVidTargetSizeField  setEnabled: target];
4935     [fVidBitrateField     setEnabled: bitrate];
4936     [fVidQualitySlider    setEnabled: quality];
4937     [fVidQualityRFField   setEnabled: quality];
4938     [fVidQualityRFLabel    setEnabled: quality];
4939     [fVidTwoPassCheck     setEnabled: !quality &&
4940         [fVidQualityMatrix isEnabled]];
4941     if( quality )
4942     {
4943         [fVidTwoPassCheck setState: NSOffState];
4944                 [fVidTurboPassCheck setHidden: YES];
4945                 [fVidTurboPassCheck setState: NSOffState];
4946     }
4947
4948     [self qualitySliderChanged: sender];
4949     [self calculateBitrate: sender];
4950         [self customSettingUsed: sender];
4951 }
4952
4953 /* Use this method to setup the quality slider for cq/rf values depending on
4954  * the video encoder selected.
4955  */
4956 - (void) setupQualitySlider
4957 {
4958     /* Get the current slider maxValue to check for a change in slider scale later
4959      * so that we can choose a new similar value on the new slider scale */
4960     float previousMaxValue = [fVidQualitySlider maxValue];
4961     float previousPercentOfSliderScale = [fVidQualitySlider floatValue] / ([fVidQualitySlider maxValue] - [fVidQualitySlider minValue] + 1);
4962     NSString * qpRFLabelString = @"QP:";
4963     /* x264 0-51 */
4964     if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_X264)
4965     {
4966         [fVidQualitySlider setMinValue:0.0];
4967         [fVidQualitySlider setMaxValue:51.0];
4968         /* As x264 allows for qp/rf values that are fractional, we get the value from the preferences */
4969         int fractionalGranularity = 1 / [[NSUserDefaults standardUserDefaults] floatForKey:@"x264CqSliderFractional"];
4970         [fVidQualitySlider setNumberOfTickMarks:(([fVidQualitySlider maxValue] - [fVidQualitySlider minValue]) * fractionalGranularity) + 1];
4971         qpRFLabelString = @"RF:";
4972     }
4973     /* ffmpeg  1-31 */
4974     if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_FFMPEG )
4975     {
4976         [fVidQualitySlider setMinValue:1.0];
4977         [fVidQualitySlider setMaxValue:31.0];
4978         [fVidQualitySlider setNumberOfTickMarks:31];
4979     }
4980     /* Theora 0-63 */
4981     if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_THEORA)
4982     {
4983         [fVidQualitySlider setMinValue:0.0];
4984         [fVidQualitySlider setMaxValue:63.0];
4985         [fVidQualitySlider setNumberOfTickMarks:64];
4986     }
4987     [fVidQualityRFLabel setStringValue:qpRFLabelString];
4988     
4989     /* check to see if we have changed slider scales */
4990     if (previousMaxValue != [fVidQualitySlider maxValue])
4991     {
4992         /* if so, convert the old setting to the new scale as close as possible based on percentages */
4993         float rf =  ([fVidQualitySlider maxValue] - [fVidQualitySlider minValue] + 1) * previousPercentOfSliderScale;
4994         [fVidQualitySlider setFloatValue:rf];
4995     }
4996     
4997     [self qualitySliderChanged:nil];
4998 }
4999
5000 - (IBAction) qualitySliderChanged: (id) sender
5001 {
5002     /* Our constant quality slider is in a range based
5003      * on each encoders qp/rf values. The range depends
5004      * on the encoder. Also, the range is inverse of quality
5005      * for all of the encoders *except* for theora
5006      * (ie. as the "quality" goes up, the cq or rf value
5007      * actually goes down). Since the IB sliders always set
5008      * their max value at the right end of the slider, we
5009      * will calculate the inverse, so as the slider floatValue
5010      * goes up, we will show the inverse in the rf field
5011      * so, the floatValue at the right for x264 would be 51
5012      * and our rf field needs to show 0 and vice versa.
5013      */
5014     
5015     float sliderRfInverse = ([fVidQualitySlider maxValue] - [fVidQualitySlider floatValue]) + [fVidQualitySlider minValue];
5016     /* If the encoder is theora, use the float, otherwise use the inverse float*/
5017     //float sliderRfToPercent;
5018     if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_THEORA)
5019     {
5020         [fVidQualityRFField setStringValue: [NSString stringWithFormat: @"%.2f", [fVidQualitySlider floatValue]]];   
5021     }
5022     else
5023     {
5024         [fVidQualityRFField setStringValue: [NSString stringWithFormat: @"%.2f", sliderRfInverse]];
5025     }
5026     [self customSettingUsed: sender];
5027 }
5028
5029 - (void) controlTextDidChange: (NSNotification *) notification
5030 {
5031     [self calculateBitrate:nil];
5032 }
5033
5034 - (IBAction) calculateBitrate: (id) sender
5035 {
5036     if( !fHandle || [fVidQualityMatrix selectedRow] != 0 || !SuccessfulScan )
5037     {
5038         return;
5039     }
5040
5041     hb_list_t  * list  = hb_get_titles( fHandle );
5042     hb_title_t * title = (hb_title_t *) hb_list_item( list,
5043             [fSrcTitlePopUp indexOfSelectedItem] );
5044     hb_job_t * job = title->job;
5045     hb_audio_config_t * audio;
5046     /* For  hb_calc_bitrate in addition to the Target Size in MB out of the
5047      * Target Size Field, we also need the job info for the Muxer, the Chapters
5048      * as well as all of the audio track info.
5049      * This used to be accomplished by simply calling prepareJob here, however
5050      * since the resilient queue sets the queue array values instead of the job
5051      * values directly, we duplicate the old prepareJob code here for the variables
5052      * needed
5053      */
5054     job->chapter_start = [fSrcChapterStartPopUp indexOfSelectedItem] + 1;
5055     job->chapter_end = [fSrcChapterEndPopUp indexOfSelectedItem] + 1; 
5056     job->mux = [[fDstFormatPopUp selectedItem] tag];
5057     
5058     /* Audio goes here */
5059     int audiotrack_count = hb_list_count(job->list_audio);
5060     for( int i = 0; i < audiotrack_count;i++)
5061     {
5062         hb_audio_t * temp_audio = (hb_audio_t*) hb_list_item( job->list_audio, 0 );
5063         hb_list_rem(job->list_audio, temp_audio);
5064     }
5065     /* Now we need our audio info here for each track if applicable */
5066     if ([fAudLang1PopUp indexOfSelectedItem] > 0)
5067     {
5068         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
5069         hb_audio_config_init(audio);
5070         audio->in.track = [fAudLang1PopUp indexOfSelectedItem] - 1;
5071         /* We go ahead and assign values to our audio->out.<properties> */
5072         audio->out.track = [fAudLang1PopUp indexOfSelectedItem] - 1;
5073         audio->out.codec = [[fAudTrack1CodecPopUp selectedItem] tag];
5074         audio->out.mixdown = [[fAudTrack1MixPopUp selectedItem] tag];
5075         audio->out.bitrate = [[fAudTrack1BitratePopUp selectedItem] tag];
5076         audio->out.samplerate = [[fAudTrack1RatePopUp selectedItem] tag];
5077         audio->out.dynamic_range_compression = [fAudTrack1DrcField floatValue];
5078         
5079         hb_audio_add( job, audio );
5080         free(audio);
5081     }  
5082     if ([fAudLang2PopUp indexOfSelectedItem] > 0)
5083     {
5084         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
5085         hb_audio_config_init(audio);
5086         audio->in.track = [fAudLang2PopUp indexOfSelectedItem] - 1;
5087         /* We go ahead and assign values to our audio->out.<properties> */
5088         audio->out.track = [fAudLang2PopUp indexOfSelectedItem] - 1;
5089         audio->out.codec = [[fAudTrack2CodecPopUp selectedItem] tag];
5090         audio->out.mixdown = [[fAudTrack2MixPopUp selectedItem] tag];
5091         audio->out.bitrate = [[fAudTrack2BitratePopUp selectedItem] tag];
5092         audio->out.samplerate = [[fAudTrack2RatePopUp selectedItem] tag];
5093         audio->out.dynamic_range_compression = [fAudTrack2DrcField floatValue];
5094         
5095         hb_audio_add( job, audio );
5096         free(audio);
5097         
5098     }
5099     
5100     if ([fAudLang3PopUp indexOfSelectedItem] > 0)
5101     {
5102         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
5103         hb_audio_config_init(audio);
5104         audio->in.track = [fAudLang3PopUp indexOfSelectedItem] - 1;
5105         /* We go ahead and assign values to our audio->out.<properties> */
5106         audio->out.track = [fAudLang3PopUp indexOfSelectedItem] - 1;
5107         audio->out.codec = [[fAudTrack3CodecPopUp selectedItem] tag];
5108         audio->out.mixdown = [[fAudTrack3MixPopUp selectedItem] tag];
5109         audio->out.bitrate = [[fAudTrack3BitratePopUp selectedItem] tag];
5110         audio->out.samplerate = [[fAudTrack3RatePopUp selectedItem] tag];
5111         audio->out.dynamic_range_compression = [fAudTrack3DrcField floatValue];
5112         
5113         hb_audio_add( job, audio );
5114         free(audio);
5115         
5116     }
5117
5118     if ([fAudLang4PopUp indexOfSelectedItem] > 0)
5119     {
5120         audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
5121         hb_audio_config_init(audio);
5122         audio->in.track = [fAudLang4PopUp indexOfSelectedItem] - 1;
5123         /* We go ahead and assign values to our audio->out.<properties> */
5124         audio->out.track = [fAudLang4PopUp indexOfSelectedItem] - 1;
5125         audio->out.codec = [[fAudTrack4CodecPopUp selectedItem] tag];
5126         audio->out.mixdown = [[fAudTrack4MixPopUp selectedItem] tag];
5127         audio->out.bitrate = [[fAudTrack4BitratePopUp selectedItem] tag];
5128         audio->out.samplerate = [[fAudTrack4RatePopUp selectedItem] tag];
5129         audio->out.dynamic_range_compression = [fAudTrack4DrcField floatValue];
5130         
5131         hb_audio_add( job, audio );
5132         free(audio);
5133         
5134     }
5135        
5136 [fVidBitrateField setIntValue: hb_calc_bitrate( job, [fVidTargetSizeField intValue] )];
5137 }
5138
5139 #pragma mark -
5140 #pragma mark - Picture
5141
5142 /* lets set the picture size back to the max from right after title scan
5143    Lets use an IBAction here as down the road we could always use a checkbox
5144    in the gui to easily take the user back to max. Remember, the compiler
5145    resolves IBActions down to -(void) during compile anyway */
5146 - (IBAction) revertPictureSizeToMax: (id) sender
5147 {
5148         hb_job_t * job = fTitle->job;
5149         /* Here we apply the title source and height */
5150     job->width = fTitle->width;
5151     job->height = fTitle->height;
5152     
5153     [self calculatePictureSizing: sender];
5154     /* We call method to change UI to reflect whether a preset is used or not*/    
5155     [self customSettingUsed: sender];
5156 }
5157
5158 /**
5159  * Registers changes made in the Picture Settings Window.
5160  */
5161
5162 - (void)pictureSettingsDidChange 
5163 {
5164         [self calculatePictureSizing:nil];
5165 }
5166
5167 /* Get and Display Current Pic Settings in main window */
5168 - (IBAction) calculatePictureSizing: (id) sender
5169 {
5170         if (fTitle->job->anamorphic.mode > 0)
5171         {
5172         fTitle->job->keep_ratio = 0;
5173         }
5174     
5175     if (fTitle->job->anamorphic.mode != 1) // we are not strict so show the modulus
5176         {
5177         [fPictureSizeField setStringValue: [NSString stringWithFormat:@"Picture Size: %@, Modulus: %d", [fPictureController getPictureSizeInfoString], fTitle->job->modulus]];
5178     }
5179     else
5180     {
5181         [fPictureSizeField setStringValue: [NSString stringWithFormat:@"Picture Size: %@", [fPictureController getPictureSizeInfoString]]];
5182     }
5183     NSString *picCropping;
5184     /* Set the display field for crop as per boolean */
5185         if (![fPictureController autoCrop])
5186         {
5187         picCropping =  @"Custom";
5188         }
5189         else
5190         {
5191                 picCropping =  @"Auto";
5192         }
5193     picCropping = [picCropping stringByAppendingString:[NSString stringWithFormat:@" %d/%d/%d/%d",fTitle->job->crop[0],fTitle->job->crop[1],fTitle->job->crop[2],fTitle->job->crop[3]]];
5194     
5195     [fPictureCroppingField setStringValue: [NSString stringWithFormat:@"Picture Cropping: %@",picCropping]];
5196     
5197     NSString *videoFilters;
5198     videoFilters = @"";
5199     /* Detelecine */
5200     if ([fPictureController detelecine] == 2) 
5201     {
5202         videoFilters = [videoFilters stringByAppendingString:@" - Detelecine (Default)"];
5203     }
5204     else if ([fPictureController detelecine] == 1) 
5205     {
5206         videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Detelecine (%@)",[fPictureController detelecineCustomString]]];
5207     }
5208     
5209     
5210     if ([fPictureController useDecomb] == 1)
5211     {
5212         /* Decomb */
5213         if ([fPictureController decomb] == 2)
5214         {
5215             videoFilters = [videoFilters stringByAppendingString:@" - Decomb (Default)"];
5216         }
5217         else if ([fPictureController decomb] == 1)
5218         {
5219             videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Decomb (%@)",[fPictureController decombCustomString]]];
5220         }
5221     }
5222     else
5223     {
5224         /* Deinterlace */
5225         if ([fPictureController deinterlace] > 0)
5226         {
5227             fTitle->job->deinterlace  = 1;
5228         }
5229         else
5230         {
5231             fTitle->job->deinterlace  = 0;
5232         }
5233         
5234         if ([fPictureController deinterlace] == 2)
5235         {
5236             videoFilters = [videoFilters stringByAppendingString:@" - Deinterlace (Fast)"];
5237         }
5238         else if ([fPictureController deinterlace] == 3)
5239         {
5240             videoFilters = [videoFilters stringByAppendingString:@" - Deinterlace (Slow)"];
5241         }
5242         else if ([fPictureController deinterlace] == 4)
5243         {
5244             videoFilters = [videoFilters stringByAppendingString:@" - Deinterlace (Slower)"];
5245         }
5246         else if ([fPictureController deinterlace] == 1)
5247         {
5248             videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Deinterlace (%@)",[fPictureController deinterlaceCustomString]]];
5249         }
5250         }
5251     
5252     
5253     /* Denoise */
5254         if ([fPictureController denoise] == 2)
5255         {
5256                 videoFilters = [videoFilters stringByAppendingString:@" - Denoise (Weak)"];
5257     }
5258         else if ([fPictureController denoise] == 3)
5259         {
5260                 videoFilters = [videoFilters stringByAppendingString:@" - Denoise (Medium)"];
5261     }
5262         else if ([fPictureController denoise] == 4)
5263         {
5264                 videoFilters = [videoFilters stringByAppendingString:@" - Denoise (Strong)"];
5265         }
5266     else if ([fPictureController denoise] == 1)
5267         {
5268                 videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Denoise (%@)",[fPictureController denoiseCustomString]]];
5269         }
5270     
5271     /* Deblock */
5272     if ([fPictureController deblock] > 0) 
5273     {
5274         videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Deblock (%d)",[fPictureController deblock]]];
5275     }
5276         
5277     /* Grayscale */
5278     if ([fPictureController grayscale]) 
5279     {
5280         videoFilters = [videoFilters stringByAppendingString:@" - Grayscale"];
5281     }
5282     [fVideoFiltersField setStringValue: [NSString stringWithFormat:@"Video Filters: %@", videoFilters]];
5283     
5284     //[fPictureController reloadStillPreview]; 
5285 }
5286
5287
5288 #pragma mark -
5289 #pragma mark - Audio and Subtitles
5290 - (IBAction) audioCodecsPopUpChanged: (id) sender
5291 {
5292     
5293     NSPopUpButton * audiotrackPopUp;
5294     NSPopUpButton * sampleratePopUp;
5295     NSPopUpButton * bitratePopUp;
5296     NSPopUpButton * audiocodecPopUp;
5297     if (sender == fAudTrack1CodecPopUp)
5298     {
5299         audiotrackPopUp = fAudLang1PopUp;
5300         audiocodecPopUp = fAudTrack1CodecPopUp;
5301         sampleratePopUp = fAudTrack1RatePopUp;
5302         bitratePopUp = fAudTrack1BitratePopUp;
5303     }
5304     else if (sender == fAudTrack2CodecPopUp)
5305     {
5306         audiotrackPopUp = fAudLang2PopUp;
5307         audiocodecPopUp = fAudTrack2CodecPopUp;
5308         sampleratePopUp = fAudTrack2RatePopUp;
5309         bitratePopUp = fAudTrack2BitratePopUp;
5310     }
5311     else if (sender == fAudTrack3CodecPopUp)
5312     {
5313         audiotrackPopUp = fAudLang3PopUp;
5314         audiocodecPopUp = fAudTrack3CodecPopUp;
5315         sampleratePopUp = fAudTrack3RatePopUp;
5316         bitratePopUp = fAudTrack3BitratePopUp;
5317     }
5318     else
5319     {
5320         audiotrackPopUp = fAudLang4PopUp;
5321         audiocodecPopUp = fAudTrack4CodecPopUp;
5322         sampleratePopUp = fAudTrack4RatePopUp;
5323         bitratePopUp = fAudTrack4BitratePopUp;
5324     }
5325         
5326     /* changing the codecs on offer may mean that we can / can't offer mono or 6ch, */
5327         /* so call audioTrackPopUpChanged for both audio tracks to update the mixdown popups */
5328     [self audioTrackPopUpChanged: audiotrackPopUp];
5329     
5330 }
5331
5332 - (IBAction) setEnabledStateOfAudioMixdownControls: (id) sender
5333 {
5334     /* We will be setting the enabled/disabled state of each tracks audio controls based on
5335      * the settings of the source audio for that track. We leave the samplerate and bitrate
5336      * to audiotrackMixdownChanged
5337      */
5338     
5339     /* We will first verify that a lower track number has been selected before enabling each track
5340      * for example, make sure a track is selected for track 1 before enabling track 2, etc.
5341      */
5342     
5343     /* If the source has no audio then disable audio track 1 */
5344     if (hb_list_count( fTitle->list_audio ) == 0)
5345     {
5346         [fAudLang1PopUp selectItemAtIndex:0];
5347     }
5348      
5349     if ([fAudLang1PopUp indexOfSelectedItem] == 0)
5350     {
5351         [fAudLang2PopUp setEnabled: NO];
5352         [fAudLang2PopUp selectItemAtIndex: 0];
5353     }
5354     else
5355     {
5356         [fAudLang2PopUp setEnabled: YES];
5357     }
5358     
5359     if ([fAudLang2PopUp indexOfSelectedItem] == 0)
5360     {
5361         [fAudLang3PopUp setEnabled: NO];
5362         [fAudLang3PopUp selectItemAtIndex: 0];
5363     }
5364     else
5365     {
5366         [fAudLang3PopUp setEnabled: YES];
5367     }
5368     if ([fAudLang3PopUp indexOfSelectedItem] == 0)
5369     {
5370         [fAudLang4PopUp setEnabled: NO];
5371         [fAudLang4PopUp selectItemAtIndex: 0];
5372     }
5373     else
5374     {
5375         [fAudLang4PopUp setEnabled: YES];
5376     }
5377     /* enable/disable the mixdown text and popupbutton for audio track 1 */
5378     [fAudTrack1CodecPopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
5379     [fAudTrack1MixPopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
5380     [fAudTrack1RatePopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
5381     [fAudTrack1BitratePopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
5382     [fAudTrack1DrcSlider setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
5383     [fAudTrack1DrcField setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
5384     if ([fAudLang1PopUp indexOfSelectedItem] == 0)
5385     {
5386         [fAudTrack1CodecPopUp removeAllItems];
5387         [fAudTrack1MixPopUp removeAllItems];
5388         [fAudTrack1RatePopUp removeAllItems];
5389         [fAudTrack1BitratePopUp removeAllItems];
5390         [fAudTrack1DrcSlider setFloatValue: 0.00];
5391         [self audioDRCSliderChanged: fAudTrack1DrcSlider];
5392     }
5393     else if ([[fAudTrack1MixPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack1MixPopUp selectedItem] tag] == HB_ACODEC_DCA)
5394     {
5395         [fAudTrack1RatePopUp setEnabled: NO];
5396         [fAudTrack1BitratePopUp setEnabled: NO];
5397         [fAudTrack1DrcSlider setEnabled: NO];
5398         [fAudTrack1DrcField setEnabled: NO];
5399     }
5400     
5401     /* enable/disable the mixdown text and popupbutton for audio track 2 */
5402     [fAudTrack2CodecPopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
5403     [fAudTrack2MixPopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
5404     [fAudTrack2RatePopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
5405     [fAudTrack2BitratePopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
5406     [fAudTrack2DrcSlider setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
5407     [fAudTrack2DrcField setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
5408     if ([fAudLang2PopUp indexOfSelectedItem] == 0)
5409     {
5410         [fAudTrack2CodecPopUp removeAllItems];
5411         [fAudTrack2MixPopUp removeAllItems];
5412         [fAudTrack2RatePopUp removeAllItems];
5413         [fAudTrack2BitratePopUp removeAllItems];
5414         [fAudTrack2DrcSlider setFloatValue: 0.00];
5415         [self audioDRCSliderChanged: fAudTrack2DrcSlider];
5416     }
5417     else if ([[fAudTrack2MixPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack2MixPopUp selectedItem] tag] == HB_ACODEC_DCA)
5418     {
5419         [fAudTrack2RatePopUp setEnabled: NO];
5420         [fAudTrack2BitratePopUp setEnabled: NO];
5421         [fAudTrack2DrcSlider setEnabled: NO];
5422         [fAudTrack2DrcField setEnabled: NO];
5423     }
5424     
5425     /* enable/disable the mixdown text and popupbutton for audio track 3 */
5426     [fAudTrack3CodecPopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
5427     [fAudTrack3MixPopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
5428     [fAudTrack3RatePopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
5429     [fAudTrack3BitratePopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
5430     [fAudTrack3DrcSlider setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
5431     [fAudTrack3DrcField setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
5432     if ([fAudLang3PopUp indexOfSelectedItem] == 0)
5433     {
5434         [fAudTrack3CodecPopUp removeAllItems];
5435         [fAudTrack3MixPopUp removeAllItems];
5436         [fAudTrack3RatePopUp removeAllItems];
5437         [fAudTrack3BitratePopUp removeAllItems];
5438         [fAudTrack3DrcSlider setFloatValue: 0.00];
5439         [self audioDRCSliderChanged: fAudTrack3DrcSlider];
5440     }
5441     else if ([[fAudTrack3MixPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack3MixPopUp selectedItem] tag] == HB_ACODEC_DCA)
5442     {
5443         [fAudTrack3RatePopUp setEnabled: NO];
5444         [fAudTrack3BitratePopUp setEnabled: NO];
5445         [fAudTrack3DrcSlider setEnabled: NO];
5446         [fAudTrack3DrcField setEnabled: NO];
5447     }
5448     
5449     /* enable/disable the mixdown text and popupbutton for audio track 4 */
5450     [fAudTrack4CodecPopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
5451     [fAudTrack4MixPopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
5452     [fAudTrack4RatePopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
5453     [fAudTrack4BitratePopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
5454     [fAudTrack4DrcSlider setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
5455     [fAudTrack4DrcField setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
5456     if ([fAudLang4PopUp indexOfSelectedItem] == 0)
5457     {
5458         [fAudTrack4CodecPopUp removeAllItems];
5459         [fAudTrack4MixPopUp removeAllItems];
5460         [fAudTrack4RatePopUp removeAllItems];
5461         [fAudTrack4BitratePopUp removeAllItems];
5462         [fAudTrack4DrcSlider setFloatValue: 0.00];
5463         [self audioDRCSliderChanged: fAudTrack4DrcSlider];
5464     }
5465     else if ([[fAudTrack4MixPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack4MixPopUp selectedItem] tag] == HB_ACODEC_DCA)
5466     {
5467         [fAudTrack4RatePopUp setEnabled: NO];
5468         [fAudTrack4BitratePopUp setEnabled: NO];
5469         [fAudTrack4DrcSlider setEnabled: NO];
5470         [fAudTrack4DrcField setEnabled: NO];
5471     }
5472     
5473 }
5474
5475 - (IBAction) addAllAudioTracksToPopUp: (id) sender
5476 {
5477
5478     hb_list_t  * list  = hb_get_titles( fHandle );
5479     hb_title_t * title = (hb_title_t*)
5480         hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
5481
5482         hb_audio_config_t * audio;
5483
5484     [sender removeAllItems];
5485     [sender addItemWithTitle: NSLocalizedString( @"None", @"" )];
5486     for( int i = 0; i < hb_list_count( title->list_audio ); i++ )
5487     {
5488         audio = (hb_audio_config_t *) hb_list_audio_config_item( title->list_audio, i );
5489         [[sender menu] addItemWithTitle:
5490             [NSString stringWithUTF8String: audio->lang.description]
5491             action: NULL keyEquivalent: @""];
5492     }
5493     [sender selectItemAtIndex: 0];
5494
5495 }
5496
5497 - (IBAction) selectAudioTrackInPopUp: (id) sender searchPrefixString: (NSString *) searchPrefixString selectIndexIfNotFound: (int) selectIndexIfNotFound
5498 {
5499
5500     /* this method can be used to find a language, or a language-and-source-format combination, by passing in the appropriate string */
5501     /* e.g. to find the first French track, pass in an NSString * of "Francais" */
5502     /* e.g. to find the first English 5.1 AC3 track, pass in an NSString * of "English (AC3) (5.1 ch)" */
5503     /* if no matching track is found, then selectIndexIfNotFound is used to choose which track to select instead */
5504     if (hb_list_count( fTitle->list_audio ) != 0)
5505     {
5506         if (searchPrefixString)
5507         {
5508             
5509             for( int i = 0; i < [sender numberOfItems]; i++ )
5510             {
5511                 /* Try to find the desired search string */
5512                 if ([[[sender itemAtIndex: i] title] hasPrefix:searchPrefixString])
5513                 {
5514                     [sender selectItemAtIndex: i];
5515                     return;
5516                 }
5517             }
5518             /* couldn't find the string, so select the requested "search string not found" item */
5519             /* index of 0 means select the "none" item */
5520             /* index of 1 means select the first audio track */
5521             [sender selectItemAtIndex: selectIndexIfNotFound];
5522         }
5523         else
5524         {
5525             /* if no search string is provided, then select the selectIndexIfNotFound item */
5526             [sender selectItemAtIndex: selectIndexIfNotFound];
5527         }
5528     }
5529     else
5530     {
5531         [sender selectItemAtIndex: 0];
5532     }
5533
5534 }
5535 - (IBAction) audioAddAudioTrackCodecs: (id)sender
5536 {
5537     int format = [fDstFormatPopUp indexOfSelectedItem];
5538     
5539     /* setup pointers to the appropriate popups for the correct track */
5540     NSPopUpButton * audiocodecPopUp;
5541     NSPopUpButton * audiotrackPopUp;
5542     if (sender == fAudTrack1CodecPopUp)
5543     {
5544         audiotrackPopUp = fAudLang1PopUp;
5545         audiocodecPopUp = fAudTrack1CodecPopUp;
5546     }
5547     else if (sender == fAudTrack2CodecPopUp)
5548     {
5549         audiotrackPopUp = fAudLang2PopUp;
5550         audiocodecPopUp = fAudTrack2CodecPopUp;
5551     }
5552     else if (sender == fAudTrack3CodecPopUp)
5553     {
5554         audiotrackPopUp = fAudLang3PopUp;
5555         audiocodecPopUp = fAudTrack3CodecPopUp;
5556     }
5557     else
5558     {
5559         audiotrackPopUp = fAudLang4PopUp;
5560         audiocodecPopUp = fAudTrack4CodecPopUp;
5561     }
5562     
5563     [audiocodecPopUp removeAllItems];
5564     /* Make sure "None" isnt selected in the source track */
5565     if ([audiotrackPopUp indexOfSelectedItem] > 0)
5566     {
5567         [audiocodecPopUp setEnabled:YES];
5568         NSMenuItem *menuItem;
5569         /* We setup our appropriate popups for codecs and put the int value in the popup tag for easy retrieval */
5570         switch( format )
5571         {
5572             case 0:
5573                 /* MP4 */
5574                 // CA_AAC
5575                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (CoreAudio)" action: NULL keyEquivalent: @""];
5576                 [menuItem setTag: HB_ACODEC_CA_AAC];
5577                 // FAAC
5578                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (faac)" action: NULL keyEquivalent: @""];
5579                 [menuItem setTag: HB_ACODEC_FAAC];
5580                 // MP3
5581                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"MP3 (lame)" action: NULL keyEquivalent: @""];
5582                 [menuItem setTag: HB_ACODEC_LAME];
5583                 // AC3 Passthru
5584                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AC3 Passthru" action: NULL keyEquivalent: @""];
5585                 [menuItem setTag: HB_ACODEC_AC3];
5586                 break;
5587                 
5588             case 1:
5589                 /* MKV */
5590                 // CA_AAC
5591                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (CoreAudio)" action: NULL keyEquivalent: @""];
5592                 [menuItem setTag: HB_ACODEC_CA_AAC];
5593                 // FAAC
5594                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (faac)" action: NULL keyEquivalent: @""];
5595                 [menuItem setTag: HB_ACODEC_FAAC];
5596                 // AC3 Passthru
5597                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AC3 Passthru" action: NULL keyEquivalent: @""];
5598                 [menuItem setTag: HB_ACODEC_AC3];
5599                 // DTS Passthru
5600                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"DTS Passthru" action: NULL keyEquivalent: @""];
5601                 [menuItem setTag: HB_ACODEC_DCA];
5602                 // MP3
5603                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"MP3 (lame)" action: NULL keyEquivalent: @""];
5604                 [menuItem setTag: HB_ACODEC_LAME];
5605                 // Vorbis
5606                 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"Vorbis (vorbis)" action: NULL keyEquivalent: @""];
5607                 [menuItem setTag: HB_ACODEC_VORBIS];
5608                 break;
5609         }
5610         [audiocodecPopUp selectItemAtIndex:0];
5611     }
5612     else
5613     {
5614         [audiocodecPopUp setEnabled:NO];
5615     }
5616 }
5617
5618 - (IBAction) audioTrackPopUpChanged: (id) sender
5619 {
5620     /* utility function to call audioTrackPopUpChanged without passing in a mixdown-to-use */
5621     [self audioTrackPopUpChanged: sender mixdownToUse: 0];
5622 }
5623
5624 - (IBAction) audioTrackPopUpChanged: (id) sender mixdownToUse: (int) mixdownToUse
5625 {
5626     
5627     /* make sure we have a selected title before continuing */
5628     if (fTitle == NULL) return;
5629     /* make sure we have a source audio track before continuing */
5630     if (hb_list_count( fTitle->list_audio ) == 0)
5631     {
5632         [sender selectItemAtIndex:0];
5633         return;
5634     }
5635     /* if the sender is the lanaguage popup and there is nothing in the codec popup, lets call
5636     * audioAddAudioTrackCodecs on the codec popup to populate it properly before moving on
5637     */
5638     if (sender == fAudLang1PopUp && [[fAudTrack1CodecPopUp menu] numberOfItems] == 0)
5639     {
5640         [self audioAddAudioTrackCodecs: fAudTrack1CodecPopUp];
5641     }
5642     if (sender == fAudLang2PopUp && [[fAudTrack2CodecPopUp menu] numberOfItems] == 0)
5643     {
5644         [self audioAddAudioTrackCodecs: fAudTrack2CodecPopUp];
5645     }
5646     if (sender == fAudLang3PopUp && [[fAudTrack3CodecPopUp menu] numberOfItems] == 0)
5647     {
5648         [self audioAddAudioTrackCodecs: fAudTrack3CodecPopUp];
5649     }
5650     if (sender == fAudLang4PopUp && [[fAudTrack4CodecPopUp menu] numberOfItems] == 0)
5651     {
5652         [self audioAddAudioTrackCodecs: fAudTrack4CodecPopUp];
5653     }
5654     
5655     /* Now lets make the sender the appropriate Audio Track popup from this point on */
5656     if (sender == fAudTrack1CodecPopUp || sender == fAudTrack1MixPopUp)
5657     {
5658         sender = fAudLang1PopUp;
5659     }
5660     if (sender == fAudTrack2CodecPopUp || sender == fAudTrack2MixPopUp)
5661     {
5662         sender = fAudLang2PopUp;
5663     }
5664     if (sender == fAudTrack3CodecPopUp || sender == fAudTrack3MixPopUp)
5665     {
5666         sender = fAudLang3PopUp;
5667     }
5668     if (sender == fAudTrack4CodecPopUp || sender == fAudTrack4MixPopUp)
5669     {
5670         sender = fAudLang4PopUp;
5671     }
5672     
5673     /* pointer to this track's mixdown, codec, sample rate and bitrate NSPopUpButton's */
5674     NSPopUpButton * mixdownPopUp;
5675     NSPopUpButton * audiocodecPopUp;
5676     NSPopUpButton * sampleratePopUp;
5677     NSPopUpButton * bitratePopUp;
5678     if (sender == fAudLang1PopUp)
5679     {
5680         mixdownPopUp = fAudTrack1MixPopUp;
5681         audiocodecPopUp = fAudTrack1CodecPopUp;
5682         sampleratePopUp = fAudTrack1RatePopUp;
5683         bitratePopUp = fAudTrack1BitratePopUp;
5684     }
5685     else if (sender == fAudLang2PopUp)
5686     {
5687         mixdownPopUp = fAudTrack2MixPopUp;
5688         audiocodecPopUp = fAudTrack2CodecPopUp;
5689         sampleratePopUp = fAudTrack2RatePopUp;
5690         bitratePopUp = fAudTrack2BitratePopUp;
5691     }
5692     else if (sender == fAudLang3PopUp)
5693     {
5694         mixdownPopUp = fAudTrack3MixPopUp;
5695         audiocodecPopUp = fAudTrack3CodecPopUp;
5696         sampleratePopUp = fAudTrack3RatePopUp;
5697         bitratePopUp = fAudTrack3BitratePopUp;
5698     }
5699     else
5700     {
5701         mixdownPopUp = fAudTrack4MixPopUp;
5702         audiocodecPopUp = fAudTrack4CodecPopUp;
5703         sampleratePopUp = fAudTrack4RatePopUp;
5704         bitratePopUp = fAudTrack4BitratePopUp;
5705     }
5706
5707     /* get the index of the selected audio Track*/
5708     int thisAudioIndex = [sender indexOfSelectedItem] - 1;
5709
5710     /* pointer for the hb_audio_s struct we will use later on */
5711     hb_audio_config_t * audio;
5712
5713     int acodec;
5714     /* check if the audio mixdown controls need their enabled state changing */
5715     [self setEnabledStateOfAudioMixdownControls:nil];
5716
5717     if (thisAudioIndex != -1)
5718     {
5719
5720         /* get the audio */
5721         audio = (hb_audio_config_t *) hb_list_audio_config_item( fTitle->list_audio, thisAudioIndex );// Should "fTitle" be title and be setup ?
5722
5723         /* actually manipulate the proper mixdowns here */
5724         /* delete the previous audio mixdown options */
5725         [mixdownPopUp removeAllItems];
5726
5727         acodec = [[audiocodecPopUp selectedItem] tag];
5728
5729         if (audio != NULL)
5730         {
5731
5732             /* find out if our selected output audio codec supports 6ch */
5733             int audioCodecsSupport6Ch = (audio->in.codec && acodec != HB_ACODEC_LAME);
5734             
5735             /* check for AC-3 passthru */
5736             if (audio->in.codec == HB_ACODEC_AC3 && acodec == HB_ACODEC_AC3)
5737             {
5738                 
5739             NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5740                  [NSString stringWithUTF8String: "AC3 Passthru"]
5741                                                action: NULL keyEquivalent: @""];
5742              [menuItem setTag: HB_ACODEC_AC3];   
5743             }
5744             else if (audio->in.codec == HB_ACODEC_DCA && acodec == HB_ACODEC_DCA)
5745             {
5746             NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5747                  [NSString stringWithUTF8String: "DTS Passthru"]
5748                                                action: NULL keyEquivalent: @""];
5749              [menuItem setTag: HB_ACODEC_DCA]; 
5750             }
5751             else
5752             {
5753                 
5754                 /* add the appropriate audio mixdown menuitems to the popupbutton */
5755                 /* in each case, we set the new menuitem's tag to be the amixdown value for that mixdown,
5756                  so that we can reference the mixdown later */
5757                 
5758                 /* keep a track of the min and max mixdowns we used, so we can select the best match later */
5759                 int minMixdownUsed = 0;
5760                 int maxMixdownUsed = 0;
5761                 
5762                 /* get the input channel layout without any lfe channels */
5763                 int layout = audio->in.channel_layout & HB_INPUT_CH_LAYOUT_DISCRETE_NO_LFE_MASK;
5764                 
5765                 /* add a mono option */
5766                 NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5767                                         [NSString stringWithUTF8String: hb_audio_mixdowns[0].human_readable_name]
5768                                                                       action: NULL keyEquivalent: @""];
5769                 [menuItem setTag: hb_audio_mixdowns[0].amixdown];
5770                 if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[0].amixdown;
5771                 maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[0].amixdown);
5772                 
5773                 /* do we want to add a stereo option? */
5774                 /* offer stereo if we have a stereo-or-better source */
5775                 if (layout >= HB_INPUT_CH_LAYOUT_STEREO)
5776                 {
5777                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5778                                             [NSString stringWithUTF8String: hb_audio_mixdowns[1].human_readable_name]
5779                                                                           action: NULL keyEquivalent: @""];
5780                     [menuItem setTag: hb_audio_mixdowns[1].amixdown];
5781                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[1].amixdown;
5782                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[1].amixdown);
5783                 }
5784                 
5785                 /* do we want to add a dolby surround (DPL1) option? */
5786                 if (layout == HB_INPUT_CH_LAYOUT_3F1R || layout == HB_INPUT_CH_LAYOUT_3F2R || layout == HB_INPUT_CH_LAYOUT_DOLBY)
5787                 {
5788                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5789                                             [NSString stringWithUTF8String: hb_audio_mixdowns[2].human_readable_name]
5790                                                                           action: NULL keyEquivalent: @""];
5791                     [menuItem setTag: hb_audio_mixdowns[2].amixdown];
5792                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[2].amixdown;
5793                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[2].amixdown);
5794                 }
5795                 
5796                 /* do we want to add a dolby pro logic 2 (DPL2) option? */
5797                 if (layout == HB_INPUT_CH_LAYOUT_3F2R)
5798                 {
5799                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5800                                             [NSString stringWithUTF8String: hb_audio_mixdowns[3].human_readable_name]
5801                                                                           action: NULL keyEquivalent: @""];
5802                     [menuItem setTag: hb_audio_mixdowns[3].amixdown];
5803                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[3].amixdown;
5804                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[3].amixdown);
5805                 }
5806                 
5807                 /* do we want to add a 6-channel discrete option? */
5808                 if (audioCodecsSupport6Ch == 1 && layout == HB_INPUT_CH_LAYOUT_3F2R && (audio->in.channel_layout & HB_INPUT_CH_LAYOUT_HAS_LFE))
5809                 {
5810                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5811                                             [NSString stringWithUTF8String: hb_audio_mixdowns[4].human_readable_name]
5812                                                                           action: NULL keyEquivalent: @""];
5813                     [menuItem setTag: hb_audio_mixdowns[4].amixdown];
5814                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[4].amixdown;
5815                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[4].amixdown);
5816                 }
5817                 
5818                 /* do we want to add an AC-3 passthrough option? */
5819                 if (audio->in.codec == HB_ACODEC_AC3 && acodec == HB_ACODEC_AC3) 
5820                 {
5821                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5822                                             [NSString stringWithUTF8String: hb_audio_mixdowns[5].human_readable_name]
5823                                                                           action: NULL keyEquivalent: @""];
5824                     [menuItem setTag: HB_ACODEC_AC3];
5825                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[5].amixdown;
5826                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[5].amixdown);
5827                 }
5828                 
5829                 /* do we want to add a DTS Passthru option ? HB_ACODEC_DCA*/
5830                 if (audio->in.codec == HB_ACODEC_DCA && acodec == HB_ACODEC_DCA) 
5831                 {
5832                     NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
5833                                             [NSString stringWithUTF8String: hb_audio_mixdowns[5].human_readable_name]
5834                                                                           action: NULL keyEquivalent: @""];
5835                     [menuItem setTag: HB_ACODEC_DCA];
5836                     if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[5].amixdown;
5837                     maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[5].amixdown);
5838                 }
5839                 
5840                 /* auto-select the best mixdown based on our saved mixdown preference */
5841                 
5842                 /* for now, this is hard-coded to a "best" mixdown of HB_AMIXDOWN_DOLBYPLII */
5843                 /* ultimately this should be a prefs option */
5844                 int useMixdown;
5845                 
5846                 /* if we passed in a mixdown to use - in order to load a preset - then try and use it */
5847                 if (mixdownToUse > 0)
5848                 {
5849                     useMixdown = mixdownToUse;
5850                 }
5851                 else
5852                 {
5853                     useMixdown = HB_AMIXDOWN_DOLBYPLII;
5854                 }
5855                 
5856                 /* if useMixdown > maxMixdownUsed, then use maxMixdownUsed */
5857                 if (useMixdown > maxMixdownUsed)
5858                 { 
5859                     useMixdown = maxMixdownUsed;
5860                 }
5861                 
5862                 /* if useMixdown < minMixdownUsed, then use minMixdownUsed */
5863                 if (useMixdown < minMixdownUsed)
5864                 { 
5865                     useMixdown = minMixdownUsed;
5866                 }
5867                 
5868                 /* select the (possibly-amended) preferred mixdown */
5869                 [mixdownPopUp selectItemWithTag: useMixdown];
5870
5871             }
5872             /* In the case of a source track that is not AC3 and the user tries to use AC3 Passthru (which does not work)
5873              * we force the Audio Codec choice back to a workable codec. We use CoreAudio aac for all containers.
5874              */
5875             if (audio->in.codec != HB_ACODEC_AC3 && [[audiocodecPopUp selectedItem] tag] == HB_ACODEC_AC3)
5876             {
5877                 [audiocodecPopUp selectItemWithTag: HB_ACODEC_CA_AAC];
5878             }
5879             
5880             /* In the case of a source track that is not DTS and the user tries to use DTS Passthru (which does not work)
5881              * we force the Audio Codec choice back to a workable codec. We use CoreAudio aac for all containers.
5882              */
5883             if (audio->in.codec != HB_ACODEC_DCA && [[audiocodecPopUp selectedItem] tag] == HB_ACODEC_DCA)
5884             {
5885                 [audiocodecPopUp selectItemWithTag: HB_ACODEC_CA_AAC];
5886             }
5887             
5888             /* Setup our samplerate and bitrate popups we will need based on mixdown */
5889             [self audioTrackMixdownChanged: mixdownPopUp];             
5890         }
5891     
5892     }
5893     if( [fDstFormatPopUp indexOfSelectedItem] == 0 )
5894     {
5895         [self autoSetM4vExtension: sender];
5896     }
5897 }
5898
5899 - (IBAction) audioTrackMixdownChanged: (id) sender
5900 {
5901     
5902     int acodec;
5903     /* setup pointers to all of the other audio track controls
5904     * we will need later
5905     */
5906     NSPopUpButton * mixdownPopUp;
5907     NSPopUpButton * sampleratePopUp;
5908     NSPopUpButton * bitratePopUp;
5909     NSPopUpButton * audiocodecPopUp;
5910     NSPopUpButton * audiotrackPopUp;
5911     NSSlider * drcSlider;
5912     NSTextField * drcField;
5913     if (sender == fAudTrack1MixPopUp)
5914     {
5915         audiotrackPopUp = fAudLang1PopUp;
5916         audiocodecPopUp = fAudTrack1CodecPopUp;
5917         mixdownPopUp = fAudTrack1MixPopUp;
5918         sampleratePopUp = fAudTrack1RatePopUp;
5919         bitratePopUp = fAudTrack1BitratePopUp;
5920         drcSlider = fAudTrack1DrcSlider;
5921         drcField = fAudTrack1DrcField;
5922     }
5923     else if (sender == fAudTrack2MixPopUp)
5924     {
5925         audiotrackPopUp = fAudLang2PopUp;
5926         audiocodecPopUp = fAudTrack2CodecPopUp;
5927         mixdownPopUp = fAudTrack2MixPopUp;
5928         sampleratePopUp = fAudTrack2RatePopUp;
5929         bitratePopUp = fAudTrack2BitratePopUp;
5930         drcSlider = fAudTrack2DrcSlider;
5931         drcField = fAudTrack2DrcField;
5932     }
5933     else if (sender == fAudTrack3MixPopUp)
5934     {
5935         audiotrackPopUp = fAudLang3PopUp;
5936         audiocodecPopUp = fAudTrack3CodecPopUp;
5937         mixdownPopUp = fAudTrack3MixPopUp;
5938         sampleratePopUp = fAudTrack3RatePopUp;
5939         bitratePopUp = fAudTrack3BitratePopUp;
5940         drcSlider = fAudTrack3DrcSlider;
5941         drcField = fAudTrack3DrcField;
5942     }
5943     else
5944     {
5945         audiotrackPopUp = fAudLang4PopUp;
5946         audiocodecPopUp = fAudTrack4CodecPopUp;
5947         mixdownPopUp = fAudTrack4MixPopUp;
5948         sampleratePopUp = fAudTrack4RatePopUp;
5949         bitratePopUp = fAudTrack4BitratePopUp;
5950         drcSlider = fAudTrack4DrcSlider;
5951         drcField = fAudTrack4DrcField;
5952     }
5953     acodec = [[audiocodecPopUp selectedItem] tag];
5954     /* storage variable for the min and max bitrate allowed for this codec */
5955     int minbitrate;
5956     int maxbitrate;
5957     
5958     switch( acodec )
5959     {
5960         case HB_ACODEC_FAAC:
5961             /* check if we have a 6ch discrete conversion in either audio track */
5962             if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH)
5963             {
5964                 /* FAAC has a minimum of 192 kbps for 6-channel discrete */
5965                 minbitrate = 192;
5966                 /* If either mixdown popup includes 6-channel discrete, then allow up to 768 kbps */
5967                 maxbitrate = 768;
5968                 break;
5969             }
5970             else
5971             {
5972                 /* FAAC is happy using our min bitrate of 32 kbps for stereo or mono */
5973                 minbitrate = 32;
5974                 /* note: haven't dealt with mono separately here, FAAC will just use the max it can */
5975                 maxbitrate = 320;
5976                 break;
5977             }
5978
5979         case HB_ACODEC_CA_AAC:
5980             /* check if we have a 6ch discrete conversion in either audio track */
5981             if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH)
5982             {
5983                 minbitrate = 128;
5984                 maxbitrate = 768;
5985                 break;
5986             }
5987             else
5988             {
5989                 minbitrate = 64;
5990                 maxbitrate = 320;
5991                 break;
5992             }
5993
5994             case HB_ACODEC_LAME:
5995             /* Lame is happy using our min bitrate of 32 kbps */
5996             minbitrate = 32;
5997             /* Lame won't encode if the bitrate is higher than 320 kbps */
5998             maxbitrate = 320;
5999             break;
6000             
6001             case HB_ACODEC_VORBIS:
6002             if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH)
6003             {
6004                 /* Vorbis causes a crash if we use a bitrate below 192 kbps with 6 channel */
6005                 minbitrate = 192;
6006                 /* If either mixdown popup includes 6-channel discrete, then allow up to 384 kbps */
6007                 maxbitrate = 384;
6008                 break;
6009             }
6010             else
6011             {
6012                 /* Vorbis causes a crash if we use a bitrate below 48 kbps */
6013                 minbitrate = 48;
6014                 /* Vorbis can cope with 384 kbps quite happily, even for stereo */
6015                 maxbitrate = 384;
6016                 break;
6017             }
6018             
6019             default:
6020             /* AC3 passthru disables the bitrate dropdown anyway, so we might as well just use the min and max bitrate */
6021             minbitrate = 32;
6022             maxbitrate = 384;
6023             
6024     }
6025     
6026     /* make sure we have a selected title before continuing */
6027     if (fTitle == NULL || hb_list_count( fTitle->list_audio ) == 0) return;
6028     /* get the audio so we can find out what input rates are*/
6029     hb_audio_config_t * audio;
6030     audio = (hb_audio_config_t *) hb_list_audio_config_item( fTitle->list_audio, [audiotrackPopUp indexOfSelectedItem] - 1 );
6031     int inputbitrate = audio->in.bitrate / 1000;
6032     int inputsamplerate = audio->in.samplerate;
6033     
6034     if ([[mixdownPopUp selectedItem] tag] != HB_ACODEC_AC3 && [[mixdownPopUp selectedItem] tag] != HB_ACODEC_DCA)
6035     {
6036         [bitratePopUp removeAllItems];
6037         
6038         for( int i = 0; i < hb_audio_bitrates_count; i++ )
6039         {
6040             if (hb_audio_bitrates[i].rate >= minbitrate && hb_audio_bitrates[i].rate <= maxbitrate)
6041             {
6042                 /* add a new menuitem for this bitrate */
6043                 NSMenuItem *menuItem = [[bitratePopUp menu] addItemWithTitle:
6044                                         [NSString stringWithUTF8String: hb_audio_bitrates[i].string]
6045                                                                       action: NULL keyEquivalent: @""];
6046                 /* set its tag to be the actual bitrate as an integer, so we can retrieve it later */
6047                 [menuItem setTag: hb_audio_bitrates[i].rate];
6048             }
6049         }
6050         
6051         /* select the default bitrate (but use 384 for 6-ch AAC) */
6052         if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH)
6053         {
6054             [bitratePopUp selectItemWithTag: 384];
6055         }
6056         else
6057         {
6058             [bitratePopUp selectItemWithTag: hb_audio_bitrates[hb_audio_bitrates_default].rate];
6059         }
6060     }
6061     /* populate and set the sample rate popup */
6062     /* Audio samplerate */
6063     [sampleratePopUp removeAllItems];
6064     /* we create a same as source selection (Auto) so that we can choose to use the input sample rate */
6065     NSMenuItem *menuItem = [[sampleratePopUp menu] addItemWithTitle: @"Auto" action: NULL keyEquivalent: @""];
6066     [menuItem setTag: inputsamplerate];
6067     
6068     for( int i = 0; i < hb_audio_rates_count; i++ )
6069     {
6070         NSMenuItem *menuItem = [[sampleratePopUp menu] addItemWithTitle:
6071                                 [NSString stringWithUTF8String: hb_audio_rates[i].string]
6072                                                                  action: NULL keyEquivalent: @""];
6073         [menuItem setTag: hb_audio_rates[i].rate];
6074     }
6075     /* We use the input sample rate as the default sample rate as downsampling just makes audio worse
6076     * and there is no compelling reason to use anything else as default, though the users default
6077     * preset will likely override any setting chosen here.
6078     */
6079     [sampleratePopUp selectItemWithTag: inputsamplerate];
6080     
6081     
6082     /* Since AC3 Pass Thru and DTS Pass Thru uses the input bitrate and sample rate, we get the input tracks
6083     * bitrate and display it in the bitrate popup even though libhb happily ignores any bitrate input from
6084     * the gui. We do this for better user feedback in the audio tab as well as the queue for the most part
6085     */
6086     if ([[mixdownPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[mixdownPopUp selectedItem] tag] == HB_ACODEC_DCA)
6087     {
6088         
6089         /* lets also set the bitrate popup to the input bitrate as thats what passthru will use */
6090         [bitratePopUp removeAllItems];
6091         NSMenuItem *menuItem = [[bitratePopUp menu] addItemWithTitle:
6092                                 [NSString stringWithFormat:@"%d", inputbitrate]
6093                                                               action: NULL keyEquivalent: @""];
6094         [menuItem setTag: inputbitrate];
6095         /* For ac3 passthru we disable the sample rate and bitrate popups as well as the drc slider*/
6096         [bitratePopUp setEnabled: NO];
6097         [sampleratePopUp setEnabled: NO];
6098         
6099         [drcSlider setFloatValue: 0.00];
6100         [self audioDRCSliderChanged: drcSlider];
6101         [drcSlider setEnabled: NO];
6102         [drcField setEnabled: NO];
6103     }
6104     else
6105     {
6106         [sampleratePopUp setEnabled: YES];
6107         [bitratePopUp setEnabled: YES];
6108         [drcSlider setEnabled: YES];
6109         [drcField setEnabled: YES];
6110     }
6111 [self calculateBitrate:nil];    
6112 }
6113
6114 - (IBAction) audioDRCSliderChanged: (id) sender
6115 {
6116     NSSlider * drcSlider;
6117     NSTextField * drcField;
6118     if (sender == fAudTrack1DrcSlider)
6119     {
6120         drcSlider = fAudTrack1DrcSlider;
6121         drcField = fAudTrack1DrcField;
6122     }
6123     else if (sender == fAudTrack2DrcSlider)
6124     {
6125         drcSlider = fAudTrack2DrcSlider;
6126         drcField = fAudTrack2DrcField;
6127     }
6128     else if (sender == fAudTrack3DrcSlider)
6129     {
6130         drcSlider = fAudTrack3DrcSlider;
6131         drcField = fAudTrack3DrcField;
6132     }
6133     else
6134     {
6135         drcSlider = fAudTrack4DrcSlider;
6136         drcField = fAudTrack4DrcField;
6137     }
6138     
6139     /* If we are between 0.0 and 1.0 on the slider, snap it to 1.0 */
6140     if ([drcSlider floatValue] > 0.0 && [drcSlider floatValue] < 1.0)
6141     {
6142         [drcSlider setFloatValue:1.0];
6143     }
6144     
6145     
6146     [drcField setStringValue: [NSString stringWithFormat: @"%.2f", [drcSlider floatValue]]];
6147     /* For now, do not call this until we have an intelligent way to determine audio track selections
6148     * compared to presets
6149     */
6150     //[self customSettingUsed: sender];
6151 }
6152
6153 #pragma mark -
6154
6155 - (IBAction) browseImportSrtFile: (id) sender
6156 {
6157
6158     NSOpenPanel * panel;
6159         
6160     panel = [NSOpenPanel openPanel];
6161     [panel setAllowsMultipleSelection: NO];
6162     [panel setCanChooseFiles: YES];
6163     [panel setCanChooseDirectories: NO ];
6164     NSString * sourceDirectory;
6165         if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastSrtImportDirectory"])
6166         {
6167                 sourceDirectory = [[NSUserDefaults standardUserDefaults] stringForKey:@"LastSrtImportDirectory"];
6168         }
6169         else
6170         {
6171                 sourceDirectory = @"~/Desktop";
6172                 sourceDirectory = [sourceDirectory stringByExpandingTildeInPath];
6173         }
6174     /* we open up the browse srt sheet here and call for browseImportSrtFileDone after the sheet is closed */
6175     NSArray *fileTypes = [NSArray arrayWithObjects:@"plist", @"srt", nil];
6176     [panel beginSheetForDirectory: sourceDirectory file: nil types: fileTypes
6177                    modalForWindow: fWindow modalDelegate: self
6178                    didEndSelector: @selector( browseImportSrtFileDone:returnCode:contextInfo: )
6179                       contextInfo: sender];
6180 }
6181
6182 - (void) browseImportSrtFileDone: (NSSavePanel *) sheet
6183                      returnCode: (int) returnCode contextInfo: (void *) contextInfo
6184 {
6185     if( returnCode == NSOKButton )
6186     {
6187         NSString *importSrtDirectory = [[sheet filename] stringByDeletingLastPathComponent];
6188         NSString *importSrtFilePath = [sheet filename];
6189         [[NSUserDefaults standardUserDefaults] setObject:importSrtDirectory forKey:@"LastSrtImportDirectory"];
6190         
6191         /* now pass the string off to fSubtitlesDelegate to add the srt file to the dropdown */
6192         [fSubtitlesDelegate createSubtitleSrtTrack:importSrtFilePath];
6193         
6194         [fSubtitlesTable reloadData];
6195         
6196     }
6197 }                                           
6198
6199 #pragma mark -
6200 #pragma mark Open New Windows
6201
6202 - (IBAction) openHomepage: (id) sender
6203 {
6204     [[NSWorkspace sharedWorkspace] openURL: [NSURL
6205         URLWithString:@"http://handbrake.fr/"]];
6206 }
6207
6208 - (IBAction) openForums: (id) sender
6209 {
6210     [[NSWorkspace sharedWorkspace] openURL: [NSURL
6211         URLWithString:@"http://handbrake.fr/forum/"]];
6212 }
6213 - (IBAction) openUserGuide: (id) sender
6214 {
6215     [[NSWorkspace sharedWorkspace] openURL: [NSURL
6216         URLWithString:@"http://handbrake.fr/trac/wiki/HandBrakeGuide"]];
6217 }
6218
6219 /**
6220  * Shows debug output window.
6221  */
6222 - (IBAction)showDebugOutputPanel:(id)sender
6223 {
6224     [outputPanel showOutputPanel:sender];
6225 }
6226
6227 /**
6228  * Shows preferences window.
6229  */
6230 - (IBAction) showPreferencesWindow: (id) sender
6231 {
6232     NSWindow * window = [fPreferencesController window];
6233     if (![window isVisible])
6234         [window center];
6235
6236     [window makeKeyAndOrderFront: nil];
6237 }
6238
6239 /**
6240  * Shows queue window.
6241  */
6242 - (IBAction) showQueueWindow:(id)sender
6243 {
6244     [fQueueController showQueueWindow:sender];
6245 }
6246
6247
6248 - (IBAction) toggleDrawer:(id)sender {
6249     [fPresetDrawer toggle:self];
6250 }
6251
6252 /**
6253  * Shows Picture Settings Window.
6254  */
6255
6256 - (IBAction) showPicturePanel: (id) sender
6257 {
6258         [fPictureController showPictureWindow:sender];
6259 }
6260
6261 - (void) picturePanelFullScreen
6262 {
6263         [fPictureController setToFullScreenMode];
6264 }
6265
6266 - (void) picturePanelWindowed
6267 {
6268         [fPictureController setToWindowedMode];
6269 }
6270
6271 - (IBAction) showPreviewWindow: (id) sender
6272 {
6273         [fPictureController showPreviewWindow:sender];
6274 }
6275
6276 #pragma mark -
6277 #pragma mark Preset Outline View Methods
6278 #pragma mark - Required
6279 /* These are required by the NSOutlineView Datasource Delegate */
6280
6281
6282 /* used to specify the number of levels to show for each item */
6283 - (int)outlineView:(NSOutlineView *)fPresetsOutlineView numberOfChildrenOfItem:(id)item
6284 {
6285     /* currently use no levels to test outline view viability */
6286     if (item == nil) // for an outline view the root level of the hierarchy is always nil
6287     {
6288         return [UserPresets count];
6289     }
6290     else
6291     {
6292         /* we need to return the count of the array in ChildrenArray for this folder */
6293         NSArray *children = nil;
6294         children = [item objectForKey:@"ChildrenArray"];
6295         if ([children count] > 0)
6296         {
6297             return [children count];
6298         }
6299         else
6300         {
6301             return 0;
6302         }
6303     }
6304 }
6305
6306 /* We use this to deterimine children of an item */
6307 - (id)outlineView:(NSOutlineView *)fPresetsOutlineView child:(NSInteger)index ofItem:(id)item
6308 {
6309     
6310     /* we need to return the count of the array in ChildrenArray for this folder */
6311     NSArray *children = nil;
6312     if (item == nil)
6313     {
6314         children = UserPresets;
6315     }
6316     else
6317     {
6318         if ([item objectForKey:@"ChildrenArray"])
6319         {
6320             children = [item objectForKey:@"ChildrenArray"];
6321         }
6322     }   
6323     if ((children == nil) || ( [children count] <= (NSUInteger) index))
6324     {
6325         return nil;
6326     }
6327     else
6328     {
6329         return [children objectAtIndex:index];
6330     }
6331     
6332     
6333     // We are only one level deep, so we can't be asked about children
6334     //NSAssert (NO, @"Presets View outlineView:child:ofItem: currently can't handle nested items.");
6335     //return nil;
6336 }
6337
6338 /* We use this to determine if an item should be expandable */
6339 - (BOOL)outlineView:(NSOutlineView *)fPresetsOutlineView isItemExpandable:(id)item
6340 {
6341     
6342     /* we need to return the count of the array in ChildrenArray for this folder */
6343     NSArray *children= nil;
6344     if (item == nil)
6345     {
6346         children = UserPresets;
6347     }
6348     else
6349     {
6350         if ([item objectForKey:@"ChildrenArray"])
6351         {
6352             children = [item objectForKey:@"ChildrenArray"];
6353         }
6354     }   
6355     
6356     /* To deterimine if an item should show a disclosure triangle
6357      * we could do it by the children count as so:
6358      * if ([children count] < 1)
6359      * However, lets leave the triangle show even if there are no
6360      * children to help indicate a folder, just like folder in the
6361      * finder can show a disclosure triangle even when empty
6362      */
6363     
6364     /* We need to determine if the item is a folder */
6365    if ([[item objectForKey:@"Folder"] intValue] == 1)
6366    {
6367         return YES;
6368     }
6369     else
6370     {
6371         return NO;
6372     }
6373     
6374 }
6375
6376 - (BOOL)outlineView:(NSOutlineView *)outlineView shouldExpandItem:(id)item
6377 {
6378     // Our outline view has no levels, but we can still expand every item. Doing so
6379     // just makes the row taller. See heightOfRowByItem below.
6380 //return ![(HBQueueOutlineView*)outlineView isDragging];
6381
6382 return YES;
6383 }
6384
6385
6386 /* Used to tell the outline view which information is to be displayed per item */
6387 - (id)outlineView:(NSOutlineView *)fPresetsOutlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
6388 {
6389         /* We have two columns right now, icon and PresetName */
6390         
6391     if ([[tableColumn identifier] isEqualToString:@"PresetName"])
6392     {
6393         return [item objectForKey:@"PresetName"];
6394     }
6395     else
6396     {
6397         //return @"";
6398         return nil;
6399     }
6400 }
6401
6402 - (id)outlineView:(NSOutlineView *)outlineView itemForPersistentObject:(id)object
6403 {
6404     return [NSKeyedUnarchiver unarchiveObjectWithData:object];
6405 }
6406 - (id)outlineView:(NSOutlineView *)outlineView persistentObjectForItem:(id)item
6407 {
6408     return [NSKeyedArchiver archivedDataWithRootObject:item];
6409 }
6410
6411 #pragma mark - Added Functionality (optional)
6412 /* Use to customize the font and display characteristics of the title cell */
6413 - (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
6414 {
6415     if ([[tableColumn identifier] isEqualToString:@"PresetName"])
6416     {
6417         NSFont *txtFont;
6418         NSColor *fontColor;
6419         NSColor *shadowColor;
6420         txtFont = [NSFont systemFontOfSize: [NSFont smallSystemFontSize]];
6421         /*check to see if its a selected row */
6422         if ([fPresetsOutlineView selectedRow] == [fPresetsOutlineView rowForItem:item])
6423         {
6424             
6425             fontColor = [NSColor blackColor];
6426             shadowColor = [NSColor colorWithDeviceRed:(127.0/255.0) green:(140.0/255.0) blue:(160.0/255.0) alpha:1.0];
6427         }
6428         else
6429         {
6430             if ([[item objectForKey:@"Type"] intValue] == 0)
6431             {
6432                 fontColor = [NSColor blueColor];
6433             }
6434             else // User created preset, use a black font
6435             {
6436                 fontColor = [NSColor blackColor];
6437             }
6438             /* check to see if its a folder */
6439             //if ([[item objectForKey:@"Folder"] intValue] == 1)
6440             //{
6441             //fontColor = [NSColor greenColor];
6442             //}
6443             
6444             
6445         }
6446         /* We use Bold Text for the HB Default */
6447         if ([[item objectForKey:@"Default"] intValue] == 1)// 1 is HB default
6448         {
6449             txtFont = [NSFont boldSystemFontOfSize: [NSFont smallSystemFontSize]];
6450         }
6451         /* We use Bold Text for the User Specified Default */
6452         if ([[item objectForKey:@"Default"] intValue] == 2)// 2 is User default
6453         {
6454             txtFont = [NSFont boldSystemFontOfSize: [NSFont smallSystemFontSize]];
6455         }
6456         
6457         
6458         [cell setTextColor:fontColor];
6459         [cell setFont:txtFont];
6460         
6461     }
6462 }
6463
6464 /* We use this to edit the name field in the outline view */
6465 - (void)outlineView:(NSOutlineView *)outlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
6466 {
6467     if ([[tableColumn identifier] isEqualToString:@"PresetName"])
6468     {
6469         id theRecord;
6470         
6471         theRecord = item;
6472         [theRecord setObject:object forKey:@"PresetName"];
6473         
6474         [self sortPresets];
6475         
6476         [fPresetsOutlineView reloadData];
6477         /* We save all of the preset data here */
6478         [self savePreset];
6479     }
6480 }
6481 /* We use this to provide tooltips for the items in the presets outline view */
6482 - (NSString *)outlineView:(NSOutlineView *)fPresetsOutlineView toolTipForCell:(NSCell *)cell rect:(NSRectPointer)rect tableColumn:(NSTableColumn *)tc item:(id)item mouseLocation:(NSPoint)mouseLocation
6483 {
6484     //if ([[tc identifier] isEqualToString:@"PresetName"])
6485     //{
6486         /* initialize the tooltip contents variable */
6487         NSString *loc_tip;
6488         /* if there is a description for the preset, we show it in the tooltip */
6489         if ([item objectForKey:@"PresetDescription"])
6490         {
6491             loc_tip = [item objectForKey:@"PresetDescription"];
6492             return (loc_tip);
6493         }
6494         else
6495         {
6496             loc_tip = @"No description available";
6497         }
6498         return (loc_tip);
6499     //}
6500 }
6501
6502 #pragma mark -
6503 #pragma mark Preset Outline View Methods (dragging related)
6504
6505
6506 - (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard
6507 {
6508         // Dragging is only allowed for custom presets.
6509     //[[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Default"] intValue] != 1
6510         if ([[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Type"] intValue] == 0) // 0 is built in preset
6511     {
6512         return NO;
6513     }
6514     // Don't retain since this is just holding temporaral drag information, and it is
6515     //only used during a drag!  We could put this in the pboard actually.
6516     fDraggedNodes = items;
6517     // Provide data for our custom type, and simple NSStrings.
6518     [pboard declareTypes:[NSArray arrayWithObjects: DragDropSimplePboardType, nil] owner:self];
6519     
6520     // the actual data doesn't matter since DragDropSimplePboardType drags aren't recognized by anyone but us!.
6521     [pboard setData:[NSData data] forType:DragDropSimplePboardType]; 
6522     
6523     return YES;
6524 }
6525
6526 - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index
6527 {
6528         
6529         // Don't allow dropping ONTO an item since they can't really contain any children.
6530     
6531     BOOL isOnDropTypeProposal = index == NSOutlineViewDropOnItemIndex;
6532     if (isOnDropTypeProposal)
6533         return NSDragOperationNone;
6534     
6535     // Don't allow dropping INTO an item since they can't really contain any children as of yet.
6536         if (item != nil)
6537         {
6538                 index = [fPresetsOutlineView rowForItem: item] + 1;
6539                 item = nil;
6540         }
6541     
6542     // Don't allow dropping into the Built In Presets.
6543     if (index < presetCurrentBuiltInCount)
6544     {
6545         return NSDragOperationNone;
6546         index = MAX (index, presetCurrentBuiltInCount);
6547         }    
6548         
6549     [outlineView setDropItem:item dropChildIndex:index];
6550     return NSDragOperationGeneric;
6551 }
6552
6553
6554
6555 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index
6556 {
6557     /* first, lets see if we are dropping into a folder */
6558     if ([[fPresetsOutlineView itemAtRow:index] objectForKey:@"Folder"] && [[[fPresetsOutlineView itemAtRow:index] objectForKey:@"Folder"] intValue] == 1) // if its a folder
6559         {
6560     NSMutableArray *childrenArray = [[NSMutableArray alloc] init];
6561     childrenArray = [[fPresetsOutlineView itemAtRow:index] objectForKey:@"ChildrenArray"];
6562     [childrenArray addObject:item];
6563     [[fPresetsOutlineView itemAtRow:index] setObject:[NSMutableArray arrayWithArray: childrenArray] forKey:@"ChildrenArray"];
6564     [childrenArray autorelease];
6565     }
6566     else // We are not, so we just move the preset into the existing array 
6567     {
6568         NSMutableIndexSet *moveItems = [NSMutableIndexSet indexSet];
6569         id obj;
6570         NSEnumerator *enumerator = [fDraggedNodes objectEnumerator];
6571         while (obj = [enumerator nextObject])
6572         {
6573             [moveItems addIndex:[UserPresets indexOfObject:obj]];
6574         }
6575         // Successful drop, lets rearrange the view and save it all
6576         [self moveObjectsInPresetsArray:UserPresets fromIndexes:moveItems toIndex: index];
6577     }
6578     [fPresetsOutlineView reloadData];
6579     [self savePreset];
6580     return YES;
6581 }
6582
6583 - (void)moveObjectsInPresetsArray:(NSMutableArray *)array fromIndexes:(NSIndexSet *)indexSet toIndex:(NSUInteger)insertIndex
6584 {
6585     NSUInteger index = [indexSet lastIndex];
6586     NSUInteger aboveInsertIndexCount = 0;
6587     
6588     NSUInteger removeIndex;
6589
6590     if (index >= insertIndex)
6591     {
6592         removeIndex = index + aboveInsertIndexCount;
6593         aboveInsertIndexCount++;
6594     }
6595     else
6596     {
6597         removeIndex = index;
6598         insertIndex--;
6599     }
6600
6601     id object = [[array objectAtIndex:removeIndex] retain];
6602     [array removeObjectAtIndex:removeIndex];
6603     [array insertObject:object atIndex:insertIndex];
6604     [object release];
6605
6606     index = [indexSet indexLessThanIndex:index];
6607 }
6608
6609
6610
6611 #pragma mark - Functional Preset NSOutlineView Methods
6612
6613 - (IBAction)selectPreset:(id)sender
6614 {
6615     
6616     if ([fPresetsOutlineView selectedRow] >= 0 && [[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Folder"] intValue] != 1)
6617     {
6618         chosenPreset = [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]];
6619         [fPresetSelectedDisplay setStringValue:[chosenPreset objectForKey:@"PresetName"]];
6620         
6621         if ([[chosenPreset objectForKey:@"Default"] intValue] == 1)
6622         {
6623             [fPresetSelectedDisplay setStringValue:[NSString stringWithFormat:@"%@ (Default)", [chosenPreset objectForKey:@"PresetName"]]];
6624         }
6625         else
6626         {
6627             [fPresetSelectedDisplay setStringValue:[chosenPreset objectForKey:@"PresetName"]];
6628         }
6629         
6630         /* File Format */
6631         [fDstFormatPopUp selectItemWithTitle:[chosenPreset objectForKey:@"FileFormat"]];
6632         [self formatPopUpChanged:nil];
6633         
6634         /* Chapter Markers*/
6635         [fCreateChapterMarkers setState:[[chosenPreset objectForKey:@"ChapterMarkers"] intValue]];
6636         /* check to see if we have only one chapter */
6637         [self chapterPopUpChanged:nil];
6638         
6639         /* Allow Mpeg4 64 bit formatting +4GB file sizes */
6640         [fDstMp4LargeFileCheck setState:[[chosenPreset objectForKey:@"Mp4LargeFile"] intValue]];
6641         /* Mux mp4 with http optimization */
6642         [fDstMp4HttpOptFileCheck setState:[[chosenPreset objectForKey:@"Mp4HttpOptimize"] intValue]];
6643         
6644         /* Video encoder */
6645         [fVidEncoderPopUp selectItemWithTitle:[chosenPreset objectForKey:@"VideoEncoder"]];
6646         /* We set the advanced opt string here if applicable*/
6647         [fAdvancedOptions setOptions:[chosenPreset objectForKey:@"x264Option"]];
6648         
6649         /* Lets run through the following functions to get variables set there */
6650         [self videoEncoderPopUpChanged:nil];
6651         /* Set the state of ipod compatible with Mp4iPodCompatible. Only for x264*/
6652         [fDstMp4iPodFileCheck setState:[[chosenPreset objectForKey:@"Mp4iPodCompatible"] intValue]];
6653         [self calculateBitrate:nil];
6654         
6655         /* Video quality */
6656         [fVidQualityMatrix selectCellAtRow:[[chosenPreset objectForKey:@"VideoQualityType"] intValue] column:0];
6657         
6658         [fVidTargetSizeField setStringValue:[chosenPreset objectForKey:@"VideoTargetSize"]];
6659         [fVidBitrateField setStringValue:[chosenPreset objectForKey:@"VideoAvgBitrate"]];
6660         
6661         /* Since we are now using RF Values for the slider, we detect if the preset uses an old quality float.
6662          * So, check to see if the quality value is less than 1.0 which should indicate the old ".062" type
6663          * quality preset. Caveat: in the case of x264, where the RF scale starts at 0, it would misinterpret
6664          * a preset that uses 0.0 - 0.99 for RF as an old style preset. Not sure how to get around that one yet,
6665          * though it should be a corner case since it would pretty much be a preset for lossless encoding. */
6666         if ([[chosenPreset objectForKey:@"VideoQualitySlider"] floatValue] < 1.0)
6667         {
6668             /* For the quality slider we need to convert the old percent's to the new rf scales */
6669             float rf =  (([fVidQualitySlider maxValue] - [fVidQualitySlider minValue]) * [[chosenPreset objectForKey:@"VideoQualitySlider"] floatValue]);
6670             [fVidQualitySlider setFloatValue:rf];
6671             
6672         }
6673         else
6674         {
6675             /* Since theora's qp value goes up from left to right, we can just set the slider float value */
6676             if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_THEORA)
6677             {
6678                 [fVidQualitySlider setFloatValue:[[chosenPreset objectForKey:@"VideoQualitySlider"] floatValue]];
6679             }
6680             else
6681             {
6682                 /* since ffmpeg and x264 use an "inverted" slider (lower qp/rf values indicate a higher quality) we invert the value on the slider */
6683                 [fVidQualitySlider setFloatValue:([fVidQualitySlider maxValue] + [fVidQualitySlider minValue]) - [[chosenPreset objectForKey:@"VideoQualitySlider"] floatValue]];
6684             }
6685         }
6686         
6687         [self videoMatrixChanged:nil];
6688         
6689         /* Video framerate */
6690         /* For video preset video framerate, we want to make sure that Same as source does not conflict with the
6691          detected framerate in the fVidRatePopUp so we use index 0*/
6692         if ([[chosenPreset objectForKey:@"VideoFramerate"] isEqualToString:@"Same as source"])
6693         {
6694             [fVidRatePopUp selectItemAtIndex: 0];
6695         }
6696         else
6697         {
6698             [fVidRatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"VideoFramerate"]];
6699         }
6700         /* Set PFR */
6701         [fFrameratePfrCheck setState:[[chosenPreset objectForKey:@"VideoFrameratePFR"] intValue]];
6702         [self videoFrameRateChanged:nil];
6703         
6704         /* 2 Pass Encoding */
6705         [fVidTwoPassCheck setState:[[chosenPreset objectForKey:@"VideoTwoPass"] intValue]];
6706         [self twoPassCheckboxChanged:nil];
6707         
6708         /* Turbo 1st pass for 2 Pass Encoding */
6709         [fVidTurboPassCheck setState:[[chosenPreset objectForKey:@"VideoTurboTwoPass"] intValue]];
6710         
6711         /*Audio*/
6712         /* First we check to see if we are using the current audio track layout based on AudioList array */
6713         if ([chosenPreset objectForKey:@"AudioList"])
6714         {
6715             
6716             /* pointer to this track's mixdown, codec, sample rate and bitrate NSPopUpButton's */
6717             NSPopUpButton * trackLangPreviousPopUp = nil;
6718             NSPopUpButton * trackLangPopUp = nil;
6719             NSPopUpButton * mixdownPopUp = nil;
6720             NSPopUpButton * audiocodecPopUp = nil;
6721             NSPopUpButton * sampleratePopUp = nil;
6722             NSPopUpButton * bitratePopUp = nil;
6723             NSSlider      * drcSlider = nil;
6724             
6725             
6726             /* Populate the audio widgets based on the contents of the AudioList array */
6727             int i = 0;
6728             NSEnumerator *enumerator = [[chosenPreset objectForKey:@"AudioList"] objectEnumerator];
6729             id tempObject;
6730             while (tempObject = [enumerator nextObject])
6731             {
6732                 i++;
6733                 if( i == 1 )
6734                 {
6735                     trackLangPopUp = fAudLang1PopUp;
6736                     mixdownPopUp = fAudTrack1MixPopUp;
6737                     audiocodecPopUp = fAudTrack1CodecPopUp;
6738                     sampleratePopUp = fAudTrack1RatePopUp;
6739                     bitratePopUp = fAudTrack1BitratePopUp;
6740                     drcSlider = fAudTrack1DrcSlider;
6741                 }
6742                 if( i == 2 )
6743                 {
6744                     trackLangPreviousPopUp = fAudLang1PopUp;
6745                     trackLangPopUp = fAudLang2PopUp;
6746                     mixdownPopUp = fAudTrack2MixPopUp;
6747                     audiocodecPopUp = fAudTrack2CodecPopUp;
6748                     sampleratePopUp = fAudTrack2RatePopUp;
6749                     bitratePopUp = fAudTrack2BitratePopUp;
6750                     drcSlider = fAudTrack2DrcSlider;
6751                 }
6752                 if( i == 3 )
6753                 {
6754                     trackLangPreviousPopUp = fAudLang2PopUp;
6755                     trackLangPopUp = fAudLang3PopUp;
6756                     mixdownPopUp = fAudTrack3MixPopUp;
6757                     audiocodecPopUp = fAudTrack3CodecPopUp;
6758                     sampleratePopUp = fAudTrack3RatePopUp;
6759                     bitratePopUp = fAudTrack3BitratePopUp;
6760                     drcSlider = fAudTrack3DrcSlider;
6761                 }
6762                 if( i == 4 )
6763                 {
6764                     trackLangPreviousPopUp = fAudLang3PopUp;
6765                     trackLangPopUp = fAudLang4PopUp;
6766                     mixdownPopUp = fAudTrack4MixPopUp;
6767                     audiocodecPopUp = fAudTrack4CodecPopUp;
6768                     sampleratePopUp = fAudTrack4RatePopUp;
6769                     bitratePopUp = fAudTrack4BitratePopUp;
6770                     drcSlider = fAudTrack4DrcSlider;
6771                 }
6772                 
6773                 
6774                 if ([trackLangPopUp indexOfSelectedItem] == 0)
6775                 {
6776                     if (i ==1)
6777                     {
6778                         [trackLangPopUp selectItemAtIndex: 1];
6779                     }
6780                     else
6781                     {
6782                         /* if we are greater than track 1, select
6783                          * the same track as the previous track */
6784                         [trackLangPopUp selectItemAtIndex: [trackLangPreviousPopUp indexOfSelectedItem]];
6785                     }
6786                 }
6787                 [self audioTrackPopUpChanged: trackLangPopUp];
6788                 [audiocodecPopUp selectItemWithTitle:[tempObject objectForKey:@"AudioEncoder"]];
6789                 /* check our pref for core audio and use it in place of faac if preset is a built in  */
6790                 if ([[chosenPreset objectForKey:@"Type"] intValue] == 0 && 
6791                     [[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES && 
6792                     [[tempObject objectForKey:@"AudioEncoder"] isEqualToString: @"AAC (faac)"])
6793                 {
6794                     [audiocodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"];
6795                 }                    
6796                 
6797                 [self audioTrackPopUpChanged: audiocodecPopUp];
6798                 [mixdownPopUp selectItemWithTitle:[tempObject objectForKey:@"AudioMixdown"]];
6799                 [self audioTrackMixdownChanged: mixdownPopUp];
6800                 /* check to see if the selection was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
6801                  * mixdown*/
6802                 if  ([mixdownPopUp selectedItem] == nil)
6803                 {
6804                     [self audioTrackPopUpChanged: audiocodecPopUp];
6805                     [self writeToActivityLog: "presetSelected mixdown not selected, rerun audioTrackPopUpChanged"];
6806                 }
6807                 [sampleratePopUp selectItemWithTitle:[tempObject objectForKey:@"AudioSamplerate"]];
6808                 /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
6809                 if (![[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"AC3 Passthru"])
6810                 {
6811                     [bitratePopUp selectItemWithTitle:[tempObject objectForKey:@"AudioBitrate"]];
6812                     /* check to see if the bitrate selection was available, if not, rerun audioTrackMixdownChanged using the mixdown to just set the
6813                      *default mixdown bitrate*/
6814                     if ([bitratePopUp selectedItem] == nil)
6815                     {
6816                         [self audioTrackMixdownChanged: mixdownPopUp];
6817                     }
6818                 }
6819                 [drcSlider setFloatValue:[[tempObject objectForKey:@"AudioTrackDRCSlider"] floatValue]];
6820                 [self audioDRCSliderChanged: drcSlider];
6821                 
6822                 
6823                 /* If we are any track greater than 1 check to make sure we have a matching source codec is using ac3 passthru or dts passthru,
6824                  * if not we will set the track to "None". Track 1 is allowed to mixdown to a suitable DPL2 mix if we cannot passthru */
6825                 
6826                 if( i > 1 )
6827                 {
6828                     /* Check to see if the preset asks for a passhthru track (AC3 or DTS) and verify there is a matching source track if not, set the track to "None". */
6829                     if (([[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"AC3 Passthru"] || [[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"DTS Passthru"])  && [trackLangPopUp indexOfSelectedItem] != 0)
6830                     {
6831                         hb_audio_config_t * audio;
6832                         /* get the audio source audio codec */
6833                         audio = (hb_audio_config_t *) hb_list_audio_config_item( fTitle->list_audio, [trackLangPopUp indexOfSelectedItem] - 1 );
6834                         if (audio != NULL && [[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"AC3 Passthru"] && audio->in.codec != HB_ACODEC_AC3 ||
6835                             [[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"DTS Passthru"] && audio->in.codec != HB_ACODEC_DCA )
6836                         {
6837                             /* We have a preset using ac3 passthru but no ac3 source audio, so set the track to "None" and bail */
6838                             if ([[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"AC3 Passthru"])
6839                             {
6840                                 [self writeToActivityLog: "Preset calls for AC3 Pass thru ..."];
6841                             }
6842                             if ([[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"DTS Passthru"])
6843                             {
6844                                 [self writeToActivityLog: "Preset calls for DTS Pass thru ..."];
6845                             }
6846                             [self writeToActivityLog: "No matching source codec, setting track  %d to None", i];
6847                             [trackLangPopUp selectItemAtIndex: 0];
6848                             [self audioTrackPopUpChanged: trackLangPopUp]; 
6849                         }   
6850                     }
6851                 }
6852             }
6853             
6854             /* We now cleanup any extra audio tracks that may have been previously set if we need to */
6855             
6856             if (i < 4)
6857             {
6858                 [fAudLang4PopUp selectItemAtIndex: 0];
6859                 [self audioTrackPopUpChanged: fAudLang4PopUp];
6860                 
6861                 if (i < 3)
6862                 {
6863                     [fAudLang3PopUp selectItemAtIndex: 0];
6864                     [self audioTrackPopUpChanged: fAudLang3PopUp];
6865                     
6866                     if (i < 2)
6867                     {
6868                         [fAudLang2PopUp selectItemAtIndex: 0];
6869                         [self audioTrackPopUpChanged: fAudLang2PopUp];
6870                     }
6871                 }
6872             }
6873             
6874         }
6875         else
6876         {
6877             if ([chosenPreset objectForKey:@"Audio1Track"] > 0)
6878             {
6879                 if ([fAudLang1PopUp indexOfSelectedItem] == 0)
6880                 {
6881                     [fAudLang1PopUp selectItemAtIndex: 1];
6882                 }
6883                 [self audioTrackPopUpChanged: fAudLang1PopUp];
6884                 [fAudTrack1CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Encoder"]];
6885                 /* check our pref for core audio and use it in place of faac if preset is built in */
6886                 if ([[chosenPreset objectForKey:@"Type"] intValue] == 0 && 
6887                     [[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES && 
6888                     [[chosenPreset objectForKey:@"Audio1Encoder"] isEqualToString: @"AAC (faac)"])
6889                 {
6890                     [fAudTrack1CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"];
6891                 }
6892                 
6893                 [self audioTrackPopUpChanged: fAudTrack1CodecPopUp];
6894                 [fAudTrack1MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Mixdown"]];
6895                 /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
6896                  * mixdown*/
6897                 if  ([fAudTrack1MixPopUp selectedItem] == nil)
6898                 {
6899                     [self audioTrackPopUpChanged: fAudTrack1CodecPopUp];
6900                 }
6901                 [fAudTrack1RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Samplerate"]];
6902                 /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
6903                 if (![[chosenPreset objectForKey:@"Audio1Encoder"] isEqualToString:@"AC3 Passthru"])
6904                 {
6905                     [fAudTrack1BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Bitrate"]];
6906                 }
6907                 [fAudTrack1DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio1TrackDRCSlider"] floatValue]];
6908                 [self audioDRCSliderChanged: fAudTrack1DrcSlider];
6909             }
6910             
6911             if ([chosenPreset objectForKey:@"Audio2Track"] > 0)
6912             {
6913                 if ([fAudLang2PopUp indexOfSelectedItem] == 0)
6914                 {
6915                     [fAudLang2PopUp selectItemAtIndex: 1];
6916                 }
6917                 [self audioTrackPopUpChanged: fAudLang2PopUp];
6918                 [fAudTrack2CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Encoder"]];
6919                 /* check our pref for core audio and use it in place of faac if preset is built in */
6920                 if ([[chosenPreset objectForKey:@"Type"] intValue] == 0 && 
6921                     [[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES && 
6922                     [[chosenPreset objectForKey:@"Audio2Encoder"] isEqualToString: @"AAC (faac)"])
6923                 {
6924                     [fAudTrack2CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"];
6925                 }
6926                 [self audioTrackPopUpChanged: fAudTrack2CodecPopUp];
6927                 [fAudTrack2MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Mixdown"]];
6928                 /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
6929                  * mixdown*/
6930                 if  ([fAudTrack2MixPopUp selectedItem] == nil)
6931                 {
6932                     [self audioTrackPopUpChanged: fAudTrack2CodecPopUp];
6933                 }
6934                 [fAudTrack2RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Samplerate"]];
6935                 /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
6936                 if (![[chosenPreset objectForKey:@"Audio2Encoder"] isEqualToString:@"AC3 Passthru"])
6937                 {
6938                     [fAudTrack2BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Bitrate"]];
6939                 }
6940                 [fAudTrack2DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio2TrackDRCSlider"] floatValue]];
6941                 [self audioDRCSliderChanged: fAudTrack2DrcSlider];
6942             }
6943             if ([chosenPreset objectForKey:@"Audio3Track"] > 0)
6944             {
6945                 if ([fAudLang3PopUp indexOfSelectedItem] == 0)
6946                 {
6947                     [fAudLang3PopUp selectItemAtIndex: 1];
6948                 }
6949                 [self audioTrackPopUpChanged: fAudLang3PopUp];
6950                 [fAudTrack3CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Encoder"]];
6951                 /* check our pref for core audio and use it in place of faac if preset is built in */
6952                 if ([[chosenPreset objectForKey:@"Type"] intValue] == 0 && 
6953                     [[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES && 
6954                     [[chosenPreset objectForKey:@"Audio3Encoder"] isEqualToString: @"AAC (faac)"])
6955                 {
6956                     [fAudTrack3CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"];
6957                 }
6958                 [self audioTrackPopUpChanged: fAudTrack3CodecPopUp];
6959                 [fAudTrack3MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Mixdown"]];
6960                 /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
6961                  * mixdown*/
6962                 if  ([fAudTrack3MixPopUp selectedItem] == nil)
6963                 {
6964                     [self audioTrackPopUpChanged: fAudTrack3CodecPopUp];
6965                 }
6966                 [fAudTrack3RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Samplerate"]];
6967                 /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
6968                 if (![[chosenPreset objectForKey:@"Audio3Encoder"] isEqualToString: @"AC3 Passthru"])
6969                 {
6970                     [fAudTrack3BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Bitrate"]];
6971                 }
6972                 [fAudTrack3DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio3TrackDRCSlider"] floatValue]];
6973                 [self audioDRCSliderChanged: fAudTrack3DrcSlider];
6974             }
6975             if ([chosenPreset objectForKey:@"Audio4Track"] > 0)
6976             {
6977                 if ([fAudLang4PopUp indexOfSelectedItem] == 0)
6978                 {
6979                     [fAudLang4PopUp selectItemAtIndex: 1];
6980                 }
6981                 [self audioTrackPopUpChanged: fAudLang4PopUp];
6982                 [fAudTrack4CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Encoder"]];
6983                 /* check our pref for core audio and use it in place of faac if preset is built in */
6984                 if ([[chosenPreset objectForKey:@"Type"] intValue] == 0 && 
6985                     [[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES && 
6986                     [[chosenPreset objectForKey:@"Audio4Encoder"] isEqualToString: @"AAC (faac)"])
6987                 {
6988                     [fAudTrack4CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"];
6989                 }
6990                 [self audioTrackPopUpChanged: fAudTrack4CodecPopUp];
6991                 [fAudTrack4MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Mixdown"]];
6992                 /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
6993                  * mixdown*/
6994                 if  ([fAudTrack4MixPopUp selectedItem] == nil)
6995                 {
6996                     [self audioTrackPopUpChanged: fAudTrack4CodecPopUp];
6997                 }
6998                 [fAudTrack4RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Samplerate"]];
6999                 /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
7000                 if (![[chosenPreset objectForKey:@"Audio4Encoder"] isEqualToString:@"AC3 Passthru"])
7001                 {
7002                     [fAudTrack4BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Bitrate"]];
7003                 }
7004                 [fAudTrack4DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio4TrackDRCSlider"] floatValue]];
7005                 [self audioDRCSliderChanged: fAudTrack4DrcSlider];
7006             }
7007             
7008             /* We now cleanup any extra audio tracks that may have been previously set if we need to */
7009             
7010             if (![chosenPreset objectForKey:@"Audio2Track"] || [chosenPreset objectForKey:@"Audio2Track"] == 0)
7011             {
7012                 [fAudLang2PopUp selectItemAtIndex: 0];
7013                 [self audioTrackPopUpChanged: fAudLang2PopUp];
7014             }
7015             if (![chosenPreset objectForKey:@"Audio3Track"] || [chosenPreset objectForKey:@"Audio3Track"] > 0)
7016             {
7017                 [fAudLang3PopUp selectItemAtIndex: 0];
7018                 [self audioTrackPopUpChanged: fAudLang3PopUp];
7019             }
7020             if (![chosenPreset objectForKey:@"Audio4Track"] || [chosenPreset objectForKey:@"Audio4Track"] > 0)
7021             {
7022                 [fAudLang4PopUp selectItemAtIndex: 0];
7023                 [self audioTrackPopUpChanged: fAudLang4PopUp];
7024             }
7025         }
7026         
7027         /*Subtitles*/
7028         [fSubPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Subtitles"]];
7029         /* Forced Subtitles */
7030         [fSubForcedCheck setState:[[chosenPreset objectForKey:@"SubtitlesForced"] intValue]];
7031         
7032         /* Picture Settings */
7033         /* Note: objectForKey:@"UsesPictureSettings" refers to picture size, which encompasses:
7034          * height, width, keep ar, anamorphic and crop settings.
7035          * picture filters are handled separately below.
7036          */
7037         /* Check to see if the objectForKey:@"UsesPictureSettings is greater than 0, as 0 means use picture sizing "None" 
7038          * ( 2 is use max for source and 1 is use exact size when the preset was created ) and the 
7039          * preset completely ignores any picture sizing values in the preset.
7040          */
7041         if ([[chosenPreset objectForKey:@"UsesPictureSettings"]  intValue] > 0)
7042         {
7043             hb_job_t * job = fTitle->job;
7044             
7045             /* If Cropping is set to custom, then recall all four crop values from
7046              when the preset was created and apply them */
7047             if ([[chosenPreset objectForKey:@"PictureAutoCrop"]  intValue] == 0)
7048             {
7049                 [fPictureController setAutoCrop:NO];
7050                 
7051                 /* Here we use the custom crop values saved at the time the preset was saved */
7052                 job->crop[0] = [[chosenPreset objectForKey:@"PictureTopCrop"]  intValue];
7053                 job->crop[1] = [[chosenPreset objectForKey:@"PictureBottomCrop"]  intValue];
7054                 job->crop[2] = [[chosenPreset objectForKey:@"PictureLeftCrop"]  intValue];
7055                 job->crop[3] = [[chosenPreset objectForKey:@"PictureRightCrop"]  intValue];
7056                 
7057             }
7058             else /* if auto crop has been saved in preset, set to auto and use post scan auto crop */
7059             {
7060                 [fPictureController setAutoCrop:YES];
7061                 /* Here we use the auto crop values determined right after scan */
7062                 job->crop[0] = AutoCropTop;
7063                 job->crop[1] = AutoCropBottom;
7064                 job->crop[2] = AutoCropLeft;
7065                 job->crop[3] = AutoCropRight;
7066                 
7067             }
7068             
7069             /* Set modulus */
7070             if ([chosenPreset objectForKey:@"PictureModulus"])
7071             {
7072                 job->modulus = [[chosenPreset objectForKey:@"PictureModulus"]  intValue];
7073             }
7074             else
7075             {
7076                 job->modulus = 16;
7077             }
7078              
7079             /* Check to see if the objectForKey:@"UsesPictureSettings is 2 which is "Use Max for the source */
7080             if ([[chosenPreset objectForKey:@"UsesPictureSettings"]  intValue] == 2 || [[chosenPreset objectForKey:@"UsesMaxPictureSettings"]  intValue] == 1)
7081             {
7082                 /* Use Max Picture settings for whatever the dvd is.*/
7083                 [self revertPictureSizeToMax:nil];
7084                 job->keep_ratio = [[chosenPreset objectForKey:@"PictureKeepRatio"]  intValue];
7085                 if (job->keep_ratio == 1)
7086                 {
7087                     hb_fix_aspect( job, HB_KEEP_WIDTH );
7088                     if( job->height > fTitle->height )
7089                     {
7090                         job->height = fTitle->height;
7091                         hb_fix_aspect( job, HB_KEEP_HEIGHT );
7092                     }
7093                 }
7094                 job->anamorphic.mode = [[chosenPreset objectForKey:@"PicturePAR"]  intValue];
7095             }
7096             else // /* If not 0 or 2 we assume objectForKey:@"UsesPictureSettings is 1 which is "Use picture sizing from when the preset was set" */
7097             {
7098                 /* we check to make sure the presets width/height does not exceed the sources width/height */
7099                 if (fTitle->width < [[chosenPreset objectForKey:@"PictureWidth"]  intValue] || fTitle->height < [[chosenPreset objectForKey:@"PictureHeight"]  intValue])
7100                 {
7101                     /* if so, then we use the sources height and width to avoid scaling up */
7102                     //job->width = fTitle->width;
7103                     //job->height = fTitle->height;
7104                     [self revertPictureSizeToMax:nil];
7105                 }
7106                 else // source width/height is >= the preset height/width
7107                 {
7108                     /* we can go ahead and use the presets values for height and width */
7109                     job->width = [[chosenPreset objectForKey:@"PictureWidth"]  intValue];
7110                     job->height = [[chosenPreset objectForKey:@"PictureHeight"]  intValue];
7111                 }
7112                 job->keep_ratio = [[chosenPreset objectForKey:@"PictureKeepRatio"]  intValue];
7113                 if (job->keep_ratio == 1)
7114                 {
7115                     int height = fTitle->height;
7116
7117                     if ( job->height && job->height < fTitle->height )
7118                         height = job->height;
7119
7120                     hb_fix_aspect( job, HB_KEEP_WIDTH );
7121                     // Make sure the resulting height is less than
7122                     // the title height and less than the height
7123                     // requested in the preset.
7124                     if( job->height > height )
7125                     {
7126                         job->height = height;
7127                         hb_fix_aspect( job, HB_KEEP_HEIGHT );
7128                     }
7129                 }
7130                 job->anamorphic.mode = [[chosenPreset objectForKey:@"PicturePAR"]  intValue];
7131                 if ( job->anamorphic.mode > 0 )
7132                 {
7133                     int w, h, par_w, par_h;
7134
7135                     job->anamorphic.par_width = fTitle->pixel_aspect_width;
7136                     job->anamorphic.par_height = fTitle->pixel_aspect_height;
7137                     job->maxWidth = job->width;
7138                     job->maxHeight = job->height;
7139                     hb_set_anamorphic_size( job, &w, &h, &par_w, &par_h );
7140                     job->maxWidth = 0;
7141                     job->maxHeight = 0;
7142                     job->width = w;
7143                     job->height = h;
7144                 }
7145                 
7146             }
7147             
7148             
7149         }
7150         /* If the preset has an objectForKey:@"UsesPictureFilters", and handle the filters here */
7151         if ([chosenPreset objectForKey:@"UsesPictureFilters"] && [[chosenPreset objectForKey:@"UsesPictureFilters"]  intValue] > 0)
7152         {
7153             /* Filters */
7154             
7155             /* We only allow *either* Decomb or Deinterlace. So check for the PictureDecombDeinterlace key.
7156              * also, older presets may not have this key, in which case we also check to see if that preset had  PictureDecomb
7157              * specified, in which case we use decomb and ignore any possible Deinterlace settings as using both was less than
7158              * sane.
7159              */
7160             [fPictureController setUseDecomb:1];
7161             [fPictureController setDecomb:0];
7162             [fPictureController setDeinterlace:0];
7163             if ([[chosenPreset objectForKey:@"PictureDecombDeinterlace"] intValue] == 1 || [[chosenPreset objectForKey:@"PictureDecomb"] intValue] > 0)
7164             {
7165                 /* we are using decomb */
7166                 /* Decomb */
7167                 if ([[chosenPreset objectForKey:@"PictureDecomb"] intValue] > 0)
7168                 {
7169                     [fPictureController setDecomb:[[chosenPreset objectForKey:@"PictureDecomb"] intValue]];
7170                     
7171                     /* if we are using "Custom" in the decomb setting, also set the custom string*/
7172                     if ([[chosenPreset objectForKey:@"PictureDecomb"] intValue] == 1)
7173                     {
7174                         [fPictureController setDecombCustomString:[chosenPreset objectForKey:@"PictureDecombCustom"]];    
7175                     }
7176                 }
7177              }
7178             else
7179             {
7180                 /* We are using Deinterlace */
7181                 /* Deinterlace */
7182                 if ([[chosenPreset objectForKey:@"PictureDeinterlace"] intValue] > 0)
7183                 {
7184                     [fPictureController setUseDecomb:0];
7185                     [fPictureController setDeinterlace:[[chosenPreset objectForKey:@"PictureDeinterlace"] intValue]];
7186                     /* if we are using "Custom" in the deinterlace setting, also set the custom string*/
7187                     if ([[chosenPreset objectForKey:@"PictureDeinterlace"] intValue] == 1)
7188                     {
7189                         [fPictureController setDeinterlaceCustomString:[chosenPreset objectForKey:@"PictureDeinterlaceCustom"]];    
7190                     }
7191                 }
7192             }
7193             
7194             
7195             /* Detelecine */
7196             if ([[chosenPreset objectForKey:@"PictureDetelecine"] intValue] > 0)
7197             {
7198                 [fPictureController setDetelecine:[[chosenPreset objectForKey:@"PictureDetelecine"] intValue]];
7199                 /* if we are using "Custom" in the detelecine setting, also set the custom string*/
7200                 if ([[chosenPreset objectForKey:@"PictureDetelecine"] intValue] == 1)
7201                 {
7202                     [fPictureController setDetelecineCustomString:[chosenPreset objectForKey:@"PictureDetelecineCustom"]];    
7203                 }
7204             }
7205             else
7206             {
7207                 [fPictureController setDetelecine:0];
7208             }
7209             
7210             /* Denoise */
7211             if ([[chosenPreset objectForKey:@"PictureDenoise"] intValue] > 0)
7212             {
7213                 [fPictureController setDenoise:[[chosenPreset objectForKey:@"PictureDenoise"] intValue]];
7214                 /* if we are using "Custom" in the denoise setting, also set the custom string*/
7215                 if ([[chosenPreset objectForKey:@"PictureDenoise"] intValue] == 1)
7216                 {
7217                     [fPictureController setDenoiseCustomString:[chosenPreset objectForKey:@"PictureDenoiseCustom"]];    
7218                 }
7219             }
7220             else
7221             {
7222                 [fPictureController setDenoise:0];
7223             }   
7224             
7225             /* Deblock */
7226             if ([[chosenPreset objectForKey:@"PictureDeblock"] intValue] == 1)
7227             {
7228                 /* if its a one, then its the old on/off deblock, set on to 5*/
7229                 [fPictureController setDeblock:5];
7230             }
7231             else
7232             {
7233                 /* use the settings intValue */
7234                 [fPictureController setDeblock:[[chosenPreset objectForKey:@"PictureDeblock"] intValue]];
7235             }
7236             
7237             if ([[chosenPreset objectForKey:@"VideoGrayScale"] intValue] == 1)
7238             {
7239                 [fPictureController setGrayscale:1];
7240             }
7241             else
7242             {
7243                 [fPictureController setGrayscale:0];
7244             }
7245         }
7246         /* we call SetTitle: in fPictureController so we get an instant update in the Picture Settings window */
7247         [fPictureController SetTitle:fTitle];
7248         [fPictureController SetTitle:fTitle];
7249         [self calculatePictureSizing:nil];
7250     }
7251 }
7252
7253
7254 #pragma mark -
7255 #pragma mark Manage Presets
7256
7257 - (void) loadPresets {
7258         /* We declare the default NSFileManager into fileManager */
7259         NSFileManager * fileManager = [NSFileManager defaultManager];
7260         /*We define the location of the user presets file */
7261     UserPresetsFile = @"~/Library/Application Support/HandBrake/UserPresets.plist";
7262         UserPresetsFile = [[UserPresetsFile stringByExpandingTildeInPath]retain];
7263     /* We check for the presets.plist */
7264         if ([fileManager fileExistsAtPath:UserPresetsFile] == 0)
7265         {
7266                 [fileManager createFileAtPath:UserPresetsFile contents:nil attributes:nil];
7267         }
7268
7269         UserPresets = [[NSMutableArray alloc] initWithContentsOfFile:UserPresetsFile];
7270         if (nil == UserPresets)
7271         {
7272                 UserPresets = [[NSMutableArray alloc] init];
7273                 [self addFactoryPresets:nil];
7274         }
7275         [fPresetsOutlineView reloadData];
7276     
7277     [self checkBuiltInsForUpdates];
7278 }
7279
7280 - (void) checkBuiltInsForUpdates {
7281     
7282         BOOL updateBuiltInPresets = NO;
7283     int i = 0;
7284     NSEnumerator *enumerator = [UserPresets objectEnumerator];
7285     id tempObject;
7286     while (tempObject = [enumerator nextObject])
7287     {
7288         /* iterate through the built in presets to see if any have an old build number */
7289         NSMutableDictionary *thisPresetDict = tempObject;
7290         /*Key Type == 0 is built in, and key PresetBuildNumber is the build number it was created with */
7291         if ([[thisPresetDict objectForKey:@"Type"] intValue] == 0)              
7292         {
7293                         if (![thisPresetDict objectForKey:@"PresetBuildNumber"] || [[thisPresetDict objectForKey:@"PresetBuildNumber"] intValue] < [[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] intValue])
7294             {
7295                 updateBuiltInPresets = YES;
7296             }   
7297                 }
7298         i++;
7299     }
7300     /* if we have built in presets to update, then do so AlertBuiltInPresetUpdate*/
7301     if ( updateBuiltInPresets == YES)
7302     {
7303         if( [[NSUserDefaults standardUserDefaults] boolForKey:@"AlertBuiltInPresetUpdate"] == YES)
7304         {
7305             /* Show an alert window that built in presets will be updated */
7306             /*On Screen Notification*/
7307             int status;
7308             NSBeep();
7309             status = NSRunAlertPanel(@"HandBrake has determined your built in presets are out of date...",@"HandBrake will now update your built-in presets.", @"OK", nil, nil);
7310             [NSApp requestUserAttention:NSCriticalRequest];
7311         }
7312         /* when alert is dismissed, go ahead and update the built in presets */
7313         [self addFactoryPresets:nil];
7314     }
7315     
7316 }
7317
7318
7319 - (IBAction) addPresetPicDropdownChanged: (id) sender
7320 {
7321     if ([fPresetNewPicSettingsPopUp indexOfSelectedItem] == 1)
7322     {
7323         [fPresetNewPicWidthHeightBox setHidden:NO];  
7324     }
7325     else
7326     {
7327         [fPresetNewPicWidthHeightBox setHidden:YES];
7328     }
7329 }
7330
7331 - (IBAction) showAddPresetPanel: (id) sender
7332 {
7333     /* Deselect the currently selected Preset if there is one*/
7334     [fPresetsOutlineView deselectRow:[fPresetsOutlineView selectedRow]];
7335
7336     /* Populate the preset picture settings popup here */
7337     [fPresetNewPicSettingsPopUp removeAllItems];
7338     [fPresetNewPicSettingsPopUp addItemWithTitle:@"None"];
7339     [fPresetNewPicSettingsPopUp addItemWithTitle:@"Custom"];
7340     [fPresetNewPicSettingsPopUp addItemWithTitle:@"Source Maximum (post source scan)"];
7341     [fPresetNewPicSettingsPopUp selectItemAtIndex: 0];  
7342     /* Uncheck the preset use filters checkbox */
7343     [fPresetNewPicFiltersCheck setState:NSOffState];
7344     // fPresetNewFolderCheck
7345     [fPresetNewFolderCheck setState:NSOffState];
7346     /* Erase info from the input fields*/
7347         [fPresetNewName setStringValue: @""];
7348         [fPresetNewDesc setStringValue: @""];
7349     
7350     /* Initialize custom height and width settings to current values */
7351     
7352         [fPresetNewPicWidth setStringValue: [NSString stringWithFormat:@"%d",fTitle->job->width]];
7353         [fPresetNewPicHeight setStringValue: [NSString stringWithFormat:@"%d",fTitle->job->height]];
7354     [self addPresetPicDropdownChanged:nil];
7355         /* Show the panel */
7356         [NSApp beginSheet:fAddPresetPanel modalForWindow:fWindow modalDelegate:nil didEndSelector:NULL contextInfo:NULL];
7357 }
7358
7359 - (IBAction) closeAddPresetPanel: (id) sender
7360 {
7361     [NSApp endSheet: fAddPresetPanel];
7362     [fAddPresetPanel orderOut: self];
7363 }
7364
7365 - (IBAction)addUserPreset:(id)sender
7366 {
7367     if (![[fPresetNewName stringValue] length])
7368             NSRunAlertPanel(@"Warning!", @"You need to insert a name for the preset.", @"OK", nil , nil);
7369     else
7370     {
7371         /* Here we create a custom user preset */
7372         [UserPresets addObject:[self createPreset]];
7373         [self addPreset];
7374
7375         [self closeAddPresetPanel:nil];
7376     }
7377 }
7378 - (void)addPreset
7379 {
7380
7381         
7382         /* We Reload the New Table data for presets */
7383     [fPresetsOutlineView reloadData];
7384    /* We save all of the preset data here */
7385     [self savePreset];
7386 }
7387
7388 - (void)sortPresets
7389 {
7390
7391         
7392         /* We Sort the Presets By Factory or Custom */
7393         NSSortDescriptor * presetTypeDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"Type" 
7394                                                     ascending:YES] autorelease];
7395         /* We Sort the Presets Alphabetically by name  We do not use this now as we have drag and drop*/
7396         /*
7397     NSSortDescriptor * presetNameDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"PresetName" 
7398                                                     ascending:YES selector:@selector(caseInsensitiveCompare:)] autorelease];
7399         //NSArray *sortDescriptors=[NSArray arrayWithObjects:presetTypeDescriptor,presetNameDescriptor,nil];
7400     
7401     */
7402     /* Since we can drag and drop our custom presets, lets just sort by type and not name */
7403     NSArray *sortDescriptors=[NSArray arrayWithObjects:presetTypeDescriptor,nil];
7404         NSArray *sortedArray=[UserPresets sortedArrayUsingDescriptors:sortDescriptors];
7405         [UserPresets setArray:sortedArray];
7406         
7407
7408 }
7409
7410 - (IBAction)insertPreset:(id)sender
7411 {
7412     int index = [fPresetsOutlineView selectedRow];
7413     [UserPresets insertObject:[self createPreset] atIndex:index];
7414     [fPresetsOutlineView reloadData];
7415     [self savePreset];
7416 }
7417
7418 - (NSDictionary *)createPreset
7419 {
7420     NSMutableDictionary *preset = [[NSMutableDictionary alloc] init];
7421     /* Preset build number */
7422     [preset setObject:[NSString stringWithFormat: @"%d", [[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] intValue]] forKey:@"PresetBuildNumber"];
7423     [preset setObject:[fPresetNewName stringValue] forKey:@"PresetName"];
7424         /* Get the New Preset Name from the field in the AddPresetPanel */
7425     [preset setObject:[fPresetNewName stringValue] forKey:@"PresetName"];
7426     /* Set whether or not this is to be a folder fPresetNewFolderCheck*/
7427     [preset setObject:[NSNumber numberWithBool:[fPresetNewFolderCheck state]] forKey:@"Folder"];
7428         /*Set whether or not this is a user preset or factory 0 is factory, 1 is user*/
7429         [preset setObject:[NSNumber numberWithInt:1] forKey:@"Type"];
7430         /*Set whether or not this is default, at creation set to 0*/
7431         [preset setObject:[NSNumber numberWithInt:0] forKey:@"Default"];
7432     if ([fPresetNewFolderCheck state] == YES)
7433     {
7434         /* initialize and set an empty array for children here since we are a new folder */
7435         NSMutableArray *childrenArray = [[NSMutableArray alloc] init];
7436         [preset setObject:[NSMutableArray arrayWithArray: childrenArray] forKey:@"ChildrenArray"];
7437         [childrenArray autorelease];
7438     }
7439     else // we are not creating a preset folder, so we go ahead with the rest of the preset info
7440     {
7441         /*Get the whether or not to apply pic Size and Cropping (includes Anamorphic)*/
7442         [preset setObject:[NSNumber numberWithInt:[fPresetNewPicSettingsPopUp indexOfSelectedItem]] forKey:@"UsesPictureSettings"];
7443         /* Get whether or not to use the current Picture Filter settings for the preset */
7444         [preset setObject:[NSNumber numberWithInt:[fPresetNewPicFiltersCheck state]] forKey:@"UsesPictureFilters"];
7445         
7446         /* Get New Preset Description from the field in the AddPresetPanel*/
7447         [preset setObject:[fPresetNewDesc stringValue] forKey:@"PresetDescription"];
7448         /* File Format */
7449         [preset setObject:[fDstFormatPopUp titleOfSelectedItem] forKey:@"FileFormat"];
7450         /* Chapter Markers fCreateChapterMarkers*/
7451         [preset setObject:[NSNumber numberWithInt:[fCreateChapterMarkers state]] forKey:@"ChapterMarkers"];
7452         /* Allow Mpeg4 64 bit formatting +4GB file sizes */
7453         [preset setObject:[NSNumber numberWithInt:[fDstMp4LargeFileCheck state]] forKey:@"Mp4LargeFile"];
7454         /* Mux mp4 with http optimization */
7455         [preset setObject:[NSNumber numberWithInt:[fDstMp4HttpOptFileCheck state]] forKey:@"Mp4HttpOptimize"];
7456         /* Add iPod uuid atom */
7457         [preset setObject:[NSNumber numberWithInt:[fDstMp4iPodFileCheck state]] forKey:@"Mp4iPodCompatible"];
7458         
7459         /* Codecs */
7460         /* Video encoder */
7461         [preset setObject:[fVidEncoderPopUp titleOfSelectedItem] forKey:@"VideoEncoder"];
7462         /* x264 Option String */
7463         [preset setObject:[fAdvancedOptions optionsString] forKey:@"x264Option"];
7464         
7465         [preset setObject:[NSNumber numberWithInt:[fVidQualityMatrix selectedRow]] forKey:@"VideoQualityType"];
7466         [preset setObject:[fVidTargetSizeField stringValue] forKey:@"VideoTargetSize"];
7467         [preset setObject:[fVidBitrateField stringValue] forKey:@"VideoAvgBitrate"];
7468         [preset setObject:[NSNumber numberWithFloat:[fVidQualityRFField floatValue]] forKey:@"VideoQualitySlider"];
7469         
7470         /* Video framerate */
7471         if ([fVidRatePopUp indexOfSelectedItem] == 0) // Same as source is selected
7472         {
7473             [preset setObject:@"Same as source" forKey:@"VideoFramerate"];
7474         }
7475         else // we can record the actual titleOfSelectedItem
7476         {
7477             [preset setObject:[fVidRatePopUp titleOfSelectedItem] forKey:@"VideoFramerate"];
7478         }
7479         [preset setObject:[NSNumber numberWithInt:[fFrameratePfrCheck state]] forKey:@"VideoFrameratePFR"];
7480         
7481         /* 2 Pass Encoding */
7482         [preset setObject:[NSNumber numberWithInt:[fVidTwoPassCheck state]] forKey:@"VideoTwoPass"];
7483         /* Turbo 2 pass Encoding fVidTurboPassCheck*/
7484         [preset setObject:[NSNumber numberWithInt:[fVidTurboPassCheck state]] forKey:@"VideoTurboTwoPass"];
7485         /*Picture Settings*/
7486         hb_job_t * job = fTitle->job;
7487         
7488         /* Picture Sizing */
7489         [preset setObject:[NSNumber numberWithInt:0] forKey:@"UsesMaxPictureSettings"];
7490         [preset setObject:[NSNumber numberWithInt:[fPresetNewPicWidth intValue]] forKey:@"PictureWidth"];
7491         [preset setObject:[NSNumber numberWithInt:[fPresetNewPicHeight intValue]] forKey:@"PictureHeight"];
7492         [preset setObject:[NSNumber numberWithInt:fTitle->job->keep_ratio] forKey:@"PictureKeepRatio"];
7493         [preset setObject:[NSNumber numberWithInt:fTitle->job->anamorphic.mode] forKey:@"PicturePAR"];
7494         [preset setObject:[NSNumber numberWithInt:fTitle->job->modulus] forKey:@"PictureModulus"];
7495         
7496         /* Set crop settings here */
7497         [preset setObject:[NSNumber numberWithInt:[fPictureController autoCrop]] forKey:@"PictureAutoCrop"];
7498         [preset setObject:[NSNumber numberWithInt:job->crop[0]] forKey:@"PictureTopCrop"];
7499         [preset setObject:[NSNumber numberWithInt:job->crop[1]] forKey:@"PictureBottomCrop"];
7500         [preset setObject:[NSNumber numberWithInt:job->crop[2]] forKey:@"PictureLeftCrop"];
7501         [preset setObject:[NSNumber numberWithInt:job->crop[3]] forKey:@"PictureRightCrop"];
7502         
7503         /* Picture Filters */
7504         [preset setObject:[NSNumber numberWithInt:[fPictureController useDecomb]] forKey:@"PictureDecombDeinterlace"];
7505         [preset setObject:[NSNumber numberWithInt:[fPictureController deinterlace]] forKey:@"PictureDeinterlace"];
7506         [preset setObject:[fPictureController deinterlaceCustomString] forKey:@"PictureDeinterlaceCustom"];
7507         [preset setObject:[NSNumber numberWithInt:[fPictureController detelecine]] forKey:@"PictureDetelecine"];
7508         [preset setObject:[fPictureController detelecineCustomString] forKey:@"PictureDetelecineCustom"];
7509         [preset setObject:[NSNumber numberWithInt:[fPictureController denoise]] forKey:@"PictureDenoise"];
7510         [preset setObject:[fPictureController denoiseCustomString] forKey:@"PictureDenoiseCustom"];
7511         [preset setObject:[NSNumber numberWithInt:[fPictureController deblock]] forKey:@"PictureDeblock"]; 
7512         [preset setObject:[NSNumber numberWithInt:[fPictureController decomb]] forKey:@"PictureDecomb"];
7513         [preset setObject:[fPictureController decombCustomString] forKey:@"PictureDecombCustom"];
7514         [preset setObject:[NSNumber numberWithInt:[fPictureController grayscale]] forKey:@"VideoGrayScale"];
7515         
7516         /*Audio*/
7517         NSMutableArray *audioListArray = [[NSMutableArray alloc] init];
7518         /* we actually call the methods for the nests here */
7519         if ([fAudLang1PopUp indexOfSelectedItem] > 0)
7520         {
7521             NSMutableDictionary *audioTrack1Array = [[NSMutableDictionary alloc] init];
7522             [audioTrack1Array setObject:[NSNumber numberWithInt:[fAudLang1PopUp indexOfSelectedItem]] forKey:@"AudioTrack"];
7523             [audioTrack1Array setObject:[fAudLang1PopUp titleOfSelectedItem] forKey:@"AudioTrackDescription"];
7524             [audioTrack1Array setObject:[fAudTrack1CodecPopUp titleOfSelectedItem] forKey:@"AudioEncoder"];
7525             [audioTrack1Array setObject:[fAudTrack1MixPopUp titleOfSelectedItem] forKey:@"AudioMixdown"];
7526             [audioTrack1Array setObject:[fAudTrack1RatePopUp titleOfSelectedItem] forKey:@"AudioSamplerate"];
7527             [audioTrack1Array setObject:[fAudTrack1BitratePopUp titleOfSelectedItem] forKey:@"AudioBitrate"];
7528             [audioTrack1Array setObject:[NSNumber numberWithFloat:[fAudTrack1DrcSlider floatValue]] forKey:@"AudioTrackDRCSlider"];
7529             [audioTrack1Array autorelease];
7530             [audioListArray addObject:audioTrack1Array];
7531         }
7532         
7533         if ([fAudLang2PopUp indexOfSelectedItem] > 0)
7534         {
7535             NSMutableDictionary *audioTrack2Array = [[NSMutableDictionary alloc] init];
7536             [audioTrack2Array setObject:[NSNumber numberWithInt:[fAudLang2PopUp indexOfSelectedItem]] forKey:@"AudioTrack"];
7537             [audioTrack2Array setObject:[fAudLang2PopUp titleOfSelectedItem] forKey:@"AudioTrackDescription"];
7538             [audioTrack2Array setObject:[fAudTrack2CodecPopUp titleOfSelectedItem] forKey:@"AudioEncoder"];
7539             [audioTrack2Array setObject:[fAudTrack2MixPopUp titleOfSelectedItem] forKey:@"AudioMixdown"];
7540             [audioTrack2Array setObject:[fAudTrack2RatePopUp titleOfSelectedItem] forKey:@"AudioSamplerate"];
7541             [audioTrack2Array setObject:[fAudTrack2BitratePopUp titleOfSelectedItem] forKey:@"AudioBitrate"];
7542             [audioTrack2Array setObject:[NSNumber numberWithFloat:[fAudTrack2DrcSlider floatValue]] forKey:@"AudioTrackDRCSlider"];
7543             [audioTrack2Array autorelease];
7544             [audioListArray addObject:audioTrack2Array];
7545         }
7546         
7547         if ([fAudLang3PopUp indexOfSelectedItem] > 0)
7548         {
7549             NSMutableDictionary *audioTrack3Array = [[NSMutableDictionary alloc] init];
7550             [audioTrack3Array setObject:[NSNumber numberWithInt:[fAudLang3PopUp indexOfSelectedItem]] forKey:@"AudioTrack"];
7551             [audioTrack3Array setObject:[fAudLang3PopUp titleOfSelectedItem] forKey:@"AudioTrackDescription"];
7552             [audioTrack3Array setObject:[fAudTrack3CodecPopUp titleOfSelectedItem] forKey:@"AudioEncoder"];
7553             [audioTrack3Array setObject:[fAudTrack3MixPopUp titleOfSelectedItem] forKey:@"AudioMixdown"];
7554             [audioTrack3Array setObject:[fAudTrack3RatePopUp titleOfSelectedItem] forKey:@"AudioSamplerate"];
7555             [audioTrack3Array setObject:[fAudTrack3BitratePopUp titleOfSelectedItem] forKey:@"AudioBitrate"];
7556             [audioTrack3Array setObject:[NSNumber numberWithFloat:[fAudTrack3DrcSlider floatValue]] forKey:@"AudioTrackDRCSlider"];
7557             [audioTrack3Array autorelease];
7558             [audioListArray addObject:audioTrack3Array];
7559         }
7560         
7561         if ([fAudLang4PopUp indexOfSelectedItem] > 0)
7562         {
7563             NSMutableDictionary *audioTrack4Array = [[NSMutableDictionary alloc] init];
7564             [audioTrack4Array setObject:[NSNumber numberWithInt:[fAudLang4PopUp indexOfSelectedItem]] forKey:@"AudioTrack"];
7565             [audioTrack4Array setObject:[fAudLang4PopUp titleOfSelectedItem] forKey:@"AudioTrackDescription"];
7566             [audioTrack4Array setObject:[fAudTrack4CodecPopUp titleOfSelectedItem] forKey:@"AudioEncoder"];
7567             [audioTrack4Array setObject:[fAudTrack4MixPopUp titleOfSelectedItem] forKey:@"AudioMixdown"];
7568             [audioTrack4Array setObject:[fAudTrack4RatePopUp titleOfSelectedItem] forKey:@"AudioSamplerate"];
7569             [audioTrack4Array setObject:[fAudTrack4BitratePopUp titleOfSelectedItem] forKey:@"AudioBitrate"];
7570             [audioTrack4Array setObject:[NSNumber numberWithFloat:[fAudTrack4DrcSlider floatValue]] forKey:@"AudioTrackDRCSlider"];
7571             [audioTrack4Array autorelease];
7572             [audioListArray addObject:audioTrack4Array];
7573         }
7574         
7575         
7576         [preset setObject:[NSMutableArray arrayWithArray: audioListArray] forKey:@"AudioList"];
7577
7578         
7579         /* Temporarily remove subtitles from creating a new preset as it has to be converted over to use the new
7580          * subititle array code. */
7581         /* Subtitles*/
7582         //[preset setObject:[fSubPopUp titleOfSelectedItem] forKey:@"Subtitles"];
7583         /* Forced Subtitles */
7584         //[preset setObject:[NSNumber numberWithInt:[fSubForcedCheck state]] forKey:@"SubtitlesForced"];
7585     }
7586     [preset autorelease];
7587     return preset;
7588     
7589 }
7590
7591 - (void)savePreset
7592 {
7593     [UserPresets writeToFile:UserPresetsFile atomically:YES];
7594         /* We get the default preset in case it changed */
7595         [self getDefaultPresets:nil];
7596
7597 }
7598
7599 - (IBAction)deletePreset:(id)sender
7600 {
7601     
7602     
7603     if ( [fPresetsOutlineView numberOfSelectedRows] == 0 )
7604     {
7605         return;
7606     }
7607     /* Alert user before deleting preset */
7608         int status;
7609     status = NSRunAlertPanel(@"Warning!", @"Are you sure that you want to delete the selected preset?", @"OK", @"Cancel", nil);
7610     
7611     if ( status == NSAlertDefaultReturn ) 
7612     {
7613         int presetToModLevel = [fPresetsOutlineView levelForItem: [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]]];
7614         NSDictionary *presetToMod = [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]];
7615         NSDictionary *presetToModParent = [fPresetsOutlineView parentForItem: presetToMod];
7616         
7617         NSEnumerator *enumerator;
7618         NSMutableArray *presetsArrayToMod;
7619         NSMutableArray *tempArray;
7620         id tempObject;
7621         /* If we are a root level preset, we are modding the UserPresets array */
7622         if (presetToModLevel == 0)
7623         {
7624             presetsArrayToMod = UserPresets;
7625         }
7626         else // We have a parent preset, so we modify the chidren array object for key
7627         {
7628             presetsArrayToMod = [presetToModParent objectForKey:@"ChildrenArray"]; 
7629         }
7630         
7631         enumerator = [presetsArrayToMod objectEnumerator];
7632         tempArray = [NSMutableArray array];
7633         
7634         while (tempObject = [enumerator nextObject]) 
7635         {
7636             NSDictionary *thisPresetDict = tempObject;
7637             if (thisPresetDict == presetToMod)
7638             {
7639                 [tempArray addObject:tempObject];
7640             }
7641         }
7642         
7643         [presetsArrayToMod removeObjectsInArray:tempArray];
7644         [fPresetsOutlineView reloadData];
7645         [self savePreset];   
7646     }
7647 }
7648
7649
7650 #pragma mark -
7651 #pragma mark Import Export Preset(s)
7652
7653 - (IBAction) browseExportPresetFile: (id) sender
7654 {
7655     /* Open a panel to let the user choose where and how to save the export file */
7656     NSSavePanel * panel = [NSSavePanel savePanel];
7657         /* We get the current file name and path from the destination field here */
7658     NSString *defaultExportDirectory = [NSString stringWithFormat: @"%@/Desktop/", NSHomeDirectory()];
7659
7660         [panel beginSheetForDirectory: defaultExportDirectory file: @"HB_Export.plist"
7661                                    modalForWindow: fWindow modalDelegate: self
7662                                    didEndSelector: @selector( browseExportPresetFileDone:returnCode:contextInfo: )
7663                                           contextInfo: NULL];
7664 }
7665
7666 - (void) browseExportPresetFileDone: (NSSavePanel *) sheet
7667                    returnCode: (int) returnCode contextInfo: (void *) contextInfo
7668 {
7669     if( returnCode == NSOKButton )
7670     {
7671         NSString *presetExportDirectory = [[sheet filename] stringByDeletingLastPathComponent];
7672         NSString *exportPresetsFile = [sheet filename];
7673         [[NSUserDefaults standardUserDefaults] setObject:presetExportDirectory forKey:@"LastPresetExportDirectory"];
7674         /* We check for the presets.plist */
7675         if ([[NSFileManager defaultManager] fileExistsAtPath:exportPresetsFile] == 0)
7676         {
7677             [[NSFileManager defaultManager] createFileAtPath:exportPresetsFile contents:nil attributes:nil];
7678         }
7679         NSMutableArray * presetsToExport = [[NSMutableArray alloc] initWithContentsOfFile:exportPresetsFile];
7680         if (nil == presetsToExport)
7681         {
7682             presetsToExport = [[NSMutableArray alloc] init];
7683             
7684             /* now get and add selected presets to export */
7685             
7686         }
7687         if ([fPresetsOutlineView selectedRow] >= 0 && [[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Folder"] intValue] != 1)
7688         {
7689             [presetsToExport addObject:[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]]];
7690             [presetsToExport writeToFile:exportPresetsFile atomically:YES];
7691             
7692         }
7693         
7694     }
7695 }
7696
7697
7698 - (IBAction) browseImportPresetFile: (id) sender
7699 {
7700
7701     NSOpenPanel * panel;
7702         
7703     panel = [NSOpenPanel openPanel];
7704     [panel setAllowsMultipleSelection: NO];
7705     [panel setCanChooseFiles: YES];
7706     [panel setCanChooseDirectories: NO ];
7707     NSString * sourceDirectory;
7708         if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastPresetImportDirectory"])
7709         {
7710                 sourceDirectory = [[NSUserDefaults standardUserDefaults] stringForKey:@"LastPresetImportDirectory"];
7711         }
7712         else
7713         {
7714                 sourceDirectory = @"~/Desktop";
7715                 sourceDirectory = [sourceDirectory stringByExpandingTildeInPath];
7716         }
7717     /* we open up the browse sources sheet here and call for browseSourcesDone after the sheet is closed
7718         * to evaluate whether we want to specify a title, we pass the sender in the contextInfo variable
7719         */
7720     /* set this for allowed file types, not sure if we should allow xml or not */
7721     NSArray *fileTypes = [NSArray arrayWithObjects:@"plist", @"xml", nil];
7722     [panel beginSheetForDirectory: sourceDirectory file: nil types: fileTypes
7723                    modalForWindow: fWindow modalDelegate: self
7724                    didEndSelector: @selector( browseImportPresetDone:returnCode:contextInfo: )
7725                       contextInfo: sender];
7726 }
7727
7728 - (void) browseImportPresetDone: (NSSavePanel *) sheet
7729                      returnCode: (int) returnCode contextInfo: (void *) contextInfo
7730 {
7731     if( returnCode == NSOKButton )
7732     {
7733         NSString *importPresetsDirectory = [[sheet filename] stringByDeletingLastPathComponent];
7734         NSString *importPresetsFile = [sheet filename];
7735         [[NSUserDefaults standardUserDefaults] setObject:importPresetsDirectory forKey:@"LastPresetImportDirectory"];
7736         /* NOTE: here we need to do some sanity checking to verify we do not hose up our presets file   */
7737         NSMutableArray * presetsToImport = [[NSMutableArray alloc] initWithContentsOfFile:importPresetsFile];
7738         /* iterate though the new array of presets to import and add them to our presets array */
7739         int i = 0;
7740         NSEnumerator *enumerator = [presetsToImport objectEnumerator];
7741         id tempObject;
7742         while (tempObject = [enumerator nextObject])
7743         {
7744             /* make any changes to the incoming preset we see fit */
7745             /* make sure the incoming preset is not tagged as default */
7746             [tempObject setObject:[NSNumber numberWithInt:0] forKey:@"Default"];
7747             /* prepend "(imported) to the name of the incoming preset for clarification since it can be changed */
7748             NSString * prependedName = [@"(import) " stringByAppendingString:[tempObject objectForKey:@"PresetName"]] ;
7749             [tempObject setObject:prependedName forKey:@"PresetName"];
7750             
7751             /* actually add the new preset to our presets array */
7752             [UserPresets addObject:tempObject];
7753             i++;
7754         }
7755         [presetsToImport autorelease];
7756         [self sortPresets];
7757         [self addPreset];
7758         
7759     }
7760 }
7761
7762 #pragma mark -
7763 #pragma mark Manage Default Preset
7764
7765 - (IBAction)getDefaultPresets:(id)sender
7766 {
7767         presetHbDefault = nil;
7768     presetUserDefault = nil;
7769     presetUserDefaultParent = nil;
7770     presetUserDefaultParentParent = nil;
7771     NSMutableDictionary *presetHbDefaultParent = nil;
7772     NSMutableDictionary *presetHbDefaultParentParent = nil;
7773     
7774     int i = 0;
7775     BOOL userDefaultFound = NO;
7776     presetCurrentBuiltInCount = 0;
7777     /* First we iterate through the root UserPresets array to check for defaults */
7778     NSEnumerator *enumerator = [UserPresets objectEnumerator];
7779         id tempObject;
7780         while (tempObject = [enumerator nextObject])
7781         {
7782                 NSMutableDictionary *thisPresetDict = tempObject;
7783                 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 1) // 1 is HB default
7784                 {
7785                         presetHbDefault = thisPresetDict;       
7786                 }
7787                 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 2) // 2 is User specified default
7788                 {
7789                         presetUserDefault = thisPresetDict;
7790             userDefaultFound = YES;
7791         }
7792         if ([[thisPresetDict objectForKey:@"Type"] intValue] == 0) // Type 0 is a built in preset               
7793         {
7794                         presetCurrentBuiltInCount++; // <--increment the current number of built in presets     
7795                 }
7796                 i++;
7797         
7798         /* if we run into a folder, go to level 1 and iterate through the children arrays for the default */
7799         if ([thisPresetDict objectForKey:@"ChildrenArray"])
7800         {
7801             NSMutableDictionary *thisPresetDictParent = thisPresetDict;
7802             NSEnumerator *enumerator = [[thisPresetDict objectForKey:@"ChildrenArray"] objectEnumerator];
7803             id tempObject;
7804             while (tempObject = [enumerator nextObject])
7805             {
7806                 NSMutableDictionary *thisPresetDict = tempObject;
7807                 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 1) // 1 is HB default
7808                 {
7809                     presetHbDefault = thisPresetDict;
7810                     presetHbDefaultParent = thisPresetDictParent;
7811                 }
7812                 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 2) // 2 is User specified default
7813                 {
7814                     presetUserDefault = thisPresetDict;
7815                     presetUserDefaultParent = thisPresetDictParent;
7816                     userDefaultFound = YES;
7817                 }
7818                 
7819                 /* if we run into a folder, go to level 2 and iterate through the children arrays for the default */
7820                 if ([thisPresetDict objectForKey:@"ChildrenArray"])
7821                 {
7822                     NSMutableDictionary *thisPresetDictParentParent = thisPresetDict;
7823                     NSEnumerator *enumerator = [[thisPresetDict objectForKey:@"ChildrenArray"] objectEnumerator];
7824                     id tempObject;
7825                     while (tempObject = [enumerator nextObject])
7826                     {
7827                         NSMutableDictionary *thisPresetDict = tempObject;
7828                         if ([[thisPresetDict objectForKey:@"Default"] intValue] == 1) // 1 is HB default
7829                         {
7830                             presetHbDefault = thisPresetDict;
7831                             presetHbDefaultParent = thisPresetDictParent;
7832                             presetHbDefaultParentParent = thisPresetDictParentParent;   
7833                         }
7834                         if ([[thisPresetDict objectForKey:@"Default"] intValue] == 2) // 2 is User specified default
7835                         {
7836                             presetUserDefault = thisPresetDict;
7837                             presetUserDefaultParent = thisPresetDictParent;
7838                             presetUserDefaultParentParent = thisPresetDictParentParent;
7839                             userDefaultFound = YES;     
7840                         }
7841                         
7842                     }
7843                 }
7844             }
7845         }
7846         
7847         }
7848     /* check to see if a user specified preset was found, if not then assign the parents for
7849      * the presetHbDefault so that we can open the parents for the nested presets
7850      */
7851     if (userDefaultFound == NO)
7852     {
7853         presetUserDefaultParent = presetHbDefaultParent;
7854         presetUserDefaultParentParent = presetHbDefaultParentParent;
7855     }
7856 }
7857
7858 - (IBAction)setDefaultPreset:(id)sender
7859 {
7860 /* We need to determine if the item is a folder */
7861    if ([[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Folder"] intValue] == 1)
7862    {
7863    return;
7864    }
7865
7866     int i = 0;
7867     NSEnumerator *enumerator = [UserPresets objectEnumerator];
7868         id tempObject;
7869         /* First make sure the old user specified default preset is removed */
7870     while (tempObject = [enumerator nextObject])
7871         {
7872                 NSMutableDictionary *thisPresetDict = tempObject;
7873                 if ([[tempObject objectForKey:@"Default"] intValue] != 1) // if not the default HB Preset, set to 0
7874                 {
7875                         [[UserPresets objectAtIndex:i] setObject:[NSNumber numberWithInt:0] forKey:@"Default"]; 
7876                 }
7877                 
7878                 /* if we run into a folder, go to level 1 and iterate through the children arrays for the default */
7879         if ([thisPresetDict objectForKey:@"ChildrenArray"])
7880         {
7881             NSEnumerator *enumerator = [[thisPresetDict objectForKey:@"ChildrenArray"] objectEnumerator];
7882             id tempObject;
7883             int ii = 0;
7884             while (tempObject = [enumerator nextObject])
7885             {
7886                 NSMutableDictionary *thisPresetDict1 = tempObject;
7887                 if ([[tempObject objectForKey:@"Default"] intValue] != 1) // if not the default HB Preset, set to 0
7888                 {
7889                     [[[thisPresetDict objectForKey:@"ChildrenArray"] objectAtIndex:ii] setObject:[NSNumber numberWithInt:0] forKey:@"Default"]; 
7890                 }
7891                 /* if we run into a folder, go to level 2 and iterate through the children arrays for the default */
7892                 if ([thisPresetDict1 objectForKey:@"ChildrenArray"])
7893                 {
7894                     NSEnumerator *enumerator = [[thisPresetDict1 objectForKey:@"ChildrenArray"] objectEnumerator];
7895                     id tempObject;
7896                     int iii = 0;
7897                     while (tempObject = [enumerator nextObject])
7898                     {
7899                         if ([[tempObject objectForKey:@"Default"] intValue] != 1) // if not the default HB Preset, set to 0
7900                         {
7901                             [[[thisPresetDict1 objectForKey:@"ChildrenArray"] objectAtIndex:iii] setObject:[NSNumber numberWithInt:0] forKey:@"Default"];       
7902                         }
7903                         iii++;
7904                     }
7905                 }
7906                 ii++;
7907             }
7908             
7909         }
7910         i++; 
7911         }
7912     
7913     
7914     int presetToModLevel = [fPresetsOutlineView levelForItem: [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]]];
7915     NSDictionary *presetToMod = [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]];
7916     NSDictionary *presetToModParent = [fPresetsOutlineView parentForItem: presetToMod];
7917     
7918     
7919     NSMutableArray *presetsArrayToMod;
7920     NSMutableArray *tempArray;
7921     
7922     /* If we are a root level preset, we are modding the UserPresets array */
7923     if (presetToModLevel == 0)
7924     {
7925         presetsArrayToMod = UserPresets;
7926     }
7927     else // We have a parent preset, so we modify the chidren array object for key
7928     {
7929         presetsArrayToMod = [presetToModParent objectForKey:@"ChildrenArray"]; 
7930     }
7931     
7932     enumerator = [presetsArrayToMod objectEnumerator];
7933     tempArray = [NSMutableArray array];
7934     int iiii = 0;
7935     while (tempObject = [enumerator nextObject]) 
7936     {
7937         NSDictionary *thisPresetDict = tempObject;
7938         if (thisPresetDict == presetToMod)
7939         {
7940             if ([[tempObject objectForKey:@"Default"] intValue] != 1) // if not the default HB Preset, set to 2
7941             {
7942                 [[presetsArrayToMod objectAtIndex:iiii] setObject:[NSNumber numberWithInt:2] forKey:@"Default"];        
7943             }
7944         }
7945      iiii++;
7946      }
7947     
7948     
7949     /* We save all of the preset data here */
7950     [self savePreset];
7951     /* We Reload the New Table data for presets */
7952     [fPresetsOutlineView reloadData];
7953 }
7954
7955 - (IBAction)selectDefaultPreset:(id)sender
7956 {
7957         NSMutableDictionary *presetToMod;
7958     /* if there is a user specified default, we use it */
7959         if (presetUserDefault)
7960         {
7961         presetToMod = presetUserDefault;
7962     }
7963         else if (presetHbDefault) //else we use the built in default presetHbDefault
7964         {
7965         presetToMod = presetHbDefault;
7966         }
7967     else
7968     {
7969     return;
7970     }
7971     
7972     if (presetUserDefaultParent != nil)
7973     {
7974         [fPresetsOutlineView expandItem:presetUserDefaultParent];
7975         
7976     }
7977     if (presetUserDefaultParentParent != nil)
7978     {
7979         [fPresetsOutlineView expandItem:presetUserDefaultParentParent];
7980         
7981     }
7982     
7983     [fPresetsOutlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:[fPresetsOutlineView rowForItem: presetToMod]] byExtendingSelection:NO];
7984         [self selectPreset:nil];
7985 }
7986
7987
7988 #pragma mark -
7989 #pragma mark Manage Built In Presets
7990
7991
7992 - (IBAction)deleteFactoryPresets:(id)sender
7993 {
7994     //int status;
7995     NSEnumerator *enumerator = [UserPresets objectEnumerator];
7996         id tempObject;
7997     
7998         //NSNumber *index;
7999     NSMutableArray *tempArray;
8000
8001
8002         tempArray = [NSMutableArray array];
8003         /* we look here to see if the preset is we move on to the next one */
8004         while ( tempObject = [enumerator nextObject] )  
8005                 {
8006                         /* if the preset is "Factory" then we put it in the array of
8007                         presets to delete */
8008                         if ([[tempObject objectForKey:@"Type"] intValue] == 0)
8009                         {
8010                                 [tempArray addObject:tempObject];
8011                         }
8012         }
8013         
8014         [UserPresets removeObjectsInArray:tempArray];
8015         [fPresetsOutlineView reloadData];
8016         [self savePreset];   
8017
8018 }
8019
8020    /* We use this method to recreate new, updated factory presets */
8021 - (IBAction)addFactoryPresets:(id)sender
8022 {
8023     
8024     /* First, we delete any existing built in presets */
8025     [self deleteFactoryPresets: sender];
8026     /* Then we generate new built in presets programmatically with fPresetsBuiltin
8027      * which is all setup in HBPresets.h and  HBPresets.m*/
8028     [fPresetsBuiltin generateBuiltinPresets:UserPresets];
8029     /* update build number for built in presets */
8030     /* iterate though the new array of presets to import and add them to our presets array */
8031     int i = 0;
8032     NSEnumerator *enumerator = [UserPresets objectEnumerator];
8033     id tempObject;
8034     while (tempObject = [enumerator nextObject])
8035     {
8036         /* Record the apps current build number in the PresetBuildNumber key */
8037         if ([[tempObject objectForKey:@"Type"] intValue] == 0) // Type 0 is a built in preset           
8038         {
8039             /* Preset build number */
8040             [[UserPresets objectAtIndex:i] setObject:[NSNumber numberWithInt:[[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] intValue]] forKey:@"PresetBuildNumber"];
8041         }
8042         i++;
8043     }
8044     /* report the built in preset updating to the activity log */
8045     [self writeToActivityLog: "built in presets updated to build number: %d", [[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] intValue]];
8046     
8047     [self sortPresets];
8048     [self addPreset];
8049     
8050 }
8051
8052 #pragma mark -
8053 #pragma mark Chapter Files Import / Export
8054
8055 - (IBAction) browseForChapterFile: (id) sender
8056 {
8057         /* Open a panel to let the user choose the file */
8058         NSOpenPanel * panel = [NSOpenPanel openPanel];
8059         /* We get the current file name and path from the destination field here */
8060         [panel beginSheetForDirectory: [NSString stringWithFormat:@"%@/",
8061                                     [[NSUserDefaults standardUserDefaults] stringForKey:@"LastDestinationDirectory"]]
8062                              file: NULL
8063                             types: [NSArray arrayWithObjects:@"csv",nil]
8064                    modalForWindow: fWindow modalDelegate: self
8065                    didEndSelector: @selector( browseForChapterFileDone:returnCode:contextInfo: )
8066                       contextInfo: NULL];
8067 }
8068
8069 - (void) browseForChapterFileDone: (NSOpenPanel *) sheet
8070     returnCode: (int) returnCode contextInfo: (void *) contextInfo
8071 {
8072     NSArray *chaptersArray; /* temp array for chapters */
8073         NSMutableArray *chaptersMutableArray; /* temp array for chapters */
8074     NSString *chapterName;      /* temp string from file */
8075     int chapters, i;
8076     
8077     if( returnCode == NSOKButton )  /* if they click OK */
8078     {   
8079         chapterName = [[NSString alloc] initWithContentsOfFile:[sheet filename] encoding:NSUTF8StringEncoding error:NULL];
8080         chaptersArray = [chapterName componentsSeparatedByString:@"\n"];
8081         chaptersMutableArray= [chaptersArray mutableCopy];
8082                 chapters = [fChapterTitlesDelegate numberOfRowsInTableView:fChapterTable];
8083         if ([chaptersMutableArray count] > 0)
8084         { 
8085         /* if last item is empty remove it */
8086             if ([[chaptersMutableArray objectAtIndex:[chaptersArray count]-1] length] == 0)
8087             {
8088                 [chaptersMutableArray removeLastObject];
8089             }
8090         }
8091         /* if chapters in table is not equal to array count */
8092         if ((unsigned int) chapters != [chaptersMutableArray count])
8093         {
8094             [sheet close];
8095             [[NSAlert alertWithMessageText:NSLocalizedString(@"Unable to load chapter file", @"Unable to load chapter file")
8096                              defaultButton:NSLocalizedString(@"OK", @"OK")
8097                            alternateButton:NULL 
8098                                otherButton:NULL
8099                  informativeTextWithFormat:NSLocalizedString(@"%d chapters expected, %d chapters found in %@", @"%d chapters expected, %d chapters found in %@"), 
8100               chapters, [chaptersMutableArray count], [[sheet filename] lastPathComponent]] runModal];
8101             return;
8102         }
8103                 /* otherwise, go ahead and populate table with array */
8104                 for (i=0; i<chapters; i++)
8105         {
8106          
8107             if([[chaptersMutableArray objectAtIndex:i] length] > 5)
8108             { 
8109                 /* avoid a segfault */
8110                 /* Get the Range.location of the first comma in the line and then put everything after that into chapterTitle */
8111                 NSRange firstCommaRange = [[chaptersMutableArray objectAtIndex:i] rangeOfString:@","];
8112                 NSString *chapterTitle = [[chaptersMutableArray objectAtIndex:i] substringFromIndex:firstCommaRange.location + 1];
8113                 /* Since we store our chapterTitle commas as "\," for the cli, we now need to remove the escaping "\" from the title */
8114                 chapterTitle = [chapterTitle stringByReplacingOccurrencesOfString:@"\\," withString:@","];
8115                 [fChapterTitlesDelegate tableView:fChapterTable 
8116                                    setObjectValue:chapterTitle
8117                                    forTableColumn:fChapterTableNameColumn
8118                                               row:i];
8119             }
8120             else 
8121             {
8122                 [sheet close];
8123                 [[NSAlert alertWithMessageText:NSLocalizedString(@"Unable to load chapter file", @"Unable to load chapter file")
8124                                  defaultButton:NSLocalizedString(@"OK", @"OK")
8125                                alternateButton:NULL 
8126                                    otherButton:NULL
8127                      informativeTextWithFormat:NSLocalizedString(@"%@ was not formatted as expected.", @"%@ was not formatted as expected."), [[sheet filename] lastPathComponent]] runModal];   
8128                 [fChapterTable reloadData];
8129                 return;
8130             }
8131         }
8132         [fChapterTable reloadData];
8133     }
8134 }
8135
8136 - (IBAction) browseForChapterFileSave: (id) sender
8137 {
8138     NSSavePanel *panel = [NSSavePanel savePanel];
8139     /* Open a panel to let the user save to a file */
8140     [panel setAllowedFileTypes:[NSArray arrayWithObjects:@"csv",nil]];
8141     [panel beginSheetForDirectory: [[fDstFile2Field stringValue] stringByDeletingLastPathComponent] 
8142                              file: [[[[fDstFile2Field stringValue] lastPathComponent] stringByDeletingPathExtension] 
8143                                      stringByAppendingString:@"-chapters.csv"]
8144                    modalForWindow: fWindow 
8145                     modalDelegate: self
8146                    didEndSelector: @selector( browseForChapterFileSaveDone:returnCode:contextInfo: )
8147                       contextInfo: NULL];
8148 }
8149
8150 - (void) browseForChapterFileSaveDone: (NSSavePanel *) sheet
8151     returnCode: (int) returnCode contextInfo: (void *) contextInfo
8152 {
8153     NSString *chapterName;      /* pointer for string for later file-writing */
8154     NSString *chapterTitle;
8155     NSError *saveError = [[NSError alloc] init];
8156     int chapters, i;    /* ints for the number of chapters in the table and the loop */
8157     
8158     if( returnCode == NSOKButton )   /* if they clicked OK */
8159     {   
8160         chapters = [fChapterTitlesDelegate numberOfRowsInTableView:fChapterTable];
8161         chapterName = [NSString string];
8162         for (i=0; i<chapters; i++)
8163         {
8164             /* put each chapter title from the table into the array */
8165             if (i<9)
8166             { /* if i is from 0 to 8 (chapters 1 to 9) add two leading zeros */
8167                 chapterName = [chapterName stringByAppendingFormat:@"00%d,",i+1];
8168             }
8169             else if (i<99)
8170             { /* if i is from 9 to 98 (chapters 10 to 99) add one leading zero */
8171                 chapterName = [chapterName stringByAppendingFormat:@"0%d,",i+1];
8172             }
8173             else if (i<999)
8174             { /* in case i is from 99 to 998 (chapters 100 to 999) no leading zeros */
8175                 chapterName = [chapterName stringByAppendingFormat:@"%d,",i+1];
8176             }
8177             
8178             chapterTitle = [fChapterTitlesDelegate tableView:fChapterTable objectValueForTableColumn:fChapterTableNameColumn row:i];
8179             /* escape any commas in the chapter name with "\," */
8180             chapterTitle = [chapterTitle stringByReplacingOccurrencesOfString:@"," withString:@"\\,"];
8181             chapterName = [chapterName stringByAppendingString:chapterTitle];
8182             if (i+1 != chapters)
8183             { /* if not the last chapter */
8184                 chapterName = [chapterName stringByAppendingString:@ "\n"];
8185             }
8186
8187             
8188         }
8189         /* try to write it to where the user wanted */
8190         if (![chapterName writeToFile:[sheet filename] 
8191                            atomically:NO 
8192                              encoding:NSUTF8StringEncoding 
8193                                 error:&saveError])
8194         {
8195             [sheet close];
8196             [[NSAlert alertWithError:saveError] runModal];
8197         }
8198     }
8199 }
8200
8201 @end
8202
8203 /*******************************
8204  * Subclass of the HBPresetsOutlineView *
8205  *******************************/
8206
8207 @implementation HBPresetsOutlineView
8208 - (NSImage *)dragImageForRowsWithIndexes:(NSIndexSet *)dragRows tableColumns:(NSArray *)tableColumns event:(NSEvent*)dragEvent offset:(NSPointPointer)dragImageOffset
8209 {
8210     fIsDragging = YES;
8211
8212     // By default, NSTableView only drags an image of the first column. Change this to
8213     // drag an image of the queue's icon and PresetName columns.
8214     NSArray * cols = [NSArray arrayWithObjects: [self tableColumnWithIdentifier:@"PresetName"], nil];
8215     return [super dragImageForRowsWithIndexes:dragRows tableColumns:cols event:dragEvent offset:dragImageOffset];
8216 }
8217
8218
8219
8220 - (void) mouseDown:(NSEvent *)theEvent
8221 {
8222     [super mouseDown:theEvent];
8223         fIsDragging = NO;
8224 }
8225
8226
8227
8228 - (BOOL) isDragging;
8229 {
8230     return fIsDragging;
8231 }
8232 @end
8233
8234
8235