OSDN Git Service

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