OSDN Git Service

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