OSDN Git Service

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