OSDN Git Service

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