OSDN Git Service

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