OSDN Git Service

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