1 /* $Id: Controller.mm,v 1.79 2005/11/04 19:41:32 titer Exp $
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. */
7 #include "Controller.h"
8 #include "a52dec/a52.h"
9 #import "HBOutputPanelController.h"
10 #import "HBPreferencesController.h"
11 /* Added to integrate scanning into HBController */
12 #include <IOKit/IOKitLib.h>
13 #include <IOKit/storage/IOMedia.h>
14 #include <IOKit/storage/IODVDMedia.h>
15 #include "HBDVDDetector.h"
16 #include "dvdread/dvd_reader.h"
17 #include "HBPresets.h"
19 #define DragDropSimplePboardType @"MyCustomOutlineViewPboardType"
21 /* We setup the toolbar values here */
22 static NSString * ToggleDrawerIdentifier = @"Toggle Drawer Item Identifier";
23 static NSString * StartEncodingIdentifier = @"Start Encoding Item Identifier";
24 static NSString * PauseEncodingIdentifier = @"Pause Encoding Item Identifier";
25 static NSString * ShowQueueIdentifier = @"Show Queue Item Identifier";
26 static NSString * AddToQueueIdentifier = @"Add to Queue Item Identifier";
27 static NSString * ShowActivityIdentifier = @"Debug Output Item Identifier";
28 static NSString * ChooseSourceIdentifier = @"Choose Source Item Identifier";
31 /*******************************
32 * HBController implementation *
33 *******************************/
34 @implementation HBController
44 [HBPreferencesController registerUserDefaults];
47 /* Check for check for the app support directory here as
48 * outputPanel needs it right away, as may other future methods
50 NSString *libraryDir = [NSSearchPathForDirectoriesInDomains( NSLibraryDirectory,
52 YES ) objectAtIndex:0];
53 AppSupportDirectory = [[libraryDir stringByAppendingPathComponent:@"Application Support"]
54 stringByAppendingPathComponent:@"HandBrake"];
55 if( ![[NSFileManager defaultManager] fileExistsAtPath:AppSupportDirectory] )
57 [[NSFileManager defaultManager] createDirectoryAtPath:AppSupportDirectory
61 outputPanel = [[HBOutputPanelController alloc] init];
62 fPictureController = [[PictureController alloc] initWithDelegate:self];
63 fQueueController = [[HBQueueController alloc] init];
64 fAdvancedOptions = [[HBAdvancedController alloc] init];
65 /* we init the HBPresets class which currently is only used
66 * for updating built in presets, may move more functionality
69 fPresetsBuiltin = [[HBPresets alloc] init];
70 fPreferencesController = [[HBPreferencesController alloc] init];
71 /* Lets report the HandBrake version number here to the activity log and text log file */
72 NSString *versionStringFull = [[NSString stringWithFormat: @"Handbrake Version: %@", [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleGetInfoString"]] stringByAppendingString: [NSString stringWithFormat: @" (%@)", [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"]]];
73 [self writeToActivityLog: "%s", [versionStringFull UTF8String]];
79 - (void) applicationDidFinishLaunching: (NSNotification *) notification
81 /* Init libhb with check for updates libhb style set to "0" so its ignored and lets sparkle take care of it */
82 fHandle = hb_init(HB_DEBUG_ALL, 0);
84 // Set the Growl Delegate
85 [GrowlApplicationBridge setGrowlDelegate: self];
86 /* Init others controllers */
87 [fPictureController SetHandle: fHandle];
88 [fQueueController setHandle: fHandle];
89 [fQueueController setHBController: self];
91 fChapterTitlesDelegate = [[ChapterTitles alloc] init];
92 [fChapterTable setDataSource:fChapterTitlesDelegate];
93 [fChapterTable setDelegate:fChapterTitlesDelegate];
95 /* Call UpdateUI every 1/2 sec */
96 [[NSRunLoop currentRunLoop] addTimer:[NSTimer
97 scheduledTimerWithTimeInterval:0.5 target:self
98 selector:@selector(updateUI:) userInfo:nil repeats:YES]
99 forMode:NSEventTrackingRunLoopMode];
101 // Open debug output window now if it was visible when HB was closed
102 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"OutputPanelIsOpen"])
103 [self showDebugOutputPanel:nil];
105 // Open queue window now if it was visible when HB was closed
106 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"QueueWindowIsOpen"])
107 [self showQueueWindow:nil];
109 [self openMainWindow:nil];
111 /* Show Browse Sources Window ASAP */
112 [self performSelectorOnMainThread:@selector(browseSources:)
113 withObject:nil waitUntilDone:NO];
116 - (NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication *) app
118 // Warn if encoding a movie
120 hb_get_state( fHandle, &s );
121 HBJobGroup * jobGroup = [fQueueController currentJobGroup];
122 if ( jobGroup && ( s.state != HB_STATE_IDLE ) )
124 int result = NSRunCriticalAlertPanel(
125 NSLocalizedString(@"Are you sure you want to quit HandBrake?", nil),
126 NSLocalizedString(@"%@ is currently encoding. If you quit HandBrake, your movie will be lost. Do you want to quit anyway?", nil),
127 NSLocalizedString(@"Quit", nil), NSLocalizedString(@"Don't Quit", nil), nil,
128 jobGroup ? [jobGroup name] : @"A movie" );
130 if (result == NSAlertDefaultReturn)
132 [self doCancelCurrentJob];
133 return NSTerminateNow;
136 return NSTerminateCancel;
139 // Warn if items still in the queue
140 else if ( hb_count( fHandle ) > 0 )
142 int result = NSRunCriticalAlertPanel(
143 NSLocalizedString(@"Are you sure you want to quit HandBrake?", nil),
144 NSLocalizedString(@"One or more encodes are queued for encoding. Do you want to quit anyway?", nil),
145 NSLocalizedString(@"Quit", nil), NSLocalizedString(@"Don't Quit", nil), nil);
147 if ( result == NSAlertDefaultReturn )
148 return NSTerminateNow;
150 return NSTerminateCancel;
153 return NSTerminateNow;
156 - (void)applicationWillTerminate:(NSNotification *)aNotification
158 [browsedSourceDisplayName release];
159 [outputPanel release];
160 [fQueueController release];
165 - (void) awakeFromNib
168 [fWindow setExcludedFromWindowsMenu:YES];
169 [fAdvancedOptions setView:fAdvancedView];
171 /* lets setup our presets drawer for drag and drop here */
172 [fPresetsOutlineView registerForDraggedTypes: [NSArray arrayWithObject:DragDropSimplePboardType] ];
173 [fPresetsOutlineView setDraggingSourceOperationMask:NSDragOperationEvery forLocal:YES];
174 [fPresetsOutlineView setVerticalMotionCanBeginDrag: YES];
176 /* Initialize currentScanCount so HB can use it to
177 evaluate successive scans */
178 currentScanCount = 0;
180 /* Init UserPresets .plist */
183 fRipIndicatorShown = NO; // initially out of view in the nib
185 /* Show/Dont Show Presets drawer upon launch based
186 on user preference DefaultPresetsDrawerShow*/
187 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultPresetsDrawerShow"] > 0)
189 [fPresetDrawer open];
195 NSMenuItem *menuItem;
196 [fDstFormatPopUp removeAllItems];
198 menuItem = [[fDstFormatPopUp menu] addItemWithTitle:@"MP4 file" action: NULL keyEquivalent: @""];
199 [menuItem setTag: HB_MUX_MP4];
201 menuItem = [[fDstFormatPopUp menu] addItemWithTitle:@"MKV file" action: NULL keyEquivalent: @""];
202 [menuItem setTag: HB_MUX_MKV];
204 menuItem = [[fDstFormatPopUp menu] addItemWithTitle:@"AVI file" action: NULL keyEquivalent: @""];
205 [menuItem setTag: HB_MUX_AVI];
207 menuItem = [[fDstFormatPopUp menu] addItemWithTitle:@"OGM file" action: NULL keyEquivalent: @""];
208 [menuItem setTag: HB_MUX_OGM];
209 [fDstFormatPopUp selectItemAtIndex: 0];
211 [self formatPopUpChanged:nil];
213 /* We enable the create chapters checkbox here since we are .mp4 */
214 [fCreateChapterMarkers setEnabled: YES];
215 if ([fDstFormatPopUp indexOfSelectedItem] == 0 && [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultChapterMarkers"] > 0)
217 [fCreateChapterMarkers setState: NSOnState];
223 [fDstFile2Field setStringValue: [NSString stringWithFormat:
224 @"%@/Desktop/Movie.mp4", NSHomeDirectory()]];
227 [fVidEncoderPopUp removeAllItems];
228 [fVidEncoderPopUp addItemWithTitle: @"FFmpeg"];
229 [fVidEncoderPopUp addItemWithTitle: @"XviD"];
234 [fVidTargetSizeField setIntValue: 700];
235 [fVidBitrateField setIntValue: 1000];
237 [fVidQualityMatrix selectCell: fVidBitrateCell];
238 [self videoMatrixChanged:nil];
240 /* Video framerate */
241 [fVidRatePopUp removeAllItems];
242 [fVidRatePopUp addItemWithTitle: NSLocalizedString( @"Same as source", @"" )];
243 for( int i = 0; i < hb_video_rates_count; i++ )
245 if ([[NSString stringWithCString: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%.3f",23.976]])
247 [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@",
248 [NSString stringWithCString: hb_video_rates[i].string], @" (NTSC Film)"]];
250 else if ([[NSString stringWithCString: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%d",25]])
252 [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@",
253 [NSString stringWithCString: hb_video_rates[i].string], @" (PAL Film/Video)"]];
255 else if ([[NSString stringWithCString: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%.2f",29.97]])
257 [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@",
258 [NSString stringWithCString: hb_video_rates[i].string], @" (NTSC Video)"]];
262 [fVidRatePopUp addItemWithTitle:
263 [NSString stringWithCString: hb_video_rates[i].string]];
266 [fVidRatePopUp selectItemAtIndex: 0];
268 /* Set Auto Crop to On at launch */
269 [fPictureController setAutoCrop:YES];
272 [fAudTrack1BitratePopUp removeAllItems];
273 for( int i = 0; i < hb_audio_bitrates_count; i++ )
275 [fAudTrack1BitratePopUp addItemWithTitle:
276 [NSString stringWithCString: hb_audio_bitrates[i].string]];
279 [fAudTrack1BitratePopUp selectItemAtIndex: hb_audio_bitrates_default];
281 /* Audio samplerate */
282 [fAudTrack1RatePopUp removeAllItems];
283 for( int i = 0; i < hb_audio_rates_count; i++ )
285 [fAudTrack1RatePopUp addItemWithTitle:
286 [NSString stringWithCString: hb_audio_rates[i].string]];
288 [fAudTrack1RatePopUp selectItemAtIndex: hb_audio_rates_default];
291 [fStatusField setStringValue: @""];
296 /* We disable the Turbo 1st pass checkbox since we are not x264 */
297 [fVidTurboPassCheck setEnabled: NO];
298 [fVidTurboPassCheck setState: NSOffState];
301 /* lets get our default prefs here */
302 [self getDefaultPresets:nil];
303 /* lets initialize the current successful scancount here to 0 */
304 currentSuccessfulScanCount = 0;
307 - (void) enableUI: (bool) b
309 NSControl * controls[] =
310 { fSrcTitleField, fSrcTitlePopUp,
311 fSrcChapterField, fSrcChapterStartPopUp, fSrcChapterToField,
312 fSrcChapterEndPopUp, fSrcDuration1Field, fSrcDuration2Field,
313 fDstFormatField, fDstFormatPopUp, fDstFile1Field, fDstFile2Field,
314 fDstBrowseButton, fVidRateField, fVidRatePopUp,
315 fVidEncoderField, fVidEncoderPopUp, fVidQualityField,
316 fVidQualityMatrix, fVidGrayscaleCheck, fSubField, fSubPopUp,
317 fAudSourceLabel, fAudCodecLabel, fAudMixdownLabel, fAudSamplerateLabel, fAudBitrateLabel,
318 fAudTrack1Label, fAudTrack2Label, fAudTrack3Label, fAudTrack4Label,
319 fAudLang1PopUp, fAudLang2PopUp, fAudLang3PopUp, fAudLang4PopUp,
320 fAudTrack1CodecPopUp, fAudTrack2CodecPopUp, fAudTrack3CodecPopUp, fAudTrack4CodecPopUp,
321 fAudTrack1MixPopUp, fAudTrack2MixPopUp, fAudTrack3MixPopUp, fAudTrack4MixPopUp,
322 fAudTrack1RatePopUp, fAudTrack2RatePopUp, fAudTrack3RatePopUp, fAudTrack4RatePopUp,
323 fAudTrack1BitratePopUp, fAudTrack2BitratePopUp, fAudTrack3BitratePopUp, fAudTrack4BitratePopUp,
324 fAudDrcLabel, fAudTrack1DrcSlider, fAudTrack1DrcField, fAudTrack2DrcSlider,
325 fAudTrack2DrcField, fAudTrack3DrcSlider, fAudTrack3DrcField, fAudTrack4DrcSlider,fAudTrack4DrcField,
326 fPictureButton,fQueueStatus,fPicSettingARkeep, fPicSettingDeinterlace,fPicLabelSettings,fPicLabelSrc,
327 fPicLabelOutp,fPicSettingsSrc,fPicSettingsOutp,fPicSettingsAnamorphic,
328 fPicLabelAr,fPicLabelDeinterlace,fPicSettingPAR,fPicLabelAnamorphic,fPresetsAdd,fPresetsDelete,
329 fCreateChapterMarkers,fVidTurboPassCheck,fDstMp4LargeFileCheck,fPicLabelAutoCrop,
330 fPicSettingAutoCrop,fPicSettingDetelecine,fPicLabelDetelecine,fPicLabelDenoise,fPicSettingDenoise,
331 fSubForcedCheck,fPicSettingDeblock,fPicLabelDeblock,fPicLabelDecomb,fPicSettingDecomb,fPresetsOutlineView,
332 fAudDrcLabel,fDstMp4HttpOptFileCheck,fDstMp4iPodFileCheck};
335 i < sizeof( controls ) / sizeof( NSControl * ); i++ )
337 if( [[controls[i] className] isEqualToString: @"NSTextField"] )
339 NSTextField * tf = (NSTextField *) controls[i];
340 if( ![tf isBezeled] )
342 [tf setTextColor: b ? [NSColor controlTextColor] :
343 [NSColor disabledControlTextColor]];
347 [controls[i] setEnabled: b];
353 /* if we're enabling the interface, check if the audio mixdown controls need to be enabled or not */
354 /* these will have been enabled by the mass control enablement above anyway, so we're sense-checking it here */
355 [self setEnabledStateOfAudioMixdownControls:nil];
356 /* we also call calculatePictureSizing here to sense check if we already have vfr selected */
357 [self calculatePictureSizing:nil];
361 [fPresetsOutlineView setEnabled: NO];
365 [self videoMatrixChanged:nil];
366 [fAdvancedOptions enableUI:b];
370 /***********************************************************************
372 ***********************************************************************
373 * Shows a progression bar on the dock icon, filled according to
374 * 'progress' (0.0 <= progress <= 1.0).
375 * Called with progress < 0.0 or progress > 1.0, restores the original
377 **********************************************************************/
378 - (void) UpdateDockIcon: (float) progress
382 NSBitmapImageRep * bmp;
384 uint32_t black = htonl( 0x000000FF );
385 uint32_t red = htonl( 0xFF0000FF );
386 uint32_t white = htonl( 0xFFFFFFFF );
387 int row_start, row_end;
390 /* Get application original icon */
391 icon = [NSImage imageNamed: @"NSApplicationIcon"];
393 if( progress < 0.0 || progress > 1.0 )
395 [NSApp setApplicationIconImage: icon];
399 /* Get it in a raw bitmap form */
400 tiff = [icon TIFFRepresentationUsingCompression:
401 NSTIFFCompressionNone factor: 1.0];
402 bmp = [NSBitmapImageRep imageRepWithData: tiff];
404 /* Draw the progression bar */
405 /* It's pretty simple (ugly?) now, but I'm no designer */
407 row_start = 3 * (int) [bmp size].height / 4;
408 row_end = 7 * (int) [bmp size].height / 8;
410 for( i = row_start; i < row_start + 2; i++ )
412 pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
413 for( j = 0; j < (int) [bmp size].width; j++ )
418 for( i = row_start + 2; i < row_end - 2; i++ )
420 pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
423 for( j = 2; j < (int) [bmp size].width - 2; j++ )
425 if( j < 2 + (int) ( ( [bmp size].width - 4.0 ) * progress ) )
437 for( i = row_end - 2; i < row_end; i++ )
439 pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] );
440 for( j = 0; j < (int) [bmp size].width; j++ )
446 /* Now update the dock icon */
447 tiff = [bmp TIFFRepresentationUsingCompression:
448 NSTIFFCompressionNone factor: 1.0];
449 icon = [[NSImage alloc] initWithData: tiff];
450 [NSApp setApplicationIconImage: icon];
454 - (void) updateUI: (NSTimer *) timer
458 list = hb_get_titles( fHandle );
459 /* check to see if there has been a new scan done
460 this bypasses the constraints of HB_STATE_WORKING
461 not allowing setting a newly scanned source */
462 int checkScanCount = hb_get_scancount( fHandle );
463 if (checkScanCount > currentScanCount)
465 currentScanCount = checkScanCount;
466 [fScanIndicator setIndeterminate: NO];
467 [fScanIndicator setDoubleValue: 0.0];
468 [fScanIndicator setHidden: YES];
469 [self showNewScan:nil];
473 hb_get_state( fHandle, &s );
480 #define p s.param.scanning
481 case HB_STATE_SCANNING:
483 [fSrcDVD2Field setStringValue: [NSString stringWithFormat:
484 NSLocalizedString( @"Scanning title %d of %d...", @"" ),
485 p.title_cur, p.title_count]];
486 [fScanIndicator setHidden: NO];
487 [fScanIndicator setDoubleValue: 100.0 * ( p.title_cur - 1 ) / p.title_count];
492 #define p s.param.scandone
493 case HB_STATE_SCANDONE:
495 [fScanIndicator setIndeterminate: NO];
496 [fScanIndicator setDoubleValue: 0.0];
497 [fScanIndicator setHidden: YES];
498 [self showNewScan:nil];
499 [[fWindow toolbar] validateVisibleItems];
504 #define p s.param.working
505 case HB_STATE_WORKING:
507 float progress_total;
508 NSMutableString * string;
509 /* Currently, p.job_cur and p.job_count get screwed up when adding
510 jobs during encoding, if they cannot be fixed in libhb, will implement a
511 nasty but working cocoa solution */
512 /* Update text field */
513 string = [NSMutableString stringWithFormat: NSLocalizedString( @"Encoding: task %d of %d, %.2f %%", @"" ), p.job_cur, p.job_count, 100.0 * p.progress];
517 [string appendFormat:
518 NSLocalizedString( @" (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)", @"" ),
519 p.rate_cur, p.rate_avg, p.hours, p.minutes, p.seconds];
521 [fStatusField setStringValue: string];
524 progress_total = ( p.progress + p.job_cur - 1 ) / p.job_count;
525 [fRipIndicator setIndeterminate: NO];
526 [fRipIndicator setDoubleValue: 100.0 * progress_total];
528 // If progress bar hasn't been revealed at the bottom of the window, do
529 // that now. This code used to be in doRip. I moved it to here to handle
530 // the case where hb_start is called by HBQueueController and not from
532 if (!fRipIndicatorShown)
534 NSRect frame = [fWindow frame];
535 if (frame.size.width <= 591)
536 frame.size.width = 591;
537 frame.size.height += 36;
538 frame.origin.y -= 36;
539 [fWindow setFrame:frame display:YES animate:YES];
540 fRipIndicatorShown = YES;
541 /* We check to see if we need to warn the user that the computer will go to sleep
542 or shut down when encoding is finished */
543 [self remindUserOfSleepOrShutdown];
546 /* Update dock icon */
547 [self UpdateDockIcon: progress_total];
549 // Has current job changed? That means the queue has probably changed as
551 [fQueueController libhbStateChanged: s];
557 #define p s.param.muxing
558 case HB_STATE_MUXING:
560 NSMutableString * string;
562 /* Update text field */
563 string = [NSMutableString stringWithFormat:
564 NSLocalizedString( @"Muxing...", @"" )];
565 [fStatusField setStringValue: string];
568 [fRipIndicator setIndeterminate: YES];
569 [fRipIndicator startAnimation: nil];
571 /* Update dock icon */
572 [self UpdateDockIcon: 1.0];
574 // Pass along the info to HBQueueController
575 [fQueueController libhbStateChanged: s];
581 case HB_STATE_PAUSED:
582 [fStatusField setStringValue: NSLocalizedString( @"Paused", @"" )];
584 // Pass along the info to HBQueueController
585 [fQueueController libhbStateChanged: s];
589 case HB_STATE_WORKDONE:
591 // HB_STATE_WORKDONE happpens as a result of libhb finishing all its jobs
592 // or someone calling hb_stop. In the latter case, hb_stop does not clear
593 // out the remaining passes/jobs in the queue. We'll do that here.
595 // Delete all remaining jobs of this encode.
597 while( ( job = hb_job( fHandle, 0 ) ) && ( !IsFirstPass(job->sequence_id) ) )
598 hb_rem( fHandle, job );
600 [fStatusField setStringValue: NSLocalizedString( @"Done.", @"" )];
601 [fRipIndicator setIndeterminate: NO];
602 [fRipIndicator setDoubleValue: 0.0];
603 [[fWindow toolbar] validateVisibleItems];
605 /* Restore dock icon */
606 [self UpdateDockIcon: -1.0];
608 if (fRipIndicatorShown)
610 NSRect frame = [fWindow frame];
611 if (frame.size.width <= 591)
612 frame.size.width = 591;
613 frame.size.height += -36;
614 frame.origin.y -= -36;
615 [fWindow setFrame:frame display:YES animate:YES];
616 fRipIndicatorShown = NO;
619 // Pass along the info to HBQueueController
620 [fQueueController libhbStateChanged: s];
622 /* Check to see if the encode state has not been cancelled
623 to determine if we should check for encode done notifications */
624 if (fEncodeState != 2) {
625 /* If Alert Window or Window and Growl has been selected */
626 if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window"] ||
627 [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window And Growl"])
629 /*On Screen Notification*/
632 status = NSRunAlertPanel(@"Put down that cocktail...",@"Your HandBrake encode is done!", @"OK", nil, nil);
633 [NSApp requestUserAttention:NSCriticalRequest];
634 if ( status == NSAlertDefaultReturn )
636 [self enableUI: YES];
641 [self enableUI: YES];
643 /* If sleep has been selected */
644 if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Put Computer To Sleep"])
647 NSDictionary* errorDict;
648 NSAppleEventDescriptor* returnDescriptor = nil;
649 NSAppleScript* scriptObject = [[NSAppleScript alloc] initWithSource:
650 @"tell application \"Finder\" to sleep"];
651 returnDescriptor = [scriptObject executeAndReturnError: &errorDict];
652 [scriptObject release];
653 [self enableUI: YES];
655 /* If Shutdown has been selected */
656 if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Shut Down Computer"])
659 NSDictionary* errorDict;
660 NSAppleEventDescriptor* returnDescriptor = nil;
661 NSAppleScript* scriptObject = [[NSAppleScript alloc] initWithSource:
662 @"tell application \"Finder\" to shut down"];
663 returnDescriptor = [scriptObject executeAndReturnError: &errorDict];
664 [scriptObject release];
665 [self enableUI: YES];
671 [self enableUI: YES];
677 /* Lets show the queue status here in the main window */
678 int queue_count = [fQueueController pendingCount];
679 if( queue_count == 1)
680 [fQueueStatus setStringValue: NSLocalizedString( @"1 encode queued", @"" ) ];
681 else if (queue_count > 1)
682 [fQueueStatus setStringValue: [NSString stringWithFormat: NSLocalizedString( @"%d encodes queued", @"" ), queue_count]];
684 [fQueueStatus setStringValue: @""];
687 /* We use this to write messages to stderr from the macgui which show up in the activity window and log*/
688 - (void) writeToActivityLog:(char *) format, ...
691 va_start(args, format);
695 vsnprintf( str, 1024, format, args );
697 time_t _now = time( NULL );
698 struct tm * now = localtime( &_now );
699 fprintf(stderr, "[%02d:%02d:%02d] macgui: %s\n", now->tm_hour, now->tm_min, now->tm_sec, str );
706 // ============================================================
707 // NSToolbar Related Methods
708 // ============================================================
710 - (void) setupToolbar {
711 NSToolbar *toolbar = [[[NSToolbar alloc] initWithIdentifier: @"HandBrake Toolbar"] autorelease];
713 [toolbar setAllowsUserCustomization: YES];
714 [toolbar setAutosavesConfiguration: YES];
715 [toolbar setDisplayMode: NSToolbarDisplayModeIconAndLabel];
717 [toolbar setDelegate: self];
719 [fWindow setToolbar: toolbar];
722 - (NSToolbarItem *) toolbar: (NSToolbar *)toolbar itemForItemIdentifier:
723 (NSString *) itemIdent willBeInsertedIntoToolbar:(BOOL) willBeInserted {
724 NSToolbarItem * item = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdent] autorelease];
726 if ([itemIdent isEqualToString: ToggleDrawerIdentifier])
728 [item setLabel: @"Toggle Presets"];
729 [item setPaletteLabel: @"Toggler Presets"];
730 [item setToolTip: @"Open/Close Preset Drawer"];
731 [item setImage: [NSImage imageNamed: @"Drawer"]];
732 [item setTarget: self];
733 [item setAction: @selector(toggleDrawer:)];
734 [item setAutovalidates: NO];
736 else if ([itemIdent isEqualToString: StartEncodingIdentifier])
738 [item setLabel: @"Start"];
739 [item setPaletteLabel: @"Start Encoding"];
740 [item setToolTip: @"Start Encoding"];
741 [item setImage: [NSImage imageNamed: @"Play"]];
742 [item setTarget: self];
743 [item setAction: @selector(Rip:)];
745 else if ([itemIdent isEqualToString: ShowQueueIdentifier])
747 [item setLabel: @"Show Queue"];
748 [item setPaletteLabel: @"Show Queue"];
749 [item setToolTip: @"Show Queue"];
750 [item setImage: [NSImage imageNamed: @"Queue"]];
751 [item setTarget: self];
752 [item setAction: @selector(showQueueWindow:)];
753 [item setAutovalidates: NO];
755 else if ([itemIdent isEqualToString: AddToQueueIdentifier])
757 [item setLabel: @"Add to Queue"];
758 [item setPaletteLabel: @"Add to Queue"];
759 [item setToolTip: @"Add to Queue"];
760 [item setImage: [NSImage imageNamed: @"AddToQueue"]];
761 [item setTarget: self];
762 [item setAction: @selector(addToQueue:)];
764 else if ([itemIdent isEqualToString: PauseEncodingIdentifier])
766 [item setLabel: @"Pause"];
767 [item setPaletteLabel: @"Pause Encoding"];
768 [item setToolTip: @"Pause Encoding"];
769 [item setImage: [NSImage imageNamed: @"Pause"]];
770 [item setTarget: self];
771 [item setAction: @selector(Pause:)];
773 else if ([itemIdent isEqualToString: ShowActivityIdentifier]) {
774 [item setLabel: @"Activity Window"];
775 [item setPaletteLabel: @"Show Activity Window"];
776 [item setToolTip: @"Show Activity Window"];
777 [item setImage: [NSImage imageNamed: @"ActivityWindow"]];
778 [item setTarget: self];
779 [item setAction: @selector(showDebugOutputPanel:)];
780 [item setAutovalidates: NO];
782 else if ([itemIdent isEqualToString: ChooseSourceIdentifier])
784 [item setLabel: @"Source"];
785 [item setPaletteLabel: @"Source"];
786 [item setToolTip: @"Choose Video Source"];
787 [item setImage: [NSImage imageNamed: @"Source"]];
788 [item setTarget: self];
789 [item setAction: @selector(browseSources:)];
799 - (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar
801 return [NSArray arrayWithObjects: ChooseSourceIdentifier, NSToolbarSeparatorItemIdentifier, StartEncodingIdentifier,
802 PauseEncodingIdentifier, AddToQueueIdentifier, ShowQueueIdentifier, NSToolbarFlexibleSpaceItemIdentifier,
803 NSToolbarSpaceItemIdentifier, ShowActivityIdentifier, ToggleDrawerIdentifier, nil];
806 - (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) toolbar
808 return [NSArray arrayWithObjects: StartEncodingIdentifier, PauseEncodingIdentifier, AddToQueueIdentifier,
809 ChooseSourceIdentifier, ShowQueueIdentifier, ShowActivityIdentifier, ToggleDrawerIdentifier,
810 NSToolbarCustomizeToolbarItemIdentifier, NSToolbarFlexibleSpaceItemIdentifier,
811 NSToolbarSpaceItemIdentifier, NSToolbarSeparatorItemIdentifier, nil];
814 - (BOOL) validateToolbarItem: (NSToolbarItem *) toolbarItem
816 NSString * ident = [toolbarItem itemIdentifier];
821 hb_get_state2( fHandle, &s );
823 if (s.state == HB_STATE_WORKING || s.state == HB_STATE_MUXING)
825 if ([ident isEqualToString: StartEncodingIdentifier])
827 [toolbarItem setImage: [NSImage imageNamed: @"Stop"]];
828 [toolbarItem setLabel: @"Stop"];
829 [toolbarItem setPaletteLabel: @"Stop"];
830 [toolbarItem setToolTip: @"Stop Encoding"];
833 if ([ident isEqualToString: PauseEncodingIdentifier])
835 [toolbarItem setImage: [NSImage imageNamed: @"Pause"]];
836 [toolbarItem setLabel: @"Pause"];
837 [toolbarItem setPaletteLabel: @"Pause Encoding"];
838 [toolbarItem setToolTip: @"Pause Encoding"];
842 if ([ident isEqualToString: AddToQueueIdentifier])
845 else if (s.state == HB_STATE_PAUSED)
847 if ([ident isEqualToString: PauseEncodingIdentifier])
849 [toolbarItem setImage: [NSImage imageNamed: @"Play"]];
850 [toolbarItem setLabel: @"Resume"];
851 [toolbarItem setPaletteLabel: @"Resume Encoding"];
852 [toolbarItem setToolTip: @"Resume Encoding"];
855 if ([ident isEqualToString: StartEncodingIdentifier])
857 if ([ident isEqualToString: AddToQueueIdentifier])
860 else if (s.state == HB_STATE_SCANNING)
862 else if (s.state == HB_STATE_WORKDONE || s.state == HB_STATE_SCANDONE || SuccessfulScan)
864 if ([ident isEqualToString: StartEncodingIdentifier])
866 [toolbarItem setImage: [NSImage imageNamed: @"Play"]];
867 if (hb_count(fHandle) > 0)
868 [toolbarItem setLabel: @"Start Queue"];
870 [toolbarItem setLabel: @"Start"];
871 [toolbarItem setPaletteLabel: @"Start Encoding"];
872 [toolbarItem setToolTip: @"Start Encoding"];
875 if ([ident isEqualToString: AddToQueueIdentifier])
881 if ([ident isEqualToString: ShowQueueIdentifier])
883 if ([ident isEqualToString: ToggleDrawerIdentifier])
885 if ([ident isEqualToString: ChooseSourceIdentifier])
887 if ([ident isEqualToString: ShowActivityIdentifier])
893 - (BOOL) validateMenuItem: (NSMenuItem *) menuItem
895 SEL action = [menuItem action];
898 hb_get_state2( fHandle, &s );
902 if (action == @selector(addToQueue:) || action == @selector(showPicturePanel:) || action == @selector(showAddPresetPanel:))
903 return SuccessfulScan && [fWindow attachedSheet] == nil;
905 if (action == @selector(browseSources:))
907 if (s.state == HB_STATE_SCANNING)
910 return [fWindow attachedSheet] == nil;
912 if (action == @selector(selectDefaultPreset:))
913 return [fPresetsOutlineView selectedRow] >= 0 && [fWindow attachedSheet] == nil;
914 if (action == @selector(Pause:))
916 if (s.state == HB_STATE_WORKING)
918 if(![[menuItem title] isEqualToString:@"Pause Encoding"])
919 [menuItem setTitle:@"Pause Encoding"];
922 else if (s.state == HB_STATE_PAUSED)
924 if(![[menuItem title] isEqualToString:@"Resume Encoding"])
925 [menuItem setTitle:@"Resume Encoding"];
931 if (action == @selector(Rip:))
933 if (s.state == HB_STATE_WORKING || s.state == HB_STATE_MUXING || s.state == HB_STATE_PAUSED)
935 if(![[menuItem title] isEqualToString:@"Stop Encoding"])
936 [menuItem setTitle:@"Stop Encoding"];
939 else if (SuccessfulScan)
941 if(![[menuItem title] isEqualToString:@"Start Encoding"])
942 [menuItem setTitle:@"Start Encoding"];
943 return [fWindow attachedSheet] == nil;
949 if( action == @selector(setDefaultPreset:) )
951 return [fPresetsOutlineView selectedRow] != -1;
958 #pragma mark Encode Done Actions
959 // register a test notification and make
960 // it enabled by default
961 #define SERVICE_NAME @"Encode Done"
962 - (NSDictionary *)registrationDictionaryForGrowl
964 NSDictionary *registrationDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
965 [NSArray arrayWithObjects:SERVICE_NAME,nil], GROWL_NOTIFICATIONS_ALL,
966 [NSArray arrayWithObjects:SERVICE_NAME,nil], GROWL_NOTIFICATIONS_DEFAULT,
969 return registrationDictionary;
972 -(void)showGrowlDoneNotification:(NSString *) filePath
974 /* This is called from HBQueueController as jobs roll off of the queue in currentJobChanged */
975 NSString * finishedEncode = filePath;
976 /* strip off the path to just show the file name */
977 finishedEncode = [finishedEncode lastPathComponent];
978 if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Growl Notification"] ||
979 [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window And Growl"])
981 NSString * growlMssg = [NSString stringWithFormat: @"your HandBrake encode %@ is done!",finishedEncode];
982 [GrowlApplicationBridge
983 notifyWithTitle:@"Put down that cocktail..."
984 description:growlMssg
985 notificationName:SERVICE_NAME
993 -(void)sendToMetaX:(NSString *) filePath
995 /* This is called from HBQueueController as jobs roll off of the queue in currentJobChanged */
996 if([[NSUserDefaults standardUserDefaults] boolForKey: @"sendToMetaX"] == YES)
998 NSAppleScript *myScript = [[NSAppleScript alloc] initWithSource: [NSString stringWithFormat: @"%@%@%@", @"tell application \"MetaX\" to open (POSIX file \"", filePath, @"\")"]];
999 [myScript executeAndReturnError: nil];
1004 #pragma mark Get New Source
1006 /*Opens the source browse window, called from Open Source widgets */
1007 - (IBAction) browseSources: (id) sender
1009 [self enableUI: NO];
1010 NSOpenPanel * panel;
1012 panel = [NSOpenPanel openPanel];
1013 [panel setAllowsMultipleSelection: NO];
1014 [panel setCanChooseFiles: YES];
1015 [panel setCanChooseDirectories: YES ];
1016 NSString * sourceDirectory;
1017 if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastSourceDirectory"])
1019 sourceDirectory = [[NSUserDefaults standardUserDefaults] stringForKey:@"LastSourceDirectory"];
1023 sourceDirectory = @"~/Desktop";
1024 sourceDirectory = [sourceDirectory stringByExpandingTildeInPath];
1026 /* we open up the browse sources sheet here and call for browseSourcesDone after the sheet is closed
1027 * to evaluate whether we want to specify a title, we pass the sender in the contextInfo variable
1029 [panel beginSheetForDirectory: sourceDirectory file: nil types: nil
1030 modalForWindow: fWindow modalDelegate: self
1031 didEndSelector: @selector( browseSourcesDone:returnCode:contextInfo: )
1032 contextInfo: sender];
1035 - (void) browseSourcesDone: (NSOpenPanel *) sheet
1036 returnCode: (int) returnCode contextInfo: (void *) contextInfo
1038 /* we convert the sender content of contextInfo back into a variable called sender
1039 * mostly just for consistency for evaluation later
1041 id sender = (id)contextInfo;
1042 /* User selected a file to open */
1043 if( returnCode == NSOKButton )
1045 /* Free display name allocated previously by this code */
1046 [browsedSourceDisplayName release];
1048 NSString *scanPath = [[sheet filenames] objectAtIndex: 0];
1049 /* we set the last searched source directory in the prefs here */
1050 NSString *sourceDirectory = [scanPath stringByDeletingLastPathComponent];
1051 [[NSUserDefaults standardUserDefaults] setObject:sourceDirectory forKey:@"LastSourceDirectory"];
1052 /* we order out sheet, which is the browse window as we need to open
1053 * the title selection sheet right away
1055 [sheet orderOut: self];
1057 if (sender == fOpenSourceTitleMMenu)
1059 /* We put the chosen source path in the source display text field for the
1060 * source title selection sheet in which the user specifies the specific title to be
1061 * scanned as well as the short source name in fSrcDsplyNameTitleScan just for display
1062 * purposes in the title panel
1065 [fScanSrcTitlePathField setStringValue:scanPath];
1066 NSString *displayTitlescanSourceName;
1068 if ([[scanPath lastPathComponent] isEqualToString: @"VIDEO_TS"])
1070 /* If VIDEO_TS Folder is chosen, choose its parent folder for the source display name
1071 we have to use the title->dvd value so we get the proper name of the volume if a physical dvd is the source*/
1072 displayTitlescanSourceName = [[scanPath stringByDeletingLastPathComponent] lastPathComponent];
1076 /* if not the VIDEO_TS Folder, we can assume the chosen folder is the source name */
1077 displayTitlescanSourceName = [scanPath lastPathComponent];
1079 /* we set the source display name in the title selection dialogue */
1080 [fSrcDsplyNameTitleScan setStringValue:displayTitlescanSourceName];
1081 /* we set the attempted scans display name for main window to displayTitlescanSourceName*/
1082 browsedSourceDisplayName = [displayTitlescanSourceName retain];
1083 /* We show the actual sheet where the user specifies the title to be scanned
1084 * as we are going to do a title specific scan
1086 [self showSourceTitleScanPanel:nil];
1090 /* We are just doing a standard full source scan, so we specify "0" to libhb */
1091 NSString *path = [[sheet filenames] objectAtIndex: 0];
1093 /* We check to see if the chosen file at path is a package */
1094 if ([[NSWorkspace sharedWorkspace] isFilePackageAtPath:path])
1096 [self writeToActivityLog: "trying to open a package at: %s", [path UTF8String]];
1097 /* We check to see if this is an .eyetv package */
1098 if ([[path pathExtension] isEqualToString: @"eyetv"])
1100 [self writeToActivityLog:"trying to open eyetv package"];
1101 /* We're looking at an EyeTV package - try to open its enclosed
1103 browsedSourceDisplayName = [[[path stringByDeletingPathExtension] lastPathComponent] retain];
1105 int n = [[path stringByAppendingString: @"/"]
1106 completePathIntoString: &mpgname caseSensitive: NO
1107 matchesIntoArray: nil
1108 filterTypes: [NSArray arrayWithObject: @"mpg"]];
1111 /* Found an mpeg inside the eyetv package, make it our scan path
1112 and call performScan on the enclosed mpeg */
1114 [self writeToActivityLog:"found mpeg in eyetv package"];
1115 [self performScan:path scanTitleNum:0];
1119 /* We did not find an mpeg file in our package, so we do not call performScan */
1120 [self writeToActivityLog:"no valid mpeg in eyetv package"];
1123 /* We check to see if this is a .dvdmedia package */
1124 else if ([[path pathExtension] isEqualToString: @"dvdmedia"])
1126 /* path IS a package - but dvdmedia packages can be treaded like normal directories */
1127 browsedSourceDisplayName = [[[path stringByDeletingPathExtension] lastPathComponent] retain];
1128 [self writeToActivityLog:"trying to open dvdmedia package"];
1129 [self performScan:path scanTitleNum:0];
1133 /* The package is not an eyetv package, so we do not call performScan */
1134 [self writeToActivityLog:"unable to open package"];
1137 else // path is not a package, so we treat it as a dvd parent folder or VIDEO_TS folder
1139 /* path is not a package, so we call perform scan directly on our file */
1140 if ([[path lastPathComponent] isEqualToString: @"VIDEO_TS"])
1142 [self writeToActivityLog:"trying to open video_ts folder (video_ts folder chosen)"];
1143 /* If VIDEO_TS Folder is chosen, choose its parent folder for the source display name*/
1144 browsedSourceDisplayName = [[[path stringByDeletingLastPathComponent] lastPathComponent] retain];
1148 [self writeToActivityLog:"trying to open video_ts folder (parent directory chosen)"];
1149 /* if not the VIDEO_TS Folder, we can assume the chosen folder is the source name */
1150 /* make sure we remove any path extension as this can also be an '.mpg' file */
1151 browsedSourceDisplayName = [[path lastPathComponent] retain];
1153 [self performScan:path scanTitleNum:0];
1159 else // User clicked Cancel in browse window
1161 /* if we have a title loaded up */
1162 if ([[fSrcDVD2Field stringValue] length] > 0 && SuccessfulScan)
1164 [self enableUI: YES];
1169 /* Here we open the title selection sheet where we can specify an exact title to be scanned */
1170 - (IBAction) showSourceTitleScanPanel: (id) sender
1172 /* We default the title number to be scanned to "0" which results in a full source scan, unless the
1175 [fScanSrcTitleNumField setStringValue: @"0"];
1176 /* Show the panel */
1177 [NSApp beginSheet:fScanSrcTitlePanel modalForWindow:fWindow modalDelegate:nil didEndSelector:NULL contextInfo:NULL];
1180 - (IBAction) closeSourceTitleScanPanel: (id) sender
1182 [NSApp endSheet: fScanSrcTitlePanel];
1183 [fScanSrcTitlePanel orderOut: self];
1185 if(sender == fScanSrcTitleOpenButton)
1187 /* We setup the scan status in the main window to indicate a source title scan */
1188 [fSrcDVD2Field setStringValue: @"Opening a new source title ..."];
1189 [fScanIndicator setHidden: NO];
1190 [fScanIndicator setIndeterminate: YES];
1191 [fScanIndicator startAnimation: nil];
1193 /* We use the performScan method to actually perform the specified scan passing the path and the title
1196 [self performScan:[fScanSrcTitlePathField stringValue] scanTitleNum:[fScanSrcTitleNumField intValue]];
1200 /* Here we actually tell hb_scan to perform the source scan, using the path to source and title number*/
1201 - (void) performScan:(NSString *) scanPath scanTitleNum: (int) scanTitleNum
1203 /* use a bool to determine whether or not we can decrypt using vlc */
1204 BOOL cancelScanDecrypt = 0;
1205 NSString *path = scanPath;
1206 HBDVDDetector *detector = [HBDVDDetector detectorForPath:path];
1208 // Notify ChapterTitles that there's no title
1209 [fChapterTitlesDelegate resetWithTitle:nil];
1210 [fChapterTable reloadData];
1212 if( [detector isVideoDVD] )
1214 // The chosen path was actually on a DVD, so use the raw block
1215 // device path instead.
1216 path = [detector devicePath];
1217 [self writeToActivityLog: "trying to open a physical dvd at: %s", [scanPath UTF8String]];
1219 /* lets check for vlc here to make sure we have a dylib available to use for decrypting */
1220 NSString *vlcPath = @"/Applications/VLC.app";
1221 NSFileManager * fileManager = [NSFileManager defaultManager];
1222 if ([fileManager fileExistsAtPath:vlcPath] == 0)
1224 /*vlc not found in /Applications so we set the bool to cancel scanning to 1 */
1225 cancelScanDecrypt = 1;
1226 [self writeToActivityLog: "VLC app not found for decrypting physical dvd"];
1228 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");
1229 [NSApp requestUserAttention:NSCriticalRequest];
1231 if (status == NSAlertDefaultReturn)
1233 /* User chose to go download vlc (as they rightfully should) so we send them to the vlc site */
1234 [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://www.videolan.org/"]];
1236 else if (status == NSAlertAlternateReturn)
1238 /* User chose to cancel the scan */
1239 [self writeToActivityLog: "cannot open physical dvd , scan cancelled"];
1243 /* 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 */
1244 cancelScanDecrypt = 0;
1245 [self writeToActivityLog: "user overrode vlc warning -trying to open physical dvd without decryption"];
1251 /* VLC was found in /Applications so all is well, we can carry on using vlc's libdvdcss.dylib for decrypting if needed */
1252 [self writeToActivityLog: "VLC app found for decrypting physical dvd"];
1256 if (cancelScanDecrypt == 0)
1258 /* we actually pass the scan off to libhb here */
1259 /* If there is no title number passed to scan, we use "0"
1260 * which causes the default behavior of a full source scan
1266 if (scanTitleNum > 0)
1268 [self writeToActivityLog: "scanning specifically for title: %d", scanTitleNum];
1271 hb_scan( fHandle, [path UTF8String], scanTitleNum );
1272 [fSrcDVD2Field setStringValue:@"Scanning new source ..."];
1276 /* if we have a title loaded up */
1277 if ([[fSrcDVD2Field stringValue] length] > 0 && SuccessfulScan)
1279 [self enableUI: YES];
1284 - (IBAction) showNewScan:(id)sender
1288 int indxpri=0; // Used to search the longuest title (default in combobox)
1289 int longuestpri=0; // Used to search the longuest title (default in combobox)
1291 list = hb_get_titles( fHandle );
1293 if( !hb_list_count( list ) )
1295 /* We display a message if a valid dvd source was not chosen */
1296 [fSrcDVD2Field setStringValue: @"No Valid Source Found"];
1297 SuccessfulScan = NO;
1299 // Notify ChapterTitles that there's no title
1300 [fChapterTitlesDelegate resetWithTitle:nil];
1301 [fChapterTable reloadData];
1305 /* We increment the successful scancount here by one,
1306 which we use at the end of this function to tell the gui
1307 if this is the first successful scan since launch and whether
1308 or not we should set all settings to the defaults */
1310 currentSuccessfulScanCount++;
1312 [[fWindow toolbar] validateVisibleItems];
1314 [fSrcTitlePopUp removeAllItems];
1315 for( int i = 0; i < hb_list_count( list ); i++ )
1317 title = (hb_title_t *) hb_list_item( list, i );
1319 currentSource = [NSString stringWithUTF8String: title->name];
1321 /*Set DVD Name at top of window with the browsedSourceDisplayName grokked right before -performScan */
1322 [fSrcDVD2Field setStringValue:browsedSourceDisplayName];
1324 /* Use the dvd name in the default output field here
1325 May want to add code to remove blank spaces for some dvd names*/
1326 /* Check to see if the last destination has been set,use if so, if not, use Desktop */
1327 if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastDestinationDirectory"])
1329 [fDstFile2Field setStringValue: [NSString stringWithFormat:
1330 @"%@/%@.mp4", [[NSUserDefaults standardUserDefaults] stringForKey:@"LastDestinationDirectory"],[browsedSourceDisplayName stringByDeletingPathExtension]]];
1334 [fDstFile2Field setStringValue: [NSString stringWithFormat:
1335 @"%@/Desktop/%@.mp4", NSHomeDirectory(),[browsedSourceDisplayName stringByDeletingPathExtension]]];
1338 if (longuestpri < title->hours*60*60 + title->minutes *60 + title->seconds)
1340 longuestpri=title->hours*60*60 + title->minutes *60 + title->seconds;
1343 /* Leave this here for now for reference. Keep it commented out or the macgui will crash in a
1344 * fiery wreck if you change sources.
1346 //[self formatPopUpChanged:NULL];
1348 [fSrcTitlePopUp addItemWithTitle: [NSString
1349 stringWithFormat: @"%d - %02dh%02dm%02ds",
1350 title->index, title->hours, title->minutes,
1354 // Select the longuest title
1355 [fSrcTitlePopUp selectItemAtIndex: indxpri];
1356 [self titlePopUpChanged:nil];
1358 SuccessfulScan = YES;
1359 [self enableUI: YES];
1361 /* if its the initial successful scan after awakeFromNib */
1362 if (currentSuccessfulScanCount == 1)
1364 [self selectDefaultPreset:nil];
1365 /* if Deinterlace upon launch is specified in the prefs, then set to 1 for "Fast",
1366 if not, then set to 0 for none */
1367 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultDeinterlaceOn"] > 0)
1369 [fPictureController setDeinterlace:1];
1373 [fPictureController setDeinterlace:0];
1375 /* lets set Denoise to index 0 or "None" since this is the first scan */
1376 [fPictureController setDenoise:0];
1378 [fPictureController setInitialPictureFilters];
1386 #pragma mark New Output Destination
1388 - (IBAction) browseFile: (id) sender
1390 /* Open a panel to let the user choose and update the text field */
1391 NSSavePanel * panel = [NSSavePanel savePanel];
1392 /* We get the current file name and path from the destination field here */
1393 [panel beginSheetForDirectory: [[fDstFile2Field stringValue] stringByDeletingLastPathComponent] file: [[fDstFile2Field stringValue] lastPathComponent]
1394 modalForWindow: fWindow modalDelegate: self
1395 didEndSelector: @selector( browseFileDone:returnCode:contextInfo: )
1399 - (void) browseFileDone: (NSSavePanel *) sheet
1400 returnCode: (int) returnCode contextInfo: (void *) contextInfo
1402 if( returnCode == NSOKButton )
1404 [fDstFile2Field setStringValue: [sheet filename]];
1410 #pragma mark Main Window Control
1412 - (IBAction) openMainWindow: (id) sender
1414 [fWindow makeKeyAndOrderFront:nil];
1417 - (BOOL) windowShouldClose: (id) sender
1422 - (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag
1425 [fWindow makeKeyAndOrderFront:nil];
1434 #pragma mark Job Handling
1439 hb_list_t * list = hb_get_titles( fHandle );
1440 hb_title_t * title = (hb_title_t *) hb_list_item( list,
1441 [fSrcTitlePopUp indexOfSelectedItem] );
1442 hb_job_t * job = title->job;
1443 hb_audio_config_t * audio;
1445 /* Chapter selection */
1446 job->chapter_start = [fSrcChapterStartPopUp indexOfSelectedItem] + 1;
1447 job->chapter_end = [fSrcChapterEndPopUp indexOfSelectedItem] + 1;
1449 /* Format (Muxer) and Video Encoder */
1450 job->mux = [[fDstFormatPopUp selectedItem] tag];
1451 job->vcodec = [[fVidEncoderPopUp selectedItem] tag];
1454 /* If mpeg-4, then set mpeg-4 specific options like chapters and > 4gb file sizes */
1455 if ([fDstFormatPopUp indexOfSelectedItem] == 0)
1457 /* We set the largeFileSize (64 bit formatting) variable here to allow for > 4gb files based on the format being
1458 mpeg4 and the checkbox being checked
1459 *Note: this will break compatibility with some target devices like iPod, etc.!!!!*/
1460 if ([fDstMp4LargeFileCheck state] == NSOnState)
1462 job->largeFileSize = 1;
1466 job->largeFileSize = 0;
1468 /* We set http optimized mp4 here */
1469 if ([fDstMp4HttpOptFileCheck state] == NSOnState)
1471 job->mp4_optimize = 1;
1475 job->mp4_optimize = 0;
1478 if ([fDstFormatPopUp indexOfSelectedItem] == 0 || [fDstFormatPopUp indexOfSelectedItem] == 1)
1480 /* We set the chapter marker extraction here based on the format being
1481 mpeg4 or mkv and the checkbox being checked */
1482 if ([fCreateChapterMarkers state] == NSOnState)
1484 job->chapter_markers = 1;
1488 job->chapter_markers = 0;
1492 if( job->vcodec & HB_VCODEC_X264 )
1494 if ([fDstMp4iPodFileCheck state] == NSOnState)
1503 /* Set this flag to switch from Constant Quantizer(default) to Constant Rate Factor Thanks jbrjake
1504 Currently only used with Constant Quality setting*/
1505 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultCrf"] > 0 && [fVidQualityMatrix selectedRow] == 2)
1510 /* Below Sends x264 options to the core library if x264 is selected*/
1511 /* Lets use this as per Nyx, Thanks Nyx!*/
1512 job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */
1513 /* Turbo first pass if two pass and Turbo First pass is selected */
1514 if( [fVidTwoPassCheck state] == NSOnState && [fVidTurboPassCheck state] == NSOnState )
1516 /* pass the "Turbo" string to be appended to the existing x264 opts string into a variable for the first pass */
1517 NSString *firstPassOptStringTurbo = @":ref=1:subme=1:me=dia:analyse=none:trellis=0:no-fast-pskip=0:8x8dct=0:weightb=0";
1518 /* append the "Turbo" string variable to the existing opts string.
1519 Note: the "Turbo" string must be appended, not prepended to work properly*/
1520 NSString *firstPassOptStringCombined = [[fAdvancedOptions optionsString] stringByAppendingString:firstPassOptStringTurbo];
1521 strcpy(job->x264opts, [firstPassOptStringCombined UTF8String]);
1525 strcpy(job->x264opts, [[fAdvancedOptions optionsString] UTF8String]);
1530 /* Video settings */
1531 if( [fVidRatePopUp indexOfSelectedItem] > 0 )
1533 job->vrate = 27000000;
1534 job->vrate_base = hb_video_rates[[fVidRatePopUp
1535 indexOfSelectedItem]-1].rate;
1536 /* We are not same as source so we set job->cfr to 1
1537 * to enable constant frame rate since user has specified
1538 * a specific framerate*/
1543 job->vrate = title->rate;
1544 job->vrate_base = title->rate_base;
1545 /* We are same as source so we set job->cfr to 0
1546 * to enable true same as source framerate */
1550 switch( [fVidQualityMatrix selectedRow] )
1554 Bitrate should already have been calculated and displayed
1555 in fVidBitrateField, so let's just use it */
1557 job->vquality = -1.0;
1558 job->vbitrate = [fVidBitrateField intValue];
1561 job->vquality = [fVidQualitySlider floatValue];
1566 job->grayscale = ( [fVidGrayscaleCheck state] == NSOnState );
1568 /* Subtitle settings */
1569 job->subtitle = [fSubPopUp indexOfSelectedItem] - 2;
1571 /* Audio tracks and mixdowns */
1572 /* Lets make sure there arent any erroneous audio tracks in the job list, so lets make sure its empty*/
1573 int audiotrack_count = hb_list_count(job->list_audio);
1574 for( int i = 0; i < audiotrack_count;i++)
1576 hb_audio_t * temp_audio = (hb_audio_t*) hb_list_item( job->list_audio, 0 );
1577 hb_list_rem(job->list_audio, temp_audio);
1579 /* Now lets add our new tracks to the audio list here */
1580 if ([fAudLang1PopUp indexOfSelectedItem] > 0)
1582 audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
1583 hb_audio_config_init(audio);
1584 audio->in.track = [fAudLang1PopUp indexOfSelectedItem] - 1;
1585 /* We go ahead and assign values to our audio->out.<properties> */
1586 audio->out.track = [fAudLang1PopUp indexOfSelectedItem] - 1;
1587 audio->out.codec = [[fAudTrack1CodecPopUp selectedItem] tag];
1588 audio->out.mixdown = [[fAudTrack1MixPopUp selectedItem] tag];
1589 audio->out.bitrate = [[fAudTrack1BitratePopUp selectedItem] tag];
1590 audio->out.samplerate = [[fAudTrack1RatePopUp selectedItem] tag];
1591 audio->out.dynamic_range_compression = [fAudTrack1DrcField floatValue];
1593 hb_audio_add( job, audio );
1596 if ([fAudLang2PopUp indexOfSelectedItem] > 0)
1598 audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
1599 hb_audio_config_init(audio);
1600 audio->in.track = [fAudLang2PopUp indexOfSelectedItem] - 1;
1601 /* We go ahead and assign values to our audio->out.<properties> */
1602 audio->out.track = [fAudLang2PopUp indexOfSelectedItem] - 1;
1603 audio->out.codec = [[fAudTrack2CodecPopUp selectedItem] tag];
1604 audio->out.mixdown = [[fAudTrack2MixPopUp selectedItem] tag];
1605 audio->out.bitrate = [[fAudTrack2BitratePopUp selectedItem] tag];
1606 audio->out.samplerate = [[fAudTrack2RatePopUp selectedItem] tag];
1607 audio->out.dynamic_range_compression = [fAudTrack2DrcField floatValue];
1609 hb_audio_add( job, audio );
1614 if ([fAudLang3PopUp indexOfSelectedItem] > 0)
1616 audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
1617 hb_audio_config_init(audio);
1618 audio->in.track = [fAudLang3PopUp indexOfSelectedItem] - 1;
1619 /* We go ahead and assign values to our audio->out.<properties> */
1620 audio->out.track = [fAudLang3PopUp indexOfSelectedItem] - 1;
1621 audio->out.codec = [[fAudTrack3CodecPopUp selectedItem] tag];
1622 audio->out.mixdown = [[fAudTrack3MixPopUp selectedItem] tag];
1623 audio->out.bitrate = [[fAudTrack3BitratePopUp selectedItem] tag];
1624 audio->out.samplerate = [[fAudTrack3RatePopUp selectedItem] tag];
1625 audio->out.dynamic_range_compression = [fAudTrack3DrcField floatValue];
1627 hb_audio_add( job, audio );
1632 if ([fAudLang4PopUp indexOfSelectedItem] > 0)
1634 audio = (hb_audio_config_t *) calloc(1, sizeof(*audio));
1635 hb_audio_config_init(audio);
1636 audio->in.track = [fAudLang4PopUp indexOfSelectedItem] - 1;
1637 /* We go ahead and assign values to our audio->out.<properties> */
1638 audio->out.track = [fAudLang4PopUp indexOfSelectedItem] - 1;
1639 audio->out.codec = [[fAudTrack4CodecPopUp selectedItem] tag];
1640 audio->out.mixdown = [[fAudTrack4MixPopUp selectedItem] tag];
1641 audio->out.bitrate = [[fAudTrack4BitratePopUp selectedItem] tag];
1642 audio->out.samplerate = [[fAudTrack4RatePopUp selectedItem] tag];
1643 audio->out.dynamic_range_compression = [fAudTrack4DrcField floatValue];
1645 hb_audio_add( job, audio );
1650 /* set vfr according to the Picture Window */
1651 if ([fPictureController vfr])
1661 job->filters = hb_list_init();
1663 /* Now lets call the filters if applicable.
1664 * The order of the filters is critical
1668 if ([fPictureController detelecine])
1670 hb_list_add( job->filters, &hb_filter_detelecine );
1674 if ([fPictureController decomb] > 0)
1676 /* Run old deinterlacer fd by default */
1678 hb_filter_decomb.settings = (char *) [[fPicSettingDecomb stringValue] UTF8String];
1679 //hb_filter_decomb.settings = "4:10:15:9:10:35:9"; // <-- jbrjakes recommended parameters as of 5/23/08
1680 hb_list_add( job->filters, &hb_filter_decomb );
1685 if ([fPictureController deinterlace] == 1)
1687 /* Run old deinterlacer fd by default */
1688 hb_filter_deinterlace.settings = "-1";
1689 hb_list_add( job->filters, &hb_filter_deinterlace );
1691 else if ([fPictureController deinterlace] == 2)
1693 /* Yadif mode 0 (without spatial deinterlacing.) */
1694 hb_filter_deinterlace.settings = "2";
1695 hb_list_add( job->filters, &hb_filter_deinterlace );
1697 else if ([fPictureController deinterlace] == 3)
1699 /* Yadif (with spatial deinterlacing) */
1700 hb_filter_deinterlace.settings = "0";
1701 hb_list_add( job->filters, &hb_filter_deinterlace );
1705 if ([fPictureController denoise] == 1) // Weak in popup
1707 hb_filter_denoise.settings = "2:1:2:3";
1708 hb_list_add( job->filters, &hb_filter_denoise );
1710 else if ([fPictureController denoise] == 2) // Medium in popup
1712 hb_filter_denoise.settings = "3:2:2:3";
1713 hb_list_add( job->filters, &hb_filter_denoise );
1715 else if ([fPictureController denoise] == 3) // Strong in popup
1717 hb_filter_denoise.settings = "7:7:5:5";
1718 hb_list_add( job->filters, &hb_filter_denoise );
1721 /* Deblock (uses pp7 default) */
1722 if ([fPictureController deblock])
1724 hb_list_add( job->filters, &hb_filter_deblock );
1731 /* addToQueue: puts up an alert before ultimately calling doAddToQueue
1733 - (IBAction) addToQueue: (id) sender
1735 /* We get the destination directory from the destination field here */
1736 NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
1737 /* We check for a valid destination here */
1738 if ([[NSFileManager defaultManager] fileExistsAtPath:destinationDirectory] == 0)
1740 NSRunAlertPanel(@"Warning!", @"This is not a valid destination directory!", @"OK", nil, nil);
1744 /* We check for duplicate name here */
1745 if( [[NSFileManager defaultManager] fileExistsAtPath:
1746 [fDstFile2Field stringValue]] )
1748 NSBeginCriticalAlertSheet( NSLocalizedString( @"File already exists", @"" ),
1749 NSLocalizedString( @"Cancel", @"" ), NSLocalizedString( @"Overwrite", @"" ), nil, fWindow, self,
1750 @selector( overwriteAddToQueueAlertDone:returnCode:contextInfo: ),
1751 NULL, NULL, [NSString stringWithFormat:
1752 NSLocalizedString( @"Do you want to overwrite %@?", @"" ),
1753 [fDstFile2Field stringValue]] );
1754 // overwriteAddToQueueAlertDone: will be called when the alert is dismissed.
1757 // Warn if another pending job in the queue has the same destination path
1758 else if ( ([fQueueController pendingJobGroupWithDestinationPath:[fDstFile2Field stringValue]] != nil)
1759 || ([[[fQueueController currentJobGroup] destinationPath] isEqualToString: [fDstFile2Field stringValue]]) )
1761 NSBeginCriticalAlertSheet( NSLocalizedString( @"Another queued encode has specified the same destination.", @"" ),
1762 NSLocalizedString( @"Cancel", @"" ), NSLocalizedString( @"Overwrite", @"" ), nil, fWindow, self,
1763 @selector( overwriteAddToQueueAlertDone:returnCode:contextInfo: ),
1764 NULL, NULL, [NSString stringWithFormat:
1765 NSLocalizedString( @"Do you want to overwrite %@?", @"" ),
1766 [fDstFile2Field stringValue]] );
1767 // overwriteAddToQueueAlertDone: will be called when the alert is dismissed.
1772 [self doAddToQueue];
1776 /* overwriteAddToQueueAlertDone: called from the alert posted by addToQueue that asks
1777 the user if they want to overwrite an exiting movie file.
1779 - (void) overwriteAddToQueueAlertDone: (NSWindow *) sheet
1780 returnCode: (int) returnCode contextInfo: (void *) contextInfo
1782 if( returnCode == NSAlertAlternateReturn )
1783 [self doAddToQueue];
1786 - (void) doAddToQueue
1788 hb_list_t * list = hb_get_titles( fHandle );
1789 hb_title_t * title = (hb_title_t *) hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
1790 hb_job_t * job = title->job;
1792 // Create a Queue Controller job group. Each job that we submit to libhb will also
1793 // get added to the job group so that the queue can track the jobs.
1794 HBJobGroup * jobGroup = [HBJobGroup jobGroup];
1795 // The job group can maintain meta data that libhb can not...
1796 [jobGroup setPresetName: [fPresetSelectedDisplay stringValue]];
1798 // Job groups require that each job within the group be assigned a unique id so
1799 // that the queue can xref between itself and the private jobs that libhb
1800 // maintains. The ID is composed a group id number and a "sequence" number. libhb
1801 // does not use this id.
1802 static int jobGroupID = 0;
1805 // A sequence number, starting at zero, is used to identifiy to each pass. This is
1806 // used by the queue UI to determine if a pass if the first pass of an encode.
1807 int sequenceNum = -1;
1811 /* Destination file */
1812 job->file = [[fDstFile2Field stringValue] UTF8String];
1814 if( [fSubForcedCheck state] == NSOnState )
1815 job->subtitle_force = 1;
1817 job->subtitle_force = 0;
1820 * subtitle of -1 is a scan
1822 if( job->subtitle == -1 )
1827 * When subtitle scan is enabled do a fast pre-scan job
1828 * which will determine which subtitles to enable, if any.
1831 x264opts_tmp = job->x264opts;
1834 job->x264opts = NULL;
1836 job->indepth_scan = 1;
1838 job->select_subtitle = (hb_subtitle_t**)malloc(sizeof(hb_subtitle_t*));
1839 *(job->select_subtitle) = NULL;
1842 * Add the pre-scan job
1844 job->sequence_id = MakeJobID(jobGroupID, ++sequenceNum);
1845 hb_add( fHandle, job );
1846 [jobGroup addJob:[HBJob jobWithLibhbJob:job]]; // add this pass to the job group
1848 job->x264opts = x264opts_tmp;
1851 job->select_subtitle = NULL;
1853 /* No subtitle were selected, so reset the subtitle to -1 (which before
1854 * this point meant we were scanning
1856 if( job->subtitle == -2 )
1859 if( [fVidTwoPassCheck state] == NSOnState )
1861 hb_subtitle_t **subtitle_tmp = job->select_subtitle;
1862 job->indepth_scan = 0;
1865 * Do not autoselect subtitles on the first pass of a two pass
1867 job->select_subtitle = NULL;
1870 job->sequence_id = MakeJobID(jobGroupID, ++sequenceNum);
1871 hb_add( fHandle, job );
1872 [jobGroup addJob:[HBJob jobWithLibhbJob:job]]; // add this pass to the job group
1875 job->sequence_id = MakeJobID(jobGroupID, ++sequenceNum);
1877 job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */
1878 strcpy(job->x264opts, [[fAdvancedOptions optionsString] UTF8String]);
1880 job->select_subtitle = subtitle_tmp;
1882 hb_add( fHandle, job );
1883 [jobGroup addJob:[HBJob jobWithLibhbJob:job]]; // add this pass to the job group
1887 job->indepth_scan = 0;
1889 job->sequence_id = MakeJobID(jobGroupID, ++sequenceNum);
1890 hb_add( fHandle, job );
1891 [jobGroup addJob:[HBJob jobWithLibhbJob:job]]; // add this pass to the job group
1894 NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
1895 [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];
1897 // Let the queue controller know about the job group
1898 [fQueueController addJobGroup:jobGroup];
1901 /* Rip: puts up an alert before ultimately calling doRip
1903 - (IBAction) Rip: (id) sender
1905 /* Rip or Cancel ? */
1907 hb_get_state2( fHandle, &s );
1909 if(s.state == HB_STATE_WORKING || s.state == HB_STATE_PAUSED)
1911 [self Cancel: sender];
1915 // If there are jobs in the queue, then this is a rip the queue
1917 if (hb_count( fHandle ) > 0)
1923 // Before adding jobs to the queue, check for a valid destination.
1925 NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
1926 if ([[NSFileManager defaultManager] fileExistsAtPath:destinationDirectory] == 0)
1928 NSRunAlertPanel(@"Warning!", @"This is not a valid destination directory!", @"OK", nil, nil);
1932 /* We check for duplicate name here */
1933 if( [[NSFileManager defaultManager] fileExistsAtPath:[fDstFile2Field stringValue]] )
1935 NSBeginCriticalAlertSheet( NSLocalizedString( @"File already exists", @"" ),
1936 NSLocalizedString( @"Cancel", "" ), NSLocalizedString( @"Overwrite", @"" ), nil, fWindow, self,
1937 @selector( overWriteAlertDone:returnCode:contextInfo: ),
1938 NULL, NULL, [NSString stringWithFormat:
1939 NSLocalizedString( @"Do you want to overwrite %@?", @"" ),
1940 [fDstFile2Field stringValue]] );
1942 // overWriteAlertDone: will be called when the alert is dismissed. It will call doRip.
1946 /* if there are no jobs in the queue, then add this one to the queue and rip
1947 otherwise, just rip the queue */
1948 if( hb_count( fHandle ) == 0)
1950 [self doAddToQueue];
1953 NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
1954 [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];
1959 /* overWriteAlertDone: called from the alert posted by Rip: that asks the user if they
1960 want to overwrite an exiting movie file.
1962 - (void) overWriteAlertDone: (NSWindow *) sheet
1963 returnCode: (int) returnCode contextInfo: (void *) contextInfo
1965 if( returnCode == NSAlertAlternateReturn )
1967 /* if there are no jobs in the queue, then add this one to the queue and rip
1968 otherwise, just rip the queue */
1969 if( hb_count( fHandle ) == 0 )
1971 [self doAddToQueue];
1974 NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent];
1975 [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"];
1980 - (void) remindUserOfSleepOrShutdown
1982 if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Put Computer To Sleep"])
1984 /*Warn that computer will sleep after encoding*/
1987 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);
1988 [NSApp requestUserAttention:NSCriticalRequest];
1989 if ( reminduser == NSAlertAlternateReturn )
1991 [self showPreferencesWindow:nil];
1994 else if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Shut Down Computer"])
1996 /*Warn that computer will shut down after encoding*/
1999 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);
2000 [NSApp requestUserAttention:NSCriticalRequest];
2001 if ( reminduser == NSAlertAlternateReturn )
2003 [self showPreferencesWindow:nil];
2012 /* Let libhb do the job */
2013 hb_start( fHandle );
2014 /*set the fEncodeState State */
2021 //------------------------------------------------------------------------------------
2022 // Removes all jobs from the queue. Does not cancel the current processing job.
2023 //------------------------------------------------------------------------------------
2024 - (void) doDeleteQueuedJobs
2027 while( ( job = hb_job( fHandle, 0 ) ) )
2028 hb_rem( fHandle, job );
2031 //------------------------------------------------------------------------------------
2032 // Cancels and deletes the current job and stops libhb from processing the remaining
2034 //------------------------------------------------------------------------------------
2035 - (void) doCancelCurrentJob
2037 // Stop the current job. hb_stop will only cancel the current pass and then set
2038 // its state to HB_STATE_WORKDONE. It also does this asynchronously. So when we
2039 // see the state has changed to HB_STATE_WORKDONE (in updateUI), we'll delete the
2040 // remaining passes of the job and then start the queue back up if there are any
2043 [fQueueController libhbWillStop];
2045 fEncodeState = 2; // don't alert at end of processing since this was a cancel
2049 //------------------------------------------------------------------------------------
2050 // Displays an alert asking user if the want to cancel encoding of current job.
2051 // Cancel: returns immediately after posting the alert. Later, when the user
2052 // acknowledges the alert, doCancelCurrentJob is called.
2053 //------------------------------------------------------------------------------------
2054 - (IBAction)Cancel: (id)sender
2056 if (!fHandle) return;
2058 HBJobGroup * jobGroup = [fQueueController currentJobGroup];
2059 if (!jobGroup) return;
2061 NSString * alertTitle = [NSString stringWithFormat:NSLocalizedString(@"Stop encoding %@?", nil),
2064 // Which window to attach the sheet to?
2065 NSWindow * docWindow;
2066 if ([sender respondsToSelector: @selector(window)])
2067 docWindow = [sender window];
2069 docWindow = fWindow;
2071 NSBeginCriticalAlertSheet(
2073 NSLocalizedString(@"Keep Encoding", nil),
2075 NSLocalizedString(@"Stop Encoding", nil),
2077 nil, @selector(didDimissCancelCurrentJob:returnCode:contextInfo:), nil,
2078 NSLocalizedString(@"Your movie will be lost if you don't continue encoding.", nil));
2080 // didDimissCancelCurrentJob:returnCode:contextInfo: will be called when the dialog is dismissed
2083 - (void) didDimissCancelCurrentJob: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo
2085 if (returnCode == NSAlertOtherReturn)
2086 [self doCancelCurrentJob]; // <- this also stops libhb
2089 - (IBAction) Pause: (id) sender
2092 hb_get_state2( fHandle, &s );
2094 if( s.state == HB_STATE_PAUSED )
2096 hb_resume( fHandle );
2100 hb_pause( fHandle );
2105 #pragma mark GUI Controls Changed Methods
2107 - (IBAction) titlePopUpChanged: (id) sender
2109 hb_list_t * list = hb_get_titles( fHandle );
2110 hb_title_t * title = (hb_title_t*)
2111 hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
2114 /* If Auto Naming is on. We create an output filename of dvd name - title number */
2115 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultAutoNaming"] > 0)
2117 [fDstFile2Field setStringValue: [NSString stringWithFormat:
2118 @"%@/%@-%d.%@", [[fDstFile2Field stringValue] stringByDeletingLastPathComponent],
2119 browsedSourceDisplayName,
2121 [[fDstFile2Field stringValue] pathExtension]]];
2124 /* Update chapter popups */
2125 [fSrcChapterStartPopUp removeAllItems];
2126 [fSrcChapterEndPopUp removeAllItems];
2127 for( int i = 0; i < hb_list_count( title->list_chapter ); i++ )
2129 [fSrcChapterStartPopUp addItemWithTitle: [NSString
2130 stringWithFormat: @"%d", i + 1]];
2131 [fSrcChapterEndPopUp addItemWithTitle: [NSString
2132 stringWithFormat: @"%d", i + 1]];
2134 [fSrcChapterStartPopUp selectItemAtIndex: 0];
2135 [fSrcChapterEndPopUp selectItemAtIndex:
2136 hb_list_count( title->list_chapter ) - 1];
2137 [self chapterPopUpChanged:nil];
2139 /* Start Get and set the initial pic size for display */
2140 hb_job_t * job = title->job;
2143 /* Pixel Ratio Setting */
2144 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"PixelRatio"])
2146 job->pixel_ratio = 1 ;
2150 job->pixel_ratio = 0 ;
2152 /*Set Source Size Field Here */
2153 [fPicSettingsSrc setStringValue: [NSString stringWithFormat: @"%d x %d", fTitle->width, fTitle->height]];
2155 /* Set Auto Crop to on upon selecting a new title */
2156 [fPictureController setAutoCrop:YES];
2158 /* We get the originial output picture width and height and put them
2159 in variables for use with some presets later on */
2160 PicOrigOutputWidth = job->width;
2161 PicOrigOutputHeight = job->height;
2162 AutoCropTop = job->crop[0];
2163 AutoCropBottom = job->crop[1];
2164 AutoCropLeft = job->crop[2];
2165 AutoCropRight = job->crop[3];
2167 /* Run Through encoderPopUpChanged to see if there
2168 needs to be any pic value modifications based on encoder settings */
2169 //[self encoderPopUpChanged: NULL];
2170 /* END Get and set the initial pic size for display */
2172 /* Update subtitle popups */
2173 hb_subtitle_t * subtitle;
2174 [fSubPopUp removeAllItems];
2175 [fSubPopUp addItemWithTitle: @"None"];
2176 [fSubPopUp addItemWithTitle: @"Autoselect"];
2177 for( int i = 0; i < hb_list_count( title->list_subtitle ); i++ )
2179 subtitle = (hb_subtitle_t *) hb_list_item( title->list_subtitle, i );
2181 /* We cannot use NSPopUpButton's addItemWithTitle because
2182 it checks for duplicate entries */
2183 [[fSubPopUp menu] addItemWithTitle: [NSString stringWithCString:
2184 subtitle->lang] action: NULL keyEquivalent: @""];
2186 [fSubPopUp selectItemAtIndex: 0];
2188 [self subtitleSelectionChanged:nil];
2190 /* Update chapter table */
2191 [fChapterTitlesDelegate resetWithTitle:title];
2192 [fChapterTable reloadData];
2194 /* Lets make sure there arent any erroneous audio tracks in the job list, so lets make sure its empty*/
2195 int audiotrack_count = hb_list_count(job->list_audio);
2196 for( int i = 0; i < audiotrack_count;i++)
2198 hb_audio_t * temp_audio = (hb_audio_t*) hb_list_item( job->list_audio, 0 );
2199 hb_list_rem(job->list_audio, temp_audio);
2202 /* Update audio popups */
2203 [self addAllAudioTracksToPopUp: fAudLang1PopUp];
2204 [self addAllAudioTracksToPopUp: fAudLang2PopUp];
2205 [self addAllAudioTracksToPopUp: fAudLang3PopUp];
2206 [self addAllAudioTracksToPopUp: fAudLang4PopUp];
2207 /* search for the first instance of our prefs default language for track 1, and set track 2 to "none" */
2208 NSString * audioSearchPrefix = [[NSUserDefaults standardUserDefaults] stringForKey:@"DefaultLanguage"];
2209 [self selectAudioTrackInPopUp: fAudLang1PopUp searchPrefixString: audioSearchPrefix selectIndexIfNotFound: 1];
2210 [self selectAudioTrackInPopUp:fAudLang2PopUp searchPrefixString:nil selectIndexIfNotFound:0];
2211 [self selectAudioTrackInPopUp:fAudLang3PopUp searchPrefixString:nil selectIndexIfNotFound:0];
2212 [self selectAudioTrackInPopUp:fAudLang4PopUp searchPrefixString:nil selectIndexIfNotFound:0];
2214 /* changing the title may have changed the audio channels on offer, */
2215 /* so call audioTrackPopUpChanged for both audio tracks to update the mixdown popups */
2216 [self audioTrackPopUpChanged: fAudLang1PopUp];
2217 [self audioTrackPopUpChanged: fAudLang2PopUp];
2218 [self audioTrackPopUpChanged: fAudLang3PopUp];
2219 [self audioTrackPopUpChanged: fAudLang4PopUp];
2221 /* We repopulate the Video Framerate popup */
2223 [fVidRatePopUp removeAllItems];
2225 [fVidRatePopUp addItemWithTitle:@"Same as source"];
2227 for( int i = 0; i < hb_video_rates_count; i++ )
2229 if ([[NSString stringWithCString: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%.3f",23.976]])
2231 [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@",
2232 [NSString stringWithCString: hb_video_rates[i].string], @" (NTSC Film)"]];
2234 else if ([[NSString stringWithCString: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%d",25]])
2236 [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@",
2237 [NSString stringWithCString: hb_video_rates[i].string], @" (PAL Film/Video)"]];
2239 else if ([[NSString stringWithCString: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%.2f",29.97]])
2241 [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@",
2242 [NSString stringWithCString: hb_video_rates[i].string], @" (NTSC Video)"]];
2246 [fVidRatePopUp addItemWithTitle:
2247 [NSString stringWithCString: hb_video_rates[i].string]];
2250 [fVidRatePopUp selectItemAtIndex: 0];
2252 /* we run the picture size values through calculatePictureSizing to get all picture setting information*/
2253 [self calculatePictureSizing:nil];
2255 /* lets call tableViewSelected to make sure that any preset we have selected is enforced after a title change */
2256 [self selectPreset:nil];
2259 - (IBAction) chapterPopUpChanged: (id) sender
2262 /* If start chapter popup is greater than end chapter popup,
2263 we set the end chapter popup to the same as start chapter popup */
2264 if ([fSrcChapterStartPopUp indexOfSelectedItem] > [fSrcChapterEndPopUp indexOfSelectedItem])
2266 [fSrcChapterEndPopUp selectItemAtIndex: [fSrcChapterStartPopUp indexOfSelectedItem]];
2270 hb_list_t * list = hb_get_titles( fHandle );
2271 hb_title_t * title = (hb_title_t *)
2272 hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
2274 hb_chapter_t * chapter;
2275 int64_t duration = 0;
2276 for( int i = [fSrcChapterStartPopUp indexOfSelectedItem];
2277 i <= [fSrcChapterEndPopUp indexOfSelectedItem]; i++ )
2279 chapter = (hb_chapter_t *) hb_list_item( title->list_chapter, i );
2280 duration += chapter->duration;
2283 duration /= 90000; /* pts -> seconds */
2284 [fSrcDuration2Field setStringValue: [NSString stringWithFormat:
2285 @"%02lld:%02lld:%02lld", duration / 3600, ( duration / 60 ) % 60,
2288 [self calculateBitrate: sender];
2291 - (IBAction) formatPopUpChanged: (id) sender
2293 NSString * string = [fDstFile2Field stringValue];
2294 int format = [fDstFormatPopUp indexOfSelectedItem];
2296 /* Initially set the large file (64 bit formatting) output checkbox to hidden */
2297 [fDstMp4LargeFileCheck setHidden: YES];
2298 [fDstMp4HttpOptFileCheck setHidden: YES];
2299 [fDstMp4iPodFileCheck setHidden: YES];
2301 /* Update the Video Codec PopUp */
2302 /* Note: we now store the video encoder int values from common.c in the tags of each popup for easy retrieval later */
2303 [fVidEncoderPopUp removeAllItems];
2304 NSMenuItem *menuItem;
2305 /* These video encoders are available to all of our current muxers, so lets list them once here */
2306 menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"MPEG-4 (FFmpeg)" action: NULL keyEquivalent: @""];
2307 [menuItem setTag: HB_VCODEC_FFMPEG];
2309 menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"MPEG-4 (XviD)" action: NULL keyEquivalent: @""];
2310 [menuItem setTag: HB_VCODEC_XVID];
2314 /*Get Default MP4 File Extension*/
2315 if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultMpegName"] > 0)
2323 /* Add additional video encoders here */
2324 menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"H.264 (x264)" action: NULL keyEquivalent: @""];
2325 [menuItem setTag: HB_VCODEC_X264];
2326 /* We show the mp4 option checkboxes here since we are mp4 */
2327 [fCreateChapterMarkers setEnabled: YES];
2328 [fDstMp4LargeFileCheck setHidden: NO];
2329 [fDstMp4HttpOptFileCheck setHidden: NO];
2330 [fDstMp4iPodFileCheck setHidden: NO];
2335 /* Add additional video encoders here */
2336 menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"H.264 (x264)" action: NULL keyEquivalent: @""];
2337 [menuItem setTag: HB_VCODEC_X264];
2338 menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"VP3 (Theora)" action: NULL keyEquivalent: @""];
2339 [menuItem setTag: HB_VCODEC_THEORA];
2340 /* We enable the create chapters checkbox here */
2341 [fCreateChapterMarkers setEnabled: YES];
2346 /* Add additional video encoders here */
2347 menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"H.264 (x264)" action: NULL keyEquivalent: @""];
2348 [menuItem setTag: HB_VCODEC_X264];
2349 /* We disable the create chapters checkbox here and make sure it is unchecked*/
2350 [fCreateChapterMarkers setEnabled: NO];
2351 [fCreateChapterMarkers setState: NSOffState];
2356 /* Add additional video encoders here */
2357 menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"VP3 (Theora)" action: NULL keyEquivalent: @""];
2358 [menuItem setTag: HB_VCODEC_THEORA];
2359 /* We disable the create chapters checkbox here and make sure it is unchecked*/
2360 [fCreateChapterMarkers setEnabled: NO];
2361 [fCreateChapterMarkers setState: NSOffState];
2364 [fVidEncoderPopUp selectItemAtIndex: 0];
2366 [self audioAddAudioTrackCodecs: fAudTrack1CodecPopUp];
2367 [self audioAddAudioTrackCodecs: fAudTrack2CodecPopUp];
2368 [self audioAddAudioTrackCodecs: fAudTrack3CodecPopUp];
2369 [self audioAddAudioTrackCodecs: fAudTrack4CodecPopUp];
2371 /* FIX ME: we need to restore changing the file extension as pwer */
2373 if( [string characterAtIndex: [string length] - 4] == '.' )
2375 [fDstFile2Field setStringValue: [NSString stringWithFormat:
2376 @"%@.%s", [string substringToIndex: [string length] - 4],
2381 [fDstFile2Field setStringValue: [NSString stringWithFormat:
2382 @"%@.%s", string, ext]];
2385 if ( SuccessfulScan )
2387 /* Add/replace to the correct extension */
2388 [self audioTrackPopUpChanged: fAudLang1PopUp];
2389 [self audioTrackPopUpChanged: fAudLang2PopUp];
2390 [self audioTrackPopUpChanged: fAudLang3PopUp];
2391 [self audioTrackPopUpChanged: fAudLang4PopUp];
2393 if ( [fVidEncoderPopUp selectedItem] == nil )
2396 [fVidEncoderPopUp selectItemAtIndex:0];
2397 [self videoEncoderPopUpChanged:nil];
2399 /* changing the format may mean that we can / can't offer mono or 6ch, */
2400 /* so call audioTrackPopUpChanged for both audio tracks to update the mixdown popups */
2404 /* We call the method to properly enable/disable turbo 2 pass */
2405 [self twoPassCheckboxChanged: sender];
2406 /* We call method method to change UI to reflect whether a preset is used or not*/
2410 /* Lets check to see if we want to auto set the .m4v extension for mp4 */
2411 [self autoSetM4vExtension: sender];
2412 [self customSettingUsed: sender];
2417 /* if MP4 format and [fDstCodecsPopUp indexOfSelectedItem] > 1 we know that the audio is going to be
2418 * either aac + ac3 passthru, or just ac3 passthru so we need to make sure the output file extension is m4v
2419 * otherwise Quicktime will not play it at all */
2420 - (IBAction) autoSetM4vExtension: (id) sender
2422 /*FIX ME: for this to work, we will now have to iterate through the audio list to see if ac3 in an mp4 is chosen
2423 * for now just comment it out.
2426 if ([fDstFormatPopUp indexOfSelectedItem] == 0 && [fDstCodecsPopUp indexOfSelectedItem] > 1)
2428 NSString *newpath = [[[fDstFile2Field stringValue] stringByDeletingPathExtension] stringByAppendingPathExtension: @"m4v"];
2429 [fDstFile2Field setStringValue: [NSString stringWithFormat:
2434 /* Method to determine if we should change the UI
2435 To reflect whether or not a Preset is being used or if
2436 the user is using "Custom" settings by determining the sender*/
2437 - (IBAction) customSettingUsed: (id) sender
2439 if ([sender stringValue])
2441 /* Deselect the currently selected Preset if there is one*/
2442 [fPresetsOutlineView deselectRow:[fPresetsOutlineView selectedRow]];
2443 /* Change UI to show "Custom" settings are being used */
2444 [fPresetSelectedDisplay setStringValue: @"Custom"];
2446 curUserPresetChosenNum = nil;
2452 #pragma mark - Video
2454 - (IBAction) videoEncoderPopUpChanged: (id) sender
2456 hb_job_t * job = fTitle->job;
2457 int videoEncoder = [[fVidEncoderPopUp selectedItem] tag];
2459 [fAdvancedOptions setHidden:YES];
2460 /* If we are using x264 then show the x264 advanced panel*/
2461 if (videoEncoder == HB_VCODEC_X264)
2463 [fAdvancedOptions setHidden:NO];
2464 [self autoSetM4vExtension: sender];
2467 /* We need to set loose anamorphic as available depending on whether or not the ffmpeg encoder
2468 is being used as it borks up loose anamorphic .
2469 For convenience lets use the titleOfSelected index. Probably should revisit whether or not we want
2470 to use the index itself but this is easier */
2471 if (videoEncoder == HB_VCODEC_FFMPEG)
2473 if (job->pixel_ratio == 2)
2475 job->pixel_ratio = 0;
2477 [fPictureController setAllowLooseAnamorphic:NO];
2478 /* We set the iPod atom checkbox to disabled and uncheck it as its only for x264 in the mp4
2479 container. Format is taken care of in formatPopUpChanged method by hiding and unchecking
2480 anything other than MP4.
2482 [fDstMp4iPodFileCheck setEnabled: NO];
2483 [fDstMp4iPodFileCheck setState: NSOffState];
2487 [fPictureController setAllowLooseAnamorphic:YES];
2488 [fDstMp4iPodFileCheck setEnabled: YES];
2491 [self calculatePictureSizing: sender];
2492 [self twoPassCheckboxChanged: sender];
2496 - (IBAction) twoPassCheckboxChanged: (id) sender
2498 /* check to see if x264 is chosen */
2499 if([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_X264)
2501 if( [fVidTwoPassCheck state] == NSOnState)
2503 [fVidTurboPassCheck setHidden: NO];
2507 [fVidTurboPassCheck setHidden: YES];
2508 [fVidTurboPassCheck setState: NSOffState];
2510 /* Make sure Two Pass is checked if Turbo is checked */
2511 if( [fVidTurboPassCheck state] == NSOnState)
2513 [fVidTwoPassCheck setState: NSOnState];
2518 [fVidTurboPassCheck setHidden: YES];
2519 [fVidTurboPassCheck setState: NSOffState];
2522 /* We call method method to change UI to reflect whether a preset is used or not*/
2523 [self customSettingUsed: sender];
2526 - (IBAction ) videoFrameRateChanged: (id) sender
2528 /* We call method method to calculatePictureSizing to error check detelecine*/
2529 [self calculatePictureSizing: sender];
2531 /* We call method method to change UI to reflect whether a preset is used or not*/
2532 [self customSettingUsed: sender];
2534 - (IBAction) videoMatrixChanged: (id) sender;
2536 bool target, bitrate, quality;
2538 target = bitrate = quality = false;
2539 if( [fVidQualityMatrix isEnabled] )
2541 switch( [fVidQualityMatrix selectedRow] )
2554 [fVidTargetSizeField setEnabled: target];
2555 [fVidBitrateField setEnabled: bitrate];
2556 [fVidQualitySlider setEnabled: quality];
2557 [fVidTwoPassCheck setEnabled: !quality &&
2558 [fVidQualityMatrix isEnabled]];
2561 [fVidTwoPassCheck setState: NSOffState];
2562 [fVidTurboPassCheck setHidden: YES];
2563 [fVidTurboPassCheck setState: NSOffState];
2566 [self qualitySliderChanged: sender];
2567 [self calculateBitrate: sender];
2568 [self customSettingUsed: sender];
2571 - (IBAction) qualitySliderChanged: (id) sender
2573 [fVidConstantCell setTitle: [NSString stringWithFormat:
2574 NSLocalizedString( @"Constant quality: %.0f %%", @"" ), 100.0 *
2575 [fVidQualitySlider floatValue]]];
2576 [self customSettingUsed: sender];
2579 - (void) controlTextDidChange: (NSNotification *) notification
2581 [self calculateBitrate:nil];
2584 - (IBAction) calculateBitrate: (id) sender
2586 if( !fHandle || [fVidQualityMatrix selectedRow] != 0 || !SuccessfulScan )
2591 hb_list_t * list = hb_get_titles( fHandle );
2592 hb_title_t * title = (hb_title_t *) hb_list_item( list,
2593 [fSrcTitlePopUp indexOfSelectedItem] );
2594 hb_job_t * job = title->job;
2598 [fVidBitrateField setIntValue: hb_calc_bitrate( job,
2599 [fVidTargetSizeField intValue] )];
2603 #pragma mark - Picture
2605 /* lets set the picture size back to the max from right after title scan
2606 Lets use an IBAction here as down the road we could always use a checkbox
2607 in the gui to easily take the user back to max. Remember, the compiler
2608 resolves IBActions down to -(void) during compile anyway */
2609 - (IBAction) revertPictureSizeToMax: (id) sender
2611 hb_job_t * job = fTitle->job;
2612 /* We use the output picture width and height
2613 as calculated from libhb right after title is set
2614 in TitlePopUpChanged */
2615 job->width = PicOrigOutputWidth;
2616 job->height = PicOrigOutputHeight;
2617 [fPictureController setAutoCrop:YES];
2618 /* Here we use the auto crop values determined right after scan */
2619 job->crop[0] = AutoCropTop;
2620 job->crop[1] = AutoCropBottom;
2621 job->crop[2] = AutoCropLeft;
2622 job->crop[3] = AutoCropRight;
2625 [self calculatePictureSizing: sender];
2626 /* We call method to change UI to reflect whether a preset is used or not*/
2627 [self customSettingUsed: sender];
2631 * Registers changes made in the Picture Settings Window.
2634 - (void)pictureSettingsDidChange {
2635 [self calculatePictureSizing:nil];
2638 /* Get and Display Current Pic Settings in main window */
2639 - (IBAction) calculatePictureSizing: (id) sender
2641 [fPicSettingsOutp setStringValue: [NSString stringWithFormat:@"%d x %d", fTitle->job->width, fTitle->job->height]];
2643 if (fTitle->job->pixel_ratio == 1)
2645 int titlewidth = fTitle->width-fTitle->job->crop[2]-fTitle->job->crop[3];
2646 int arpwidth = fTitle->job->pixel_aspect_width;
2647 int arpheight = fTitle->job->pixel_aspect_height;
2648 int displayparwidth = titlewidth * arpwidth / arpheight;
2649 int displayparheight = fTitle->height-fTitle->job->crop[0]-fTitle->job->crop[1];
2650 [fPicSettingsOutp setStringValue: [NSString stringWithFormat:@"%d x %d", titlewidth, displayparheight]];
2651 [fPicSettingsAnamorphic setStringValue: [NSString stringWithFormat:@"%d x %d Strict", displayparwidth, displayparheight]];
2652 fTitle->job->keep_ratio = 0;
2654 else if (fTitle->job->pixel_ratio == 2)
2656 hb_job_t * job = fTitle->job;
2657 int output_width, output_height, output_par_width, output_par_height;
2658 hb_set_anamorphic_size(job, &output_width, &output_height, &output_par_width, &output_par_height);
2660 display_width = output_width * output_par_width / output_par_height;
2662 [fPicSettingsOutp setStringValue: [NSString stringWithFormat:@"%d x %d", output_width, output_height]];
2663 [fPicSettingsAnamorphic setStringValue: [NSString stringWithFormat:@"%d x %d Loose", display_width, output_height]];
2665 fTitle->job->keep_ratio = 0;
2669 [fPicSettingsAnamorphic setStringValue:@"Off"];
2672 /* Set ON/Off values for the deinterlace/keep aspect ratio according to boolean */
2673 if (fTitle->job->keep_ratio > 0)
2675 [fPicSettingARkeep setStringValue: @"On"];
2679 [fPicSettingARkeep setStringValue: @"Off"];
2683 if ([fPictureController detelecine]) {
2684 [fPicSettingDetelecine setStringValue: @"Yes"];
2687 [fPicSettingDetelecine setStringValue: @"No"];
2691 if ([fPictureController decomb] == 0)
2693 [fPicSettingDecomb setStringValue: @"Off"];
2695 else if ([fPictureController decomb] == 1)
2697 [fPicSettingDecomb setStringValue: @"1:2:6:9:80:16:16"];
2699 else if ([fPictureController decomb] == 2)
2701 [fPicSettingDecomb setStringValue:[[NSUserDefaults standardUserDefaults] stringForKey:@"DecombCustomString"]];
2704 /* VFR (Variable Frame Rate) */
2705 if ([fPictureController vfr]) {
2706 /* We change the string of the fps popup to warn that vfr is on Framerate (FPS): */
2707 [fVidRateField setStringValue: @"Framerate (VFR On):"];
2708 /* for VFR we select same as source (or title framerate) and disable the popup.
2709 * We know its index 0 as that is determined in titlePopUpChanged */
2710 [fVidRatePopUp selectItemAtIndex: 0];
2711 [fVidRatePopUp setEnabled: NO];
2715 /* make sure the label for framerate is set to its default */
2716 [fVidRateField setStringValue: @"Framerate (FPS):"];
2717 [fVidRatePopUp setEnabled: YES];
2721 if ([fPictureController deinterlace] == 0)
2723 [fPicSettingDeinterlace setStringValue: @"Off"];
2725 else if ([fPictureController deinterlace] == 1)
2727 [fPicSettingDeinterlace setStringValue: @"Fast"];
2729 else if ([fPictureController deinterlace] == 2)
2731 [fPicSettingDeinterlace setStringValue: @"Slow"];
2733 else if ([fPictureController deinterlace] == 3)
2735 [fPicSettingDeinterlace setStringValue: @"Slower"];
2739 if ([fPictureController denoise] == 0)
2741 [fPicSettingDenoise setStringValue: @"Off"];
2743 else if ([fPictureController denoise] == 1)
2745 [fPicSettingDenoise setStringValue: @"Weak"];
2747 else if ([fPictureController denoise] == 2)
2749 [fPicSettingDenoise setStringValue: @"Medium"];
2751 else if ([fPictureController denoise] == 3)
2753 [fPicSettingDenoise setStringValue: @"Strong"];
2757 if ([fPictureController deblock]) {
2758 [fPicSettingDeblock setStringValue: @"Yes"];
2761 [fPicSettingDeblock setStringValue: @"No"];
2764 if (fTitle->job->pixel_ratio > 0)
2766 [fPicSettingPAR setStringValue: @""];
2770 [fPicSettingPAR setStringValue: @"Off"];
2773 /* Set the display field for crop as per boolean */
2774 if (![fPictureController autoCrop])
2776 [fPicSettingAutoCrop setStringValue: @"Custom"];
2780 [fPicSettingAutoCrop setStringValue: @"Auto"];
2788 #pragma mark - Audio and Subtitles
2789 - (IBAction) audioCodecsPopUpChanged: (id) sender
2792 NSPopUpButton * audiotrackPopUp;
2793 NSPopUpButton * sampleratePopUp;
2794 NSPopUpButton * bitratePopUp;
2795 NSPopUpButton * audiocodecPopUp;
2796 if (sender == fAudTrack1CodecPopUp)
2798 audiotrackPopUp = fAudLang1PopUp;
2799 audiocodecPopUp = fAudTrack1CodecPopUp;
2800 sampleratePopUp = fAudTrack1RatePopUp;
2801 bitratePopUp = fAudTrack1BitratePopUp;
2803 else if (sender == fAudTrack2CodecPopUp)
2805 audiotrackPopUp = fAudLang2PopUp;
2806 audiocodecPopUp = fAudTrack2CodecPopUp;
2807 sampleratePopUp = fAudTrack2RatePopUp;
2808 bitratePopUp = fAudTrack2BitratePopUp;
2810 else if (sender == fAudTrack3CodecPopUp)
2812 audiotrackPopUp = fAudLang3PopUp;
2813 audiocodecPopUp = fAudTrack3CodecPopUp;
2814 sampleratePopUp = fAudTrack3RatePopUp;
2815 bitratePopUp = fAudTrack3BitratePopUp;
2819 audiotrackPopUp = fAudLang4PopUp;
2820 audiocodecPopUp = fAudTrack4CodecPopUp;
2821 sampleratePopUp = fAudTrack4RatePopUp;
2822 bitratePopUp = fAudTrack4BitratePopUp;
2825 /* changing the codecs on offer may mean that we can / can't offer mono or 6ch, */
2826 /* so call audioTrackPopUpChanged for both audio tracks to update the mixdown popups */
2827 [self audioTrackPopUpChanged: audiotrackPopUp];
2831 - (IBAction) setEnabledStateOfAudioMixdownControls: (id) sender
2833 /* We will be setting the enabled/disabled state of each tracks audio controls based on
2834 * the settings of the source audio for that track. We leave the samplerate and bitrate
2835 * to audiotrackMixdownChanged
2838 /* We will first verify that a lower track number has been selected before enabling each track
2839 * for example, make sure a track is selected for track 1 before enabling track 2, etc.
2841 if ([fAudLang1PopUp indexOfSelectedItem] == 0)
2843 [fAudLang2PopUp setEnabled: NO];
2844 [fAudLang2PopUp selectItemAtIndex: 0];
2848 [fAudLang2PopUp setEnabled: YES];
2851 if ([fAudLang2PopUp indexOfSelectedItem] == 0)
2853 [fAudLang3PopUp setEnabled: NO];
2854 [fAudLang3PopUp selectItemAtIndex: 0];
2858 [fAudLang3PopUp setEnabled: YES];
2860 if ([fAudLang3PopUp indexOfSelectedItem] == 0)
2862 [fAudLang4PopUp setEnabled: NO];
2863 [fAudLang4PopUp selectItemAtIndex: 0];
2867 [fAudLang4PopUp setEnabled: YES];
2869 /* enable/disable the mixdown text and popupbutton for audio track 1 */
2870 [fAudTrack1CodecPopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
2871 [fAudTrack1MixPopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
2872 [fAudTrack1RatePopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
2873 [fAudTrack1BitratePopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
2874 [fAudTrack1DrcSlider setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
2875 [fAudTrack1DrcField setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES];
2876 if ([fAudLang1PopUp indexOfSelectedItem] == 0)
2878 [fAudTrack1CodecPopUp removeAllItems];
2879 [fAudTrack1MixPopUp removeAllItems];
2880 [fAudTrack1RatePopUp removeAllItems];
2881 [fAudTrack1BitratePopUp removeAllItems];
2882 [fAudTrack1DrcSlider setFloatValue: 1.00];
2883 [self audioDRCSliderChanged: fAudTrack1DrcSlider];
2885 else if ([[fAudTrack1MixPopUp selectedItem] tag] == HB_ACODEC_AC3)
2887 [fAudTrack1RatePopUp setEnabled: NO];
2888 [fAudTrack1BitratePopUp setEnabled: NO];
2889 [fAudTrack1DrcSlider setEnabled: NO];
2890 [fAudTrack1DrcField setEnabled: NO];
2893 /* enable/disable the mixdown text and popupbutton for audio track 2 */
2894 [fAudTrack2CodecPopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
2895 [fAudTrack2MixPopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
2896 [fAudTrack2RatePopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
2897 [fAudTrack2BitratePopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
2898 [fAudTrack2DrcSlider setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
2899 [fAudTrack2DrcField setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES];
2900 if ([fAudLang2PopUp indexOfSelectedItem] == 0)
2902 [fAudTrack2CodecPopUp removeAllItems];
2903 [fAudTrack2MixPopUp removeAllItems];
2904 [fAudTrack2RatePopUp removeAllItems];
2905 [fAudTrack2BitratePopUp removeAllItems];
2906 [fAudTrack2DrcSlider setFloatValue: 1.00];
2907 [self audioDRCSliderChanged: fAudTrack2DrcSlider];
2909 else if ([[fAudTrack2MixPopUp selectedItem] tag] == HB_ACODEC_AC3)
2911 [fAudTrack2RatePopUp setEnabled: NO];
2912 [fAudTrack2BitratePopUp setEnabled: NO];
2913 [fAudTrack2DrcSlider setEnabled: NO];
2914 [fAudTrack2DrcField setEnabled: NO];
2917 /* enable/disable the mixdown text and popupbutton for audio track 3 */
2918 [fAudTrack3CodecPopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
2919 [fAudTrack3MixPopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
2920 [fAudTrack3RatePopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
2921 [fAudTrack3BitratePopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
2922 [fAudTrack3DrcSlider setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
2923 [fAudTrack3DrcField setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES];
2924 if ([fAudLang3PopUp indexOfSelectedItem] == 0)
2926 [fAudTrack3CodecPopUp removeAllItems];
2927 [fAudTrack3MixPopUp removeAllItems];
2928 [fAudTrack3RatePopUp removeAllItems];
2929 [fAudTrack3BitratePopUp removeAllItems];
2930 [fAudTrack3DrcSlider setFloatValue: 1.00];
2931 [self audioDRCSliderChanged: fAudTrack3DrcSlider];
2933 else if ([[fAudTrack3MixPopUp selectedItem] tag] == HB_ACODEC_AC3)
2935 [fAudTrack3RatePopUp setEnabled: NO];
2936 [fAudTrack3BitratePopUp setEnabled: NO];
2937 [fAudTrack3DrcSlider setEnabled: NO];
2938 [fAudTrack3DrcField setEnabled: NO];
2941 /* enable/disable the mixdown text and popupbutton for audio track 4 */
2942 [fAudTrack4CodecPopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
2943 [fAudTrack4MixPopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
2944 [fAudTrack4RatePopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
2945 [fAudTrack4BitratePopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
2946 [fAudTrack4DrcSlider setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
2947 [fAudTrack4DrcField setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES];
2948 if ([fAudLang4PopUp indexOfSelectedItem] == 0)
2950 [fAudTrack4CodecPopUp removeAllItems];
2951 [fAudTrack4MixPopUp removeAllItems];
2952 [fAudTrack4RatePopUp removeAllItems];
2953 [fAudTrack4BitratePopUp removeAllItems];
2954 [fAudTrack4DrcSlider setFloatValue: 1.00];
2955 [self audioDRCSliderChanged: fAudTrack4DrcSlider];
2957 else if ([[fAudTrack4MixPopUp selectedItem] tag] == HB_ACODEC_AC3)
2959 [fAudTrack4RatePopUp setEnabled: NO];
2960 [fAudTrack4BitratePopUp setEnabled: NO];
2961 [fAudTrack4DrcSlider setEnabled: NO];
2962 [fAudTrack4DrcField setEnabled: NO];
2967 - (IBAction) addAllAudioTracksToPopUp: (id) sender
2970 hb_list_t * list = hb_get_titles( fHandle );
2971 hb_title_t * title = (hb_title_t*)
2972 hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] );
2974 hb_audio_config_t * audio;
2976 [sender removeAllItems];
2977 [sender addItemWithTitle: NSLocalizedString( @"None", @"" )];
2978 for( int i = 0; i < hb_list_count( title->list_audio ); i++ )
2980 audio = (hb_audio_config_t *) hb_list_audio_config_item( title->list_audio, i );
2981 [[sender menu] addItemWithTitle:
2982 [NSString stringWithCString: audio->lang.description]
2983 action: NULL keyEquivalent: @""];
2985 [sender selectItemAtIndex: 0];
2989 - (IBAction) selectAudioTrackInPopUp: (id) sender searchPrefixString: (NSString *) searchPrefixString selectIndexIfNotFound: (int) selectIndexIfNotFound
2992 /* this method can be used to find a language, or a language-and-source-format combination, by passing in the appropriate string */
2993 /* e.g. to find the first French track, pass in an NSString * of "Francais" */
2994 /* e.g. to find the first English 5.1 AC3 track, pass in an NSString * of "English (AC3) (5.1 ch)" */
2995 /* if no matching track is found, then selectIndexIfNotFound is used to choose which track to select instead */
2997 if (searchPrefixString)
3000 for( int i = 0; i < [sender numberOfItems]; i++ )
3002 /* Try to find the desired search string */
3003 if ([[[sender itemAtIndex: i] title] hasPrefix:searchPrefixString])
3005 [sender selectItemAtIndex: i];
3009 /* couldn't find the string, so select the requested "search string not found" item */
3010 /* index of 0 means select the "none" item */
3011 /* index of 1 means select the first audio track */
3012 [sender selectItemAtIndex: selectIndexIfNotFound];
3016 /* if no search string is provided, then select the selectIndexIfNotFound item */
3017 [sender selectItemAtIndex: selectIndexIfNotFound];
3021 - (IBAction) audioAddAudioTrackCodecs: (id)sender
3023 int format = [fDstFormatPopUp indexOfSelectedItem];
3025 /* setup pointers to the appropriate popups for the correct track */
3026 NSPopUpButton * audiocodecPopUp;
3027 NSPopUpButton * audiotrackPopUp;
3028 if (sender == fAudTrack1CodecPopUp)
3030 audiotrackPopUp = fAudLang1PopUp;
3031 audiocodecPopUp = fAudTrack1CodecPopUp;
3033 else if (sender == fAudTrack2CodecPopUp)
3035 audiotrackPopUp = fAudLang2PopUp;
3036 audiocodecPopUp = fAudTrack2CodecPopUp;
3038 else if (sender == fAudTrack3CodecPopUp)
3040 audiotrackPopUp = fAudLang3PopUp;
3041 audiocodecPopUp = fAudTrack3CodecPopUp;
3045 audiotrackPopUp = fAudLang4PopUp;
3046 audiocodecPopUp = fAudTrack4CodecPopUp;
3049 [audiocodecPopUp removeAllItems];
3050 /* Make sure "None" isnt selected in the source track */
3051 if ([audiotrackPopUp indexOfSelectedItem] > 0)
3053 [audiocodecPopUp setEnabled:YES];
3054 NSMenuItem *menuItem;
3055 /* We setup our appropriate popups for codecs and put the int value in the popup tag for easy retrieval */
3061 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (faac)" action: NULL keyEquivalent: @""];
3062 [menuItem setTag: HB_ACODEC_FAAC];
3065 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AC3 Passthru" action: NULL keyEquivalent: @""];
3066 [menuItem setTag: HB_ACODEC_AC3];
3072 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (faac)" action: NULL keyEquivalent: @""];
3073 [menuItem setTag: HB_ACODEC_FAAC];
3075 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AC3 Passthru" action: NULL keyEquivalent: @""];
3076 [menuItem setTag: HB_ACODEC_AC3];
3078 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"MP3 (lame)" action: NULL keyEquivalent: @""];
3079 [menuItem setTag: HB_ACODEC_LAME];
3081 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"Vorbis (vorbis)" action: NULL keyEquivalent: @""];
3082 [menuItem setTag: HB_ACODEC_VORBIS];
3088 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"MP3 (lame)" action: NULL keyEquivalent: @""];
3089 [menuItem setTag: HB_ACODEC_LAME];
3091 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AC3 Passthru" action: NULL keyEquivalent: @""];
3092 [menuItem setTag: HB_ACODEC_AC3];
3098 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"Vorbis (vorbis)" action: NULL keyEquivalent: @""];
3099 [menuItem setTag: HB_ACODEC_VORBIS];
3101 menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"MP3 (lame)" action: NULL keyEquivalent: @""];
3102 [menuItem setTag: HB_ACODEC_LAME];
3105 [audiocodecPopUp selectItemAtIndex:0];
3109 [audiocodecPopUp setEnabled:NO];
3113 - (IBAction) audioTrackPopUpChanged: (id) sender
3115 /* utility function to call audioTrackPopUpChanged without passing in a mixdown-to-use */
3116 [self audioTrackPopUpChanged: sender mixdownToUse: 0];
3119 - (IBAction) audioTrackPopUpChanged: (id) sender mixdownToUse: (int) mixdownToUse
3122 /* make sure we have a selected title before continuing */
3123 if (fTitle == NULL) return;
3124 /* if the sender is the lanaguage popup and there is nothing in the codec popup, lets call
3125 * audioAddAudioTrackCodecs on the codec popup to populate it properly before moving on
3127 if (sender == fAudLang1PopUp && [[fAudTrack1CodecPopUp menu] numberOfItems] == 0)
3129 [self audioAddAudioTrackCodecs: fAudTrack1CodecPopUp];
3131 if (sender == fAudLang2PopUp && [[fAudTrack2CodecPopUp menu] numberOfItems] == 0)
3133 [self audioAddAudioTrackCodecs: fAudTrack2CodecPopUp];
3135 if (sender == fAudLang3PopUp && [[fAudTrack3CodecPopUp menu] numberOfItems] == 0)
3137 [self audioAddAudioTrackCodecs: fAudTrack3CodecPopUp];
3139 if (sender == fAudLang4PopUp && [[fAudTrack4CodecPopUp menu] numberOfItems] == 0)
3141 [self audioAddAudioTrackCodecs: fAudTrack4CodecPopUp];
3144 /* Now lets make the sender the appropriate Audio Track popup from this point on */
3145 if (sender == fAudTrack1CodecPopUp || sender == fAudTrack1MixPopUp)
3147 sender = fAudLang1PopUp;
3149 if (sender == fAudTrack2CodecPopUp || sender == fAudTrack2MixPopUp)
3151 sender = fAudLang2PopUp;
3153 if (sender == fAudTrack3CodecPopUp || sender == fAudTrack3MixPopUp)
3155 sender = fAudLang3PopUp;
3157 if (sender == fAudTrack4CodecPopUp || sender == fAudTrack4MixPopUp)
3159 sender = fAudLang4PopUp;
3162 /* pointer to this track's mixdown, codec, sample rate and bitrate NSPopUpButton's */
3163 NSPopUpButton * mixdownPopUp;
3164 NSPopUpButton * audiocodecPopUp;
3165 NSPopUpButton * sampleratePopUp;
3166 NSPopUpButton * bitratePopUp;
3167 if (sender == fAudLang1PopUp)
3169 mixdownPopUp = fAudTrack1MixPopUp;
3170 audiocodecPopUp = fAudTrack1CodecPopUp;
3171 sampleratePopUp = fAudTrack1RatePopUp;
3172 bitratePopUp = fAudTrack1BitratePopUp;
3174 else if (sender == fAudLang2PopUp)
3176 mixdownPopUp = fAudTrack2MixPopUp;
3177 audiocodecPopUp = fAudTrack2CodecPopUp;
3178 sampleratePopUp = fAudTrack2RatePopUp;
3179 bitratePopUp = fAudTrack2BitratePopUp;
3181 else if (sender == fAudLang3PopUp)
3183 mixdownPopUp = fAudTrack3MixPopUp;
3184 audiocodecPopUp = fAudTrack3CodecPopUp;
3185 sampleratePopUp = fAudTrack3RatePopUp;
3186 bitratePopUp = fAudTrack3BitratePopUp;
3190 mixdownPopUp = fAudTrack4MixPopUp;
3191 audiocodecPopUp = fAudTrack4CodecPopUp;
3192 sampleratePopUp = fAudTrack4RatePopUp;
3193 bitratePopUp = fAudTrack4BitratePopUp;
3196 /* get the index of the selected audio Track*/
3197 int thisAudioIndex = [sender indexOfSelectedItem] - 1;
3199 /* pointer for the hb_audio_s struct we will use later on */
3200 hb_audio_config_t * audio;
3203 /* check if the audio mixdown controls need their enabled state changing */
3204 [self setEnabledStateOfAudioMixdownControls:nil];
3206 if (thisAudioIndex != -1)
3210 audio = (hb_audio_config_t *) hb_list_audio_config_item( fTitle->list_audio, thisAudioIndex );// Should "fTitle" be title and be setup ?
3212 /* actually manipulate the proper mixdowns here */
3213 /* delete the previous audio mixdown options */
3214 [mixdownPopUp removeAllItems];
3216 acodec = [[audiocodecPopUp selectedItem] tag];
3221 /* find out if our selected output audio codec supports mono and / or 6ch */
3222 /* we also check for an input codec of AC3 or DCA,
3223 as they are the only libraries able to do the mixdown to mono / conversion to 6-ch */
3224 /* audioCodecsSupportMono and audioCodecsSupport6Ch are the same for now,
3225 but this may change in the future, so they are separated for flexibility */
3226 int audioCodecsSupportMono =
3227 (audio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)) &&
3228 (acodec != HB_ACODEC_LAME);
3229 int audioCodecsSupport6Ch =
3230 (audio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)) &&
3231 (acodec != HB_ACODEC_LAME);
3233 /* check for AC-3 passthru */
3234 if (audio->in.codec == HB_ACODEC_AC3 && acodec == HB_ACODEC_AC3)
3237 NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
3238 [NSString stringWithCString: "AC3 Passthru"]
3239 action: NULL keyEquivalent: @""];
3240 [menuItem setTag: HB_ACODEC_AC3];
3245 /* add the appropriate audio mixdown menuitems to the popupbutton */
3246 /* in each case, we set the new menuitem's tag to be the amixdown value for that mixdown,
3247 so that we can reference the mixdown later */
3249 /* keep a track of the min and max mixdowns we used, so we can select the best match later */
3250 int minMixdownUsed = 0;
3251 int maxMixdownUsed = 0;
3253 /* get the input channel layout without any lfe channels */
3254 int layout = audio->in.channel_layout & HB_INPUT_CH_LAYOUT_DISCRETE_NO_LFE_MASK;
3256 /* do we want to add a mono option? */
3257 if (audioCodecsSupportMono == 1)
3259 NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
3260 [NSString stringWithCString: hb_audio_mixdowns[0].human_readable_name]
3261 action: NULL keyEquivalent: @""];
3262 [menuItem setTag: hb_audio_mixdowns[0].amixdown];
3263 if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[0].amixdown;
3264 maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[0].amixdown);
3267 /* do we want to add a stereo option? */
3268 /* offer stereo if we have a mono source and non-mono-supporting codecs, as otherwise we won't have a mixdown at all */
3269 /* also offer stereo if we have a stereo-or-better source */
3270 if ((layout == HB_INPUT_CH_LAYOUT_MONO && audioCodecsSupportMono == 0) || layout >= HB_INPUT_CH_LAYOUT_STEREO)
3272 NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
3273 [NSString stringWithCString: hb_audio_mixdowns[1].human_readable_name]
3274 action: NULL keyEquivalent: @""];
3275 [menuItem setTag: hb_audio_mixdowns[1].amixdown];
3276 if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[1].amixdown;
3277 maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[1].amixdown);
3280 /* do we want to add a dolby surround (DPL1) option? */
3281 if (layout == HB_INPUT_CH_LAYOUT_3F1R || layout == HB_INPUT_CH_LAYOUT_3F2R || layout == HB_INPUT_CH_LAYOUT_DOLBY)
3283 NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
3284 [NSString stringWithCString: hb_audio_mixdowns[2].human_readable_name]
3285 action: NULL keyEquivalent: @""];
3286 [menuItem setTag: hb_audio_mixdowns[2].amixdown];
3287 if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[2].amixdown;
3288 maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[2].amixdown);
3291 /* do we want to add a dolby pro logic 2 (DPL2) option? */
3292 if (layout == HB_INPUT_CH_LAYOUT_3F2R)
3294 NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
3295 [NSString stringWithCString: hb_audio_mixdowns[3].human_readable_name]
3296 action: NULL keyEquivalent: @""];
3297 [menuItem setTag: hb_audio_mixdowns[3].amixdown];
3298 if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[3].amixdown;
3299 maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[3].amixdown);
3302 /* do we want to add a 6-channel discrete option? */
3303 if (audioCodecsSupport6Ch == 1 && layout == HB_INPUT_CH_LAYOUT_3F2R && (audio->in.channel_layout & HB_INPUT_CH_LAYOUT_HAS_LFE))
3305 NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
3306 [NSString stringWithCString: hb_audio_mixdowns[4].human_readable_name]
3307 action: NULL keyEquivalent: @""];
3308 [menuItem setTag: hb_audio_mixdowns[4].amixdown];
3309 if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[4].amixdown;
3310 maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[4].amixdown);
3313 /* do we want to add an AC-3 passthrough option? */
3314 if (audio->in.codec == HB_ACODEC_AC3 && acodec == HB_ACODEC_AC3)
3316 NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle:
3317 [NSString stringWithCString: hb_audio_mixdowns[5].human_readable_name]
3318 action: NULL keyEquivalent: @""];
3319 [menuItem setTag: HB_ACODEC_AC3];
3320 if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[5].amixdown;
3321 maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[5].amixdown);
3324 /* auto-select the best mixdown based on our saved mixdown preference */
3326 /* for now, this is hard-coded to a "best" mixdown of HB_AMIXDOWN_DOLBYPLII */
3327 /* ultimately this should be a prefs option */
3330 /* if we passed in a mixdown to use - in order to load a preset - then try and use it */
3331 if (mixdownToUse > 0)
3333 useMixdown = mixdownToUse;
3337 useMixdown = HB_AMIXDOWN_DOLBYPLII;
3340 /* if useMixdown > maxMixdownUsed, then use maxMixdownUsed */
3341 if (useMixdown > maxMixdownUsed)
3343 useMixdown = maxMixdownUsed;
3346 /* if useMixdown < minMixdownUsed, then use minMixdownUsed */
3347 if (useMixdown < minMixdownUsed)
3349 useMixdown = minMixdownUsed;
3352 /* select the (possibly-amended) preferred mixdown */
3353 [mixdownPopUp selectItemWithTag: useMixdown];
3356 /* In the case of a source track that is not AC3 and the user tries to use AC3 Passthru (which does not work)
3357 * we force the Audio Codec choice back to a workable codec. We use MP3 for avi and aac for all
3360 if (audio->in.codec != HB_ACODEC_AC3 && [[audiocodecPopUp selectedItem] tag] == HB_ACODEC_AC3)
3362 /* If we are using the avi container, we select MP3 as there is no aac available*/
3363 if ([[fDstFormatPopUp selectedItem] tag] == HB_MUX_AVI)
3365 [audiocodecPopUp selectItemWithTag: HB_ACODEC_LAME];
3369 [audiocodecPopUp selectItemWithTag: HB_ACODEC_FAAC];
3372 /* Setup our samplerate and bitrate popups we will need based on mixdown */
3373 [self audioTrackMixdownChanged: mixdownPopUp];
3379 - (IBAction) audioTrackMixdownChanged: (id) sender
3383 /* setup pointers to all of the other audio track controls
3384 * we will need later
3386 NSPopUpButton * mixdownPopUp;
3387 NSPopUpButton * sampleratePopUp;
3388 NSPopUpButton * bitratePopUp;
3389 NSPopUpButton * audiocodecPopUp;
3390 NSPopUpButton * audiotrackPopUp;
3391 NSSlider * drcSlider;
3392 NSTextField * drcField;
3393 if (sender == fAudTrack1MixPopUp)
3395 audiotrackPopUp = fAudLang1PopUp;
3396 audiocodecPopUp = fAudTrack1CodecPopUp;
3397 mixdownPopUp = fAudTrack1MixPopUp;
3398 sampleratePopUp = fAudTrack1RatePopUp;
3399 bitratePopUp = fAudTrack1BitratePopUp;
3400 drcSlider = fAudTrack1DrcSlider;
3401 drcField = fAudTrack1DrcField;
3403 else if (sender == fAudTrack2MixPopUp)
3405 audiotrackPopUp = fAudLang2PopUp;
3406 audiocodecPopUp = fAudTrack2CodecPopUp;
3407 mixdownPopUp = fAudTrack2MixPopUp;
3408 sampleratePopUp = fAudTrack2RatePopUp;
3409 bitratePopUp = fAudTrack2BitratePopUp;
3410 drcSlider = fAudTrack2DrcSlider;
3411 drcField = fAudTrack2DrcField;
3413 else if (sender == fAudTrack3MixPopUp)
3415 audiotrackPopUp = fAudLang3PopUp;
3416 audiocodecPopUp = fAudTrack3CodecPopUp;
3417 mixdownPopUp = fAudTrack3MixPopUp;
3418 sampleratePopUp = fAudTrack3RatePopUp;
3419 bitratePopUp = fAudTrack3BitratePopUp;
3420 drcSlider = fAudTrack3DrcSlider;
3421 drcField = fAudTrack3DrcField;
3425 audiotrackPopUp = fAudLang4PopUp;
3426 audiocodecPopUp = fAudTrack4CodecPopUp;
3427 mixdownPopUp = fAudTrack4MixPopUp;
3428 sampleratePopUp = fAudTrack4RatePopUp;
3429 bitratePopUp = fAudTrack4BitratePopUp;
3430 drcSlider = fAudTrack4DrcSlider;
3431 drcField = fAudTrack4DrcField;
3433 acodec = [[audiocodecPopUp selectedItem] tag];
3434 /* storage variable for the min and max bitrate allowed for this codec */
3440 case HB_ACODEC_FAAC:
3441 /* check if we have a 6ch discrete conversion in either audio track */
3442 if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH)
3444 /* FAAC is happy using our min bitrate of 32 kbps, even for 6ch */
3446 /* If either mixdown popup includes 6-channel discrete, then allow up to 384 kbps */
3452 /* FAAC is happy using our min bitrate of 32 kbps for stereo or mono */
3454 /* FAAC won't honour anything more than 160 for stereo, so let's not offer it */
3455 /* note: haven't dealt with mono separately here, FAAC will just use the max it can */
3460 case HB_ACODEC_LAME:
3461 /* Lame is happy using our min bitrate of 32 kbps */
3463 /* Lame won't encode if the bitrate is higher than 320 kbps */
3467 case HB_ACODEC_VORBIS:
3468 if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH)
3470 /* Vorbis causes a crash if we use a bitrate below 192 kbps with 6 channel */
3472 /* If either mixdown popup includes 6-channel discrete, then allow up to 384 kbps */
3478 /* Vorbis causes a crash if we use a bitrate below 48 kbps */
3480 /* Vorbis can cope with 384 kbps quite happily, even for stereo */
3486 /* AC3 passthru disables the bitrate dropdown anyway, so we might as well just use the min and max bitrate */
3492 /* make sure we have a selected title before continuing */
3493 if (fTitle == NULL) return;
3494 /* get the audio so we can find out what input rates are*/
3495 hb_audio_config_t * audio;
3496 audio = (hb_audio_config_t *) hb_list_audio_config_item( fTitle->list_audio, [audiotrackPopUp indexOfSelectedItem] - 1 );
3497 int inputbitrate = audio->in.bitrate / 1000;
3498 int inputsamplerate = audio->in.samplerate;
3500 if ([[mixdownPopUp selectedItem] tag] != HB_ACODEC_AC3)
3502 [bitratePopUp removeAllItems];
3504 for( int i = 0; i < hb_audio_bitrates_count; i++ )
3506 if (hb_audio_bitrates[i].rate >= minbitrate && hb_audio_bitrates[i].rate <= maxbitrate)
3508 /* add a new menuitem for this bitrate */
3509 NSMenuItem *menuItem = [[bitratePopUp menu] addItemWithTitle:
3510 [NSString stringWithCString: hb_audio_bitrates[i].string]
3511 action: NULL keyEquivalent: @""];
3512 /* set its tag to be the actual bitrate as an integer, so we can retrieve it later */
3513 [menuItem setTag: hb_audio_bitrates[i].rate];
3517 /* select the default bitrate (but use 384 for 6-ch AAC) */
3518 if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH)
3520 [bitratePopUp selectItemWithTag: 384];
3524 [bitratePopUp selectItemWithTag: hb_audio_bitrates[hb_audio_bitrates_default].rate];
3527 /* populate and set the sample rate popup */
3528 /* Audio samplerate */
3529 [sampleratePopUp removeAllItems];
3530 /* we create a same as source selection (Auto) so that we can choose to use the input sample rate */
3531 NSMenuItem *menuItem = [[sampleratePopUp menu] addItemWithTitle: @"Auto" action: NULL keyEquivalent: @""];
3532 [menuItem setTag: inputsamplerate];
3534 for( int i = 0; i < hb_audio_rates_count; i++ )
3536 NSMenuItem *menuItem = [[sampleratePopUp menu] addItemWithTitle:
3537 [NSString stringWithCString: hb_audio_rates[i].string]
3538 action: NULL keyEquivalent: @""];
3539 [menuItem setTag: hb_audio_rates[i].rate];
3541 /* We use the input sample rate as the default sample rate as downsampling just makes audio worse
3542 * and there is no compelling reason to use anything else as default, though the users default
3543 * preset will likely override any setting chosen here.
3545 [sampleratePopUp selectItemWithTag: inputsamplerate];
3548 /* Since AC3 Pass Thru uses the input ac3 bitrate and sample rate, we get the input tracks
3549 * bitrate and dispay it in the bitrate popup even though libhb happily ignores any bitrate input from
3550 * the gui. We do this for better user feedback in the audio tab as well as the queue for the most part
3552 if ([[mixdownPopUp selectedItem] tag] == HB_ACODEC_AC3)
3555 /* lets also set the bitrate popup to the input bitrate as thats what passthru will use */
3556 [bitratePopUp removeAllItems];
3557 NSMenuItem *menuItem = [[bitratePopUp menu] addItemWithTitle:
3558 [NSString stringWithFormat:@"%d", inputbitrate]
3559 action: NULL keyEquivalent: @""];
3560 [menuItem setTag: inputbitrate];
3561 /* For ac3 passthru we disable the sample rate and bitrate popups as well as the drc slider*/
3562 [bitratePopUp setEnabled: NO];
3563 [sampleratePopUp setEnabled: NO];
3565 [drcSlider setFloatValue: 1.00];
3566 [self audioDRCSliderChanged: drcSlider];
3567 [drcSlider setEnabled: NO];
3568 [drcField setEnabled: NO];
3572 [sampleratePopUp setEnabled: YES];
3573 [bitratePopUp setEnabled: YES];
3574 [drcSlider setEnabled: YES];
3575 [drcField setEnabled: YES];
3580 - (IBAction) audioDRCSliderChanged: (id) sender
3582 NSSlider * drcSlider;
3583 NSTextField * drcField;
3584 if (sender == fAudTrack1DrcSlider)
3586 drcSlider = fAudTrack1DrcSlider;
3587 drcField = fAudTrack1DrcField;
3589 else if (sender == fAudTrack2DrcSlider)
3591 drcSlider = fAudTrack2DrcSlider;
3592 drcField = fAudTrack2DrcField;
3594 else if (sender == fAudTrack3DrcSlider)
3596 drcSlider = fAudTrack3DrcSlider;
3597 drcField = fAudTrack3DrcField;
3601 drcSlider = fAudTrack4DrcSlider;
3602 drcField = fAudTrack4DrcField;
3604 [drcField setStringValue: [NSString stringWithFormat: @"%.2f", [drcSlider floatValue]]];
3605 /* For now, do not call this until we have an intelligent way to determine audio track selections
3606 * compared to presets
3608 //[self customSettingUsed: sender];
3611 - (IBAction) subtitleSelectionChanged: (id) sender
3613 if ([fSubPopUp indexOfSelectedItem] == 0)
3615 [fSubForcedCheck setState: NSOffState];
3616 [fSubForcedCheck setEnabled: NO];
3620 [fSubForcedCheck setEnabled: YES];
3629 #pragma mark Open New Windows
3631 - (IBAction) openHomepage: (id) sender
3633 [[NSWorkspace sharedWorkspace] openURL: [NSURL
3634 URLWithString:@"http://handbrake.fr/"]];
3637 - (IBAction) openForums: (id) sender
3639 [[NSWorkspace sharedWorkspace] openURL: [NSURL
3640 URLWithString:@"http://handbrake.fr/forum/"]];
3642 - (IBAction) openUserGuide: (id) sender
3644 [[NSWorkspace sharedWorkspace] openURL: [NSURL
3645 URLWithString:@"http://handbrake.fr/trac/wiki/HandBrakeGuide"]];
3649 * Shows debug output window.
3651 - (IBAction)showDebugOutputPanel:(id)sender
3653 [outputPanel showOutputPanel:sender];
3657 * Shows preferences window.
3659 - (IBAction) showPreferencesWindow: (id) sender
3661 NSWindow * window = [fPreferencesController window];
3662 if (![window isVisible])
3665 [window makeKeyAndOrderFront: nil];
3669 * Shows queue window.
3671 - (IBAction) showQueueWindow:(id)sender
3673 [fQueueController showQueueWindow:sender];
3677 - (IBAction) toggleDrawer:(id)sender {
3678 [fPresetDrawer toggle:self];
3682 * Shows Picture Settings Window.
3685 - (IBAction) showPicturePanel: (id) sender
3687 hb_list_t * list = hb_get_titles( fHandle );
3688 hb_title_t * title = (hb_title_t *) hb_list_item( list,
3689 [fSrcTitlePopUp indexOfSelectedItem] );
3690 [fPictureController showPanelInWindow:fWindow forTitle:title];
3694 #pragma mark Preset Outline View Methods
3695 #pragma mark - Required
3696 /* These are required by the NSOutlineView Datasource Delegate */
3697 /* We use this to deterimine children of an item */
3698 - (id)outlineView:(NSOutlineView *)fPresetsOutlineView child:(NSInteger)index ofItem:(id)item
3701 return [UserPresets objectAtIndex:index];
3703 // We are only one level deep, so we can't be asked about children
3704 NSAssert (NO, @"Presets View outlineView:child:ofItem: currently can't handle nested items.");
3707 /* We use this to determine if an item should be expandable */
3708 - (BOOL)outlineView:(NSOutlineView *)fPresetsOutlineView isItemExpandable:(id)item
3711 /* For now, we maintain one level, so set to no
3712 * when nested, we set to yes for any preset "folders"
3717 /* used to specify the number of levels to show for each item */
3718 - (int)outlineView:(NSOutlineView *)fPresetsOutlineView numberOfChildrenOfItem:(id)item
3720 /* currently use no levels to test outline view viability */
3722 return [UserPresets count];
3726 /* Used to tell the outline view which information is to be displayed per item */
3727 - (id)outlineView:(NSOutlineView *)fPresetsOutlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
3729 /* We have two columns right now, icon and PresetName */
3731 if ([[tableColumn identifier] isEqualToString:@"PresetName"])
3733 return [item objectForKey:@"PresetName"];
3737 return @"something";
3741 #pragma mark - Added Functionality (optional)
3742 /* Use to customize the font and display characteristics of the title cell */
3743 - (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
3745 if ([[tableColumn identifier] isEqualToString:@"PresetName"])
3747 NSDictionary *userPresetDict = item;
3750 NSColor *shadowColor;
3751 txtFont = [NSFont systemFontOfSize: [NSFont smallSystemFontSize]];
3752 /*check to see if its a selected row */
3753 if ([fPresetsOutlineView selectedRow] == [fPresetsOutlineView rowForItem:item])
3756 fontColor = [NSColor blackColor];
3757 shadowColor = [NSColor colorWithDeviceRed:(127.0/255.0) green:(140.0/255.0) blue:(160.0/255.0) alpha:1.0];
3761 if ([[userPresetDict objectForKey:@"Type"] intValue] == 0)
3763 fontColor = [NSColor blueColor];
3765 else // User created preset, use a black font
3767 fontColor = [NSColor blackColor];
3771 /* We use Bold Text for the HB Default */
3772 if ([[userPresetDict objectForKey:@"Default"] intValue] == 1)// 1 is HB default
3774 txtFont = [NSFont boldSystemFontOfSize: [NSFont smallSystemFontSize]];
3776 /* We use Bold Text for the User Specified Default */
3777 if ([[userPresetDict objectForKey:@"Default"] intValue] == 2)// 2 is User default
3779 txtFont = [NSFont boldSystemFontOfSize: [NSFont smallSystemFontSize]];
3783 [cell setTextColor:fontColor];
3784 [cell setFont:txtFont];
3789 /* We use this to edit the name field in the outline view */
3790 - (void)outlineView:(NSOutlineView *)outlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
3792 if ([[tableColumn identifier] isEqualToString:@"PresetName"])
3797 [theRecord setObject:object forKey:@"PresetName"];
3801 [fPresetsOutlineView reloadData];
3802 /* We save all of the preset data here */
3806 /* We use this to provide tooltips for the items in the presets outline view */
3807 - (NSString *)outlineView:(NSOutlineView *)fPresetsOutlineView toolTipForCell:(NSCell *)cell rect:(NSRectPointer)rect tableColumn:(NSTableColumn *)tc item:(id)item mouseLocation:(NSPoint)mouseLocation
3809 //if ([[tc identifier] isEqualToString:@"PresetName"])
3811 /* initialize the tooltip contents variable */
3813 /* if there is a description for the preset, we show it in the tooltip */
3814 if ([item objectForKey:@"PresetDescription"])
3816 loc_tip = [item objectForKey:@"PresetDescription"];
3821 loc_tip = @"No description available";
3828 #pragma mark Preset Outline View Methods (dragging related)
3831 - (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard
3833 // Dragging is only allowed for custom presets.
3834 if ([[[UserPresets objectAtIndex:[fPresetsOutlineView selectedRow]] objectForKey:@"Type"] intValue] == 0) // 0 is built in preset
3838 // Don't retain since this is just holding temporaral drag information, and it is
3839 //only used during a drag! We could put this in the pboard actually.
3840 fDraggedNodes = items;
3841 // Provide data for our custom type, and simple NSStrings.
3842 [pboard declareTypes:[NSArray arrayWithObjects: DragDropSimplePboardType, nil] owner:self];
3844 // the actual data doesn't matter since DragDropSimplePboardType drags aren't recognized by anyone but us!.
3845 [pboard setData:[NSData data] forType:DragDropSimplePboardType];
3850 - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(int)index
3852 // Don't allow dropping ONTO an item since they can't really contain any children.
3854 BOOL isOnDropTypeProposal = index == NSOutlineViewDropOnItemIndex;
3855 if (isOnDropTypeProposal)
3856 return NSDragOperationNone;
3859 // Don't allow dropping INTO an item since they can't really contain any children as of yet.
3863 index = [fPresetsOutlineView rowForItem: item] + 1;
3867 // Don't allow dropping into the Built In Presets.
3868 if (index < presetCurrentBuiltInCount)
3870 return NSDragOperationNone;
3871 index = MAX (index, presetCurrentBuiltInCount);
3874 [outlineView setDropItem:item dropChildIndex:index];
3875 return NSDragOperationGeneric;
3880 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(int)index
3882 NSMutableIndexSet *moveItems = [NSMutableIndexSet indexSet];
3885 NSEnumerator *enumerator = [fDraggedNodes objectEnumerator];
3886 while (obj = [enumerator nextObject])
3888 [moveItems addIndex:[UserPresets indexOfObject:obj]];
3890 // Successful drop, lets rearrange the view and save it all
3891 [self moveObjectsInPresetsArray:UserPresets fromIndexes:moveItems toIndex: index];
3892 [fPresetsOutlineView reloadData];
3897 - (void)moveObjectsInPresetsArray:(NSMutableArray *)array fromIndexes:(NSIndexSet *)indexSet toIndex:(unsigned)insertIndex
3899 unsigned index = [indexSet lastIndex];
3900 unsigned aboveInsertIndexCount = 0;
3902 while (index != NSNotFound)
3904 unsigned removeIndex;
3906 if (index >= insertIndex)
3908 removeIndex = index + aboveInsertIndexCount;
3909 aboveInsertIndexCount++;
3913 removeIndex = index;
3917 id object = [[array objectAtIndex:removeIndex] retain];
3918 [array removeObjectAtIndex:removeIndex];
3919 [array insertObject:object atIndex:insertIndex];
3922 index = [indexSet indexLessThanIndex:index];
3928 #pragma mark - Functional Preset NSOutlineView Methods
3930 - (IBAction)selectPreset:(id)sender
3933 if ([fPresetsOutlineView selectedRow] >= 0)
3935 chosenPreset = [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]];
3936 /* we set the preset display field in main window here */
3937 [fPresetSelectedDisplay setStringValue:[chosenPreset objectForKey:@"PresetName"]];
3938 if ([[chosenPreset objectForKey:@"Default"] intValue] == 1)
3940 [fPresetSelectedDisplay setStringValue:[NSString stringWithFormat:@"%@ (Default)", [chosenPreset objectForKey:@"PresetName"]]];
3944 [fPresetSelectedDisplay setStringValue:[chosenPreset objectForKey:@"PresetName"]];
3947 [fDstFormatPopUp selectItemWithTitle:[chosenPreset objectForKey:@"FileFormat"]];
3948 [self formatPopUpChanged:nil];
3950 /* Chapter Markers*/
3951 [fCreateChapterMarkers setState:[[chosenPreset objectForKey:@"ChapterMarkers"] intValue]];
3952 /* Allow Mpeg4 64 bit formatting +4GB file sizes */
3953 [fDstMp4LargeFileCheck setState:[[chosenPreset objectForKey:@"Mp4LargeFile"] intValue]];
3954 /* Mux mp4 with http optimization */
3955 [fDstMp4HttpOptFileCheck setState:[[chosenPreset objectForKey:@"Mp4HttpOptimize"] intValue]];
3958 /* We set the advanced opt string here if applicable*/
3959 [fAdvancedOptions setOptions:[chosenPreset objectForKey:@"x264Option"]];
3960 /* We use a conditional to account for the new x264 encoder dropdown as well as presets made using legacy x264 settings*/
3961 if ([[chosenPreset objectForKey:@"VideoEncoder"] isEqualToString:@"x264 (h.264 Main)"] ||
3962 [[chosenPreset objectForKey:@"VideoEncoder"] isEqualToString:@"x264 (h.264 iPod)"] ||
3963 [[chosenPreset objectForKey:@"VideoEncoder"] isEqualToString:@"x264"])
3965 [fVidEncoderPopUp selectItemWithTitle:@"H.264 (x264)"];
3966 /* special case for legacy preset to check the new fDstMp4HttpOptFileCheck checkbox to set the ipod atom */
3967 if ([[chosenPreset objectForKey:@"VideoEncoder"] isEqualToString:@"x264 (h.264 iPod)"])
3969 [fDstMp4iPodFileCheck setState:NSOnState];
3970 /* We also need to add "level=30:" to the advanced opts string to set the correct level for the iPod when
3971 encountering a legacy preset as it used to be handled separately from the opt string*/
3972 [fAdvancedOptions setOptions:[@"level=30:" stringByAppendingString:[fAdvancedOptions optionsString]]];
3976 [fDstMp4iPodFileCheck setState:NSOffState];
3979 else if ([[chosenPreset objectForKey:@"VideoEncoder"] isEqualToString:@"FFmpeg"])
3981 [fVidEncoderPopUp selectItemWithTitle:@"MPEG-4 (FFmpeg)"];
3983 else if ([[chosenPreset objectForKey:@"VideoEncoder"] isEqualToString:@"XviD"])
3985 [fVidEncoderPopUp selectItemWithTitle:@"MPEG-4 (XviD)"];
3989 [fVidEncoderPopUp selectItemWithTitle:[chosenPreset objectForKey:@"VideoEncoder"]];
3992 /* Lets run through the following functions to get variables set there */
3993 [self videoEncoderPopUpChanged:nil];
3994 /* Set the state of ipod compatible with Mp4iPodCompatible. Only for x264*/
3995 [fDstMp4iPodFileCheck setState:[[chosenPreset objectForKey:@"Mp4iPodCompatible"] intValue]];
3996 [self calculateBitrate:nil];
3999 [fVidQualityMatrix selectCellAtRow:[[chosenPreset objectForKey:@"VideoQualityType"] intValue] column:0];
4001 [fVidTargetSizeField setStringValue:[chosenPreset objectForKey:@"VideoTargetSize"]];
4002 [fVidBitrateField setStringValue:[chosenPreset objectForKey:@"VideoAvgBitrate"]];
4003 [fVidQualitySlider setFloatValue:[[chosenPreset objectForKey:@"VideoQualitySlider"] floatValue]];
4005 [self videoMatrixChanged:nil];
4007 /* Video framerate */
4008 /* For video preset video framerate, we want to make sure that Same as source does not conflict with the
4009 detected framerate in the fVidRatePopUp so we use index 0*/
4010 if ([[chosenPreset objectForKey:@"VideoFramerate"] isEqualToString:@"Same as source"])
4012 [fVidRatePopUp selectItemAtIndex: 0];
4016 [fVidRatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"VideoFramerate"]];
4020 [fVidGrayscaleCheck setState:[[chosenPreset objectForKey:@"VideoGrayScale"] intValue]];
4022 /* 2 Pass Encoding */
4023 [fVidTwoPassCheck setState:[[chosenPreset objectForKey:@"VideoTwoPass"] intValue]];
4024 [self twoPassCheckboxChanged:nil];
4025 /* Turbo 1st pass for 2 Pass Encoding */
4026 [fVidTurboPassCheck setState:[[chosenPreset objectForKey:@"VideoTurboTwoPass"] intValue]];
4029 if ([chosenPreset objectForKey:@"FileCodecs"])
4031 /* We need to handle the audio codec popup by determining what was chosen from the deprecated Codecs PopUp for past presets*/
4032 if ([[chosenPreset objectForKey:@"FileCodecs"] isEqualToString: @"AVC/H.264 Video / AAC + AC3 Audio"])
4034 /* We need to address setting languages etc. here in the new multi track audio panel */
4035 /* Track One set here */
4036 /*for track one though a track should be selected but lets check here anyway and use track one if its not.*/
4037 if ([fAudLang1PopUp indexOfSelectedItem] == 0)
4039 [fAudLang1PopUp selectItemAtIndex: 1];
4040 [self audioTrackPopUpChanged: fAudLang1PopUp];
4042 [fAudTrack1CodecPopUp selectItemWithTitle: @"AAC (faac)"];
4043 [self audioTrackPopUpChanged: fAudTrack1CodecPopUp];
4044 /* Track Two, set source same as track one */
4045 [fAudLang2PopUp selectItemAtIndex: [fAudLang1PopUp indexOfSelectedItem]];
4046 [self audioTrackPopUpChanged: fAudLang2PopUp];
4047 [fAudTrack2CodecPopUp selectItemWithTitle: @"AC3 Passthru"];
4048 [self audioTrackPopUpChanged: fAudTrack2CodecPopUp];
4050 else if ([[chosenPreset objectForKey:@"FileCodecs"] isEqualToString:@"MPEG-4 Video / AAC Audio"] ||
4051 [[chosenPreset objectForKey:@"FileCodecs"] isEqualToString:@"AVC/H.264 Video / AAC Audio"])
4053 if ([fAudLang1PopUp indexOfSelectedItem] > 0)
4055 [fAudTrack1CodecPopUp selectItemWithTitle: @"AAC (faac)"];
4056 [self audioTrackPopUpChanged: fAudTrack1CodecPopUp];
4058 if ([fAudLang2PopUp indexOfSelectedItem] > 0)
4060 [fAudTrack2CodecPopUp selectItemWithTitle: @"AAC (faac)"];
4061 [self audioTrackPopUpChanged: fAudTrack2CodecPopUp];
4063 if ([fAudLang3PopUp indexOfSelectedItem] > 0)
4065 [fAudTrack3CodecPopUp selectItemWithTitle: @"AAC (faac)"];
4066 [self audioTrackPopUpChanged: fAudTrack3CodecPopUp];
4068 if ([fAudLang4PopUp indexOfSelectedItem] > 0)
4070 [fAudTrack4CodecPopUp selectItemWithTitle: @"AAC (faac)"];
4071 [self audioTrackPopUpChanged: fAudTrack4CodecPopUp];
4074 else if ([[chosenPreset objectForKey:@"FileCodecs"] isEqualToString:@"MPEG-4 Video / AC-3 Audio"] ||
4075 [[chosenPreset objectForKey:@"FileCodecs"] isEqualToString:@"AVC/H.264 Video / AC-3 Audio"])
4077 if ([fAudLang1PopUp indexOfSelectedItem] > 0)
4079 [fAudTrack1CodecPopUp selectItemWithTitle: @"AC3 Passthru"];
4080 [self audioTrackPopUpChanged: fAudTrack1CodecPopUp];
4082 if ([fAudLang2PopUp indexOfSelectedItem] > 0)
4084 [fAudTrack2CodecPopUp selectItemWithTitle: @"AC3 Passthru"];
4085 [self audioTrackPopUpChanged: fAudTrack2CodecPopUp];
4087 if ([fAudLang3PopUp indexOfSelectedItem] > 0)
4089 [fAudTrack3CodecPopUp selectItemWithTitle: @"AC3 Passthru"];
4090 [self audioTrackPopUpChanged: fAudTrack3CodecPopUp];
4092 if ([fAudLang4PopUp indexOfSelectedItem] > 0)
4094 [fAudTrack4CodecPopUp selectItemWithTitle: @"AC3 Passthru"];
4095 [self audioTrackPopUpChanged: fAudTrack4CodecPopUp];
4098 else if ([[chosenPreset objectForKey:@"FileCodecs"] isEqualToString:@"MPEG-4 Video / MP3 Audio"] ||
4099 [[chosenPreset objectForKey:@"FileCodecs"] isEqualToString:@"AVC/H.264 Video / MP3 Audio"])
4101 if ([fAudLang1PopUp indexOfSelectedItem] > 0)
4103 [fAudTrack1CodecPopUp selectItemWithTitle: @"MP3 (lame)"];
4104 [self audioTrackPopUpChanged: fAudTrack1CodecPopUp];
4106 if ([fAudLang2PopUp indexOfSelectedItem] > 0)
4108 [fAudTrack2CodecPopUp selectItemWithTitle: @"MP3 (lame)"];
4109 [self audioTrackPopUpChanged: fAudTrack2CodecPopUp];
4111 if ([fAudLang3PopUp indexOfSelectedItem] > 0)
4113 [fAudTrack3CodecPopUp selectItemWithTitle: @"MP3 (lame)"];
4114 [self audioTrackPopUpChanged: fAudTrack3CodecPopUp];
4116 if ([fAudLang4PopUp indexOfSelectedItem] > 0)
4118 [fAudTrack4CodecPopUp selectItemWithTitle: @"MP3 (lame)"];
4119 [self audioTrackPopUpChanged: fAudTrack4CodecPopUp];
4122 else if ([[chosenPreset objectForKey:@"FileCodecs"] isEqualToString:@"MPEG-4 Video / Vorbis Audio"])
4124 if ([fAudLang1PopUp indexOfSelectedItem] > 0)
4126 [fAudTrack1CodecPopUp selectItemWithTitle: @"Vorbis (vorbis)"];
4127 [self audioTrackPopUpChanged: fAudTrack1CodecPopUp];
4129 if ([fAudLang2PopUp indexOfSelectedItem] > 0)
4131 [fAudTrack2CodecPopUp selectItemWithTitle: @"Vorbis (vorbis)"];
4132 [self audioTrackPopUpChanged: fAudTrack2CodecPopUp];
4134 if ([fAudLang3PopUp indexOfSelectedItem] > 0)
4136 [fAudTrack3CodecPopUp selectItemWithTitle: @"Vorbis (vorbis)"];
4137 [self audioTrackPopUpChanged: fAudTrack3CodecPopUp];
4139 if ([fAudLang4PopUp indexOfSelectedItem] > 0)
4141 [fAudTrack4CodecPopUp selectItemWithTitle: @"Vorbis (vorbis)"];
4142 [self audioTrackPopUpChanged: fAudTrack4CodecPopUp];
4145 /* We detect here if we have the old audio sample rate and if so we apply samplerate and bitrate to the existing four tracks if chosen
4146 * UNLESS the CodecPopUp is AC3 in which case the preset values are ignored in favor of rates set in audioTrackMixdownChanged*/
4147 if ([chosenPreset objectForKey:@"AudioSampleRate"])
4149 if ([fAudLang1PopUp indexOfSelectedItem] > 0 && [fAudTrack1CodecPopUp titleOfSelectedItem] != @"AC3 Passthru")
4151 [fAudTrack1RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"AudioSampleRate"]];
4152 [fAudTrack1BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"AudioBitRate"]];
4154 if ([fAudLang2PopUp indexOfSelectedItem] > 0 && [fAudTrack2CodecPopUp titleOfSelectedItem] != @"AC3 Passthru")
4156 [fAudTrack2RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"AudioSampleRate"]];
4157 [fAudTrack2BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"AudioBitRate"]];
4159 if ([fAudLang3PopUp indexOfSelectedItem] > 0 && [fAudTrack3CodecPopUp titleOfSelectedItem] != @"AC3 Passthru")
4161 [fAudTrack3RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"AudioSampleRate"]];
4162 [fAudTrack3BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"AudioBitRate"]];
4164 if ([fAudLang4PopUp indexOfSelectedItem] > 0 && [fAudTrack4CodecPopUp titleOfSelectedItem] != @"AC3 Passthru")
4166 [fAudTrack4RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"AudioSampleRate"]];
4167 [fAudTrack4BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"AudioBitRate"]];
4170 /* We detect here if we have the old DRC Slider and if so we apply it to the existing four tracks if chosen */
4171 if ([chosenPreset objectForKey:@"AudioDRCSlider"])
4173 if ([fAudLang1PopUp indexOfSelectedItem] > 0)
4175 [fAudTrack1DrcSlider setFloatValue:[[chosenPreset objectForKey:@"AudioDRCSlider"] floatValue]];
4176 [self audioDRCSliderChanged: fAudTrack1DrcSlider];
4178 if ([fAudLang2PopUp indexOfSelectedItem] > 0)
4180 [fAudTrack2DrcSlider setFloatValue:[[chosenPreset objectForKey:@"AudioDRCSlider"] floatValue]];
4181 [self audioDRCSliderChanged: fAudTrack2DrcSlider];
4183 if ([fAudLang3PopUp indexOfSelectedItem] > 0)
4185 [fAudTrack3DrcSlider setFloatValue:[[chosenPreset objectForKey:@"AudioDRCSlider"] floatValue]];
4186 [self audioDRCSliderChanged: fAudTrack3DrcSlider];
4188 if ([fAudLang4PopUp indexOfSelectedItem] > 0)
4190 [fAudTrack4DrcSlider setFloatValue:[[chosenPreset objectForKey:@"AudioDRCSlider"] floatValue]];
4191 [self audioDRCSliderChanged: fAudTrack4DrcSlider];
4195 else // since there was no codecs key in the preset we know we can use new multi-audio track presets
4197 if ([chosenPreset objectForKey:@"Audio1Track"] > 0)
4199 if ([fAudLang1PopUp indexOfSelectedItem] == 0)
4201 [fAudLang1PopUp selectItemAtIndex: 1];
4203 [self audioTrackPopUpChanged: fAudLang1PopUp];
4204 [fAudTrack1CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Encoder"]];
4205 [self audioTrackPopUpChanged: fAudTrack1CodecPopUp];
4206 [fAudTrack1MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Mixdown"]];
4207 /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
4209 if ([fAudTrack1MixPopUp selectedItem] == nil)
4211 [self audioTrackPopUpChanged: fAudTrack1CodecPopUp];
4213 [fAudTrack1RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Samplerate"]];
4214 /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
4215 if (![[chosenPreset objectForKey:@"Audio1Encoder"] isEqualToString:@"AC3 Passthru"])
4217 [fAudTrack1BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Bitrate"]];
4219 [fAudTrack1DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio1TrackDRCSlider"] floatValue]];
4220 [self audioDRCSliderChanged: fAudTrack1DrcSlider];
4222 if ([chosenPreset objectForKey:@"Audio2Track"] > 0)
4224 if ([fAudLang2PopUp indexOfSelectedItem] == 0)
4226 [fAudLang2PopUp selectItemAtIndex: 1];
4228 [self audioTrackPopUpChanged: fAudLang2PopUp];
4229 [fAudTrack2CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Encoder"]];
4230 [self audioTrackPopUpChanged: fAudTrack2CodecPopUp];
4231 [fAudTrack2MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Mixdown"]];
4232 /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
4234 if ([fAudTrack2MixPopUp selectedItem] == nil)
4236 [self audioTrackPopUpChanged: fAudTrack2CodecPopUp];
4238 [fAudTrack2RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Samplerate"]];
4239 /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
4240 if (![[chosenPreset objectForKey:@"Audio2Encoder"] isEqualToString:@"AC3 Passthru"])
4242 [fAudTrack2BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Bitrate"]];
4244 [fAudTrack2DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio2TrackDRCSlider"] floatValue]];
4245 [self audioDRCSliderChanged: fAudTrack2DrcSlider];
4247 if ([chosenPreset objectForKey:@"Audio3Track"] > 0)
4249 if ([fAudLang3PopUp indexOfSelectedItem] == 0)
4251 [fAudLang3PopUp selectItemAtIndex: 1];
4253 [self audioTrackPopUpChanged: fAudLang3PopUp];
4254 [fAudTrack3CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Encoder"]];
4255 [self audioTrackPopUpChanged: fAudTrack3CodecPopUp];
4256 [fAudTrack3MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Mixdown"]];
4257 /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
4259 if ([fAudTrack3MixPopUp selectedItem] == nil)
4261 [self audioTrackPopUpChanged: fAudTrack3CodecPopUp];
4263 [fAudTrack3RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Samplerate"]];
4264 /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
4265 if (![[chosenPreset objectForKey:@"Audio3Encoder"] isEqualToString: @"AC3 Passthru"])
4267 [fAudTrack3BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Bitrate"]];
4269 [fAudTrack3DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio3TrackDRCSlider"] floatValue]];
4270 [self audioDRCSliderChanged: fAudTrack3DrcSlider];
4272 if ([chosenPreset objectForKey:@"Audio4Track"] > 0)
4274 if ([fAudLang4PopUp indexOfSelectedItem] == 0)
4276 [fAudLang4PopUp selectItemAtIndex: 1];
4278 [self audioTrackPopUpChanged: fAudLang4PopUp];
4279 [fAudTrack4CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Encoder"]];
4280 [self audioTrackPopUpChanged: fAudTrack4CodecPopUp];
4281 [fAudTrack4MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Mixdown"]];
4282 /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default
4284 if ([fAudTrack4MixPopUp selectedItem] == nil)
4286 [self audioTrackPopUpChanged: fAudTrack4CodecPopUp];
4288 [fAudTrack4RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Samplerate"]];
4289 /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */
4290 if (![[chosenPreset objectForKey:@"Audio4Encoder"] isEqualToString:@"AC3 Passthru"])
4292 [fAudTrack4BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Bitrate"]];
4294 [fAudTrack4DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio4TrackDRCSlider"] floatValue]];
4295 [self audioDRCSliderChanged: fAudTrack4DrcSlider];
4301 /* We now cleanup any extra audio tracks that may be previously set if we need to, we do it here so we don't have to
4302 * duplicate any code for legacy presets.*/
4303 /* First we handle the legacy Codecs crazy AVC/H.264 Video / AAC + AC3 Audio atv hybrid */
4304 if ([chosenPreset objectForKey:@"FileCodecs"] && [[chosenPreset objectForKey:@"FileCodecs"] isEqualToString:@"AVC/H.264 Video / AAC + AC3 Audio"])
4306 [fAudLang3PopUp selectItemAtIndex: 0];
4307 [self audioTrackPopUpChanged: fAudLang3PopUp];
4308 [fAudLang4PopUp selectItemAtIndex: 0];
4309 [self audioTrackPopUpChanged: fAudLang4PopUp];
4313 if (![chosenPreset objectForKey:@"Audio2Track"] || [chosenPreset objectForKey:@"Audio2Track"] == 0)
4315 [fAudLang2PopUp selectItemAtIndex: 0];
4316 [self audioTrackPopUpChanged: fAudLang2PopUp];
4318 if (![chosenPreset objectForKey:@"Audio3Track"] || [chosenPreset objectForKey:@"Audio3Track"] > 0)
4320 [fAudLang3PopUp selectItemAtIndex: 0];
4321 [self audioTrackPopUpChanged: fAudLang3PopUp];
4323 if (![chosenPreset objectForKey:@"Audio4Track"] || [chosenPreset objectForKey:@"Audio4Track"] > 0)
4325 [fAudLang4PopUp selectItemAtIndex: 0];
4326 [self audioTrackPopUpChanged: fAudLang4PopUp];
4331 [fSubPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Subtitles"]];
4332 /* Forced Subtitles */
4333 [fSubForcedCheck setState:[[chosenPreset objectForKey:@"SubtitlesForced"] intValue]];
4335 /* Picture Settings */
4336 /* Note: objectForKey:@"UsesPictureSettings" now refers to picture size, this encompasses:
4337 * height, width, keep ar, anamorphic and crop settings.
4338 * picture filters are now handled separately.
4339 * We will be able to actually change the key names for legacy preset keys when preset file
4340 * update code is done. But for now, lets hang onto the old legacy key name for backwards compatibility.
4342 /* Check to see if the objectForKey:@"UsesPictureSettings is greater than 0, as 0 means use picture sizing "None"
4343 * and the preset completely ignores any picture sizing values in the preset.
4345 if ([[chosenPreset objectForKey:@"UsesPictureSettings"] intValue] > 0)
4347 hb_job_t * job = fTitle->job;
4348 /* Check to see if the objectForKey:@"UsesPictureSettings is 2 which is "Use Max for the source */
4349 if ([[chosenPreset objectForKey:@"UsesPictureSettings"] intValue] == 2 || [[chosenPreset objectForKey:@"UsesMaxPictureSettings"] intValue] == 1)
4351 /* Use Max Picture settings for whatever the dvd is.*/
4352 [self revertPictureSizeToMax:nil];
4353 job->keep_ratio = [[chosenPreset objectForKey:@"PictureKeepRatio"] intValue];
4354 if (job->keep_ratio == 1)
4356 hb_fix_aspect( job, HB_KEEP_WIDTH );
4357 if( job->height > fTitle->height )
4359 job->height = fTitle->height;
4360 hb_fix_aspect( job, HB_KEEP_HEIGHT );
4363 job->pixel_ratio = [[chosenPreset objectForKey:@"PicturePAR"] intValue];
4365 else // /* If not 0 or 2 we assume objectForKey:@"UsesPictureSettings is 1 which is "Use picture sizing from when the preset was set" */
4367 /* we check to make sure the presets width/height does not exceed the sources width/height */
4368 if (fTitle->width < [[chosenPreset objectForKey:@"PictureWidth"] intValue] || fTitle->height < [[chosenPreset objectForKey:@"PictureHeight"] intValue])
4370 /* if so, then we use the sources height and width to avoid scaling up */
4371 job->width = fTitle->width;
4372 job->height = fTitle->height;
4374 else // source width/height is >= the preset height/width
4376 /* we can go ahead and use the presets values for height and width */
4377 job->width = [[chosenPreset objectForKey:@"PictureWidth"] intValue];
4378 job->height = [[chosenPreset objectForKey:@"PictureHeight"] intValue];
4380 job->keep_ratio = [[chosenPreset objectForKey:@"PictureKeepRatio"] intValue];
4381 if (job->keep_ratio == 1)
4383 hb_fix_aspect( job, HB_KEEP_WIDTH );
4384 if( job->height > fTitle->height )
4386 job->height = fTitle->height;
4387 hb_fix_aspect( job, HB_KEEP_HEIGHT );
4390 job->pixel_ratio = [[chosenPreset objectForKey:@"PicturePAR"] intValue];
4393 /* If Cropping is set to custom, then recall all four crop values from
4394 when the preset was created and apply them */
4395 if ([[chosenPreset objectForKey:@"PictureAutoCrop"] intValue] == 0)
4397 [fPictureController setAutoCrop:NO];
4399 /* Here we use the custom crop values saved at the time the preset was saved */
4400 job->crop[0] = [[chosenPreset objectForKey:@"PictureTopCrop"] intValue];
4401 job->crop[1] = [[chosenPreset objectForKey:@"PictureBottomCrop"] intValue];
4402 job->crop[2] = [[chosenPreset objectForKey:@"PictureLeftCrop"] intValue];
4403 job->crop[3] = [[chosenPreset objectForKey:@"PictureRightCrop"] intValue];
4406 else /* if auto crop has been saved in preset, set to auto and use post scan auto crop */
4408 [fPictureController setAutoCrop:YES];
4409 /* Here we use the auto crop values determined right after scan */
4410 job->crop[0] = AutoCropTop;
4411 job->crop[1] = AutoCropBottom;
4412 job->crop[2] = AutoCropLeft;
4413 job->crop[3] = AutoCropRight;
4416 /* If the preset has no objectForKey:@"UsesPictureFilters", then we know it is a legacy preset
4417 * and handle the filters here as before.
4418 * NOTE: This should be removed when the update presets code is done as we can be assured that legacy
4419 * presets are updated to work properly with new keys.
4421 if (![chosenPreset objectForKey:@"UsesPictureFilters"])
4425 if ([chosenPreset objectForKey:@"PictureDeinterlace"])
4427 /* We check to see if the preset used the past fourth "Slowest" deinterlaceing and set that to "Slower
4428 * since we no longer have a fourth "Slowest" deinterlacing due to the mcdeint bug */
4429 if ([[chosenPreset objectForKey:@"PictureDeinterlace"] intValue] == 4)
4431 [fPictureController setDeinterlace:3];
4436 [fPictureController setDeinterlace:[[chosenPreset objectForKey:@"PictureDeinterlace"] intValue]];
4441 [fPictureController setDeinterlace:0];
4444 if ([[chosenPreset objectForKey:@"VFR"] intValue] == 1)
4446 [fPictureController setVFR:[[chosenPreset objectForKey:@"VFR"] intValue]];
4450 [fPictureController setVFR:0];
4453 if ([[chosenPreset objectForKey:@"PictureDetelecine"] intValue] == 1)
4455 [fPictureController setDetelecine:[[chosenPreset objectForKey:@"PictureDetelecine"] intValue]];
4459 [fPictureController setDetelecine:0];
4462 if ([chosenPreset objectForKey:@"PictureDenoise"])
4464 [fPictureController setDenoise:[[chosenPreset objectForKey:@"PictureDenoise"] intValue]];
4468 [fPictureController setDenoise:0];
4471 if ([[chosenPreset objectForKey:@"PictureDeblock"] intValue] == 1)
4473 [fPictureController setDeblock:[[chosenPreset objectForKey:@"PictureDeblock"] intValue]];
4477 [fPictureController setDeblock:0];
4480 [self calculatePictureSizing:nil];
4487 /* If the preset has an objectForKey:@"UsesPictureFilters", then we know it is a newer style filters preset
4488 * and handle the filters here depending on whether or not the preset specifies applying the filter.
4490 if ([chosenPreset objectForKey:@"UsesPictureFilters"] && [[chosenPreset objectForKey:@"UsesPictureFilters"] intValue] > 0)
4494 if ([chosenPreset objectForKey:@"PictureDeinterlace"])
4496 /* We check to see if the preset used the past fourth "Slowest" deinterlaceing and set that to "Slower
4497 * since we no longer have a fourth "Slowest" deinterlacing due to the mcdeint bug */
4498 if ([[chosenPreset objectForKey:@"PictureDeinterlace"] intValue] == 4)
4500 [fPictureController setDeinterlace:3];
4504 [fPictureController setDeinterlace:[[chosenPreset objectForKey:@"PictureDeinterlace"] intValue]];
4509 [fPictureController setDeinterlace:0];
4512 if ([[chosenPreset objectForKey:@"VFR"] intValue] == 1)
4514 [fPictureController setVFR:[[chosenPreset objectForKey:@"VFR"] intValue]];
4518 [fPictureController setVFR:0];
4521 if ([[chosenPreset objectForKey:@"PictureDetelecine"] intValue] == 1)
4523 [fPictureController setDetelecine:[[chosenPreset objectForKey:@"PictureDetelecine"] intValue]];
4527 [fPictureController setDetelecine:0];
4530 if ([chosenPreset objectForKey:@"PictureDenoise"])
4532 [fPictureController setDenoise:[[chosenPreset objectForKey:@"PictureDenoise"] intValue]];
4536 [fPictureController setDenoise:0];
4539 if ([[chosenPreset objectForKey:@"PictureDeblock"] intValue] == 1)
4541 [fPictureController setDeblock:[[chosenPreset objectForKey:@"PictureDeblock"] intValue]];
4545 [fPictureController setDeblock:0];
4548 /* Even though we currently allow for a custom setting for decomb, ultimately it will only have Off and
4549 * Default so we just pay attention to anything greater than 0 as 1 (Default). 0 is Off. */
4550 if ([[chosenPreset objectForKey:@"PictureDecomb"] intValue] > 0)
4552 [fPictureController setDecomb:1];
4556 [fPictureController setDecomb:0];
4559 [self calculatePictureSizing:nil];
4565 #pragma mark Manage Presets
4567 - (void) loadPresets {
4568 /* We declare the default NSFileManager into fileManager */
4569 NSFileManager * fileManager = [NSFileManager defaultManager];
4570 /*We define the location of the user presets file */
4571 UserPresetsFile = @"~/Library/Application Support/HandBrake/UserPresets.plist";
4572 UserPresetsFile = [[UserPresetsFile stringByExpandingTildeInPath]retain];
4573 /* We check for the presets.plist */
4574 if ([fileManager fileExistsAtPath:UserPresetsFile] == 0)
4576 [fileManager createFileAtPath:UserPresetsFile contents:nil attributes:nil];
4579 UserPresets = [[NSMutableArray alloc] initWithContentsOfFile:UserPresetsFile];
4580 if (nil == UserPresets)
4582 UserPresets = [[NSMutableArray alloc] init];
4583 [self addFactoryPresets:nil];
4585 [fPresetsOutlineView reloadData];
4589 - (IBAction) showAddPresetPanel: (id) sender
4591 /* Deselect the currently selected Preset if there is one*/
4592 [fPresetsOutlineView deselectRow:[fPresetsOutlineView selectedRow]];
4594 /* Populate the preset picture settings popup here */
4595 [fPresetNewPicSettingsPopUp removeAllItems];
4596 [fPresetNewPicSettingsPopUp addItemWithTitle:@"None"];
4597 [fPresetNewPicSettingsPopUp addItemWithTitle:@"Current"];
4598 [fPresetNewPicSettingsPopUp addItemWithTitle:@"Source Maximum (post source scan)"];
4599 [fPresetNewPicSettingsPopUp selectItemAtIndex: 0];
4600 /* Uncheck the preset use filters checkbox */
4601 [fPresetNewPicFiltersCheck setState:NSOffState];
4602 /* Erase info from the input fields*/
4603 [fPresetNewName setStringValue: @""];
4604 [fPresetNewDesc setStringValue: @""];
4605 /* Show the panel */
4606 [NSApp beginSheet:fAddPresetPanel modalForWindow:fWindow modalDelegate:nil didEndSelector:NULL contextInfo:NULL];
4609 - (IBAction) closeAddPresetPanel: (id) sender
4611 [NSApp endSheet: fAddPresetPanel];
4612 [fAddPresetPanel orderOut: self];
4615 - (IBAction)addUserPreset:(id)sender
4617 if (![[fPresetNewName stringValue] length])
4618 NSRunAlertPanel(@"Warning!", @"You need to insert a name for the preset.", @"OK", nil , nil);
4621 /* Here we create a custom user preset */
4622 [UserPresets addObject:[self createPreset]];
4625 [self closeAddPresetPanel:nil];
4632 /* We Reload the New Table data for presets */
4633 [fPresetsOutlineView reloadData];
4634 /* We save all of the preset data here */
4642 /* We Sort the Presets By Factory or Custom */
4643 NSSortDescriptor * presetTypeDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"Type"
4644 ascending:YES] autorelease];
4645 /* We Sort the Presets Alphabetically by name We do not use this now as we have drag and drop*/
4647 NSSortDescriptor * presetNameDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"PresetName"
4648 ascending:YES selector:@selector(caseInsensitiveCompare:)] autorelease];
4649 //NSArray *sortDescriptors=[NSArray arrayWithObjects:presetTypeDescriptor,presetNameDescriptor,nil];
4652 /* Since we can drag and drop our custom presets, lets just sort by type and not name */
4653 NSArray *sortDescriptors=[NSArray arrayWithObjects:presetTypeDescriptor,nil];
4654 NSArray *sortedArray=[UserPresets sortedArrayUsingDescriptors:sortDescriptors];
4655 [UserPresets setArray:sortedArray];
4660 - (IBAction)insertPreset:(id)sender
4662 int index = [fPresetsOutlineView selectedRow];
4663 [UserPresets insertObject:[self createPreset] atIndex:index];
4664 [fPresetsOutlineView reloadData];
4668 - (NSDictionary *)createPreset
4670 NSMutableDictionary *preset = [[NSMutableDictionary alloc] init];
4671 /* Get the New Preset Name from the field in the AddPresetPanel */
4672 [preset setObject:[fPresetNewName stringValue] forKey:@"PresetName"];
4673 /*Set whether or not this is a user preset or factory 0 is factory, 1 is user*/
4674 [preset setObject:[NSNumber numberWithInt:1] forKey:@"Type"];
4675 /*Set whether or not this is default, at creation set to 0*/
4676 [preset setObject:[NSNumber numberWithInt:0] forKey:@"Default"];
4677 /*Get the whether or not to apply pic Size and Cropping (includes Anamorphic)*/
4678 [preset setObject:[NSNumber numberWithInt:[fPresetNewPicSettingsPopUp indexOfSelectedItem]] forKey:@"UsesPictureSettings"];
4679 /* Get whether or not to use the current Picture Filter settings for the preset */
4680 [preset setObject:[NSNumber numberWithInt:[fPresetNewPicFiltersCheck state]] forKey:@"UsesPictureFilters"];
4682 /* Get New Preset Description from the field in the AddPresetPanel*/
4683 [preset setObject:[fPresetNewDesc stringValue] forKey:@"PresetDescription"];
4685 [preset setObject:[fDstFormatPopUp titleOfSelectedItem] forKey:@"FileFormat"];
4686 /* Chapter Markers fCreateChapterMarkers*/
4687 [preset setObject:[NSNumber numberWithInt:[fCreateChapterMarkers state]] forKey:@"ChapterMarkers"];
4688 /* Allow Mpeg4 64 bit formatting +4GB file sizes */
4689 [preset setObject:[NSNumber numberWithInt:[fDstMp4LargeFileCheck state]] forKey:@"Mp4LargeFile"];
4690 /* Mux mp4 with http optimization */
4691 [preset setObject:[NSNumber numberWithInt:[fDstMp4HttpOptFileCheck state]] forKey:@"Mp4HttpOptimize"];
4692 /* Add iPod uuid atom */
4693 [preset setObject:[NSNumber numberWithInt:[fDstMp4iPodFileCheck state]] forKey:@"Mp4iPodCompatible"];
4697 [preset setObject:[fVidEncoderPopUp titleOfSelectedItem] forKey:@"VideoEncoder"];
4698 /* x264 Option String */
4699 [preset setObject:[fAdvancedOptions optionsString] forKey:@"x264Option"];
4701 [preset setObject:[NSNumber numberWithInt:[fVidQualityMatrix selectedRow]] forKey:@"VideoQualityType"];
4702 [preset setObject:[fVidTargetSizeField stringValue] forKey:@"VideoTargetSize"];
4703 [preset setObject:[fVidBitrateField stringValue] forKey:@"VideoAvgBitrate"];
4704 [preset setObject:[NSNumber numberWithFloat:[fVidQualitySlider floatValue]] forKey:@"VideoQualitySlider"];
4706 /* Video framerate */
4707 if ([fVidRatePopUp indexOfSelectedItem] == 0) // Same as source is selected
4709 [preset setObject:@"Same as source" forKey:@"VideoFramerate"];
4711 else // we can record the actual titleOfSelectedItem
4713 [preset setObject:[fVidRatePopUp titleOfSelectedItem] forKey:@"VideoFramerate"];
4716 [preset setObject:[NSNumber numberWithInt:[fVidGrayscaleCheck state]] forKey:@"VideoGrayScale"];
4717 /* 2 Pass Encoding */
4718 [preset setObject:[NSNumber numberWithInt:[fVidTwoPassCheck state]] forKey:@"VideoTwoPass"];
4719 /* Turbo 2 pass Encoding fVidTurboPassCheck*/
4720 [preset setObject:[NSNumber numberWithInt:[fVidTurboPassCheck state]] forKey:@"VideoTurboTwoPass"];
4721 /*Picture Settings*/
4722 hb_job_t * job = fTitle->job;
4723 /* Picture Sizing */
4724 /* Use Max Picture settings for whatever the dvd is.*/
4725 [preset setObject:[NSNumber numberWithInt:0] forKey:@"UsesMaxPictureSettings"];
4726 [preset setObject:[NSNumber numberWithInt:fTitle->job->width] forKey:@"PictureWidth"];
4727 [preset setObject:[NSNumber numberWithInt:fTitle->job->height] forKey:@"PictureHeight"];
4728 [preset setObject:[NSNumber numberWithInt:fTitle->job->keep_ratio] forKey:@"PictureKeepRatio"];
4729 [preset setObject:[NSNumber numberWithInt:fTitle->job->pixel_ratio] forKey:@"PicturePAR"];
4731 /* Set crop settings here */
4732 [preset setObject:[NSNumber numberWithInt:[fPictureController autoCrop]] forKey:@"PictureAutoCrop"];
4733 [preset setObject:[NSNumber numberWithInt:job->crop[0]] forKey:@"PictureTopCrop"];
4734 [preset setObject:[NSNumber numberWithInt:job->crop[1]] forKey:@"PictureBottomCrop"];
4735 [preset setObject:[NSNumber numberWithInt:job->crop[2]] forKey:@"PictureLeftCrop"];
4736 [preset setObject:[NSNumber numberWithInt:job->crop[3]] forKey:@"PictureRightCrop"];
4738 /* Picture Filters */
4739 [preset setObject:[NSNumber numberWithInt:[fPictureController deinterlace]] forKey:@"PictureDeinterlace"];
4740 [preset setObject:[NSNumber numberWithInt:[fPictureController detelecine]] forKey:@"PictureDetelecine"];
4741 [preset setObject:[NSNumber numberWithInt:[fPictureController vfr]] forKey:@"VFR"];
4742 [preset setObject:[NSNumber numberWithInt:[fPictureController denoise]] forKey:@"PictureDenoise"];
4743 [preset setObject:[NSNumber numberWithInt:[fPictureController deblock]] forKey:@"PictureDeblock"];
4744 [preset setObject:[NSNumber numberWithInt:[fPictureController decomb]] forKey:@"PictureDecomb"];
4748 if ([fAudLang1PopUp indexOfSelectedItem] > 0)
4750 [preset setObject:[NSNumber numberWithInt:[fAudLang1PopUp indexOfSelectedItem]] forKey:@"Audio1Track"];
4751 [preset setObject:[fAudLang1PopUp titleOfSelectedItem] forKey:@"Audio1TrackDescription"];
4752 [preset setObject:[fAudTrack1CodecPopUp titleOfSelectedItem] forKey:@"Audio1Encoder"];
4753 [preset setObject:[fAudTrack1MixPopUp titleOfSelectedItem] forKey:@"Audio1Mixdown"];
4754 [preset setObject:[fAudTrack1RatePopUp titleOfSelectedItem] forKey:@"Audio1Samplerate"];
4755 [preset setObject:[fAudTrack1BitratePopUp titleOfSelectedItem] forKey:@"Audio1Bitrate"];
4756 [preset setObject:[NSNumber numberWithFloat:[fAudTrack1DrcSlider floatValue]] forKey:@"Audio1TrackDRCSlider"];
4758 if ([fAudLang2PopUp indexOfSelectedItem] > 0)
4760 [preset setObject:[NSNumber numberWithInt:[fAudLang2PopUp indexOfSelectedItem]] forKey:@"Audio2Track"];
4761 [preset setObject:[fAudLang2PopUp titleOfSelectedItem] forKey:@"Audio2TrackDescription"];
4762 [preset setObject:[fAudTrack2CodecPopUp titleOfSelectedItem] forKey:@"Audio2Encoder"];
4763 [preset setObject:[fAudTrack2MixPopUp titleOfSelectedItem] forKey:@"Audio2Mixdown"];
4764 [preset setObject:[fAudTrack2RatePopUp titleOfSelectedItem] forKey:@"Audio2Samplerate"];
4765 [preset setObject:[fAudTrack2BitratePopUp titleOfSelectedItem] forKey:@"Audio2Bitrate"];
4766 [preset setObject:[NSNumber numberWithFloat:[fAudTrack2DrcSlider floatValue]] forKey:@"Audio2TrackDRCSlider"];
4768 if ([fAudLang3PopUp indexOfSelectedItem] > 0)
4770 [preset setObject:[NSNumber numberWithInt:[fAudLang3PopUp indexOfSelectedItem]] forKey:@"Audio3Track"];
4771 [preset setObject:[fAudLang3PopUp titleOfSelectedItem] forKey:@"Audio3TrackDescription"];
4772 [preset setObject:[fAudTrack3CodecPopUp titleOfSelectedItem] forKey:@"Audio3Encoder"];
4773 [preset setObject:[fAudTrack3MixPopUp titleOfSelectedItem] forKey:@"Audio3Mixdown"];
4774 [preset setObject:[fAudTrack3RatePopUp titleOfSelectedItem] forKey:@"Audio3Samplerate"];
4775 [preset setObject:[fAudTrack3BitratePopUp titleOfSelectedItem] forKey:@"Audio3Bitrate"];
4776 [preset setObject:[NSNumber numberWithFloat:[fAudTrack3DrcSlider floatValue]] forKey:@"Audio3TrackDRCSlider"];
4778 if ([fAudLang4PopUp indexOfSelectedItem] > 0)
4780 [preset setObject:[NSNumber numberWithInt:[fAudLang4PopUp indexOfSelectedItem]] forKey:@"Audio4Track"];
4781 [preset setObject:[fAudLang4PopUp titleOfSelectedItem] forKey:@"Audio4TrackDescription"];
4782 [preset setObject:[fAudTrack4CodecPopUp titleOfSelectedItem] forKey:@"Audio4Encoder"];
4783 [preset setObject:[fAudTrack4MixPopUp titleOfSelectedItem] forKey:@"Audio4Mixdown"];
4784 [preset setObject:[fAudTrack4RatePopUp titleOfSelectedItem] forKey:@"Audio4Samplerate"];
4785 [preset setObject:[fAudTrack4BitratePopUp titleOfSelectedItem] forKey:@"Audio4Bitrate"];
4786 [preset setObject:[NSNumber numberWithFloat:[fAudTrack4DrcSlider floatValue]] forKey:@"Audio4TrackDRCSlider"];
4790 [preset setObject:[fSubPopUp titleOfSelectedItem] forKey:@"Subtitles"];
4791 /* Forced Subtitles */
4792 [preset setObject:[NSNumber numberWithInt:[fSubForcedCheck state]] forKey:@"SubtitlesForced"];
4794 [preset autorelease];
4801 [UserPresets writeToFile:UserPresetsFile atomically:YES];
4802 /* We get the default preset in case it changed */
4803 [self getDefaultPresets:nil];
4807 - (IBAction)deletePreset:(id)sender
4810 NSEnumerator *enumerator;
4812 NSMutableArray *tempArray;
4815 if ( [fPresetsOutlineView numberOfSelectedRows] == 0 )
4817 /* Alert user before deleting preset */
4818 /* Comment out for now, tie to user pref eventually */
4821 status = NSRunAlertPanel(@"Warning!", @"Are you sure that you want to delete the selected preset?", @"OK", @"Cancel", nil);
4823 if ( status == NSAlertDefaultReturn ) {
4824 enumerator = [fPresetsOutlineView selectedRowEnumerator];
4825 tempArray = [NSMutableArray array];
4827 while ( (index = [enumerator nextObject]) ) {
4828 tempObject = [UserPresets objectAtIndex:[index intValue]];
4829 [tempArray addObject:tempObject];
4832 [UserPresets removeObjectsInArray:tempArray];
4833 [fPresetsOutlineView reloadData];
4839 #pragma mark Manage Default Preset
4841 - (IBAction)getDefaultPresets:(id)sender
4844 presetCurrentBuiltInCount = 0;
4845 NSEnumerator *enumerator = [UserPresets objectEnumerator];
4847 while (tempObject = [enumerator nextObject])
4849 NSDictionary *thisPresetDict = tempObject;
4850 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 1) // 1 is HB default
4852 presetHbDefault = i;
4854 if ([[thisPresetDict objectForKey:@"Default"] intValue] == 2) // 2 is User specified default
4856 presetUserDefault = i;
4858 if ([[thisPresetDict objectForKey:@"Type"] intValue] == 0) // Type 0 is a built in preset
4860 presetCurrentBuiltInCount++; // <--increment the current number of built in presets
4866 - (IBAction)setDefaultPreset:(id)sender
4869 NSEnumerator *enumerator = [UserPresets objectEnumerator];
4871 /* First make sure the old user specified default preset is removed */
4872 while (tempObject = [enumerator nextObject])
4874 /* make sure we are not removing the default HB preset */
4875 if ([[[UserPresets objectAtIndex:i] objectForKey:@"Default"] intValue] != 1) // 1 is HB default
4877 [[UserPresets objectAtIndex:i] setObject:[NSNumber numberWithInt:0] forKey:@"Default"];
4881 /* Second, go ahead and set the appropriate user specfied preset */
4882 /* we get the chosen preset from the UserPresets array */
4883 if ([[[UserPresets objectAtIndex:[fPresetsOutlineView selectedRow]] objectForKey:@"Default"] intValue] != 1) // 1 is HB default
4885 [[UserPresets objectAtIndex:[fPresetsOutlineView selectedRow]] setObject:[NSNumber numberWithInt:2] forKey:@"Default"];
4887 /*FIX ME: I think we now need to use the items not rows in NSOutlineView */
4888 presetUserDefault = [fPresetsOutlineView selectedRow];
4890 /* We save all of the preset data here */
4892 /* We Reload the New Table data for presets */
4893 [fPresetsOutlineView reloadData];
4896 - (IBAction)selectDefaultPreset:(id)sender
4898 /* if there is a user specified default, we use it */
4899 if (presetUserDefault)
4901 [fPresetsOutlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:presetUserDefault] byExtendingSelection:NO];
4902 [self selectPreset:nil];
4904 else if (presetHbDefault) //else we use the built in default presetHbDefault
4906 [fPresetsOutlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:presetHbDefault] byExtendingSelection:NO];
4907 [self selectPreset:nil];
4913 #pragma mark Manage Built In Presets
4916 - (IBAction)deleteFactoryPresets:(id)sender
4919 NSEnumerator *enumerator = [UserPresets objectEnumerator];
4923 NSMutableArray *tempArray;
4926 tempArray = [NSMutableArray array];
4927 /* we look here to see if the preset is we move on to the next one */
4928 while ( tempObject = [enumerator nextObject] )
4930 /* if the preset is "Factory" then we put it in the array of
4931 presets to delete */
4932 if ([[tempObject objectForKey:@"Type"] intValue] == 0)
4934 [tempArray addObject:tempObject];
4938 [UserPresets removeObjectsInArray:tempArray];
4939 [fPresetsOutlineView reloadData];
4944 /* We use this method to recreate new, updated factory
4946 - (IBAction)addFactoryPresets:(id)sender
4949 /* First, we delete any existing built in presets */
4950 [self deleteFactoryPresets: sender];
4951 /* Then we generate new built in presets programmatically with fPresetsBuiltin
4952 * which is all setup in HBPresets.h and HBPresets.m*/
4953 [fPresetsBuiltin generateBuiltinPresets:UserPresets];
4965 /*******************************
4966 * Subclass of the HBPresetsOutlineView *
4967 *******************************/
4969 @implementation HBPresetsOutlineView
4970 - (NSImage *)dragImageForRowsWithIndexes:(NSIndexSet *)dragRows tableColumns:(NSArray *)tableColumns event:(NSEvent*)dragEvent offset:(NSPointPointer)dragImageOffset
4974 // By default, NSTableView only drags an image of the first column. Change this to
4975 // drag an image of the queue's icon and PresetName columns.
4976 NSArray * cols = [NSArray arrayWithObjects: [self tableColumnWithIdentifier:@"icon"], [self tableColumnWithIdentifier:@"PresetName"], nil];
4977 return [super dragImageForRowsWithIndexes:dragRows tableColumns:cols event:dragEvent offset:dragImageOffset];
4982 - (void) mouseDown:(NSEvent *)theEvent
4984 [super mouseDown:theEvent];
4990 - (BOOL) isDragging;