OSDN Git Service

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