OSDN Git Service

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