OSDN Git Service

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