OSDN Git Service

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