OSDN Git Service

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