OSDN Git Service

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