diff options
author | ritsuka <[email protected]> | 2009-08-29 07:20:25 +0000 |
---|---|---|
committer | ritsuka <[email protected]> | 2009-08-29 07:20:25 +0000 |
commit | 5f9fd99f84eb5d5cf582318c180130aa2a46f4f8 (patch) | |
tree | f23a3f9e3e299a8cb905d01dcd6ba9109832abed /macosx/Controller.m | |
parent | a6ff6f6faaa80c1876e29f621183c592c5e61f43 (diff) |
MacGUI: Renamed Controller.mm to Controller.m, changed the default std to C99.
git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@2789 b64f7644-9d1e-0410-96f1-a4d463321fa5
Diffstat (limited to 'macosx/Controller.m')
-rw-r--r-- | macosx/Controller.m | 7519 |
1 files changed, 7519 insertions, 0 deletions
diff --git a/macosx/Controller.m b/macosx/Controller.m new file mode 100644 index 000000000..e6860859a --- /dev/null +++ b/macosx/Controller.m @@ -0,0 +1,7519 @@ +/* $Id: Controller.mm,v 1.79 2005/11/04 19:41:32 titer Exp $ + + This file is part of the HandBrake source code. + Homepage: <http://handbrake.fr/>. + It may be used under the terms of the GNU General Public License. */ + +#import "Controller.h" +#import "HBOutputPanelController.h" +#import "HBPreferencesController.h" +#import "HBDVDDetector.h" +#import "HBPresets.h" +#import "HBPreviewController.h" + +#define DragDropSimplePboardType @"MyCustomOutlineViewPboardType" + +/* We setup the toolbar values here ShowPreviewIdentifier */ +static NSString * ToggleDrawerIdentifier = @"Toggle Drawer Item Identifier"; +static NSString * StartEncodingIdentifier = @"Start Encoding Item Identifier"; +static NSString * PauseEncodingIdentifier = @"Pause Encoding Item Identifier"; +static NSString * ShowQueueIdentifier = @"Show Queue Item Identifier"; +static NSString * AddToQueueIdentifier = @"Add to Queue Item Identifier"; +static NSString * ShowPictureIdentifier = @"Show Picture Window Item Identifier"; +static NSString * ShowPreviewIdentifier = @"Show Preview Window Item Identifier"; +static NSString * ShowActivityIdentifier = @"Debug Output Item Identifier"; +static NSString * ChooseSourceIdentifier = @"Choose Source Item Identifier"; + + +/******************************* + * HBController implementation * + *******************************/ +@implementation HBController + +- (id)init +{ + self = [super init]; + if( !self ) + { + return nil; + } + + /* replace bundled app icon with one which is 32/64-bit savvy */ +#if defined( __LP64__ ) + fApplicationIcon = [[NSImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForImageResource:@"HandBrake-64.icns"]]; +#else + fApplicationIcon = [[NSImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForImageResource:@"HandBrake.icns"]]; +#endif + if( fApplicationIcon != nil ) + [NSApp setApplicationIconImage:fApplicationIcon]; + + [HBPreferencesController registerUserDefaults]; + fHandle = NULL; + fQueueEncodeLibhb = NULL; + /* Check for check for the app support directory here as + * outputPanel needs it right away, as may other future methods + */ + NSString *libraryDir = [NSSearchPathForDirectoriesInDomains( NSLibraryDirectory, + NSUserDomainMask, + YES ) objectAtIndex:0]; + AppSupportDirectory = [[libraryDir stringByAppendingPathComponent:@"Application Support"] + stringByAppendingPathComponent:@"HandBrake"]; + if( ![[NSFileManager defaultManager] fileExistsAtPath:AppSupportDirectory] ) + { + [[NSFileManager defaultManager] createDirectoryAtPath:AppSupportDirectory + attributes:nil]; + } + /* Check for and create the App Support Preview directory if necessary */ + NSString *PreviewDirectory = [AppSupportDirectory stringByAppendingPathComponent:@"Previews"]; + if( ![[NSFileManager defaultManager] fileExistsAtPath:PreviewDirectory] ) + { + [[NSFileManager defaultManager] createDirectoryAtPath:PreviewDirectory + attributes:nil]; + } + outputPanel = [[HBOutputPanelController alloc] init]; + fPictureController = [[PictureController alloc] init]; + fQueueController = [[HBQueueController alloc] init]; + fAdvancedOptions = [[HBAdvancedController alloc] init]; + /* we init the HBPresets class which currently is only used + * for updating built in presets, may move more functionality + * there in the future + */ + fPresetsBuiltin = [[HBPresets alloc] init]; + fPreferencesController = [[HBPreferencesController alloc] init]; + /* Lets report the HandBrake version number here to the activity log and text log file */ + NSString *versionStringFull = [[NSString stringWithFormat: @"Handbrake Version: %@", [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"]] stringByAppendingString: [NSString stringWithFormat: @" (%@)", [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"]]]; + [self writeToActivityLog: "%s", [versionStringFull UTF8String]]; + + return self; +} + + +- (void) applicationDidFinishLaunching: (NSNotification *) notification +{ + /* Init libhb with check for updates libhb style set to "0" so its ignored and lets sparkle take care of it */ + int loggingLevel = [[[NSUserDefaults standardUserDefaults] objectForKey:@"LoggingLevel"] intValue]; + fHandle = hb_init(loggingLevel, 0); + /* Optional dvd nav UseDvdNav*/ + hb_dvd_set_dvdnav([[[NSUserDefaults standardUserDefaults] objectForKey:@"UseDvdNav"] boolValue]); + /* Init a separate instance of libhb for user scanning and setting up jobs */ + fQueueEncodeLibhb = hb_init(loggingLevel, 0); + + // Set the Growl Delegate + [GrowlApplicationBridge setGrowlDelegate: self]; + /* Init others controllers */ + [fPictureController SetHandle: fHandle]; + [fPictureController setHBController: self]; + + [fQueueController setHandle: fQueueEncodeLibhb]; + [fQueueController setHBController: self]; + + fChapterTitlesDelegate = [[ChapterTitles alloc] init]; + [fChapterTable setDataSource:fChapterTitlesDelegate]; + [fChapterTable setDelegate:fChapterTitlesDelegate]; + + /* setup the subtitles delegate and connections to table */ + fSubtitlesDelegate = [[HBSubtitles alloc] init]; + [fSubtitlesTable setDataSource:fSubtitlesDelegate]; + [fSubtitlesTable setDelegate:fSubtitlesDelegate]; + [fSubtitlesTable setRowHeight:25.0]; + + [fPresetsOutlineView setAutosaveName:@"Presets View"]; + [fPresetsOutlineView setAutosaveExpandedItems:YES]; + + dockIconProgress = 0; + + /* Call UpdateUI every 1/2 sec */ + [[NSRunLoop currentRunLoop] addTimer:[NSTimer + scheduledTimerWithTimeInterval:0.5 target:self + selector:@selector(updateUI:) userInfo:nil repeats:YES] + forMode:NSDefaultRunLoopMode]; + + // Open debug output window now if it was visible when HB was closed + if ([[NSUserDefaults standardUserDefaults] boolForKey:@"OutputPanelIsOpen"]) + [self showDebugOutputPanel:nil]; + + // Open queue window now if it was visible when HB was closed + if ([[NSUserDefaults standardUserDefaults] boolForKey:@"QueueWindowIsOpen"]) + [self showQueueWindow:nil]; + + [self openMainWindow:nil]; + + /* We have to set the bool to tell hb what to do after a scan + * Initially we set it to NO until we start processing the queue + */ + applyQueueToScan = NO; + + /* Now we re-check the queue array to see if there are + * any remaining encodes to be done in it and ask the + * user if they want to reload the queue */ + if ([QueueFileArray count] > 0) + { + /* run getQueueStats to see whats in the queue file */ + [self getQueueStats]; + /* this results in these values + * fEncodingQueueItem = 0; + * fPendingCount = 0; + * fCompletedCount = 0; + * fCanceledCount = 0; + * fWorkingCount = 0; + */ + + /*On Screen Notification*/ + NSString * alertTitle; + + /* We check to see if there is already another instance of hb running. + * Note: hbInstances == 1 means we are the only instance of HandBrake.app + */ + if ([self hbInstances] > 1) + { + alertTitle = [NSString stringWithFormat: + NSLocalizedString(@"There is already an instance of HandBrake running.", @"")]; + NSBeginCriticalAlertSheet( + alertTitle, + NSLocalizedString(@"Reload Queue", nil), + nil, + nil, + fWindow, self, + nil, @selector(didDimissReloadQueue:returnCode:contextInfo:), nil, + NSLocalizedString(@" HandBrake will now load up the existing queue.", nil)); + } + else + { + if (fWorkingCount > 0) + { + alertTitle = [NSString stringWithFormat: + NSLocalizedString(@"HandBrake Has Detected %d Previously Encoding Item and %d Pending Item(s) In Your Queue.", @""), + fWorkingCount,fPendingCount]; + } + else + { + alertTitle = [NSString stringWithFormat: + NSLocalizedString(@"HandBrake Has Detected %d Pending Item(s) In Your Queue.", @""), + fPendingCount]; + } + + NSBeginCriticalAlertSheet( + alertTitle, + NSLocalizedString(@"Reload Queue", nil), + nil, + NSLocalizedString(@"Empty Queue", nil), + fWindow, self, + nil, @selector(didDimissReloadQueue:returnCode:contextInfo:), nil, + NSLocalizedString(@" Do you want to reload them ?", nil)); + } + + // call didDimissReloadQueue: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo + // right below to either clear the old queue or keep it loaded up. + } + else + { + /* We show whichever open source window specified in LaunchSourceBehavior preference key */ + if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"LaunchSourceBehavior"] isEqualToString: @"Open Source"]) + { + [self browseSources:nil]; + } + + if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"LaunchSourceBehavior"] isEqualToString: @"Open Source (Title Specific)"]) + { + [self browseSources:(id)fOpenSourceTitleMMenu]; + } + } +} + +- (int) hbInstances +{ + /* check to see if another instance of HandBrake.app is running */ + NSArray *runningAppDictionaries = [[NSWorkspace sharedWorkspace] launchedApplications]; + NSDictionary *aDictionary; + int hbInstances = 0; + for (aDictionary in runningAppDictionaries) + { + // NSLog(@"Open App: %@", [aDictionary valueForKey:@"NSApplicationName"]); + + if ([[aDictionary valueForKey:@"NSApplicationName"] isEqualToString:@"HandBrake"]) + { + hbInstances++; + } + } + return hbInstances; +} + +- (void) didDimissReloadQueue: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo +{ + if (returnCode == NSAlertOtherReturn) + { + [self clearQueueAllItems]; + /* We show whichever open source window specified in LaunchSourceBehavior preference key */ + if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"LaunchSourceBehavior"] isEqualToString: @"Open Source"]) + { + [self browseSources:nil]; + } + + if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"LaunchSourceBehavior"] isEqualToString: @"Open Source (Title Specific)"]) + { + [self browseSources:(id)fOpenSourceTitleMMenu]; + } + } + else + { + if ([self hbInstances] == 1) + { + [self setQueueEncodingItemsAsPending]; + } + [self showQueueWindow:NULL]; + } +} + +- (NSApplicationTerminateReply) applicationShouldTerminate: (NSApplication *) app +{ + /* if we are in preview full screen mode, we need to go to + * windowed mode and release the display before we terminate. + * We do it here (instead of applicationWillTerminate) so we + * release the displays and can then see the alerts below. + */ + if ([fPictureController previewFullScreenMode] == YES) + { + [fPictureController previewGoWindowed:nil]; + } + + hb_state_t s; + hb_get_state( fQueueEncodeLibhb, &s ); + + if ( s.state != HB_STATE_IDLE ) + { + int result = NSRunCriticalAlertPanel( + NSLocalizedString(@"Are you sure you want to quit HandBrake?", nil), + NSLocalizedString(@"If you quit HandBrake your current encode will be reloaded into your queue at next launch. Do you want to quit anyway?", nil), + NSLocalizedString(@"Quit", nil), NSLocalizedString(@"Don't Quit", nil), nil, @"A movie" ); + + if (result == NSAlertDefaultReturn) + { + return NSTerminateNow; + } + else + return NSTerminateCancel; + } + + // Warn if items still in the queue + else if ( fPendingCount > 0 ) + { + int result = NSRunCriticalAlertPanel( + NSLocalizedString(@"Are you sure you want to quit HandBrake?", nil), + NSLocalizedString(@"There are pending encodes in your queue. Do you want to quit anyway?",nil), + NSLocalizedString(@"Quit", nil), NSLocalizedString(@"Don't Quit", nil), nil); + + if ( result == NSAlertDefaultReturn ) + return NSTerminateNow; + else + return NSTerminateCancel; + } + + return NSTerminateNow; +} + +- (void)applicationWillTerminate:(NSNotification *)aNotification +{ + + [browsedSourceDisplayName release]; + [outputPanel release]; + [fQueueController release]; + [fPreviewController release]; + [fPictureController release]; + [fApplicationIcon release]; + + hb_close(&fHandle); + hb_close(&fQueueEncodeLibhb); +} + + +- (void) awakeFromNib +{ + [fWindow center]; + [fWindow setExcludedFromWindowsMenu:YES]; + [fAdvancedOptions setView:fAdvancedView]; + + /* lets setup our presets drawer for drag and drop here */ + [fPresetsOutlineView registerForDraggedTypes: [NSArray arrayWithObject:DragDropSimplePboardType] ]; + [fPresetsOutlineView setDraggingSourceOperationMask:NSDragOperationEvery forLocal:YES]; + [fPresetsOutlineView setVerticalMotionCanBeginDrag: YES]; + + /* Initialize currentScanCount so HB can use it to + evaluate successive scans */ + currentScanCount = 0; + + + /* Init UserPresets .plist */ + [self loadPresets]; + + /* Init QueueFile .plist */ + [self loadQueueFile]; + + fRipIndicatorShown = NO; // initially out of view in the nib + + /* For 64 bit builds, the threaded animation in the progress + * indicators conflicts with the animation in the advanced tab + * for reasons not completely clear. jbrjake found a note in the + * 10.5 dev notes regarding this possiblility. It was also noted + * that unless specified, setUsesThreadedAnimation defaults to true. + * So, at least for now we set the indicator animation to NO for + * both the scan and regular progress indicators for both 32 and 64 bit + * as it test out fine on both and there is no reason our progress indicators + * should require their own thread. + */ + + [fScanIndicator setUsesThreadedAnimation:NO]; + [fRipIndicator setUsesThreadedAnimation:NO]; + + + + /* Show/Dont Show Presets drawer upon launch based + on user preference DefaultPresetsDrawerShow*/ + if( [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultPresetsDrawerShow"] > 0 ) + { + [fPresetDrawer setDelegate:self]; + NSSize drawerSize = NSSizeFromString( [[NSUserDefaults standardUserDefaults] + stringForKey:@"Drawer Size"] ); + if( drawerSize.width ) + [fPresetDrawer setContentSize: drawerSize]; + [fPresetDrawer open]; + } + + /* Initially set the dvd angle widgets to hidden (dvdnav only) */ + [fSrcAngleLabel setHidden:YES]; + [fSrcAnglePopUp setHidden:YES]; + + /* Destination box*/ + NSMenuItem *menuItem; + [fDstFormatPopUp removeAllItems]; + // MP4 file + menuItem = [[fDstFormatPopUp menu] addItemWithTitle:@"MP4 file" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_MUX_MP4]; + // MKV file + menuItem = [[fDstFormatPopUp menu] addItemWithTitle:@"MKV file" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_MUX_MKV]; + + [fDstFormatPopUp selectItemAtIndex: 0]; + + [self formatPopUpChanged:nil]; + + /* We enable the create chapters checkbox here since we are .mp4 */ + [fCreateChapterMarkers setEnabled: YES]; + if ([fDstFormatPopUp indexOfSelectedItem] == 0 && [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultChapterMarkers"] > 0) + { + [fCreateChapterMarkers setState: NSOnState]; + } + + + + + [fDstFile2Field setStringValue: [NSString stringWithFormat: + @"%@/Desktop/Movie.mp4", NSHomeDirectory()]]; + + /* Video encoder */ + [fVidEncoderPopUp removeAllItems]; + [fVidEncoderPopUp addItemWithTitle: @"FFmpeg"]; + + + + /* Video quality */ + [fVidTargetSizeField setIntValue: 700]; + [fVidBitrateField setIntValue: 1000]; + + [fVidQualityMatrix selectCell: fVidBitrateCell]; + [self videoMatrixChanged:nil]; + + /* Video framerate */ + [fVidRatePopUp removeAllItems]; + [fVidRatePopUp addItemWithTitle: NSLocalizedString( @"Same as source", @"" )]; + for( int i = 0; i < hb_video_rates_count; i++ ) + { + if ([[NSString stringWithCString: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%.3f",23.976]]) + { + [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@", + [NSString stringWithCString: hb_video_rates[i].string], @" (NTSC Film)"]]; + } + else if ([[NSString stringWithCString: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%d",25]]) + { + [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@", + [NSString stringWithCString: hb_video_rates[i].string], @" (PAL Film/Video)"]]; + } + else if ([[NSString stringWithCString: hb_video_rates[i].string] isEqualToString: [NSString stringWithFormat: @"%.2f",29.97]]) + { + [fVidRatePopUp addItemWithTitle:[NSString stringWithFormat: @"%@%@", + [NSString stringWithCString: hb_video_rates[i].string], @" (NTSC Video)"]]; + } + else + { + [fVidRatePopUp addItemWithTitle: + [NSString stringWithCString: hb_video_rates[i].string]]; + } + } + [fVidRatePopUp selectItemAtIndex: 0]; + + /* Set Auto Crop to On at launch */ + [fPictureController setAutoCrop:YES]; + + /* Audio bitrate */ + [fAudTrack1BitratePopUp removeAllItems]; + for( int i = 0; i < hb_audio_bitrates_count; i++ ) + { + [fAudTrack1BitratePopUp addItemWithTitle: + [NSString stringWithCString: hb_audio_bitrates[i].string]]; + + } + [fAudTrack1BitratePopUp selectItemAtIndex: hb_audio_bitrates_default]; + + /* Audio samplerate */ + [fAudTrack1RatePopUp removeAllItems]; + for( int i = 0; i < hb_audio_rates_count; i++ ) + { + [fAudTrack1RatePopUp addItemWithTitle: + [NSString stringWithCString: hb_audio_rates[i].string]]; + } + [fAudTrack1RatePopUp selectItemAtIndex: hb_audio_rates_default]; + + /* Bottom */ + [fStatusField setStringValue: @""]; + + [self enableUI: NO]; + [self setupToolbar]; + + /* We disable the Turbo 1st pass checkbox since we are not x264 */ + [fVidTurboPassCheck setEnabled: NO]; + [fVidTurboPassCheck setState: NSOffState]; + + + /* lets get our default prefs here */ + [self getDefaultPresets:nil]; + /* lets initialize the current successful scancount here to 0 */ + currentSuccessfulScanCount = 0; + + +} + +- (void) enableUI: (bool) b +{ + NSControl * controls[] = + { fSrcTitleField, fSrcTitlePopUp, + fSrcChapterField, fSrcChapterStartPopUp, fSrcChapterToField, + fSrcChapterEndPopUp, fSrcDuration1Field, fSrcDuration2Field, + fDstFormatField, fDstFormatPopUp, fDstFile1Field, fDstFile2Field, + fDstBrowseButton, fVidRateField, fVidRatePopUp,fVidEncoderField, fVidEncoderPopUp, fVidQualityField, + fPictureSizeField,fPictureCroppingField, fVideoFiltersField,fVidQualityMatrix, fSubField, fSubPopUp, + fAudSourceLabel, fAudCodecLabel, fAudMixdownLabel, fAudSamplerateLabel, fAudBitrateLabel, + fAudTrack1Label, fAudTrack2Label, fAudTrack3Label, fAudTrack4Label, + fAudLang1PopUp, fAudLang2PopUp, fAudLang3PopUp, fAudLang4PopUp, + fAudTrack1CodecPopUp, fAudTrack2CodecPopUp, fAudTrack3CodecPopUp, fAudTrack4CodecPopUp, + fAudTrack1MixPopUp, fAudTrack2MixPopUp, fAudTrack3MixPopUp, fAudTrack4MixPopUp, + fAudTrack1RatePopUp, fAudTrack2RatePopUp, fAudTrack3RatePopUp, fAudTrack4RatePopUp, + fAudTrack1BitratePopUp, fAudTrack2BitratePopUp, fAudTrack3BitratePopUp, fAudTrack4BitratePopUp, + fAudDrcLabel, fAudTrack1DrcSlider, fAudTrack1DrcField, fAudTrack2DrcSlider, + fAudTrack2DrcField, fAudTrack3DrcSlider, fAudTrack3DrcField, fAudTrack4DrcSlider,fAudTrack4DrcField, + fQueueStatus,fPresetsAdd,fPresetsDelete,fSrcAngleLabel,fSrcAnglePopUp, + fCreateChapterMarkers,fVidTurboPassCheck,fDstMp4LargeFileCheck,fSubForcedCheck,fPresetsOutlineView, + fAudDrcLabel,fDstMp4HttpOptFileCheck,fDstMp4iPodFileCheck,fVidQualityRFField,fVidQualityRFLabel}; + + for( unsigned i = 0; + i < sizeof( controls ) / sizeof( NSControl * ); i++ ) + { + if( [[controls[i] className] isEqualToString: @"NSTextField"] ) + { + NSTextField * tf = (NSTextField *) controls[i]; + if( ![tf isBezeled] ) + { + [tf setTextColor: b ? [NSColor controlTextColor] : + [NSColor disabledControlTextColor]]; + continue; + } + } + [controls[i] setEnabled: b]; + + } + + if (b) { + + /* if we're enabling the interface, check if the audio mixdown controls need to be enabled or not */ + /* these will have been enabled by the mass control enablement above anyway, so we're sense-checking it here */ + [self setEnabledStateOfAudioMixdownControls:nil]; + /* we also call calculatePictureSizing here to sense check if we already have vfr selected */ + [self calculatePictureSizing:nil]; + + } else { + + [fPresetsOutlineView setEnabled: NO]; + + } + + [self videoMatrixChanged:nil]; + [fAdvancedOptions enableUI:b]; +} + + +/*********************************************************************** + * UpdateDockIcon + *********************************************************************** + * Shows a progression bar on the dock icon, filled according to + * 'progress' (0.0 <= progress <= 1.0). + * Called with progress < 0.0 or progress > 1.0, restores the original + * icon. + **********************************************************************/ +- (void) UpdateDockIcon: (float) progress +{ + NSData * tiff; + NSBitmapImageRep * bmp; + uint32_t * pen; + uint32_t black = htonl( 0x000000FF ); + uint32_t red = htonl( 0xFF0000FF ); + uint32_t white = htonl( 0xFFFFFFFF ); + int row_start, row_end; + int i, j; + + if( progress < 0.0 || progress > 1.0 ) + { + [NSApp setApplicationIconImage: fApplicationIcon]; + return; + } + + /* Get it in a raw bitmap form */ + tiff = [fApplicationIcon TIFFRepresentationUsingCompression: + NSTIFFCompressionNone factor: 1.0]; + bmp = [NSBitmapImageRep imageRepWithData: tiff]; + + /* Draw the progression bar */ + /* It's pretty simple (ugly?) now, but I'm no designer */ + + row_start = 3 * (int) [bmp size].height / 4; + row_end = 7 * (int) [bmp size].height / 8; + + for( i = row_start; i < row_start + 2; i++ ) + { + pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] ); + for( j = 0; j < (int) [bmp size].width; j++ ) + { + pen[j] = black; + } + } + for( i = row_start + 2; i < row_end - 2; i++ ) + { + pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] ); + pen[0] = black; + pen[1] = black; + for( j = 2; j < (int) [bmp size].width - 2; j++ ) + { + if( j < 2 + (int) ( ( [bmp size].width - 4.0 ) * progress ) ) + { + pen[j] = red; + } + else + { + pen[j] = white; + } + } + pen[j] = black; + pen[j+1] = black; + } + for( i = row_end - 2; i < row_end; i++ ) + { + pen = (uint32_t *) ( [bmp bitmapData] + i * [bmp bytesPerRow] ); + for( j = 0; j < (int) [bmp size].width; j++ ) + { + pen[j] = black; + } + } + + /* Now update the dock icon */ + tiff = [bmp TIFFRepresentationUsingCompression: + NSTIFFCompressionNone factor: 1.0]; + NSImage* icon = [[NSImage alloc] initWithData: tiff]; + [NSApp setApplicationIconImage: icon]; + [icon release]; +} + +- (void) updateUI: (NSTimer *) timer +{ + + /* Update UI for fHandle (user scanning instance of libhb ) */ + + hb_list_t * list; + list = hb_get_titles( fHandle ); + /* check to see if there has been a new scan done + this bypasses the constraints of HB_STATE_WORKING + not allowing setting a newly scanned source */ + int checkScanCount = hb_get_scancount( fHandle ); + if( checkScanCount > currentScanCount ) + { + currentScanCount = checkScanCount; + [fScanIndicator setIndeterminate: NO]; + [fScanIndicator setDoubleValue: 0.0]; + [fScanIndicator setHidden: YES]; + [self showNewScan:nil]; + } + + hb_state_t s; + hb_get_state( fHandle, &s ); + + switch( s.state ) + { + case HB_STATE_IDLE: + break; +#define p s.param.scanning + case HB_STATE_SCANNING: + { + [fSrcDVD2Field setStringValue: [NSString stringWithFormat: + NSLocalizedString( @"Scanning title %d of %d...", @"" ), + p.title_cur, p.title_count]]; + [fScanIndicator setHidden: NO]; + [fScanIndicator setDoubleValue: 100.0 * ((double)( p.title_cur - 1 ) / p.title_count)]; + break; + } +#undef p + +#define p s.param.scandone + case HB_STATE_SCANDONE: + { + [fScanIndicator setIndeterminate: NO]; + [fScanIndicator setDoubleValue: 0.0]; + [fScanIndicator setHidden: YES]; + [self writeToActivityLog:"ScanDone state received from fHandle"]; + [self showNewScan:nil]; + [[fWindow toolbar] validateVisibleItems]; + + break; + } +#undef p + +#define p s.param.working + case HB_STATE_WORKING: + { + + break; + } +#undef p + +#define p s.param.muxing + case HB_STATE_MUXING: + { + + break; + } +#undef p + + case HB_STATE_PAUSED: + break; + + case HB_STATE_WORKDONE: + { + break; + } + } + + + /* Update UI for fQueueEncodeLibhb */ + // hb_list_t * list; + // list = hb_get_titles( fQueueEncodeLibhb ); //fQueueEncodeLibhb + /* check to see if there has been a new scan done + this bypasses the constraints of HB_STATE_WORKING + not allowing setting a newly scanned source */ + + checkScanCount = hb_get_scancount( fQueueEncodeLibhb ); + if( checkScanCount > currentScanCount ) + { + currentScanCount = checkScanCount; + } + + //hb_state_t s; + hb_get_state( fQueueEncodeLibhb, &s ); + + switch( s.state ) + { + case HB_STATE_IDLE: + break; +#define p s.param.scanning + case HB_STATE_SCANNING: + { + [fStatusField setStringValue: [NSString stringWithFormat: + NSLocalizedString( @"Queue Scanning title %d of %d...", @"" ), + p.title_cur, p.title_count]]; + + /* Set the status string in fQueueController as well */ + [fQueueController setQueueStatusString: [NSString stringWithFormat: + NSLocalizedString( @"Queue Scanning title %d of %d...", @"" ), + p.title_cur, p.title_count]]; + break; + } +#undef p + +#define p s.param.scandone + case HB_STATE_SCANDONE: + { + [self writeToActivityLog:"ScanDone state received from fQueueEncodeLibhb"]; + [self processNewQueueEncode]; + [[fWindow toolbar] validateVisibleItems]; + + break; + } +#undef p + +#define p s.param.working + case HB_STATE_WORKING: + { + NSMutableString * string; + NSString * pass_desc; + /* Update text field */ + if (p.job_cur == 1 && p.job_count > 1) + { + if ([[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SubtitleList"] && [[[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex]objectForKey:@"SubtitleList"] objectAtIndex:0] objectForKey:@"subtitleSourceTrackNum"] intValue] == 1) + { + pass_desc = @"(subtitle scan)"; + } + else + { + pass_desc = @""; + } + } + else + { + pass_desc = @""; + } + + string = [NSMutableString stringWithFormat: NSLocalizedString( @"Encoding: pass %d %@ of %d, %.2f %%", @"" ), p.job_cur, pass_desc, p.job_count, 100.0 * p.progress]; + + if( p.seconds > -1 ) + { + [string appendFormat: + NSLocalizedString( @" (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)", @"" ), + p.rate_cur, p.rate_avg, p.hours, p.minutes, p.seconds]; + } + + [fStatusField setStringValue: string]; + /* Set the status string in fQueueController as well */ + [fQueueController setQueueStatusString: string]; + /* Update slider */ + CGFloat progress_total = ( p.progress + p.job_cur - 1 ) / p.job_count; + [fRipIndicator setIndeterminate: NO]; + [fRipIndicator setDoubleValue:100.0 * progress_total]; + + // If progress bar hasn't been revealed at the bottom of the window, do + // that now. This code used to be in doRip. I moved it to here to handle + // the case where hb_start is called by HBQueueController and not from + // HBController. + if( !fRipIndicatorShown ) + { + NSRect frame = [fWindow frame]; + if( frame.size.width <= 591 ) + frame.size.width = 591; + frame.size.height += 36; + frame.origin.y -= 36; + [fWindow setFrame:frame display:YES animate:YES]; + fRipIndicatorShown = YES; + + } + + /* Update dock icon */ + if( dockIconProgress < 100.0 * progress_total ) + { + [self UpdateDockIcon: progress_total]; + dockIconProgress += 5; + } + + break; + } +#undef p + +#define p s.param.muxing + case HB_STATE_MUXING: + { + /* Update text field */ + [fStatusField setStringValue: NSLocalizedString( @"Muxing...", @"" )]; + /* Set the status string in fQueueController as well */ + [fQueueController setQueueStatusString: NSLocalizedString( @"Muxing...", @"" )]; + /* Update slider */ + [fRipIndicator setIndeterminate: YES]; + [fRipIndicator startAnimation: nil]; + + /* Update dock icon */ + [self UpdateDockIcon: 1.0]; + + break; + } +#undef p + + case HB_STATE_PAUSED: + [fStatusField setStringValue: NSLocalizedString( @"Paused", @"" )]; + [fQueueController setQueueStatusString: NSLocalizedString( @"Paused", @"" )]; + + break; + + case HB_STATE_WORKDONE: + { + // HB_STATE_WORKDONE happpens as a result of libhb finishing all its jobs + // or someone calling hb_stop. In the latter case, hb_stop does not clear + // out the remaining passes/jobs in the queue. We'll do that here. + + // Delete all remaining jobs of this encode. + [fStatusField setStringValue: NSLocalizedString( @"Encode Finished.", @"" )]; + /* Set the status string in fQueueController as well */ + [fQueueController setQueueStatusString: NSLocalizedString( @"Encode Finished.", @"" )]; + [fRipIndicator setIndeterminate: NO]; + [fRipIndicator stopAnimation: nil]; + [fRipIndicator setDoubleValue: 0.0]; + [[fWindow toolbar] validateVisibleItems]; + + /* Restore dock icon */ + [self UpdateDockIcon: -1.0]; + dockIconProgress = 0; + + if( fRipIndicatorShown ) + { + NSRect frame = [fWindow frame]; + if( frame.size.width <= 591 ) + frame.size.width = 591; + frame.size.height += -36; + frame.origin.y -= -36; + [fWindow setFrame:frame display:YES animate:YES]; + fRipIndicatorShown = NO; + } + /* Since we are done with this encode, tell output to stop writing to the + * individual encode log + */ + [outputPanel endEncodeLog]; + /* Check to see if the encode state has not been cancelled + to determine if we should check for encode done notifications */ + if( fEncodeState != 2 ) + { + NSString *pathOfFinishedEncode; + /* Get the output file name for the finished encode */ + pathOfFinishedEncode = [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"DestinationPath"]; + + /* Both the Growl Alert and Sending to MetaX can be done as encodes roll off the queue */ + /* Growl alert */ + [self showGrowlDoneNotification:pathOfFinishedEncode]; + /* Send to MetaX */ + [self sendToMetaX:pathOfFinishedEncode]; + + /* since we have successfully completed an encode, we increment the queue counter */ + [self incrementQueueItemDone:nil]; + + /* all end of queue actions below need to be done after all queue encodes have finished + * and there are no pending jobs left to process + */ + if (fPendingCount == 0) + { + /* If Alert Window or Window and Growl has been selected */ + if( [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window"] || + [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window And Growl"] ) + { + /*On Screen Notification*/ + int status; + NSBeep(); + status = NSRunAlertPanel(@"Put down that cocktail...",@"Your HandBrake queue is done!", @"OK", nil, nil); + [NSApp requestUserAttention:NSCriticalRequest]; + } + + /* If sleep has been selected */ + if( [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Put Computer To Sleep"] ) + { + /* Sleep */ + NSDictionary* errorDict; + NSAppleEventDescriptor* returnDescriptor = nil; + NSAppleScript* scriptObject = [[NSAppleScript alloc] initWithSource: + @"tell application \"Finder\" to sleep"]; + returnDescriptor = [scriptObject executeAndReturnError: &errorDict]; + [scriptObject release]; + } + /* If Shutdown has been selected */ + if( [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Shut Down Computer"] ) + { + /* Shut Down */ + NSDictionary* errorDict; + NSAppleEventDescriptor* returnDescriptor = nil; + NSAppleScript* scriptObject = [[NSAppleScript alloc] initWithSource: + @"tell application \"Finder\" to shut down"]; + returnDescriptor = [scriptObject executeAndReturnError: &errorDict]; + [scriptObject release]; + } + + } + + + } + + break; + } + } + +} + +/* We use this to write messages to stderr from the macgui which show up in the activity window and log*/ +- (void) writeToActivityLog:(const char *) format, ... +{ + va_list args; + va_start(args, format); + if (format != nil) + { + char str[1024]; + vsnprintf( str, 1024, format, args ); + + time_t _now = time( NULL ); + struct tm * now = localtime( &_now ); + fprintf(stderr, "[%02d:%02d:%02d] macgui: %s\n", now->tm_hour, now->tm_min, now->tm_sec, str ); + } + va_end(args); +} + +#pragma mark - +#pragma mark Toolbar +// ============================================================ +// NSToolbar Related Methods +// ============================================================ + +- (void) setupToolbar { + NSToolbar *toolbar = [[[NSToolbar alloc] initWithIdentifier: @"HandBrake Toolbar"] autorelease]; + + [toolbar setAllowsUserCustomization: YES]; + [toolbar setAutosavesConfiguration: YES]; + [toolbar setDisplayMode: NSToolbarDisplayModeIconAndLabel]; + + [toolbar setDelegate: self]; + + [fWindow setToolbar: toolbar]; +} + +- (NSToolbarItem *) toolbar: (NSToolbar *)toolbar itemForItemIdentifier: + (NSString *) itemIdent willBeInsertedIntoToolbar:(BOOL) willBeInserted { + NSToolbarItem * item = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdent] autorelease]; + + if ([itemIdent isEqualToString: ToggleDrawerIdentifier]) + { + [item setLabel: @"Toggle Presets"]; + [item setPaletteLabel: @"Toggler Presets"]; + [item setToolTip: @"Open/Close Preset Drawer"]; + [item setImage: [NSImage imageNamed: @"Drawer"]]; + [item setTarget: self]; + [item setAction: @selector(toggleDrawer:)]; + [item setAutovalidates: NO]; + } + else if ([itemIdent isEqualToString: StartEncodingIdentifier]) + { + [item setLabel: @"Start"]; + [item setPaletteLabel: @"Start Encoding"]; + [item setToolTip: @"Start Encoding"]; + [item setImage: [NSImage imageNamed: @"Play"]]; + [item setTarget: self]; + [item setAction: @selector(Rip:)]; + } + else if ([itemIdent isEqualToString: ShowQueueIdentifier]) + { + [item setLabel: @"Show Queue"]; + [item setPaletteLabel: @"Show Queue"]; + [item setToolTip: @"Show Queue"]; + [item setImage: [NSImage imageNamed: @"Queue"]]; + [item setTarget: self]; + [item setAction: @selector(showQueueWindow:)]; + [item setAutovalidates: NO]; + } + else if ([itemIdent isEqualToString: AddToQueueIdentifier]) + { + [item setLabel: @"Add to Queue"]; + [item setPaletteLabel: @"Add to Queue"]; + [item setToolTip: @"Add to Queue"]; + [item setImage: [NSImage imageNamed: @"AddToQueue"]]; + [item setTarget: self]; + [item setAction: @selector(addToQueue:)]; + } + else if ([itemIdent isEqualToString: PauseEncodingIdentifier]) + { + [item setLabel: @"Pause"]; + [item setPaletteLabel: @"Pause Encoding"]; + [item setToolTip: @"Pause Encoding"]; + [item setImage: [NSImage imageNamed: @"Pause"]]; + [item setTarget: self]; + [item setAction: @selector(Pause:)]; + } + else if ([itemIdent isEqualToString: ShowPictureIdentifier]) + { + [item setLabel: @"Picture Settings"]; + [item setPaletteLabel: @"Show Picture Settings"]; + [item setToolTip: @"Show Picture Settings"]; + [item setImage: [NSImage imageNamed: @"pref-picture"]]; + [item setTarget: self]; + [item setAction: @selector(showPicturePanel:)]; + } + else if ([itemIdent isEqualToString: ShowPreviewIdentifier]) + { + [item setLabel: @"Preview Window"]; + [item setPaletteLabel: @"Show Preview"]; + [item setToolTip: @"Show Preview"]; + //[item setImage: [NSImage imageNamed: @"pref-picture"]]; + [item setImage: [NSImage imageNamed: @"Brushed_Window"]]; + [item setTarget: self]; + [item setAction: @selector(showPreviewWindow:)]; + } + else if ([itemIdent isEqualToString: ShowActivityIdentifier]) + { + [item setLabel: @"Activity Window"]; + [item setPaletteLabel: @"Show Activity Window"]; + [item setToolTip: @"Show Activity Window"]; + [item setImage: [NSImage imageNamed: @"ActivityWindow"]]; + [item setTarget: self]; + [item setAction: @selector(showDebugOutputPanel:)]; + [item setAutovalidates: NO]; + } + else if ([itemIdent isEqualToString: ChooseSourceIdentifier]) + { + [item setLabel: @"Source"]; + [item setPaletteLabel: @"Source"]; + [item setToolTip: @"Choose Video Source"]; + [item setImage: [NSImage imageNamed: @"Source"]]; + [item setTarget: self]; + [item setAction: @selector(browseSources:)]; + } + else + { + return nil; + } + + return item; +} + +- (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar +{ + return [NSArray arrayWithObjects: ChooseSourceIdentifier, NSToolbarSeparatorItemIdentifier, StartEncodingIdentifier, + PauseEncodingIdentifier, AddToQueueIdentifier, ShowQueueIdentifier, NSToolbarFlexibleSpaceItemIdentifier, + NSToolbarSpaceItemIdentifier, ShowPictureIdentifier, ShowPreviewIdentifier, ShowActivityIdentifier, ToggleDrawerIdentifier, nil]; +} + +- (NSArray *) toolbarAllowedItemIdentifiers: (NSToolbar *) toolbar +{ + return [NSArray arrayWithObjects: StartEncodingIdentifier, PauseEncodingIdentifier, AddToQueueIdentifier, + ChooseSourceIdentifier, ShowQueueIdentifier, ShowPictureIdentifier, ShowPreviewIdentifier, ShowActivityIdentifier, ToggleDrawerIdentifier, + NSToolbarCustomizeToolbarItemIdentifier, NSToolbarFlexibleSpaceItemIdentifier, + NSToolbarSpaceItemIdentifier, NSToolbarSeparatorItemIdentifier, nil]; +} + +- (BOOL) validateToolbarItem: (NSToolbarItem *) toolbarItem +{ + NSString * ident = [toolbarItem itemIdentifier]; + + if (fHandle) + { + hb_state_t s; + hb_get_state2( fQueueEncodeLibhb, &s ); + + if (s.state == HB_STATE_WORKING || s.state == HB_STATE_MUXING) + { + if ([ident isEqualToString: StartEncodingIdentifier]) + { + [toolbarItem setImage: [NSImage imageNamed: @"Stop"]]; + [toolbarItem setLabel: @"Stop"]; + [toolbarItem setPaletteLabel: @"Stop"]; + [toolbarItem setToolTip: @"Stop Encoding"]; + return YES; + } + if ([ident isEqualToString: PauseEncodingIdentifier]) + { + [toolbarItem setImage: [NSImage imageNamed: @"Pause"]]; + [toolbarItem setLabel: @"Pause"]; + [toolbarItem setPaletteLabel: @"Pause Encoding"]; + [toolbarItem setToolTip: @"Pause Encoding"]; + return YES; + } + if (SuccessfulScan) + { + if ([ident isEqualToString: AddToQueueIdentifier]) + return YES; + if ([ident isEqualToString: ShowPictureIdentifier]) + return YES; + if ([ident isEqualToString: ShowPreviewIdentifier]) + return YES; + } + } + else if (s.state == HB_STATE_PAUSED) + { + if ([ident isEqualToString: PauseEncodingIdentifier]) + { + [toolbarItem setImage: [NSImage imageNamed: @"Play"]]; + [toolbarItem setLabel: @"Resume"]; + [toolbarItem setPaletteLabel: @"Resume Encoding"]; + [toolbarItem setToolTip: @"Resume Encoding"]; + return YES; + } + if ([ident isEqualToString: StartEncodingIdentifier]) + return YES; + if ([ident isEqualToString: AddToQueueIdentifier]) + return YES; + if ([ident isEqualToString: ShowPictureIdentifier]) + return YES; + if ([ident isEqualToString: ShowPreviewIdentifier]) + return YES; + } + else if (s.state == HB_STATE_SCANNING) + return NO; + else if (s.state == HB_STATE_WORKDONE || s.state == HB_STATE_SCANDONE || SuccessfulScan) + { + if ([ident isEqualToString: StartEncodingIdentifier]) + { + [toolbarItem setImage: [NSImage imageNamed: @"Play"]]; + if (hb_count(fHandle) > 0) + [toolbarItem setLabel: @"Start Queue"]; + else + [toolbarItem setLabel: @"Start"]; + [toolbarItem setPaletteLabel: @"Start Encoding"]; + [toolbarItem setToolTip: @"Start Encoding"]; + return YES; + } + if ([ident isEqualToString: AddToQueueIdentifier]) + return YES; + if ([ident isEqualToString: ShowPictureIdentifier]) + return YES; + if ([ident isEqualToString: ShowPreviewIdentifier]) + return YES; + } + + } + /* If there are any pending queue items, make sure the start/stop button is active */ + if ([ident isEqualToString: StartEncodingIdentifier] && fPendingCount > 0) + return YES; + if ([ident isEqualToString: ShowQueueIdentifier]) + return YES; + if ([ident isEqualToString: ToggleDrawerIdentifier]) + return YES; + if ([ident isEqualToString: ChooseSourceIdentifier]) + return YES; + if ([ident isEqualToString: ShowActivityIdentifier]) + return YES; + + return NO; +} + +- (BOOL) validateMenuItem: (NSMenuItem *) menuItem +{ + SEL action = [menuItem action]; + + hb_state_t s; + hb_get_state2( fHandle, &s ); + + if (fHandle) + { + if (action == @selector(addToQueue:) || action == @selector(showPicturePanel:) || action == @selector(showAddPresetPanel:)) + return SuccessfulScan && [fWindow attachedSheet] == nil; + + if (action == @selector(browseSources:)) + { + if (s.state == HB_STATE_SCANNING) + return NO; + else + return [fWindow attachedSheet] == nil; + } + if (action == @selector(selectDefaultPreset:)) + return [fPresetsOutlineView selectedRow] >= 0 && [fWindow attachedSheet] == nil; + if (action == @selector(Pause:)) + { + if (s.state == HB_STATE_WORKING) + { + if(![[menuItem title] isEqualToString:@"Pause Encoding"]) + [menuItem setTitle:@"Pause Encoding"]; + return YES; + } + else if (s.state == HB_STATE_PAUSED) + { + if(![[menuItem title] isEqualToString:@"Resume Encoding"]) + [menuItem setTitle:@"Resume Encoding"]; + return YES; + } + else + return NO; + } + if (action == @selector(Rip:)) + { + if (s.state == HB_STATE_WORKING || s.state == HB_STATE_MUXING || s.state == HB_STATE_PAUSED) + { + if(![[menuItem title] isEqualToString:@"Stop Encoding"]) + [menuItem setTitle:@"Stop Encoding"]; + return YES; + } + else if (SuccessfulScan) + { + if(![[menuItem title] isEqualToString:@"Start Encoding"]) + [menuItem setTitle:@"Start Encoding"]; + return [fWindow attachedSheet] == nil; + } + else + return NO; + } + } + if( action == @selector(setDefaultPreset:) ) + { + return [fPresetsOutlineView selectedRow] != -1; + } + + return YES; +} + +#pragma mark - +#pragma mark Encode Done Actions +// register a test notification and make +// it enabled by default +#define SERVICE_NAME @"Encode Done" +- (NSDictionary *)registrationDictionaryForGrowl +{ + NSDictionary *registrationDictionary = [NSDictionary dictionaryWithObjectsAndKeys: + [NSArray arrayWithObjects:SERVICE_NAME,nil], GROWL_NOTIFICATIONS_ALL, + [NSArray arrayWithObjects:SERVICE_NAME,nil], GROWL_NOTIFICATIONS_DEFAULT, + nil]; + + return registrationDictionary; +} + +-(void)showGrowlDoneNotification:(NSString *) filePath +{ + /* This end of encode action is called as each encode rolls off of the queue */ + NSString * finishedEncode = filePath; + /* strip off the path to just show the file name */ + finishedEncode = [finishedEncode lastPathComponent]; + if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Growl Notification"] || + [[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Alert Window And Growl"]) + { + NSString * growlMssg = [NSString stringWithFormat: @"your HandBrake encode %@ is done!",finishedEncode]; + [GrowlApplicationBridge + notifyWithTitle:@"Put down that cocktail..." + description:growlMssg + notificationName:SERVICE_NAME + iconData:nil + priority:0 + isSticky:1 + clickContext:nil]; + } + +} +-(void)sendToMetaX:(NSString *) filePath +{ + /* This end of encode action is called as each encode rolls off of the queue */ + if([[NSUserDefaults standardUserDefaults] boolForKey: @"sendToMetaX"] == YES) + { + NSAppleScript *myScript = [[NSAppleScript alloc] initWithSource: [NSString stringWithFormat: @"%@%@%@", @"tell application \"MetaX\" to open (POSIX file \"", filePath, @"\")"]]; + [myScript executeAndReturnError: nil]; + [myScript release]; + } +} +#pragma mark - +#pragma mark Get New Source + +/*Opens the source browse window, called from Open Source widgets */ +- (IBAction) browseSources: (id) sender +{ + NSOpenPanel * panel; + + panel = [NSOpenPanel openPanel]; + [panel setAllowsMultipleSelection: NO]; + [panel setCanChooseFiles: YES]; + [panel setCanChooseDirectories: YES ]; + NSString * sourceDirectory; + if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastSourceDirectory"]) + { + sourceDirectory = [[NSUserDefaults standardUserDefaults] stringForKey:@"LastSourceDirectory"]; + } + else + { + sourceDirectory = @"~/Desktop"; + sourceDirectory = [sourceDirectory stringByExpandingTildeInPath]; + } + /* we open up the browse sources sheet here and call for browseSourcesDone after the sheet is closed + * to evaluate whether we want to specify a title, we pass the sender in the contextInfo variable + */ + [panel beginSheetForDirectory: sourceDirectory file: nil types: nil + modalForWindow: fWindow modalDelegate: self + didEndSelector: @selector( browseSourcesDone:returnCode:contextInfo: ) + contextInfo: sender]; +} + +- (void) browseSourcesDone: (NSOpenPanel *) sheet + returnCode: (int) returnCode contextInfo: (void *) contextInfo +{ + /* we convert the sender content of contextInfo back into a variable called sender + * mostly just for consistency for evaluation later + */ + id sender = (id)contextInfo; + /* User selected a file to open */ + if( returnCode == NSOKButton ) + { + /* Free display name allocated previously by this code */ + [browsedSourceDisplayName release]; + + NSString *scanPath = [[sheet filenames] objectAtIndex: 0]; + /* we set the last searched source directory in the prefs here */ + NSString *sourceDirectory = [scanPath stringByDeletingLastPathComponent]; + [[NSUserDefaults standardUserDefaults] setObject:sourceDirectory forKey:@"LastSourceDirectory"]; + /* we order out sheet, which is the browse window as we need to open + * the title selection sheet right away + */ + [sheet orderOut: self]; + + if (sender == fOpenSourceTitleMMenu) + { + /* We put the chosen source path in the source display text field for the + * source title selection sheet in which the user specifies the specific title to be + * scanned as well as the short source name in fSrcDsplyNameTitleScan just for display + * purposes in the title panel + */ + /* Full Path */ + [fScanSrcTitlePathField setStringValue:scanPath]; + NSString *displayTitlescanSourceName; + + if ([[scanPath lastPathComponent] isEqualToString: @"VIDEO_TS"]) + { + /* If VIDEO_TS Folder is chosen, choose its parent folder for the source display name + we have to use the title->dvd value so we get the proper name of the volume if a physical dvd is the source*/ + displayTitlescanSourceName = [[scanPath stringByDeletingLastPathComponent] lastPathComponent]; + } + else + { + /* if not the VIDEO_TS Folder, we can assume the chosen folder is the source name */ + displayTitlescanSourceName = [scanPath lastPathComponent]; + } + /* we set the source display name in the title selection dialogue */ + [fSrcDsplyNameTitleScan setStringValue:displayTitlescanSourceName]; + /* we set the attempted scans display name for main window to displayTitlescanSourceName*/ + browsedSourceDisplayName = [displayTitlescanSourceName retain]; + /* We show the actual sheet where the user specifies the title to be scanned + * as we are going to do a title specific scan + */ + [self showSourceTitleScanPanel:nil]; + } + else + { + /* We are just doing a standard full source scan, so we specify "0" to libhb */ + NSString *path = [[sheet filenames] objectAtIndex: 0]; + + /* We check to see if the chosen file at path is a package */ + if ([[NSWorkspace sharedWorkspace] isFilePackageAtPath:path]) + { + [self writeToActivityLog: "trying to open a package at: %s", [path UTF8String]]; + /* We check to see if this is an .eyetv package */ + if ([[path pathExtension] isEqualToString: @"eyetv"]) + { + [self writeToActivityLog:"trying to open eyetv package"]; + /* We're looking at an EyeTV package - try to open its enclosed + .mpg media file */ + browsedSourceDisplayName = [[[path stringByDeletingPathExtension] lastPathComponent] retain]; + NSString *mpgname; + int n = [[path stringByAppendingString: @"/"] + completePathIntoString: &mpgname caseSensitive: NO + matchesIntoArray: nil + filterTypes: [NSArray arrayWithObject: @"mpg"]]; + if (n > 0) + { + /* Found an mpeg inside the eyetv package, make it our scan path + and call performScan on the enclosed mpeg */ + path = mpgname; + [self writeToActivityLog:"found mpeg in eyetv package"]; + [self performScan:path scanTitleNum:0]; + } + else + { + /* We did not find an mpeg file in our package, so we do not call performScan */ + [self writeToActivityLog:"no valid mpeg in eyetv package"]; + } + } + /* We check to see if this is a .dvdmedia package */ + else if ([[path pathExtension] isEqualToString: @"dvdmedia"]) + { + /* path IS a package - but dvdmedia packages can be treaded like normal directories */ + browsedSourceDisplayName = [[[path stringByDeletingPathExtension] lastPathComponent] retain]; + [self writeToActivityLog:"trying to open dvdmedia package"]; + [self performScan:path scanTitleNum:0]; + } + else + { + /* The package is not an eyetv package, so we do not call performScan */ + [self writeToActivityLog:"unable to open package"]; + } + } + else // path is not a package, so we treat it as a dvd parent folder or VIDEO_TS folder + { + /* path is not a package, so we call perform scan directly on our file */ + if ([[path lastPathComponent] isEqualToString: @"VIDEO_TS"]) + { + [self writeToActivityLog:"trying to open video_ts folder (video_ts folder chosen)"]; + /* If VIDEO_TS Folder is chosen, choose its parent folder for the source display name*/ + browsedSourceDisplayName = [[[path stringByDeletingLastPathComponent] lastPathComponent] retain]; + } + else + { + [self writeToActivityLog:"trying to open video_ts folder (parent directory chosen)"]; + /* if not the VIDEO_TS Folder, we can assume the chosen folder is the source name */ + /* make sure we remove any path extension as this can also be an '.mpg' file */ + browsedSourceDisplayName = [[path lastPathComponent] retain]; + } + [self performScan:path scanTitleNum:0]; + } + + } + + } +} + +- (IBAction)showAboutPanel:(id)sender +{ + NSMutableDictionary* d = [[NSMutableDictionary alloc] initWithObjectsAndKeys: + fApplicationIcon, @"ApplicationIcon", + nil ]; + [NSApp orderFrontStandardAboutPanelWithOptions:d]; + [d release]; +} + +/* Here we open the title selection sheet where we can specify an exact title to be scanned */ +- (IBAction) showSourceTitleScanPanel: (id) sender +{ + /* We default the title number to be scanned to "0" which results in a full source scan, unless the + * user changes it + */ + [fScanSrcTitleNumField setStringValue: @"0"]; + /* Show the panel */ + [NSApp beginSheet:fScanSrcTitlePanel modalForWindow:fWindow modalDelegate:nil didEndSelector:NULL contextInfo:NULL]; +} + +- (IBAction) closeSourceTitleScanPanel: (id) sender +{ + [NSApp endSheet: fScanSrcTitlePanel]; + [fScanSrcTitlePanel orderOut: self]; + + if(sender == fScanSrcTitleOpenButton) + { + /* We setup the scan status in the main window to indicate a source title scan */ + [fSrcDVD2Field setStringValue: @"Opening a new source title ..."]; + [fScanIndicator setHidden: NO]; + [fScanIndicator setIndeterminate: YES]; + [fScanIndicator startAnimation: nil]; + + /* We use the performScan method to actually perform the specified scan passing the path and the title + * to be scanned + */ + [self performScan:[fScanSrcTitlePathField stringValue] scanTitleNum:[fScanSrcTitleNumField intValue]]; + } +} + +/* Here we actually tell hb_scan to perform the source scan, using the path to source and title number*/ +- (void) performScan:(NSString *) scanPath scanTitleNum: (int) scanTitleNum +{ + /* set the bool applyQueueToScan so that we dont apply a queue setting to the final scan */ + applyQueueToScan = NO; + /* use a bool to determine whether or not we can decrypt using vlc */ + BOOL cancelScanDecrypt = 0; + NSString *path = scanPath; + HBDVDDetector *detector = [HBDVDDetector detectorForPath:path]; + + // Notify ChapterTitles that there's no title + [fChapterTitlesDelegate resetWithTitle:nil]; + [fChapterTable reloadData]; + + // Notify Subtitles that there's no title + [fSubtitlesDelegate resetWithTitle:nil]; + [fSubtitlesTable reloadData]; + + [self enableUI: NO]; + + if( [detector isVideoDVD] ) + { + // The chosen path was actually on a DVD, so use the raw block + // device path instead. + path = [detector devicePath]; + [self writeToActivityLog: "trying to open a physical dvd at: %s", [scanPath UTF8String]]; + +#if defined( __LP64__ ) + /* If we are 64 bit, we cannot read encrypted dvd's as vlc is 32 bit only */ + cancelScanDecrypt = 1; + [self writeToActivityLog: "64 bit mode cannot read dvd's, scan cancelled"]; + /*On Screen Notification*/ + int status; + NSBeep(); + status = NSRunAlertPanel(@"64-bit HandBrake cannot read encrypted dvds!",@"", @"Cancel Scan", @"Attempt Scan Anyway", nil); + [NSApp requestUserAttention:NSCriticalRequest]; + + if (status == NSAlertDefaultReturn) + { + /* User chose to cancel the scan */ + [self writeToActivityLog: "cannot open physical dvd , scan cancelled"]; + cancelScanDecrypt = 1; + } + else + { + [self writeToActivityLog: "user overrode 64-bit warning trying to open physical dvd without decryption"]; + cancelScanDecrypt = 0; + } + +#else + /* lets check for vlc here to make sure we have a dylib available to use for decrypting */ + NSString *vlcPath = @"/Applications/VLC.app/Contents/MacOS/lib/libdvdcss.2.dylib"; + NSFileManager * fileManager = [NSFileManager defaultManager]; + if ([fileManager fileExistsAtPath:vlcPath] == 0) + { + /*vlc not found in /Applications so we set the bool to cancel scanning to 1 */ + cancelScanDecrypt = 1; + [self writeToActivityLog: "VLC app not found for decrypting physical dvd"]; + int status; + status = NSRunAlertPanel(@"HandBrake could not find VLC or your VLC is out of date.",@"Please download and install VLC media player in your /Applications folder if you wish to read encrypted DVDs.", @"Get VLC", @"Cancel Scan", @"Attempt Scan Anyway"); + [NSApp requestUserAttention:NSCriticalRequest]; + + if (status == NSAlertDefaultReturn) + { + /* User chose to go download vlc (as they rightfully should) so we send them to the vlc site */ + [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://www.videolan.org/"]]; + } + else if (status == NSAlertAlternateReturn) + { + /* User chose to cancel the scan */ + [self writeToActivityLog: "cannot open physical dvd , scan cancelled"]; + } + else + { + /* 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 */ + cancelScanDecrypt = 0; + [self writeToActivityLog: "user overrode vlc warning -trying to open physical dvd without decryption"]; + } + + } + else + { + /* VLC was found in /Applications so all is well, we can carry on using vlc's libdvdcss.dylib for decrypting if needed */ + [self writeToActivityLog: "VLC app found for decrypting physical dvd"]; + } +#endif + } + + if (cancelScanDecrypt == 0) + { + /* we actually pass the scan off to libhb here */ + /* If there is no title number passed to scan, we use "0" + * which causes the default behavior of a full source scan + */ + if (!scanTitleNum) + { + scanTitleNum = 0; + } + if (scanTitleNum > 0) + { + [self writeToActivityLog: "scanning specifically for title: %d", scanTitleNum]; + } + /* We use our advance pref to determine how many previews to scan */ + int hb_num_previews = [[[NSUserDefaults standardUserDefaults] objectForKey:@"PreviewsNumber"] intValue]; + /* set title to NULL */ + //fTitle = NULL; + hb_scan( fHandle, [path UTF8String], scanTitleNum, hb_num_previews, 1 ); + [fSrcDVD2Field setStringValue:@"Scanning new source ..."]; + } +} + +- (IBAction) showNewScan:(id)sender +{ + hb_list_t * list; + hb_title_t * title; + int indxpri=0; // Used to search the longuest title (default in combobox) + int longuestpri=0; // Used to search the longuest title (default in combobox) + + + list = hb_get_titles( fHandle ); + + if( !hb_list_count( list ) ) + { + /* We display a message if a valid dvd source was not chosen */ + [fSrcDVD2Field setStringValue: @"No Valid Source Found"]; + SuccessfulScan = NO; + + // Notify ChapterTitles that there's no title + [fSubtitlesDelegate resetWithTitle:nil]; + [fSubtitlesTable reloadData]; + + // Notify Subtitles that there's no title + [fChapterTitlesDelegate resetWithTitle:nil]; + [fChapterTable reloadData]; + } + else + { + /* We increment the successful scancount here by one, + which we use at the end of this function to tell the gui + if this is the first successful scan since launch and whether + or not we should set all settings to the defaults */ + + currentSuccessfulScanCount++; + + [[fWindow toolbar] validateVisibleItems]; + + [fSrcTitlePopUp removeAllItems]; + for( int i = 0; i < hb_list_count( list ); i++ ) + { + title = (hb_title_t *) hb_list_item( list, i ); + + currentSource = [NSString stringWithUTF8String: title->name]; + /*Set DVD Name at top of window with the browsedSourceDisplayName grokked right before -performScan */ + [fSrcDVD2Field setStringValue:browsedSourceDisplayName]; + + /* Use the dvd name in the default output field here + May want to add code to remove blank spaces for some dvd names*/ + /* Check to see if the last destination has been set,use if so, if not, use Desktop */ + if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastDestinationDirectory"]) + { + [fDstFile2Field setStringValue: [NSString stringWithFormat: + @"%@/%@.mp4", [[NSUserDefaults standardUserDefaults] stringForKey:@"LastDestinationDirectory"],[browsedSourceDisplayName stringByDeletingPathExtension]]]; + } + else + { + [fDstFile2Field setStringValue: [NSString stringWithFormat: + @"%@/Desktop/%@.mp4", NSHomeDirectory(),[browsedSourceDisplayName stringByDeletingPathExtension]]]; + } + + + if (longuestpri < title->hours*60*60 + title->minutes *60 + title->seconds) + { + longuestpri=title->hours*60*60 + title->minutes *60 + title->seconds; + indxpri=i; + } + + [fSrcTitlePopUp addItemWithTitle: [NSString + stringWithFormat: @"%d - %02dh%02dm%02ds", + title->index, title->hours, title->minutes, + title->seconds]]; + } + + // Select the longuest title + [fSrcTitlePopUp selectItemAtIndex: indxpri]; + [self titlePopUpChanged:nil]; + + SuccessfulScan = YES; + [self enableUI: YES]; + + /* if its the initial successful scan after awakeFromNib */ + if (currentSuccessfulScanCount == 1) + { + [self selectDefaultPreset:nil]; + + // Open preview window now if it was visible when HB was closed + if ([[NSUserDefaults standardUserDefaults] boolForKey:@"PreviewWindowIsOpen"]) + [self showPreviewWindow:nil]; + + // Open picture sizing window now if it was visible when HB was closed + if ([[NSUserDefaults standardUserDefaults] boolForKey:@"PictureSizeWindowIsOpen"]) + [self showPicturePanel:nil]; + + } + + + } + +} + + +#pragma mark - +#pragma mark New Output Destination + +- (IBAction) browseFile: (id) sender +{ + /* Open a panel to let the user choose and update the text field */ + NSSavePanel * panel = [NSSavePanel savePanel]; + /* We get the current file name and path from the destination field here */ + [panel beginSheetForDirectory: [[fDstFile2Field stringValue] stringByDeletingLastPathComponent] file: [[fDstFile2Field stringValue] lastPathComponent] + modalForWindow: fWindow modalDelegate: self + didEndSelector: @selector( browseFileDone:returnCode:contextInfo: ) + contextInfo: NULL]; +} + +- (void) browseFileDone: (NSSavePanel *) sheet + returnCode: (int) returnCode contextInfo: (void *) contextInfo +{ + if( returnCode == NSOKButton ) + { + [fDstFile2Field setStringValue: [sheet filename]]; + /* Save this path to the prefs so that on next browse destination window it opens there */ + NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent]; + [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"]; + } +} + + +#pragma mark - +#pragma mark Main Window Control + +- (IBAction) openMainWindow: (id) sender +{ + [fWindow makeKeyAndOrderFront:nil]; +} + +- (BOOL) windowShouldClose: (id) sender +{ + return YES; +} + +- (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag +{ + if( !flag ) { + [fWindow makeKeyAndOrderFront:nil]; + + return YES; + } + + return NO; +} + +- (NSSize) drawerWillResizeContents:(NSDrawer *) drawer toSize:(NSSize) contentSize { + [[NSUserDefaults standardUserDefaults] setObject:NSStringFromSize( contentSize ) forKey:@"Drawer Size"]; + return contentSize; +} + +#pragma mark - +#pragma mark Queue File + +- (void) loadQueueFile { + /* We declare the default NSFileManager into fileManager */ + NSFileManager * fileManager = [NSFileManager defaultManager]; + /*We define the location of the user presets file */ + QueueFile = @"~/Library/Application Support/HandBrake/Queue.plist"; + QueueFile = [[QueueFile stringByExpandingTildeInPath]retain]; + /* We check for the presets.plist */ + if ([fileManager fileExistsAtPath:QueueFile] == 0) + { + [fileManager createFileAtPath:QueueFile contents:nil attributes:nil]; + } + + QueueFileArray = [[NSMutableArray alloc] initWithContentsOfFile:QueueFile]; + /* lets check to see if there is anything in the queue file .plist */ + if (nil == QueueFileArray) + { + /* if not, then lets initialize an empty array */ + QueueFileArray = [[NSMutableArray alloc] init]; + + /* Initialize our curQueueEncodeIndex to 0 + * so we can use it to track which queue + * item is to be used to track our encodes */ + /* NOTE: this should be changed if and when we + * are able to get the last unfinished encode + * in the case of a crash or shutdown */ + + } + else + { + [self clearQueueEncodedItems]; + } + currentQueueEncodeIndex = 0; +} + +- (void)addQueueFileItem +{ + [QueueFileArray addObject:[self createQueueFileItem]]; + [self saveQueueFileItem]; + +} + +- (void) removeQueueFileItem:(int) queueItemToRemove +{ + + /* Find out if the item we are removing is a cancelled (3) or a finished (0) item*/ + if ([[[QueueFileArray objectAtIndex:queueItemToRemove] objectForKey:@"Status"] intValue] == 3 || [[[QueueFileArray objectAtIndex:queueItemToRemove] objectForKey:@"Status"] intValue] == 0) + { + /* Since we are removing a cancelled or finished item, WE need to decrement the currentQueueEncodeIndex + * by one to keep in sync with the queue array + */ + currentQueueEncodeIndex--; + [self writeToActivityLog: "removeQueueFileItem: Removing a cancelled/finished encode, decrement currentQueueEncodeIndex to %d", currentQueueEncodeIndex]; + } + [QueueFileArray removeObjectAtIndex:queueItemToRemove]; + [self saveQueueFileItem]; + +} + +- (void)saveQueueFileItem +{ + [QueueFileArray writeToFile:QueueFile atomically:YES]; + [fQueueController setQueueArray: QueueFileArray]; + [self getQueueStats]; +} + +- (void)getQueueStats +{ +/* lets get the stats on the status of the queue array */ + +fEncodingQueueItem = 0; +fPendingCount = 0; +fCompletedCount = 0; +fCanceledCount = 0; +fWorkingCount = 0; + + /* We use a number system to set the encode status of the queue item + * in controller.mm + * 0 == already encoded + * 1 == is being encoded + * 2 == is yet to be encoded + * 3 == cancelled + */ + + int i = 0; + NSEnumerator *enumerator = [QueueFileArray objectEnumerator]; + id tempObject; + while (tempObject = [enumerator nextObject]) + { + NSDictionary *thisQueueDict = tempObject; + if ([[thisQueueDict objectForKey:@"Status"] intValue] == 0) // Completed + { + fCompletedCount++; + } + if ([[thisQueueDict objectForKey:@"Status"] intValue] == 1) // being encoded + { + fWorkingCount++; + fEncodingQueueItem = i; + } + if ([[thisQueueDict objectForKey:@"Status"] intValue] == 2) // pending + { + fPendingCount++; + } + if ([[thisQueueDict objectForKey:@"Status"] intValue] == 3) // cancelled + { + fCanceledCount++; + } + i++; + } + + /* Set the queue status field in the main window */ + NSMutableString * string; + if (fPendingCount == 1) + { + string = [NSMutableString stringWithFormat: NSLocalizedString( @"%d encode pending in the queue", @"" ), fPendingCount]; + } + else + { + string = [NSMutableString stringWithFormat: NSLocalizedString( @"%d encode(s) pending in the queue", @"" ), fPendingCount]; + } + [fQueueStatus setStringValue:string]; +} + +/* This method will set any item marked as encoding back to pending + * currently used right after a queue reload + */ +- (void) setQueueEncodingItemsAsPending +{ + NSEnumerator *enumerator = [QueueFileArray objectEnumerator]; + id tempObject; + NSMutableArray *tempArray; + tempArray = [NSMutableArray array]; + /* we look here to see if the preset is we move on to the next one */ + while ( tempObject = [enumerator nextObject] ) + { + /* If the queue item is marked as "encoding" (1) + * then change its status back to pending (2) which effectively + * puts it back into the queue to be encoded + */ + if ([[tempObject objectForKey:@"Status"] intValue] == 1) + { + [tempObject setObject:[NSNumber numberWithInt: 2] forKey:@"Status"]; + } + [tempArray addObject:tempObject]; + } + + [QueueFileArray setArray:tempArray]; + [self saveQueueFileItem]; +} + + +/* This method will clear the queue of any encodes that are not still pending + * this includes both successfully completed encodes as well as cancelled encodes */ +- (void) clearQueueEncodedItems +{ + NSEnumerator *enumerator = [QueueFileArray objectEnumerator]; + id tempObject; + NSMutableArray *tempArray; + tempArray = [NSMutableArray array]; + /* we look here to see if the preset is we move on to the next one */ + while ( tempObject = [enumerator nextObject] ) + { + /* If the queue item is either completed (0) or cancelled (3) from the + * last session, then we put it in tempArray to be deleted from QueueFileArray. + * NOTE: this means we retain pending (2) and also an item that is marked as + * still encoding (1). If the queue has an item that is still marked as encoding + * from a previous session, we can conlude that HB was either shutdown, or crashed + * during the encodes so we keep it and tell the user in the "Load Queue Alert" + */ + if ([[tempObject objectForKey:@"Status"] intValue] == 0 || [[tempObject objectForKey:@"Status"] intValue] == 3) + { + [tempArray addObject:tempObject]; + } + } + + [QueueFileArray removeObjectsInArray:tempArray]; + [self saveQueueFileItem]; +} + +/* This method will clear the queue of all encodes. effectively creating an empty queue */ +- (void) clearQueueAllItems +{ + NSEnumerator *enumerator = [QueueFileArray objectEnumerator]; + id tempObject; + NSMutableArray *tempArray; + tempArray = [NSMutableArray array]; + /* we look here to see if the preset is we move on to the next one */ + while ( tempObject = [enumerator nextObject] ) + { + [tempArray addObject:tempObject]; + } + + [QueueFileArray removeObjectsInArray:tempArray]; + [self saveQueueFileItem]; +} + +/* This method will duplicate prepareJob however into the + * queue .plist instead of into the job structure so it can + * be recalled later */ +- (NSDictionary *)createQueueFileItem +{ + NSMutableDictionary *queueFileJob = [[NSMutableDictionary alloc] init]; + + hb_list_t * list = hb_get_titles( fHandle ); + hb_title_t * title = (hb_title_t *) hb_list_item( list, + [fSrcTitlePopUp indexOfSelectedItem] ); + hb_job_t * job = title->job; + + + + /* We use a number system to set the encode status of the queue item + * 0 == already encoded + * 1 == is being encoded + * 2 == is yet to be encoded + * 3 == cancelled + */ + [queueFileJob setObject:[NSNumber numberWithInt:2] forKey:@"Status"]; + /* Source and Destination Information */ + + [queueFileJob setObject:[NSString stringWithUTF8String: title->dvd] forKey:@"SourcePath"]; + [queueFileJob setObject:[fSrcDVD2Field stringValue] forKey:@"SourceName"]; + [queueFileJob setObject:[NSNumber numberWithInt:title->index] forKey:@"TitleNumber"]; + [queueFileJob setObject:[NSNumber numberWithInt:[fSrcAnglePopUp indexOfSelectedItem] + 1] forKey:@"TitleAngle"]; + [queueFileJob setObject:[NSNumber numberWithInt:[fSrcChapterStartPopUp indexOfSelectedItem] + 1] forKey:@"ChapterStart"]; + + [queueFileJob setObject:[NSNumber numberWithInt:[fSrcChapterEndPopUp indexOfSelectedItem] + 1] forKey:@"ChapterEnd"]; + + [queueFileJob setObject:[fDstFile2Field stringValue] forKey:@"DestinationPath"]; + + /* Lets get the preset info if there is any */ + [queueFileJob setObject:[fPresetSelectedDisplay stringValue] forKey:@"PresetName"]; + [queueFileJob setObject:[NSNumber numberWithInt:[fPresetsOutlineView selectedRow]] forKey:@"PresetIndexNum"]; + + [queueFileJob setObject:[fDstFormatPopUp titleOfSelectedItem] forKey:@"FileFormat"]; + /* Chapter Markers*/ + /* If we have only one chapter or a title without chapters, set chapter markers to off */ + if ([fSrcChapterStartPopUp indexOfSelectedItem] == [fSrcChapterEndPopUp indexOfSelectedItem]) + { + [queueFileJob setObject:[NSNumber numberWithInt:0] forKey:@"ChapterMarkers"]; + } + else + { + [queueFileJob setObject:[NSNumber numberWithInt:[fCreateChapterMarkers state]] forKey:@"ChapterMarkers"]; + } + + /* We need to get the list of chapter names to put into an array and store + * in our queue, so they can be reapplied in prepareJob when this queue + * item comes up if Chapter Markers is set to on. + */ + int i; + NSMutableArray *ChapterNamesArray = [[NSMutableArray alloc] init]; + int chaptercount = hb_list_count( fTitle->list_chapter ); + for( i = 0; i < chaptercount; i++ ) + { + hb_chapter_t *chapter = (hb_chapter_t *) hb_list_item( fTitle->list_chapter, i ); + if( chapter != NULL ) + { + [ChapterNamesArray addObject:[NSString stringWithCString:chapter->title encoding:NSUTF8StringEncoding]]; + } + } + [queueFileJob setObject:[NSMutableArray arrayWithArray: ChapterNamesArray] forKey:@"ChapterNames"]; + [ChapterNamesArray autorelease]; + + /* Allow Mpeg4 64 bit formatting +4GB file sizes */ + [queueFileJob setObject:[NSNumber numberWithInt:[fDstMp4LargeFileCheck state]] forKey:@"Mp4LargeFile"]; + /* Mux mp4 with http optimization */ + [queueFileJob setObject:[NSNumber numberWithInt:[fDstMp4HttpOptFileCheck state]] forKey:@"Mp4HttpOptimize"]; + /* Add iPod uuid atom */ + [queueFileJob setObject:[NSNumber numberWithInt:[fDstMp4iPodFileCheck state]] forKey:@"Mp4iPodCompatible"]; + + /* Codecs */ + /* Video encoder */ + [queueFileJob setObject:[fVidEncoderPopUp titleOfSelectedItem] forKey:@"VideoEncoder"]; + /* x264 Option String */ + [queueFileJob setObject:[fAdvancedOptions optionsString] forKey:@"x264Option"]; + + [queueFileJob setObject:[NSNumber numberWithInt:[fVidQualityMatrix selectedRow]] forKey:@"VideoQualityType"]; + [queueFileJob setObject:[fVidTargetSizeField stringValue] forKey:@"VideoTargetSize"]; + [queueFileJob setObject:[fVidBitrateField stringValue] forKey:@"VideoAvgBitrate"]; + [queueFileJob setObject:[NSNumber numberWithFloat:[fVidQualityRFField floatValue]] forKey:@"VideoQualitySlider"]; + /* Framerate */ + [queueFileJob setObject:[fVidRatePopUp titleOfSelectedItem] forKey:@"VideoFramerate"]; + + /* 2 Pass Encoding */ + [queueFileJob setObject:[NSNumber numberWithInt:[fVidTwoPassCheck state]] forKey:@"VideoTwoPass"]; + /* Turbo 2 pass Encoding fVidTurboPassCheck*/ + [queueFileJob setObject:[NSNumber numberWithInt:[fVidTurboPassCheck state]] forKey:@"VideoTurboTwoPass"]; + + /* Picture Sizing */ + /* Use Max Picture settings for whatever the dvd is.*/ + [queueFileJob setObject:[NSNumber numberWithInt:0] forKey:@"UsesMaxPictureSettings"]; + [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->width] forKey:@"PictureWidth"]; + [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->height] forKey:@"PictureHeight"]; + [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->keep_ratio] forKey:@"PictureKeepRatio"]; + [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->anamorphic.mode] forKey:@"PicturePAR"]; + /* if we are custom anamorphic, store the exact storage, par and display dims */ + if (fTitle->job->anamorphic.mode == 3) + { + [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->width] forKey:@"PicturePARStorageWidth"]; + [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->height] forKey:@"PicturePARStorageHeight"]; + + [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->anamorphic.par_width] forKey:@"PicturePARPixelWidth"]; + [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->anamorphic.par_height] forKey:@"PicturePARPixelHeight"]; + + [queueFileJob setObject:[NSNumber numberWithFloat:fTitle->job->anamorphic.dar_width] forKey:@"PicturePARDisplayWidth"]; + [queueFileJob setObject:[NSNumber numberWithFloat:fTitle->job->anamorphic.dar_height] forKey:@"PicturePARDisplayHeight"]; + + } + NSString * pictureSummary; + pictureSummary = [fPictureSizeField stringValue]; + [queueFileJob setObject:pictureSummary forKey:@"PictureSizingSummary"]; + /* Set crop settings here */ + [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController autoCrop]] forKey:@"PictureAutoCrop"]; + [queueFileJob setObject:[NSNumber numberWithInt:job->crop[0]] forKey:@"PictureTopCrop"]; + [queueFileJob setObject:[NSNumber numberWithInt:job->crop[1]] forKey:@"PictureBottomCrop"]; + [queueFileJob setObject:[NSNumber numberWithInt:job->crop[2]] forKey:@"PictureLeftCrop"]; + [queueFileJob setObject:[NSNumber numberWithInt:job->crop[3]] forKey:@"PictureRightCrop"]; + + /* Picture Filters */ + [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController detelecine]] forKey:@"PictureDetelecine"]; + [queueFileJob setObject:[fPictureController detelecineCustomString] forKey:@"PictureDetelecineCustom"]; + + [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController useDecomb]] forKey:@"PictureDecombDeinterlace"]; + [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController decomb]] forKey:@"PictureDecomb"]; + [queueFileJob setObject:[fPictureController decombCustomString] forKey:@"PictureDecombCustom"]; + + [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController deinterlace]] forKey:@"PictureDeinterlace"]; + [queueFileJob setObject:[fPictureController deinterlaceCustomString] forKey:@"PictureDeinterlaceCustom"]; + + [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController denoise]] forKey:@"PictureDenoise"]; + [queueFileJob setObject:[fPictureController denoiseCustomString] forKey:@"PictureDenoiseCustom"]; + + [queueFileJob setObject:[NSString stringWithFormat:@"%d",[fPictureController deblock]] forKey:@"PictureDeblock"]; + + [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController grayscale]] forKey:@"VideoGrayScale"]; + + /*Audio*/ + if ([fAudLang1PopUp indexOfSelectedItem] > 0) + { + [queueFileJob setObject:[NSNumber numberWithInt:[fAudLang1PopUp indexOfSelectedItem]] forKey:@"Audio1Track"]; + [queueFileJob setObject:[fAudLang1PopUp titleOfSelectedItem] forKey:@"Audio1TrackDescription"]; + [queueFileJob setObject:[fAudTrack1CodecPopUp titleOfSelectedItem] forKey:@"Audio1Encoder"]; + [queueFileJob setObject:[fAudTrack1MixPopUp titleOfSelectedItem] forKey:@"Audio1Mixdown"]; + [queueFileJob setObject:[fAudTrack1RatePopUp titleOfSelectedItem] forKey:@"Audio1Samplerate"]; + [queueFileJob setObject:[fAudTrack1BitratePopUp titleOfSelectedItem] forKey:@"Audio1Bitrate"]; + [queueFileJob setObject:[NSNumber numberWithFloat:[fAudTrack1DrcSlider floatValue]] forKey:@"Audio1TrackDRCSlider"]; + } + if ([fAudLang2PopUp indexOfSelectedItem] > 0) + { + [queueFileJob setObject:[NSNumber numberWithInt:[fAudLang2PopUp indexOfSelectedItem]] forKey:@"Audio2Track"]; + [queueFileJob setObject:[fAudLang2PopUp titleOfSelectedItem] forKey:@"Audio2TrackDescription"]; + [queueFileJob setObject:[fAudTrack2CodecPopUp titleOfSelectedItem] forKey:@"Audio2Encoder"]; + [queueFileJob setObject:[fAudTrack2MixPopUp titleOfSelectedItem] forKey:@"Audio2Mixdown"]; + [queueFileJob setObject:[fAudTrack2RatePopUp titleOfSelectedItem] forKey:@"Audio2Samplerate"]; + [queueFileJob setObject:[fAudTrack2BitratePopUp titleOfSelectedItem] forKey:@"Audio2Bitrate"]; + [queueFileJob setObject:[NSNumber numberWithFloat:[fAudTrack2DrcSlider floatValue]] forKey:@"Audio2TrackDRCSlider"]; + } + if ([fAudLang3PopUp indexOfSelectedItem] > 0) + { + [queueFileJob setObject:[NSNumber numberWithInt:[fAudLang3PopUp indexOfSelectedItem]] forKey:@"Audio3Track"]; + [queueFileJob setObject:[fAudLang3PopUp titleOfSelectedItem] forKey:@"Audio3TrackDescription"]; + [queueFileJob setObject:[fAudTrack3CodecPopUp titleOfSelectedItem] forKey:@"Audio3Encoder"]; + [queueFileJob setObject:[fAudTrack3MixPopUp titleOfSelectedItem] forKey:@"Audio3Mixdown"]; + [queueFileJob setObject:[fAudTrack3RatePopUp titleOfSelectedItem] forKey:@"Audio3Samplerate"]; + [queueFileJob setObject:[fAudTrack3BitratePopUp titleOfSelectedItem] forKey:@"Audio3Bitrate"]; + [queueFileJob setObject:[NSNumber numberWithFloat:[fAudTrack3DrcSlider floatValue]] forKey:@"Audio3TrackDRCSlider"]; + } + if ([fAudLang4PopUp indexOfSelectedItem] > 0) + { + [queueFileJob setObject:[NSNumber numberWithInt:[fAudLang4PopUp indexOfSelectedItem]] forKey:@"Audio4Track"]; + [queueFileJob setObject:[fAudLang4PopUp titleOfSelectedItem] forKey:@"Audio4TrackDescription"]; + [queueFileJob setObject:[fAudTrack4CodecPopUp titleOfSelectedItem] forKey:@"Audio4Encoder"]; + [queueFileJob setObject:[fAudTrack4MixPopUp titleOfSelectedItem] forKey:@"Audio4Mixdown"]; + [queueFileJob setObject:[fAudTrack4RatePopUp titleOfSelectedItem] forKey:@"Audio4Samplerate"]; + [queueFileJob setObject:[fAudTrack4BitratePopUp titleOfSelectedItem] forKey:@"Audio4Bitrate"]; + [queueFileJob setObject:[NSNumber numberWithFloat:[fAudTrack4DrcSlider floatValue]] forKey:@"Audio4TrackDRCSlider"]; + } + + /* Subtitles*/ + NSMutableArray *subtitlesArray = [[NSMutableArray alloc] init]; + [queueFileJob setObject:[NSArray arrayWithArray: [fSubtitlesDelegate getSubtitleArray: subtitlesArray]] forKey:@"SubtitleList"]; + [subtitlesArray autorelease]; + + /* Now we go ahead and set the "job->values in the plist for passing right to fQueueEncodeLibhb */ + + [queueFileJob setObject:[NSNumber numberWithInt:[fSrcChapterStartPopUp indexOfSelectedItem] + 1] forKey:@"JobChapterStart"]; + + [queueFileJob setObject:[NSNumber numberWithInt:[fSrcChapterEndPopUp indexOfSelectedItem] + 1] forKey:@"JobChapterEnd"]; + + + [queueFileJob setObject:[NSNumber numberWithInt:[[fDstFormatPopUp selectedItem] tag]] forKey:@"JobFileFormatMux"]; + + /* Codecs */ + /* Video encoder */ + [queueFileJob setObject:[NSNumber numberWithInt:[[fVidEncoderPopUp selectedItem] tag]] forKey:@"JobVideoEncoderVcodec"]; + + /* Framerate */ + [queueFileJob setObject:[NSNumber numberWithInt:[fVidRatePopUp indexOfSelectedItem]] forKey:@"JobIndexVideoFramerate"]; + [queueFileJob setObject:[NSNumber numberWithInt:title->rate] forKey:@"JobVrate"]; + [queueFileJob setObject:[NSNumber numberWithInt:title->rate_base] forKey:@"JobVrateBase"]; + + /* Picture Sizing */ + /* Use Max Picture settings for whatever the dvd is.*/ + [queueFileJob setObject:[NSNumber numberWithInt:0] forKey:@"UsesMaxPictureSettings"]; + [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->width] forKey:@"PictureWidth"]; + [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->height] forKey:@"PictureHeight"]; + [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->keep_ratio] forKey:@"PictureKeepRatio"]; + [queueFileJob setObject:[NSNumber numberWithInt:fTitle->job->anamorphic.mode] forKey:@"PicturePAR"]; + + /* Set crop settings here */ + [queueFileJob setObject:[NSNumber numberWithInt:[fPictureController autoCrop]] forKey:@"PictureAutoCrop"]; + [queueFileJob setObject:[NSNumber numberWithInt:job->crop[0]] forKey:@"PictureTopCrop"]; + [queueFileJob setObject:[NSNumber numberWithInt:job->crop[1]] forKey:@"PictureBottomCrop"]; + [queueFileJob setObject:[NSNumber numberWithInt:job->crop[2]] forKey:@"PictureLeftCrop"]; + [queueFileJob setObject:[NSNumber numberWithInt:job->crop[3]] forKey:@"PictureRightCrop"]; + + + /*Audio*/ + if ([fAudLang1PopUp indexOfSelectedItem] > 0) + { + //[queueFileJob setObject:[fAudTrack1CodecPopUp indexOfSelectedItem] forKey:@"JobAudio1Encoder"]; + [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack1CodecPopUp selectedItem] tag]] forKey:@"JobAudio1Encoder"]; + [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack1MixPopUp selectedItem] tag]] forKey:@"JobAudio1Mixdown"]; + [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack1RatePopUp selectedItem] tag]] forKey:@"JobAudio1Samplerate"]; + [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack1BitratePopUp selectedItem] tag]] forKey:@"JobAudio1Bitrate"]; + } + if ([fAudLang2PopUp indexOfSelectedItem] > 0) + { + //[queueFileJob setObject:[fAudTrack1CodecPopUp indexOfSelectedItem] forKey:@"JobAudio2Encoder"]; + [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack2CodecPopUp selectedItem] tag]] forKey:@"JobAudio2Encoder"]; + [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack2MixPopUp selectedItem] tag]] forKey:@"JobAudio2Mixdown"]; + [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack2RatePopUp selectedItem] tag]] forKey:@"JobAudio2Samplerate"]; + [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack2BitratePopUp selectedItem] tag]] forKey:@"JobAudio2Bitrate"]; + } + if ([fAudLang3PopUp indexOfSelectedItem] > 0) + { + //[queueFileJob setObject:[fAudTrack1CodecPopUp indexOfSelectedItem] forKey:@"JobAudio3Encoder"]; + [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack3CodecPopUp selectedItem] tag]] forKey:@"JobAudio3Encoder"]; + [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack3MixPopUp selectedItem] tag]] forKey:@"JobAudio3Mixdown"]; + [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack3RatePopUp selectedItem] tag]] forKey:@"JobAudio3Samplerate"]; + [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack3BitratePopUp selectedItem] tag]] forKey:@"JobAudio3Bitrate"]; + } + if ([fAudLang4PopUp indexOfSelectedItem] > 0) + { + //[queueFileJob setObject:[fAudTrack1CodecPopUp indexOfSelectedItem] forKey:@"JobAudio4Encoder"]; + [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack4CodecPopUp selectedItem] tag]] forKey:@"JobAudio4Encoder"]; + [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack4MixPopUp selectedItem] tag]] forKey:@"JobAudio4Mixdown"]; + [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack4RatePopUp selectedItem] tag]] forKey:@"JobAudio4Samplerate"]; + [queueFileJob setObject:[NSNumber numberWithInt:[[fAudTrack4BitratePopUp selectedItem] tag]] forKey:@"JobAudio4Bitrate"]; + } + + + /* we need to auto relase the queueFileJob and return it */ + [queueFileJob autorelease]; + return queueFileJob; + +} + +/* this is actually called from the queue controller to modify the queue array and return it back to the queue controller */ +- (void)moveObjectsInQueueArray:(NSMutableArray *)array fromIndexes:(NSIndexSet *)indexSet toIndex:(NSUInteger)insertIndex +{ + NSUInteger index = [indexSet lastIndex]; + NSUInteger aboveInsertIndexCount = 0; + + + NSUInteger removeIndex; + + if (index >= insertIndex) + { + removeIndex = index + aboveInsertIndexCount; + aboveInsertIndexCount++; + } + else + { + removeIndex = index; + insertIndex--; + } + + id object = [[QueueFileArray objectAtIndex:removeIndex] retain]; + [QueueFileArray removeObjectAtIndex:removeIndex]; + [QueueFileArray insertObject:object atIndex:insertIndex]; + [object release]; + + index = [indexSet indexLessThanIndex:index]; + + /* We save all of the Queue data here + * and it also gets sent back to the queue controller*/ + [self saveQueueFileItem]; + +} + + +#pragma mark - +#pragma mark Queue Job Processing + +- (void) incrementQueueItemDone:(int) queueItemDoneIndexNum +{ + int i = currentQueueEncodeIndex; + [[QueueFileArray objectAtIndex:i] setObject:[NSNumber numberWithInt:0] forKey:@"Status"]; + + /* We save all of the Queue data here */ + [self saveQueueFileItem]; + /* We Reload the New Table data for presets */ + //[fPresetsOutlineView reloadData]; + + /* Since we have now marked a queue item as done + * we can go ahead and increment currentQueueEncodeIndex + * so that if there is anything left in the queue we can + * go ahead and move to the next item if we want to */ + currentQueueEncodeIndex++ ; + [self writeToActivityLog: "incrementQueueItemDone currentQueueEncodeIndex is incremented to: %d", currentQueueEncodeIndex]; + int queueItems = [QueueFileArray count]; + /* If we still have more items in our queue, lets go to the next one */ + if (currentQueueEncodeIndex < queueItems) + { + [self writeToActivityLog: "incrementQueueItemDone currentQueueEncodeIndex is incremented to: %d", currentQueueEncodeIndex]; + [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]]; + } + else + { + [self writeToActivityLog: "incrementQueueItemDone the %d item queue is complete", currentQueueEncodeIndex - 1]; + } +} + +/* Here we actually tell hb_scan to perform the source scan, using the path to source and title number*/ +- (void) performNewQueueScan:(NSString *) scanPath scanTitleNum: (int) scanTitleNum +{ + /* Tell HB to output a new activity log file for this encode */ + [outputPanel startEncodeLog:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"DestinationPath"]]; + + + /* use a bool to determine whether or not we can decrypt using vlc */ + BOOL cancelScanDecrypt = 0; + /* set the bool so that showNewScan knows to apply the appropriate queue + * settings as this is a queue rescan + */ + applyQueueToScan = YES; + NSString *path = scanPath; + HBDVDDetector *detector = [HBDVDDetector detectorForPath:path]; + + /*On Screen Notification*/ + //int status; + //status = NSRunAlertPanel(@"HandBrake is now loading up a new queue item...",@"Would You Like to wait until you add another encode?", @"Cancel", @"Okay", nil); + //[NSApp requestUserAttention:NSCriticalRequest]; + + if( [detector isVideoDVD] ) + { + // The chosen path was actually on a DVD, so use the raw block + // device path instead. + path = [detector devicePath]; + [self writeToActivityLog: "trying to open a physical dvd at: %s", [scanPath UTF8String]]; + + /* lets check for vlc here to make sure we have a dylib available to use for decrypting */ + NSString *vlcPath = @"/Applications/VLC.app"; + NSFileManager * fileManager = [NSFileManager defaultManager]; + if ([fileManager fileExistsAtPath:vlcPath] == 0) + { + /*vlc not found in /Applications so we set the bool to cancel scanning to 1 */ + cancelScanDecrypt = 1; + [self writeToActivityLog: "VLC app not found for decrypting physical dvd"]; + int status; + 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"); + [NSApp requestUserAttention:NSCriticalRequest]; + + if (status == NSAlertDefaultReturn) + { + /* User chose to go download vlc (as they rightfully should) so we send them to the vlc site */ + [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"http://www.videolan.org/"]]; + } + else if (status == NSAlertAlternateReturn) + { + /* User chose to cancel the scan */ + [self writeToActivityLog: "cannot open physical dvd , scan cancelled"]; + } + else + { + /* 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 */ + cancelScanDecrypt = 0; + [self writeToActivityLog: "user overrode vlc warning -trying to open physical dvd without decryption"]; + } + + } + else + { + /* VLC was found in /Applications so all is well, we can carry on using vlc's libdvdcss.dylib for decrypting if needed */ + [self writeToActivityLog: "VLC app found for decrypting physical dvd"]; + } + } + + if (cancelScanDecrypt == 0) + { + /* we actually pass the scan off to libhb here */ + /* If there is no title number passed to scan, we use "0" + * which causes the default behavior of a full source scan + */ + if (!scanTitleNum) + { + scanTitleNum = 0; + } + if (scanTitleNum > 0) + { + [self writeToActivityLog: "scanning specifically for title: %d", scanTitleNum]; + } + + [self writeToActivityLog: "performNewQueueScan currentQueueEncodeIndex is: %d", currentQueueEncodeIndex]; + /* We use our advance pref to determine how many previews to scan */ + int hb_num_previews = [[[NSUserDefaults standardUserDefaults] objectForKey:@"PreviewsNumber"] intValue]; + hb_scan( fQueueEncodeLibhb, [path UTF8String], scanTitleNum, hb_num_previews, 0 ); + } +} + +/* This method was originally used to load up a new queue item in the gui and + * then start processing it. However we now have modified -prepareJob and use a second + * instance of libhb to do our actual encoding, therefor right now it is not required. + * Nonetheless I want to leave this in here + * because basically its everything we need to be able to actually modify a pending queue + * item in the gui and resave it. At least for now - dynaflash + */ + +- (IBAction)applyQueueSettings:(id)sender +{ + NSMutableDictionary * queueToApply = [QueueFileArray objectAtIndex:currentQueueEncodeIndex]; + hb_job_t * job = fTitle->job; + + /* Set title number and chapters */ + /* since the queue only scans a single title, we really don't need to pick a title */ + //[fSrcTitlePopUp selectItemAtIndex: [[queueToApply objectForKey:@"TitleNumber"] intValue] - 1]; + + [fSrcChapterStartPopUp selectItemAtIndex: [[queueToApply objectForKey:@"ChapterStart"] intValue] - 1]; + [fSrcChapterEndPopUp selectItemAtIndex: [[queueToApply objectForKey:@"ChapterEnd"] intValue] - 1]; + + /* File Format */ + [fDstFormatPopUp selectItemWithTitle:[queueToApply objectForKey:@"FileFormat"]]; + [self formatPopUpChanged:nil]; + + /* Chapter Markers*/ + [fCreateChapterMarkers setState:[[queueToApply objectForKey:@"ChapterMarkers"] intValue]]; + /* Allow Mpeg4 64 bit formatting +4GB file sizes */ + [fDstMp4LargeFileCheck setState:[[queueToApply objectForKey:@"Mp4LargeFile"] intValue]]; + /* Mux mp4 with http optimization */ + [fDstMp4HttpOptFileCheck setState:[[queueToApply objectForKey:@"Mp4HttpOptimize"] intValue]]; + + /* Video encoder */ + /* We set the advanced opt string here if applicable*/ + [fVidEncoderPopUp selectItemWithTitle:[queueToApply objectForKey:@"VideoEncoder"]]; + [fAdvancedOptions setOptions:[queueToApply objectForKey:@"x264Option"]]; + + /* Lets run through the following functions to get variables set there */ + [self videoEncoderPopUpChanged:nil]; + /* Set the state of ipod compatible with Mp4iPodCompatible. Only for x264*/ + [fDstMp4iPodFileCheck setState:[[queueToApply objectForKey:@"Mp4iPodCompatible"] intValue]]; + [self calculateBitrate:nil]; + + /* Video quality */ + [fVidQualityMatrix selectCellAtRow:[[queueToApply objectForKey:@"VideoQualityType"] intValue] column:0]; + + [fVidTargetSizeField setStringValue:[queueToApply objectForKey:@"VideoTargetSize"]]; + [fVidBitrateField setStringValue:[queueToApply objectForKey:@"VideoAvgBitrate"]]; + [fVidQualitySlider setFloatValue:[[queueToApply objectForKey:@"VideoQualitySlider"] floatValue]]; + + [self videoMatrixChanged:nil]; + + /* Video framerate */ + /* For video preset video framerate, we want to make sure that Same as source does not conflict with the + detected framerate in the fVidRatePopUp so we use index 0*/ + if ([[queueToApply objectForKey:@"VideoFramerate"] isEqualToString:@"Same as source"]) + { + [fVidRatePopUp selectItemAtIndex: 0]; + } + else + { + [fVidRatePopUp selectItemWithTitle:[queueToApply objectForKey:@"VideoFramerate"]]; + } + + /* 2 Pass Encoding */ + [fVidTwoPassCheck setState:[[queueToApply objectForKey:@"VideoTwoPass"] intValue]]; + [self twoPassCheckboxChanged:nil]; + /* Turbo 1st pass for 2 Pass Encoding */ + [fVidTurboPassCheck setState:[[queueToApply objectForKey:@"VideoTurboTwoPass"] intValue]]; + + /*Audio*/ + if ([queueToApply objectForKey:@"Audio1Track"] > 0) + { + if ([fAudLang1PopUp indexOfSelectedItem] == 0) + { + [fAudLang1PopUp selectItemAtIndex: 1]; + } + [self audioTrackPopUpChanged: fAudLang1PopUp]; + [fAudTrack1CodecPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio1Encoder"]]; + [self audioTrackPopUpChanged: fAudTrack1CodecPopUp]; + [fAudTrack1MixPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio1Mixdown"]]; + /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default + * mixdown*/ + if ([fAudTrack1MixPopUp selectedItem] == nil) + { + [self audioTrackPopUpChanged: fAudTrack1CodecPopUp]; + } + [fAudTrack1RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Samplerate"]]; + /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */ + if (![[queueToApply objectForKey:@"Audio1Encoder"] isEqualToString:@"AC3 Passthru"]) + { + [fAudTrack1BitratePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio1Bitrate"]]; + } + [fAudTrack1DrcSlider setFloatValue:[[queueToApply objectForKey:@"Audio1TrackDRCSlider"] floatValue]]; + [self audioDRCSliderChanged: fAudTrack1DrcSlider]; + } + if ([queueToApply objectForKey:@"Audio2Track"] > 0) + { + if ([fAudLang2PopUp indexOfSelectedItem] == 0) + { + [fAudLang2PopUp selectItemAtIndex: 1]; + } + [self audioTrackPopUpChanged: fAudLang2PopUp]; + [fAudTrack2CodecPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio2Encoder"]]; + [self audioTrackPopUpChanged: fAudTrack2CodecPopUp]; + [fAudTrack2MixPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio2Mixdown"]]; + /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default + * mixdown*/ + if ([fAudTrack2MixPopUp selectedItem] == nil) + { + [self audioTrackPopUpChanged: fAudTrack2CodecPopUp]; + } + [fAudTrack2RatePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio2Samplerate"]]; + /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */ + if (![[queueToApply objectForKey:@"Audio2Encoder"] isEqualToString:@"AC3 Passthru"]) + { + [fAudTrack2BitratePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio2Bitrate"]]; + } + [fAudTrack2DrcSlider setFloatValue:[[queueToApply objectForKey:@"Audio2TrackDRCSlider"] floatValue]]; + [self audioDRCSliderChanged: fAudTrack2DrcSlider]; + } + if ([queueToApply objectForKey:@"Audio3Track"] > 0) + { + if ([fAudLang3PopUp indexOfSelectedItem] == 0) + { + [fAudLang3PopUp selectItemAtIndex: 1]; + } + [self audioTrackPopUpChanged: fAudLang3PopUp]; + [fAudTrack3CodecPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio3Encoder"]]; + [self audioTrackPopUpChanged: fAudTrack3CodecPopUp]; + [fAudTrack3MixPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio3Mixdown"]]; + /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default + * mixdown*/ + if ([fAudTrack3MixPopUp selectedItem] == nil) + { + [self audioTrackPopUpChanged: fAudTrack3CodecPopUp]; + } + [fAudTrack3RatePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio3Samplerate"]]; + /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */ + if (![[queueToApply objectForKey:@"Audio3Encoder"] isEqualToString: @"AC3 Passthru"]) + { + [fAudTrack3BitratePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio3Bitrate"]]; + } + [fAudTrack3DrcSlider setFloatValue:[[queueToApply objectForKey:@"Audio3TrackDRCSlider"] floatValue]]; + [self audioDRCSliderChanged: fAudTrack3DrcSlider]; + } + if ([queueToApply objectForKey:@"Audio4Track"] > 0) + { + if ([fAudLang4PopUp indexOfSelectedItem] == 0) + { + [fAudLang4PopUp selectItemAtIndex: 1]; + } + [self audioTrackPopUpChanged: fAudLang4PopUp]; + [fAudTrack4CodecPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio4Encoder"]]; + [self audioTrackPopUpChanged: fAudTrack4CodecPopUp]; + [fAudTrack4MixPopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio4Mixdown"]]; + /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default + * mixdown*/ + if ([fAudTrack4MixPopUp selectedItem] == nil) + { + [self audioTrackPopUpChanged: fAudTrack4CodecPopUp]; + } + [fAudTrack4RatePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio4Samplerate"]]; + /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */ + if (![[chosenPreset objectForKey:@"Audio4Encoder"] isEqualToString:@"AC3 Passthru"]) + { + [fAudTrack4BitratePopUp selectItemWithTitle:[queueToApply objectForKey:@"Audio4Bitrate"]]; + } + [fAudTrack4DrcSlider setFloatValue:[[queueToApply objectForKey:@"Audio4TrackDRCSlider"] floatValue]]; + [self audioDRCSliderChanged: fAudTrack4DrcSlider]; + } + + + /*Subtitles*/ + [fSubPopUp selectItemWithTitle:[queueToApply objectForKey:@"Subtitles"]]; + /* Forced Subtitles */ + [fSubForcedCheck setState:[[queueToApply objectForKey:@"SubtitlesForced"] intValue]]; + + /* Picture Settings */ + /* we check to make sure the presets width/height does not exceed the sources width/height */ + if (fTitle->width < [[queueToApply objectForKey:@"PictureWidth"] intValue] || fTitle->height < [[queueToApply objectForKey:@"PictureHeight"] intValue]) + { + /* if so, then we use the sources height and width to avoid scaling up */ + job->width = fTitle->width; + job->height = fTitle->height; + } + else // source width/height is >= the preset height/width + { + /* we can go ahead and use the presets values for height and width */ + job->width = [[queueToApply objectForKey:@"PictureWidth"] intValue]; + job->height = [[queueToApply objectForKey:@"PictureHeight"] intValue]; + } + job->keep_ratio = [[queueToApply objectForKey:@"PictureKeepRatio"] intValue]; + if (job->keep_ratio == 1) + { + hb_fix_aspect( job, HB_KEEP_WIDTH ); + if( job->height > fTitle->height ) + { + job->height = fTitle->height; + hb_fix_aspect( job, HB_KEEP_HEIGHT ); + } + } + job->anamorphic.mode = [[queueToApply objectForKey:@"PicturePAR"] intValue]; + + + /* If Cropping is set to custom, then recall all four crop values from + when the preset was created and apply them */ + if ([[queueToApply objectForKey:@"PictureAutoCrop"] intValue] == 0) + { + [fPictureController setAutoCrop:NO]; + + /* Here we use the custom crop values saved at the time the preset was saved */ + job->crop[0] = [[queueToApply objectForKey:@"PictureTopCrop"] intValue]; + job->crop[1] = [[queueToApply objectForKey:@"PictureBottomCrop"] intValue]; + job->crop[2] = [[queueToApply objectForKey:@"PictureLeftCrop"] intValue]; + job->crop[3] = [[queueToApply objectForKey:@"PictureRightCrop"] intValue]; + + } + else /* if auto crop has been saved in preset, set to auto and use post scan auto crop */ + { + [fPictureController setAutoCrop:YES]; + /* Here we use the auto crop values determined right after scan */ + job->crop[0] = AutoCropTop; + job->crop[1] = AutoCropBottom; + job->crop[2] = AutoCropLeft; + job->crop[3] = AutoCropRight; + + } + + /* Filters */ + /* Deinterlace */ + [fPictureController setDeinterlace:[[queueToApply objectForKey:@"PictureDeinterlace"] intValue]]; + + /* Detelecine */ + [fPictureController setDetelecine:[[queueToApply objectForKey:@"PictureDetelecine"] intValue]]; + /* Denoise */ + [fPictureController setDenoise:[[queueToApply objectForKey:@"PictureDenoise"] intValue]]; + /* Deblock */ + [fPictureController setDeblock:[[queueToApply objectForKey:@"PictureDeblock"] intValue]]; + /* Decomb */ + [fPictureController setDecomb:[[queueToApply objectForKey:@"PictureDecomb"] intValue]]; + /* Grayscale */ + [fPictureController setGrayscale:[[queueToApply objectForKey:@"VideoGrayScale"] intValue]]; + + [self calculatePictureSizing:nil]; + + + /* somehow we need to figure out a way to tie the queue item to a preset if it used one */ + //[queueFileJob setObject:[fPresetSelectedDisplay stringValue] forKey:@"PresetName"]; + // [queueFileJob setObject:[NSNumber numberWithInt:[fPresetsOutlineView selectedRow]] forKey:@"PresetIndexNum"]; + if ([queueToApply objectForKey:@"PresetIndexNum"]) // This item used a preset so insert that info + { + /* Deselect the currently selected Preset if there is one*/ + //[fPresetsOutlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:[[queueToApply objectForKey:@"PresetIndexNum"] intValue]] byExtendingSelection:NO]; + //[self selectPreset:nil]; + + //[fPresetsOutlineView selectRow:[[queueToApply objectForKey:@"PresetIndexNum"] intValue]]; + /* Change UI to show "Custom" settings are being used */ + //[fPresetSelectedDisplay setStringValue: [[queueToApply objectForKey:@"PresetName"] stringValue]]; + + curUserPresetChosenNum = nil; + } + else + { + /* Deselect the currently selected Preset if there is one*/ + [fPresetsOutlineView deselectRow:[fPresetsOutlineView selectedRow]]; + /* Change UI to show "Custom" settings are being used */ + [fPresetSelectedDisplay setStringValue: @"Custom"]; + + //curUserPresetChosenNum = nil; + } + + /* We need to set this bool back to NO, in case the user wants to do a scan */ + //applyQueueToScan = NO; + + /* so now we go ahead and process the new settings */ + [self processNewQueueEncode]; +} + + + +/* This assumes that we have re-scanned and loaded up a new queue item to send to libhb as fQueueEncodeLibhb */ +- (void) processNewQueueEncode +{ + hb_list_t * list = hb_get_titles( fQueueEncodeLibhb ); + hb_title_t * title = (hb_title_t *) hb_list_item( list,0 ); // is always zero since now its a single title scan + hb_job_t * job = title->job; + + if( !hb_list_count( list ) ) + { + [self writeToActivityLog: "processNewQueueEncode WARNING nothing found in the title list"]; + } + + NSMutableDictionary * queueToApply = [QueueFileArray objectAtIndex:currentQueueEncodeIndex]; + [self writeToActivityLog: "Preset: %s", [[queueToApply objectForKey:@"PresetName"] UTF8String]]; + [self writeToActivityLog: "processNewQueueEncode number of passes expected is: %d", ([[queueToApply objectForKey:@"VideoTwoPass"] intValue] + 1)]; + job->file = [[queueToApply objectForKey:@"DestinationPath"] UTF8String]; + //[self writeToActivityLog: "processNewQueueEncode sending to prepareJob"]; + [self prepareJob]; + + /* + * If scanning we need to do some extra setup of the job. + */ + if( job->indepth_scan == 1 ) + { + char *x264opts_tmp; + + /* + * When subtitle scan is enabled do a fast pre-scan job + * which will determine which subtitles to enable, if any. + */ + job->pass = -1; + x264opts_tmp = job->x264opts; + + job->x264opts = NULL; + + job->indepth_scan = 1; + + + /* + * Add the pre-scan job + */ + hb_add( fQueueEncodeLibhb, job ); + job->x264opts = x264opts_tmp; + } + + + if( [[queueToApply objectForKey:@"VideoTwoPass"] intValue] == 1 ) + { + job->indepth_scan = 0; + + + + job->pass = 1; + + hb_add( fQueueEncodeLibhb, job ); + + job->pass = 2; + + job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */ + strcpy(job->x264opts, [[queueToApply objectForKey:@"x264Option"] UTF8String]); + + hb_add( fQueueEncodeLibhb, job ); + + } + else + { + job->indepth_scan = 0; + job->pass = 0; + + hb_add( fQueueEncodeLibhb, job ); + } + + NSString *destinationDirectory = [[queueToApply objectForKey:@"DestinationPath"] stringByDeletingLastPathComponent]; + [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"]; + /* Lets mark our new encode as 1 or "Encoding" */ + [queueToApply setObject:[NSNumber numberWithInt:1] forKey:@"Status"]; + [self saveQueueFileItem]; + + /* we need to clean up the subtitle tracks after the job(s) have been set */ + int num_subtitle_tracks = hb_list_count(job->list_subtitle); + int ii; + for(ii = 0; ii < num_subtitle_tracks; ii++) + { + hb_subtitle_t * subtitle; + subtitle = (hb_subtitle_t *)hb_list_item(job->list_subtitle, 0); + + + hb_list_rem(job->list_subtitle, subtitle); + free(subtitle); + } + + + /* We should be all setup so let 'er rip */ + [self doRip]; +} + +#pragma mark - +#pragma mark Live Preview +/* Note,this is much like prepareJob, but directly sets the job vars so Picture Preview + * can encode to its temp preview directory and playback. This is *not* used for any actual user + * encodes + */ +- (void) prepareJobForPreview +{ + hb_list_t * list = hb_get_titles( fHandle ); + hb_title_t * title = (hb_title_t *) hb_list_item( list, + [fSrcTitlePopUp indexOfSelectedItem] ); + hb_job_t * job = title->job; + hb_audio_config_t * audio; + /* set job->angle for libdvdnav */ + job->angle = [fSrcAnglePopUp indexOfSelectedItem] + 1; + /* Chapter selection */ + job->chapter_start = [fSrcChapterStartPopUp indexOfSelectedItem] + 1; + job->chapter_end = [fSrcChapterEndPopUp indexOfSelectedItem] + 1; + + /* Format (Muxer) and Video Encoder */ + job->mux = [[fDstFormatPopUp selectedItem] tag]; + job->vcodec = [[fVidEncoderPopUp selectedItem] tag]; + + job->chapter_markers = 0; + + if( job->vcodec & HB_VCODEC_X264 ) + { + /* Set this flag to switch from Constant Quantizer(default) to Constant Rate Factor Thanks jbrjake + Currently only used with Constant Quality setting*/ + if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultCrf"] > 0 && [fVidQualityMatrix selectedRow] == 2) + { + job->crf = 1; + } + + /* Below Sends x264 options to the core library if x264 is selected*/ + /* Lets use this as per Nyx, Thanks Nyx!*/ + job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */ + /* For previews we ignore the turbo option for the first pass of two since we only use 1 pass */ + strcpy(job->x264opts, [[fAdvancedOptions optionsString] UTF8String]); + + + } + + /* Video settings */ + /* Set vfr to 0 as it's only on if using same as source in the framerate popup + * and detelecine is on, so we handle that in the logic below + */ + job->vfr = 0; + if( [fVidRatePopUp indexOfSelectedItem] > 0 ) + { + /* a specific framerate has been chosen */ + job->vrate = 27000000; + job->vrate_base = hb_video_rates[[fVidRatePopUp indexOfSelectedItem]-1].rate; + /* We are not same as source so we set job->cfr to 1 + * to enable constant frame rate since user has specified + * a specific framerate*/ + job->cfr = 1; + } + else + { + /* We are same as source (variable) */ + job->vrate = title->rate; + job->vrate_base = title->rate_base; + /* We are same as source so we set job->cfr to 0 + * to enable true same as source framerate */ + job->cfr = 0; + /* If we are same as source and we have detelecine on, we need to turn on + * job->vfr + */ + if ([fPictureController detelecine] == 1) + { + job->vfr = 1; + } + } + + switch( [fVidQualityMatrix selectedRow] ) + { + case 0: + /* Target size. + Bitrate should already have been calculated and displayed + in fVidBitrateField, so let's just use it */ + case 1: + job->vquality = -1.0; + job->vbitrate = [fVidBitrateField intValue]; + break; + case 2: + job->vquality = [fVidQualityRFField floatValue]; + job->vbitrate = 0; + break; + } + + /* Subtitle settings */ + NSMutableArray *subtitlesArray = nil; + subtitlesArray = [[NSMutableArray alloc] initWithArray:[fSubtitlesDelegate getSubtitleArray: subtitlesArray]]; + + + + int subtitle = nil; +int force; +int burned; +int def; +bool one_burned = FALSE; + + int i = 0; + NSEnumerator *enumerator = [subtitlesArray objectEnumerator]; + id tempObject; + while (tempObject = [enumerator nextObject]) + { + + subtitle = [[tempObject objectForKey:@"subtitleSourceTrackNum"] intValue]; + force = [[tempObject objectForKey:@"subtitleTrackForced"] intValue]; + burned = [[tempObject objectForKey:@"subtitleTrackBurned"] intValue]; + def = [[tempObject objectForKey:@"subtitleTrackDefault"] intValue]; + + /* since the subtitleSourceTrackNum 0 is "None" in our array of the subtitle popups, + * we want to ignore it for display as well as encoding. + */ + if (subtitle > 0) + { + /* if i is 0, then we are in the first item of the subtitles which we need to + * check for the "Foreign Audio Search" which would be subtitleSourceTrackNum of 1 + * bearing in mind that for all tracks subtitleSourceTrackNum of 0 is None. + */ + + /* if we are on the first track and using "Foreign Audio Search" */ + if (i == 0 && subtitle == 1) + { + /* NOTE: Currently foreign language search is borked for preview. + * Commented out but left in for initial commit. */ + + + [self writeToActivityLog: "Foreign Language Search: %d", 1]; + + job->indepth_scan = 1; + if (burned == 1 || job->mux != HB_MUX_MP4) + { + if (burned != 1 && job->mux == HB_MUX_MKV) + { + job->select_subtitle_config.dest = PASSTHRUSUB; + } + else + { + job->select_subtitle_config.dest = RENDERSUB; + } + + job->select_subtitle_config.force = force; + job->select_subtitle_config.default_track = def; + + } + + + } + else + { + + /* for the actual source tracks, we must subtract the non source entries so + * that the menu index matches the source subtitle_list index for convenience */ + if (i == 0) + { + /* for the first track, the source tracks start at menu index 2 ( None is 0, + * Foreign Language Search is 1) so subtract 2 */ + subtitle = subtitle - 2; + } + else + { + /* for all other tracks, the source tracks start at menu index 1 (None is 0) + * so subtract 1. */ + + subtitle = subtitle - 1; + } + + /* We are setting a source subtitle so access the source subtitle info */ + hb_subtitle_t * subt; + + subt = (hb_subtitle_t *)hb_list_item(title->list_subtitle, subtitle); + + /* if we are getting the subtitles from an external srt file */ + if ([[tempObject objectForKey:@"subtitleSourceTrackType"] isEqualToString:@"SRT"]) + { + hb_subtitle_config_t sub_config; + + sub_config.offset = [[tempObject objectForKey:@"subtitleTrackSrtOffset"] intValue]; + + /* we need to srncpy file path and char code */ + strncpy(sub_config.src_filename, [[tempObject objectForKey:@"subtitleSourceSrtFilePath"] UTF8String], 128); + strncpy(sub_config.src_codeset, [[tempObject objectForKey:@"subtitleTrackSrtCharCode"] UTF8String], 40); + + sub_config.force = 0; + sub_config.dest = PASSTHRUSUB; + sub_config.default_track = def; + + hb_srt_add( job, &sub_config, [[tempObject objectForKey:@"subtitleTrackSrtLanguageIso3"] UTF8String]); + } + + if (subt != NULL) + { + [self writeToActivityLog: "Setting Subtitle: %s", subt]; + + hb_subtitle_config_t sub_config = subt->config; + + if (!burned && job->mux == HB_MUX_MKV && + subt->format == PICTURESUB) + { + sub_config.dest = PASSTHRUSUB; + } + else if (!burned && job->mux == HB_MUX_MP4 && + subt->format == PICTURESUB) + { + // Skip any non-burned vobsubs when output is mp4 + continue; + } + else if ( burned && subt->format == PICTURESUB ) + { + // Only allow one subtitle to be burned into the video + if (one_burned) + continue; + one_burned = TRUE; + } + sub_config.force = force; + sub_config.default_track = def; + hb_subtitle_add( job, &sub_config, subtitle ); + } + + } + } + i++; + } + + + +[subtitlesArray autorelease]; + + + /* Audio tracks and mixdowns */ + /* Lets make sure there arent any erroneous audio tracks in the job list, so lets make sure its empty*/ + int audiotrack_count = hb_list_count(job->list_audio); + for( int i = 0; i < audiotrack_count;i++) + { + hb_audio_t * temp_audio = (hb_audio_t*) hb_list_item( job->list_audio, 0 ); + hb_list_rem(job->list_audio, temp_audio); + } + /* Now lets add our new tracks to the audio list here */ + if ([fAudLang1PopUp indexOfSelectedItem] > 0) + { + audio = (hb_audio_config_t *) calloc(1, sizeof(*audio)); + hb_audio_config_init(audio); + audio->in.track = [fAudLang1PopUp indexOfSelectedItem] - 1; + /* We go ahead and assign values to our audio->out.<properties> */ + audio->out.track = [fAudLang1PopUp indexOfSelectedItem] - 1; + audio->out.codec = [[fAudTrack1CodecPopUp selectedItem] tag]; + audio->out.mixdown = [[fAudTrack1MixPopUp selectedItem] tag]; + audio->out.bitrate = [[fAudTrack1BitratePopUp selectedItem] tag]; + audio->out.samplerate = [[fAudTrack1RatePopUp selectedItem] tag]; + audio->out.dynamic_range_compression = [fAudTrack1DrcField floatValue]; + + hb_audio_add( job, audio ); + free(audio); + } + if ([fAudLang2PopUp indexOfSelectedItem] > 0) + { + audio = (hb_audio_config_t *) calloc(1, sizeof(*audio)); + hb_audio_config_init(audio); + audio->in.track = [fAudLang2PopUp indexOfSelectedItem] - 1; + /* We go ahead and assign values to our audio->out.<properties> */ + audio->out.track = [fAudLang2PopUp indexOfSelectedItem] - 1; + audio->out.codec = [[fAudTrack2CodecPopUp selectedItem] tag]; + audio->out.mixdown = [[fAudTrack2MixPopUp selectedItem] tag]; + audio->out.bitrate = [[fAudTrack2BitratePopUp selectedItem] tag]; + audio->out.samplerate = [[fAudTrack2RatePopUp selectedItem] tag]; + audio->out.dynamic_range_compression = [fAudTrack2DrcField floatValue]; + + hb_audio_add( job, audio ); + free(audio); + + } + + if ([fAudLang3PopUp indexOfSelectedItem] > 0) + { + audio = (hb_audio_config_t *) calloc(1, sizeof(*audio)); + hb_audio_config_init(audio); + audio->in.track = [fAudLang3PopUp indexOfSelectedItem] - 1; + /* We go ahead and assign values to our audio->out.<properties> */ + audio->out.track = [fAudLang3PopUp indexOfSelectedItem] - 1; + audio->out.codec = [[fAudTrack3CodecPopUp selectedItem] tag]; + audio->out.mixdown = [[fAudTrack3MixPopUp selectedItem] tag]; + audio->out.bitrate = [[fAudTrack3BitratePopUp selectedItem] tag]; + audio->out.samplerate = [[fAudTrack3RatePopUp selectedItem] tag]; + audio->out.dynamic_range_compression = [fAudTrack3DrcField floatValue]; + + hb_audio_add( job, audio ); + free(audio); + + } + + if ([fAudLang4PopUp indexOfSelectedItem] > 0) + { + audio = (hb_audio_config_t *) calloc(1, sizeof(*audio)); + hb_audio_config_init(audio); + audio->in.track = [fAudLang4PopUp indexOfSelectedItem] - 1; + /* We go ahead and assign values to our audio->out.<properties> */ + audio->out.track = [fAudLang4PopUp indexOfSelectedItem] - 1; + audio->out.codec = [[fAudTrack4CodecPopUp selectedItem] tag]; + audio->out.mixdown = [[fAudTrack4MixPopUp selectedItem] tag]; + audio->out.bitrate = [[fAudTrack4BitratePopUp selectedItem] tag]; + audio->out.samplerate = [[fAudTrack4RatePopUp selectedItem] tag]; + audio->out.dynamic_range_compression = [fAudTrack4DrcField floatValue]; + + hb_audio_add( job, audio ); + free(audio); + + } + + + + /* Filters */ + + /* Though Grayscale is not really a filter, per se + * we put it here since its in the filters panel + */ + + if ([fPictureController grayscale]) + { + job->grayscale = 1; + } + else + { + job->grayscale = 0; + } + + /* Initialize the filters list */ + job->filters = hb_list_init(); + + /* Now lets call the filters if applicable. + * The order of the filters is critical + */ + + /* Detelecine */ + if ([fPictureController detelecine] == 1) + { + /* use a custom detelecine string */ + hb_filter_detelecine.settings = (char *) [[fPictureController detelecineCustomString] UTF8String]; + hb_list_add( job->filters, &hb_filter_detelecine ); + } + if ([fPictureController detelecine] == 2) + { + /* Default */ + hb_list_add( job->filters, &hb_filter_detelecine ); + } + + + + if ([fPictureController useDecomb] == 1) + { + /* Decomb */ + /* we add the custom string if present */ + if ([fPictureController decomb] == 1) + { + /* use a custom decomb string */ + hb_filter_decomb.settings = (char *) [[fPictureController decombCustomString] UTF8String]; + hb_list_add( job->filters, &hb_filter_decomb ); + } + if ([fPictureController decomb] == 2) + { + /* Run old deinterlacer fd by default */ + //hb_filter_decomb.settings = (char *) [[fPicSettingDecomb stringValue] UTF8String]; + hb_list_add( job->filters, &hb_filter_decomb ); + } + } + else + { + + /* Deinterlace */ + if ([fPictureController deinterlace] == 1) + { + /* we add the custom string if present */ + hb_filter_deinterlace.settings = (char *) [[fPictureController deinterlaceCustomString] UTF8String]; + hb_list_add( job->filters, &hb_filter_deinterlace ); + } + else if ([fPictureController deinterlace] == 2) + { + /* Run old deinterlacer fd by default */ + hb_filter_deinterlace.settings = "-1"; + hb_list_add( job->filters, &hb_filter_deinterlace ); + } + else if ([fPictureController deinterlace] == 3) + { + /* Yadif mode 0 (without spatial deinterlacing.) */ + hb_filter_deinterlace.settings = "2"; + hb_list_add( job->filters, &hb_filter_deinterlace ); + } + else if ([fPictureController deinterlace] == 4) + { + /* Yadif (with spatial deinterlacing) */ + hb_filter_deinterlace.settings = "0"; + hb_list_add( job->filters, &hb_filter_deinterlace ); + } + + } + + /* Denoise */ + if ([fPictureController denoise] == 1) // custom in popup + { + /* we add the custom string if present */ + hb_filter_denoise.settings = (char *) [[fPictureController denoiseCustomString] UTF8String]; + hb_list_add( job->filters, &hb_filter_denoise ); + } + else if ([fPictureController denoise] == 2) // Weak in popup + { + hb_filter_denoise.settings = "2:1:2:3"; + hb_list_add( job->filters, &hb_filter_denoise ); + } + else if ([fPictureController denoise] == 3) // Medium in popup + { + hb_filter_denoise.settings = "3:2:2:3"; + hb_list_add( job->filters, &hb_filter_denoise ); + } + else if ([fPictureController denoise] == 4) // Strong in popup + { + hb_filter_denoise.settings = "7:7:5:5"; + hb_list_add( job->filters, &hb_filter_denoise ); + } + + + /* Deblock (uses pp7 default) */ + /* NOTE: even though there is a valid deblock setting of 0 for the filter, for + * the macgui's purposes a value of 0 actually means to not even use the filter + * current hb_filter_deblock.settings valid ranges are from 5 - 15 + */ + if ([fPictureController deblock] != 0) + { + NSString *deblockStringValue = [NSString stringWithFormat: @"%d",[fPictureController deblock]]; + hb_filter_deblock.settings = (char *) [deblockStringValue UTF8String]; + hb_list_add( job->filters, &hb_filter_deblock ); + } + +} + + +#pragma mark - +#pragma mark Job Handling + + +- (void) prepareJob +{ + + NSMutableDictionary * queueToApply = [QueueFileArray objectAtIndex:currentQueueEncodeIndex]; + hb_list_t * list = hb_get_titles( fQueueEncodeLibhb ); + hb_title_t * title = (hb_title_t *) hb_list_item( list,0 ); // is always zero since now its a single title scan + hb_job_t * job = title->job; + hb_audio_config_t * audio; + /* Title Angle for dvdnav */ + job->angle = [[queueToApply objectForKey:@"TitleAngle"] intValue]; + /* Chapter selection */ + job->chapter_start = [[queueToApply objectForKey:@"JobChapterStart"] intValue]; + job->chapter_end = [[queueToApply objectForKey:@"JobChapterEnd"] intValue]; + + /* Format (Muxer) and Video Encoder */ + job->mux = [[queueToApply objectForKey:@"JobFileFormatMux"] intValue]; + job->vcodec = [[queueToApply objectForKey:@"JobVideoEncoderVcodec"] intValue]; + + + /* If mpeg-4, then set mpeg-4 specific options like chapters and > 4gb file sizes */ + if( [[queueToApply objectForKey:@"Mp4LargeFile"] intValue] == 1) + { + job->largeFileSize = 1; + } + else + { + job->largeFileSize = 0; + } + /* We set http optimized mp4 here */ + if( [[queueToApply objectForKey:@"Mp4HttpOptimize"] intValue] == 1 ) + { + job->mp4_optimize = 1; + } + else + { + job->mp4_optimize = 0; + } + + + /* We set the chapter marker extraction here based on the format being + mpeg4 or mkv and the checkbox being checked */ + if ([[queueToApply objectForKey:@"ChapterMarkers"] intValue] == 1) + { + job->chapter_markers = 1; + + /* now lets get our saved chapter names out the array in the queue file + * and insert them back into the title chapter list. We have it here, + * because unless we are inserting chapter markers there is no need to + * spend the overhead of iterating through the chapter names array imo + * Also, note that if for some reason we don't apply chapter names, the + * chapters just come out 001, 002, etc. etc. + */ + + NSMutableArray *ChapterNamesArray = [queueToApply objectForKey:@"ChapterNames"]; + int i = 0; + NSEnumerator *enumerator = [ChapterNamesArray objectEnumerator]; + id tempObject; + while (tempObject = [enumerator nextObject]) + { + hb_chapter_t *chapter = (hb_chapter_t *) hb_list_item( title->list_chapter, i ); + if( chapter != NULL ) + { + strncpy( chapter->title, [tempObject UTF8String], 1023); + chapter->title[1023] = '\0'; + } + i++; + } + } + else + { + job->chapter_markers = 0; + } + + if( job->vcodec & HB_VCODEC_X264 ) + { + if ([[queueToApply objectForKey:@"Mp4iPodCompatible"] intValue] == 1) + { + job->ipod_atom = 1; + } + else + { + job->ipod_atom = 0; + } + + /* Set this flag to switch from Constant Quantizer(default) to Constant Rate Factor Thanks jbrjake + Currently only used with Constant Quality setting*/ + if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultCrf"] > 0 && [[queueToApply objectForKey:@"VideoQualityType"] intValue] == 2) + { + job->crf = 1; + } + /* Below Sends x264 options to the core library if x264 is selected*/ + /* Lets use this as per Nyx, Thanks Nyx!*/ + job->x264opts = (char *)calloc(1024, 1); /* Fixme, this just leaks */ + /* Turbo first pass if two pass and Turbo First pass is selected */ + if( [[queueToApply objectForKey:@"VideoTwoPass"] intValue] == 1 && [[queueToApply objectForKey:@"VideoTurboTwoPass"] intValue] == 1 ) + { + /* pass the "Turbo" string to be appended to the existing x264 opts string into a variable for the first pass */ + NSString *firstPassOptStringTurbo = @":ref=1:subme=1:me=dia:analyse=none:trellis=0:no-fast-pskip=0:8x8dct=0:weightb=0"; + /* append the "Turbo" string variable to the existing opts string. + Note: the "Turbo" string must be appended, not prepended to work properly*/ + NSString *firstPassOptStringCombined = [[queueToApply objectForKey:@"x264Option"] stringByAppendingString:firstPassOptStringTurbo]; + strcpy(job->x264opts, [firstPassOptStringCombined UTF8String]); + } + else + { + strcpy(job->x264opts, [[queueToApply objectForKey:@"x264Option"] UTF8String]); + } + + } + + + /* Picture Size Settings */ + job->width = [[queueToApply objectForKey:@"PictureWidth"] intValue]; + job->height = [[queueToApply objectForKey:@"PictureHeight"] intValue]; + + job->keep_ratio = [[queueToApply objectForKey:@"PictureKeepRatio"] intValue]; + job->anamorphic.mode = [[queueToApply objectForKey:@"PicturePAR"] intValue]; + if ([[queueToApply objectForKey:@"PicturePAR"] intValue] == 3) + { + /* insert our custom values here for capuj */ + job->width = [[queueToApply objectForKey:@"PicturePARStorageWidth"] intValue]; + job->height = [[queueToApply objectForKey:@"PicturePARStorageHeight"] intValue]; + + job->anamorphic.par_width = [[queueToApply objectForKey:@"PicturePARPixelWidth"] intValue]; + job->anamorphic.par_height = [[queueToApply objectForKey:@"PicturePARPixelHeight"] intValue]; + + job->anamorphic.dar_width = [[queueToApply objectForKey:@"PicturePARDisplayWidth"] floatValue]; + job->anamorphic.dar_height = [[queueToApply objectForKey:@"PicturePARDisplayHeight"] floatValue]; + } + + /* Here we use the crop values saved at the time the preset was saved */ + job->crop[0] = [[queueToApply objectForKey:@"PictureTopCrop"] intValue]; + job->crop[1] = [[queueToApply objectForKey:@"PictureBottomCrop"] intValue]; + job->crop[2] = [[queueToApply objectForKey:@"PictureLeftCrop"] intValue]; + job->crop[3] = [[queueToApply objectForKey:@"PictureRightCrop"] intValue]; + + /* Video settings */ + /* Framerate */ + + /* Set vfr to 0 as it's only on if using same as source in the framerate popup + * and detelecine is on, so we handle that in the logic below + */ + job->vfr = 0; + if( [[queueToApply objectForKey:@"JobIndexVideoFramerate"] intValue] > 0 ) + { + /* a specific framerate has been chosen */ + job->vrate = 27000000; + job->vrate_base = hb_video_rates[[[queueToApply objectForKey:@"JobIndexVideoFramerate"] intValue]-1].rate; + /* We are not same as source so we set job->cfr to 1 + * to enable constant frame rate since user has specified + * a specific framerate*/ + job->cfr = 1; + } + else + { + /* We are same as source (variable) */ + job->vrate = [[queueToApply objectForKey:@"JobVrate"] intValue]; + job->vrate_base = [[queueToApply objectForKey:@"JobVrateBase"] intValue]; + /* We are same as source so we set job->cfr to 0 + * to enable true same as source framerate */ + job->cfr = 0; + /* If we are same as source and we have detelecine on, we need to turn on + * job->vfr + */ + if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] == 1) + { + job->vfr = 1; + } + } + + if ( [[queueToApply objectForKey:@"VideoQualityType"] intValue] != 2 ) + { + /* Target size. + Bitrate should already have been calculated and displayed + in fVidBitrateField, so let's just use it same as abr*/ + job->vquality = -1.0; + job->vbitrate = [[queueToApply objectForKey:@"VideoAvgBitrate"] intValue]; + } + if ( [[queueToApply objectForKey:@"VideoQualityType"] intValue] == 2 ) + { + job->vquality = [[queueToApply objectForKey:@"VideoQualitySlider"] floatValue]; + job->vbitrate = 0; + + } + + job->grayscale = [[queueToApply objectForKey:@"VideoGrayScale"] intValue]; + + + +#pragma mark - +#pragma mark Process Subtitles to libhb + +/* Map the settings in the dictionaries for the SubtitleList array to match title->list_subtitle + * which means that we need to account for the offset of non source language settings in from + * the NSPopUpCell menu. For all of the objects in the SubtitleList array this means 0 is "None" + * from the popup menu, additionally the first track has "Foreign Audio Search" at 1. So we use + * an int to offset the index number for the objectForKey:@"subtitleSourceTrackNum" to map that + * to the source tracks position in title->list_subtitle. + */ + +int subtitle = nil; +int force; +int burned; +int def; +bool one_burned = FALSE; + + int i = 0; + NSEnumerator *enumerator = [[queueToApply objectForKey:@"SubtitleList"] objectEnumerator]; + id tempObject; + while (tempObject = [enumerator nextObject]) + { + + subtitle = [[tempObject objectForKey:@"subtitleSourceTrackNum"] intValue]; + force = [[tempObject objectForKey:@"subtitleTrackForced"] intValue]; + burned = [[tempObject objectForKey:@"subtitleTrackBurned"] intValue]; + def = [[tempObject objectForKey:@"subtitleTrackDefault"] intValue]; + + /* since the subtitleSourceTrackNum 0 is "None" in our array of the subtitle popups, + * we want to ignore it for display as well as encoding. + */ + if (subtitle > 0) + { + /* if i is 0, then we are in the first item of the subtitles which we need to + * check for the "Foreign Audio Search" which would be subtitleSourceTrackNum of 1 + * bearing in mind that for all tracks subtitleSourceTrackNum of 0 is None. + */ + + /* if we are on the first track and using "Foreign Audio Search" */ + if (i == 0 && subtitle == 1) + { + [self writeToActivityLog: "Foreign Language Search: %d", 1]; + + job->indepth_scan = 1; + if (burned == 1 || job->mux != HB_MUX_MP4) + { + if (burned != 1 && job->mux == HB_MUX_MKV) + { + job->select_subtitle_config.dest = PASSTHRUSUB; + } + else + { + job->select_subtitle_config.dest = RENDERSUB; + } + + job->select_subtitle_config.force = force; + job->select_subtitle_config.default_track = def; + } + + + } + else + { + + /* for the actual source tracks, we must subtract the non source entries so + * that the menu index matches the source subtitle_list index for convenience */ + if (i == 0) + { + /* for the first track, the source tracks start at menu index 2 ( None is 0, + * Foreign Language Search is 1) so subtract 2 */ + subtitle = subtitle - 2; + } + else + { + /* for all other tracks, the source tracks start at menu index 1 (None is 0) + * so subtract 1. */ + + subtitle = subtitle - 1; + } + + /* We are setting a source subtitle so access the source subtitle info */ + hb_subtitle_t * subt; + + subt = (hb_subtitle_t *)hb_list_item(title->list_subtitle, subtitle); + + /* if we are getting the subtitles from an external srt file */ + if ([[tempObject objectForKey:@"subtitleSourceTrackType"] isEqualToString:@"SRT"]) + { + hb_subtitle_config_t sub_config; + + sub_config.offset = [[tempObject objectForKey:@"subtitleTrackSrtOffset"] intValue]; + + /* we need to srncpy file name and codeset */ + //sub_config.src_filename = [[tempObject objectForKey:@"subtitleSourceSrtFilePath"] UTF8String]; + strncpy(sub_config.src_filename, [[tempObject objectForKey:@"subtitleSourceSrtFilePath"] UTF8String], 128); + //sub_config.src_codeset = [[tempObject objectForKey:@"subtitleTrackSrtCharCode"] UTF8String]; + strncpy(sub_config.src_codeset, [[tempObject objectForKey:@"subtitleTrackSrtCharCode"] UTF8String], 40); + + sub_config.force = 0; + sub_config.dest = PASSTHRUSUB; + sub_config.default_track = def; + + hb_srt_add( job, &sub_config, [[tempObject objectForKey:@"subtitleTrackSrtLanguageIso3"] UTF8String]); + } + + + if (subt != NULL) + { + [self writeToActivityLog: "Setting Subtitle: %s", subt]; + + hb_subtitle_config_t sub_config = subt->config; + + if (!burned && job->mux == HB_MUX_MKV && + subt->format == PICTURESUB) + { + sub_config.dest = PASSTHRUSUB; + } + else if (!burned && job->mux == HB_MUX_MP4 && + subt->format == PICTURESUB) + { + // Skip any non-burned vobsubs when output is mp4 + continue; + } + else if ( burned && subt->format == PICTURESUB ) + { + // Only allow one subtitle to be burned into the video + if (one_burned) + continue; + one_burned = TRUE; + } + sub_config.force = force; + sub_config.default_track = def; + hb_subtitle_add( job, &sub_config, subtitle ); + } + + } + } + i++; + } + +#pragma mark - + + + /* Audio tracks and mixdowns */ + /* Lets make sure there arent any erroneous audio tracks in the job list, so lets make sure its empty*/ + int audiotrack_count = hb_list_count(job->list_audio); + for( int i = 0; i < audiotrack_count;i++) + { + hb_audio_t * temp_audio = (hb_audio_t*) hb_list_item( job->list_audio, 0 ); + hb_list_rem(job->list_audio, temp_audio); + } + /* Now lets add our new tracks to the audio list here */ + if ([[queueToApply objectForKey:@"Audio1Track"] intValue] > 0) + { + audio = (hb_audio_config_t *) calloc(1, sizeof(*audio)); + hb_audio_config_init(audio); + audio->in.track = [[queueToApply objectForKey:@"Audio1Track"] intValue] - 1; + /* We go ahead and assign values to our audio->out.<properties> */ + audio->out.track = [[queueToApply objectForKey:@"Audio1Track"] intValue] - 1; + audio->out.codec = [[queueToApply objectForKey:@"JobAudio1Encoder"] intValue]; + audio->out.mixdown = [[queueToApply objectForKey:@"JobAudio1Mixdown"] intValue]; + audio->out.bitrate = [[queueToApply objectForKey:@"JobAudio1Bitrate"] intValue]; + audio->out.samplerate = [[queueToApply objectForKey:@"JobAudio1Samplerate"] intValue]; + audio->out.dynamic_range_compression = [[queueToApply objectForKey:@"Audio1TrackDRCSlider"] floatValue]; + + hb_audio_add( job, audio ); + free(audio); + } + if ([[queueToApply objectForKey:@"Audio2Track"] intValue] > 0) + { + + audio = (hb_audio_config_t *) calloc(1, sizeof(*audio)); + hb_audio_config_init(audio); + audio->in.track = [[queueToApply objectForKey:@"Audio2Track"] intValue] - 1; + [self writeToActivityLog: "prepareJob audiotrack 2 is: %d", audio->in.track]; + /* We go ahead and assign values to our audio->out.<properties> */ + audio->out.track = [[queueToApply objectForKey:@"Audio2Track"] intValue] - 1; + audio->out.codec = [[queueToApply objectForKey:@"JobAudio2Encoder"] intValue]; + audio->out.mixdown = [[queueToApply objectForKey:@"JobAudio2Mixdown"] intValue]; + audio->out.bitrate = [[queueToApply objectForKey:@"JobAudio2Bitrate"] intValue]; + audio->out.samplerate = [[queueToApply objectForKey:@"JobAudio2Samplerate"] intValue]; + audio->out.dynamic_range_compression = [[queueToApply objectForKey:@"Audio2TrackDRCSlider"] floatValue]; + + hb_audio_add( job, audio ); + free(audio); + } + + if ([[queueToApply objectForKey:@"Audio3Track"] intValue] > 0) + { + audio = (hb_audio_config_t *) calloc(1, sizeof(*audio)); + hb_audio_config_init(audio); + audio->in.track = [[queueToApply objectForKey:@"Audio3Track"] intValue] - 1; + /* We go ahead and assign values to our audio->out.<properties> */ + audio->out.track = [[queueToApply objectForKey:@"Audio3Track"] intValue] - 1; + audio->out.codec = [[queueToApply objectForKey:@"JobAudio3Encoder"] intValue]; + audio->out.mixdown = [[queueToApply objectForKey:@"JobAudio3Mixdown"] intValue]; + audio->out.bitrate = [[queueToApply objectForKey:@"JobAudio3Bitrate"] intValue]; + audio->out.samplerate = [[queueToApply objectForKey:@"JobAudio3Samplerate"] intValue]; + audio->out.dynamic_range_compression = [[queueToApply objectForKey:@"Audio3TrackDRCSlider"] floatValue]; + + hb_audio_add( job, audio ); + free(audio); + } + + if ([[queueToApply objectForKey:@"Audio4Track"] intValue] > 0) + { + audio = (hb_audio_config_t *) calloc(1, sizeof(*audio)); + hb_audio_config_init(audio); + audio->in.track = [[queueToApply objectForKey:@"Audio4Track"] intValue] - 1; + /* We go ahead and assign values to our audio->out.<properties> */ + audio->out.track = [[queueToApply objectForKey:@"Audio4Track"] intValue] - 1; + audio->out.codec = [[queueToApply objectForKey:@"JobAudio4Encoder"] intValue]; + audio->out.mixdown = [[queueToApply objectForKey:@"JobAudio4Mixdown"] intValue]; + audio->out.bitrate = [[queueToApply objectForKey:@"JobAudio4Bitrate"] intValue]; + audio->out.samplerate = [[queueToApply objectForKey:@"JobAudio4Samplerate"] intValue]; + audio->out.dynamic_range_compression = [[queueToApply objectForKey:@"Audio4TrackDRCSlider"] floatValue]; + + hb_audio_add( job, audio ); + + + } + + /* Filters */ + job->filters = hb_list_init(); + + /* Now lets call the filters if applicable. + * The order of the filters is critical + */ + /* Detelecine */ + if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] == 1) + { + /* use a custom detelecine string */ + hb_filter_detelecine.settings = (char *) [[queueToApply objectForKey:@"PictureDetelecineCustom"] UTF8String]; + hb_list_add( job->filters, &hb_filter_detelecine ); + } + if ([[queueToApply objectForKey:@"PictureDetelecine"] intValue] == 2) + { + /* Use libhb's default values */ + hb_list_add( job->filters, &hb_filter_detelecine ); + } + + if ([[queueToApply objectForKey:@"PictureDecombDeinterlace"] intValue] == 1) + { + /* Decomb */ + /* we add the custom string if present */ + if ([[queueToApply objectForKey:@"PictureDecomb"] intValue] == 1) + { + /* use a custom decomb string */ + hb_filter_decomb.settings = (char *) [[queueToApply objectForKey:@"PictureDecombCustom"] UTF8String]; + hb_list_add( job->filters, &hb_filter_decomb ); + } + if ([[queueToApply objectForKey:@"PictureDecomb"] intValue] == 2) + { + /* Use libhb default */ + hb_list_add( job->filters, &hb_filter_decomb ); + } + + } + else + { + + /* Deinterlace */ + if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 1) + { + /* we add the custom string if present */ + hb_filter_deinterlace.settings = (char *) [[queueToApply objectForKey:@"PictureDeinterlaceCustom"] UTF8String]; + hb_list_add( job->filters, &hb_filter_deinterlace ); + } + else if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 2) + { + /* Run old deinterlacer fd by default */ + hb_filter_deinterlace.settings = "-1"; + hb_list_add( job->filters, &hb_filter_deinterlace ); + } + else if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 3) + { + /* Yadif mode 0 (without spatial deinterlacing.) */ + hb_filter_deinterlace.settings = "2"; + hb_list_add( job->filters, &hb_filter_deinterlace ); + } + else if ([[queueToApply objectForKey:@"PictureDeinterlace"] intValue] == 4) + { + /* Yadif (with spatial deinterlacing) */ + hb_filter_deinterlace.settings = "0"; + hb_list_add( job->filters, &hb_filter_deinterlace ); + } + + + } + /* Denoise */ + if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 1) // Custom in popup + { + /* we add the custom string if present */ + hb_filter_denoise.settings = (char *) [[queueToApply objectForKey:@"PictureDenoiseCustom"] UTF8String]; + hb_list_add( job->filters, &hb_filter_denoise ); + } + else if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 2) // Weak in popup + { + hb_filter_denoise.settings = "2:1:2:3"; + hb_list_add( job->filters, &hb_filter_denoise ); + } + else if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 3) // Medium in popup + { + hb_filter_denoise.settings = "3:2:2:3"; + hb_list_add( job->filters, &hb_filter_denoise ); + } + else if ([[queueToApply objectForKey:@"PictureDenoise"] intValue] == 4) // Strong in popup + { + hb_filter_denoise.settings = "7:7:5:5"; + hb_list_add( job->filters, &hb_filter_denoise ); + } + + + /* Deblock (uses pp7 default) */ + /* NOTE: even though there is a valid deblock setting of 0 for the filter, for + * the macgui's purposes a value of 0 actually means to not even use the filter + * current hb_filter_deblock.settings valid ranges are from 5 - 15 + */ + if ([[queueToApply objectForKey:@"PictureDeblock"] intValue] != 0) + { + hb_filter_deblock.settings = (char *) [[queueToApply objectForKey:@"PictureDeblock"] UTF8String]; + hb_list_add( job->filters, &hb_filter_deblock ); + } +[self writeToActivityLog: "prepareJob exiting"]; +} + + + +/* addToQueue: puts up an alert before ultimately calling doAddToQueue +*/ +- (IBAction) addToQueue: (id) sender +{ + /* We get the destination directory from the destination field here */ + NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent]; + /* We check for a valid destination here */ + if ([[NSFileManager defaultManager] fileExistsAtPath:destinationDirectory] == 0) + { + NSRunAlertPanel(@"Warning!", @"This is not a valid destination directory!", @"OK", nil, nil); + return; + } + + BOOL fileExists; + fileExists = NO; + + BOOL fileExistsInQueue; + fileExistsInQueue = NO; + + /* We check for and existing file here */ + if([[NSFileManager defaultManager] fileExistsAtPath: [fDstFile2Field stringValue]]) + { + fileExists = YES; + } + + /* We now run through the queue and make sure we are not overwriting an exisiting queue item */ + int i = 0; + NSEnumerator *enumerator = [QueueFileArray objectEnumerator]; + id tempObject; + while (tempObject = [enumerator nextObject]) + { + NSDictionary *thisQueueDict = tempObject; + if ([[thisQueueDict objectForKey:@"DestinationPath"] isEqualToString: [fDstFile2Field stringValue]]) + { + fileExistsInQueue = YES; + } + i++; + } + + + if(fileExists == YES) + { + NSBeginCriticalAlertSheet( NSLocalizedString( @"File already exists.", @"" ), + NSLocalizedString( @"Cancel", @"" ), NSLocalizedString( @"Overwrite", @"" ), nil, fWindow, self, + @selector( overwriteAddToQueueAlertDone:returnCode:contextInfo: ), + NULL, NULL, [NSString stringWithFormat: + NSLocalizedString( @"Do you want to overwrite %@?", @"" ), + [fDstFile2Field stringValue]] ); + } + else if (fileExistsInQueue == YES) + { + NSBeginCriticalAlertSheet( NSLocalizedString( @"There is already a queue item for this destination.", @"" ), + NSLocalizedString( @"Cancel", @"" ), NSLocalizedString( @"Overwrite", @"" ), nil, fWindow, self, + @selector( overwriteAddToQueueAlertDone:returnCode:contextInfo: ), + NULL, NULL, [NSString stringWithFormat: + NSLocalizedString( @"Do you want to overwrite %@?", @"" ), + [fDstFile2Field stringValue]] ); + } + else + { + [self doAddToQueue]; + } +} + +/* overwriteAddToQueueAlertDone: called from the alert posted by addToQueue that asks + the user if they want to overwrite an exiting movie file. +*/ +- (void) overwriteAddToQueueAlertDone: (NSWindow *) sheet + returnCode: (int) returnCode contextInfo: (void *) contextInfo +{ + if( returnCode == NSAlertAlternateReturn ) + [self doAddToQueue]; +} + +- (void) doAddToQueue +{ + [self addQueueFileItem ]; +} + + + +/* Rip: puts up an alert before ultimately calling doRip +*/ +- (IBAction) Rip: (id) sender +{ + [self writeToActivityLog: "Rip: Pending queue count is %d", fPendingCount]; + /* Rip or Cancel ? */ + hb_state_t s; + hb_get_state2( fQueueEncodeLibhb, &s ); + + if(s.state == HB_STATE_WORKING || s.state == HB_STATE_PAUSED) + { + [self Cancel: sender]; + return; + } + + /* We check to see if we need to warn the user that the computer will go to sleep + or shut down when encoding is finished */ + [self remindUserOfSleepOrShutdown]; + + // If there are pending jobs in the queue, then this is a rip the queue + if (fPendingCount > 0) + { + /* here lets start the queue with the first pending item */ + [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]]; + + return; + } + + // Before adding jobs to the queue, check for a valid destination. + + NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent]; + if ([[NSFileManager defaultManager] fileExistsAtPath:destinationDirectory] == 0) + { + NSRunAlertPanel(@"Warning!", @"This is not a valid destination directory!", @"OK", nil, nil); + return; + } + + /* We check for duplicate name here */ + if( [[NSFileManager defaultManager] fileExistsAtPath:[fDstFile2Field stringValue]] ) + { + NSBeginCriticalAlertSheet( NSLocalizedString( @"File already exists", @"" ), + NSLocalizedString( @"Cancel", "" ), NSLocalizedString( @"Overwrite", @"" ), nil, fWindow, self, + @selector( overWriteAlertDone:returnCode:contextInfo: ), + NULL, NULL, [NSString stringWithFormat: + NSLocalizedString( @"Do you want to overwrite %@?", @"" ), + [fDstFile2Field stringValue]] ); + + // overWriteAlertDone: will be called when the alert is dismissed. It will call doRip. + } + else + { + /* if there are no pending jobs in the queue, then add this one to the queue and rip + otherwise, just rip the queue */ + if(fPendingCount == 0) + { + [self doAddToQueue]; + } + + /* go right to processing the new queue encode */ + [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]]; + + } +} + +/* overWriteAlertDone: called from the alert posted by Rip: that asks the user if they + want to overwrite an exiting movie file. +*/ +- (void) overWriteAlertDone: (NSWindow *) sheet + returnCode: (int) returnCode contextInfo: (void *) contextInfo +{ + if( returnCode == NSAlertAlternateReturn ) + { + /* if there are no jobs in the queue, then add this one to the queue and rip + otherwise, just rip the queue */ + if( fPendingCount == 0 ) + { + [self doAddToQueue]; + } + + NSString *destinationDirectory = [[fDstFile2Field stringValue] stringByDeletingLastPathComponent]; + [[NSUserDefaults standardUserDefaults] setObject:destinationDirectory forKey:@"LastDestinationDirectory"]; + [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]]; + + } +} + +- (void) remindUserOfSleepOrShutdown +{ + if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Put Computer To Sleep"]) + { + /*Warn that computer will sleep after encoding*/ + int reminduser; + NSBeep(); + 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); + [NSApp requestUserAttention:NSCriticalRequest]; + if ( reminduser == NSAlertAlternateReturn ) + { + [self showPreferencesWindow:nil]; + } + } + else if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AlertWhenDone"] isEqualToString: @"Shut Down Computer"]) + { + /*Warn that computer will shut down after encoding*/ + int reminduser; + NSBeep(); + 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); + [NSApp requestUserAttention:NSCriticalRequest]; + if ( reminduser == NSAlertAlternateReturn ) + { + [self showPreferencesWindow:nil]; + } + } + +} + + +- (void) doRip +{ + /* Let libhb do the job */ + hb_start( fQueueEncodeLibhb ); + /*set the fEncodeState State */ + fEncodeState = 1; +} + + +//------------------------------------------------------------------------------------ +// Displays an alert asking user if the want to cancel encoding of current job. +// Cancel: returns immediately after posting the alert. Later, when the user +// acknowledges the alert, doCancelCurrentJob is called. +//------------------------------------------------------------------------------------ +- (IBAction)Cancel: (id)sender +{ + if (!fQueueController) return; + + hb_pause( fQueueEncodeLibhb ); + NSString * alertTitle = [NSString stringWithFormat:NSLocalizedString(@"You are currently encoding. What would you like to do ?", nil)]; + + // Which window to attach the sheet to? + NSWindow * docWindow; + if ([sender respondsToSelector: @selector(window)]) + docWindow = [sender window]; + else + docWindow = fWindow; + + NSBeginCriticalAlertSheet( + alertTitle, + NSLocalizedString(@"Continue Encoding", nil), + NSLocalizedString(@"Cancel Current and Stop", nil), + NSLocalizedString(@"Cancel Current and Continue", nil), + docWindow, self, + nil, @selector(didDimissCancel:returnCode:contextInfo:), nil, + NSLocalizedString(@"Your encode will be cancelled if you don't continue encoding.", nil)); + + // didDimissCancelCurrentJob:returnCode:contextInfo: will be called when the dialog is dismissed +} + +- (void) didDimissCancel: (NSWindow *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo +{ + hb_resume( fQueueEncodeLibhb ); + if (returnCode == NSAlertOtherReturn) + { + [self doCancelCurrentJob]; // <- this also stops libhb + } + if (returnCode == NSAlertAlternateReturn) + { + [self doCancelCurrentJobAndStop]; + } +} + +//------------------------------------------------------------------------------------ +// Cancels and deletes the current job and stops libhb from processing the remaining +// encodes. +//------------------------------------------------------------------------------------ +- (void) doCancelCurrentJob +{ + // Stop the current job. hb_stop will only cancel the current pass and then set + // its state to HB_STATE_WORKDONE. It also does this asynchronously. So when we + // see the state has changed to HB_STATE_WORKDONE (in updateUI), we'll delete the + // remaining passes of the job and then start the queue back up if there are any + // remaining jobs. + + + hb_stop( fQueueEncodeLibhb ); + + // Delete all remaining jobs since libhb doesn't do this on its own. + hb_job_t * job; + while( ( job = hb_job(fQueueEncodeLibhb, 0) ) ) + hb_rem( fQueueEncodeLibhb, job ); + + fEncodeState = 2; // don't alert at end of processing since this was a cancel + + // now that we've stopped the currently encoding job, lets mark it as cancelled + [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] setObject:[NSNumber numberWithInt:3] forKey:@"Status"]; + // and as always, save it in the queue .plist... + /* We save all of the Queue data here */ + [self saveQueueFileItem]; + // so now lets move to + currentQueueEncodeIndex++ ; + // ... and see if there are more items left in our queue + int queueItems = [QueueFileArray count]; + /* If we still have more items in our queue, lets go to the next one */ + if (currentQueueEncodeIndex < queueItems) + { + [self writeToActivityLog: "doCancelCurrentJob currentQueueEncodeIndex is incremented to: %d", currentQueueEncodeIndex]; + [self writeToActivityLog: "doCancelCurrentJob moving to the next job"]; + + [self performNewQueueScan:[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"SourcePath"] scanTitleNum:[[[QueueFileArray objectAtIndex:currentQueueEncodeIndex] objectForKey:@"TitleNumber"]intValue]]; + } + else + { + [self writeToActivityLog: "doCancelCurrentJob the item queue is complete"]; + } + +} + +- (void) doCancelCurrentJobAndStop +{ + hb_stop( fQueueEncodeLibhb ); + + // Delete all remaining jobs since libhb doesn't do this on its own. + hb_job_t * job; + while( ( job = hb_job(fQueueEncodeLibhb, 0) ) ) + hb_rem( fQueueEncodeLibhb, job ); + + + fEncodeState = 2; // don't alert at end of processing since this was a cancel + + // now that we've stopped the currently encoding job, lets mark it as cancelled + [[QueueFileArray objectAtIndex:currentQueueEncodeIndex] setObject:[NSNumber numberWithInt:3] forKey:@"Status"]; + // and as always, save it in the queue .plist... + /* We save all of the Queue data here */ + [self saveQueueFileItem]; + // so now lets move to + currentQueueEncodeIndex++ ; + [self writeToActivityLog: "cancelling current job and stopping the queue"]; +} +- (IBAction) Pause: (id) sender +{ + hb_state_t s; + hb_get_state2( fQueueEncodeLibhb, &s ); + + if( s.state == HB_STATE_PAUSED ) + { + hb_resume( fQueueEncodeLibhb ); + } + else + { + hb_pause( fQueueEncodeLibhb ); + } +} + +#pragma mark - +#pragma mark GUI Controls Changed Methods + +- (IBAction) titlePopUpChanged: (id) sender +{ + hb_list_t * list = hb_get_titles( fHandle ); + hb_title_t * title = (hb_title_t*) + hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] ); + + /* If Auto Naming is on. We create an output filename of dvd name - title number */ + if( [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultAutoNaming"] > 0 && ( hb_list_count( list ) > 1 ) ) + { + [fDstFile2Field setStringValue: [NSString stringWithFormat: + @"%@/%@-%d.%@", [[fDstFile2Field stringValue] stringByDeletingLastPathComponent], + [browsedSourceDisplayName stringByDeletingPathExtension], + title->index, + [[fDstFile2Field stringValue] pathExtension]]]; + } + + /* Update chapter popups */ + [fSrcChapterStartPopUp removeAllItems]; + [fSrcChapterEndPopUp removeAllItems]; + for( int i = 0; i < hb_list_count( title->list_chapter ); i++ ) + { + [fSrcChapterStartPopUp addItemWithTitle: [NSString + stringWithFormat: @"%d", i + 1]]; + [fSrcChapterEndPopUp addItemWithTitle: [NSString + stringWithFormat: @"%d", i + 1]]; + } + + [fSrcChapterStartPopUp selectItemAtIndex: 0]; + [fSrcChapterEndPopUp selectItemAtIndex: + hb_list_count( title->list_chapter ) - 1]; + [self chapterPopUpChanged:nil]; + + /* if using dvd nav, show the angle widget */ + if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"UseDvdNav"] boolValue]) + { + [fSrcAngleLabel setHidden:NO]; + [fSrcAnglePopUp setHidden:NO]; + + [fSrcAnglePopUp removeAllItems]; + for( int i = 0; i < title->angle_count; i++ ) + { + [fSrcAnglePopUp addItemWithTitle: [NSString stringWithFormat: @"%d", i + 1]]; + } + [fSrcAnglePopUp selectItemAtIndex: 0]; + } + else + { + [fSrcAngleLabel setHidden:YES]; + [fSrcAnglePopUp setHidden:YES]; + } + + /* Start Get and set the initial pic size for display */ + hb_job_t * job = title->job; + fTitle = title; + + /* Set Auto Crop to on upon selecting a new title */ + [fPictureController setAutoCrop:YES]; + + /* We get the originial output picture width and height and put them + in variables for use with some presets later on */ + PicOrigOutputWidth = job->width; + PicOrigOutputHeight = job->height; + AutoCropTop = job->crop[0]; + AutoCropBottom = job->crop[1]; + AutoCropLeft = job->crop[2]; + AutoCropRight = job->crop[3]; + + /* Reset the new title in fPictureController && fPreviewController*/ + [fPictureController SetTitle:title]; + + + /* Update Subtitle Table */ + [fSubtitlesDelegate resetWithTitle:title]; + [fSubtitlesTable reloadData]; + + + /* Update chapter table */ + [fChapterTitlesDelegate resetWithTitle:title]; + [fChapterTable reloadData]; + + /* Lets make sure there arent any erroneous audio tracks in the job list, so lets make sure its empty*/ + int audiotrack_count = hb_list_count(job->list_audio); + for( int i = 0; i < audiotrack_count;i++) + { + hb_audio_t * temp_audio = (hb_audio_t*) hb_list_item( job->list_audio, 0 ); + hb_list_rem(job->list_audio, temp_audio); + } + + /* Update audio popups */ + [self addAllAudioTracksToPopUp: fAudLang1PopUp]; + [self addAllAudioTracksToPopUp: fAudLang2PopUp]; + [self addAllAudioTracksToPopUp: fAudLang3PopUp]; + [self addAllAudioTracksToPopUp: fAudLang4PopUp]; + /* search for the first instance of our prefs default language for track 1, and set track 2 to "none" */ + NSString * audioSearchPrefix = [[NSUserDefaults standardUserDefaults] stringForKey:@"DefaultLanguage"]; + [self selectAudioTrackInPopUp: fAudLang1PopUp searchPrefixString: audioSearchPrefix selectIndexIfNotFound: 1]; + [self selectAudioTrackInPopUp:fAudLang2PopUp searchPrefixString:nil selectIndexIfNotFound:0]; + [self selectAudioTrackInPopUp:fAudLang3PopUp searchPrefixString:nil selectIndexIfNotFound:0]; + [self selectAudioTrackInPopUp:fAudLang4PopUp searchPrefixString:nil selectIndexIfNotFound:0]; + + /* changing the title may have changed the audio channels on offer, */ + /* so call audioTrackPopUpChanged for both audio tracks to update the mixdown popups */ + [self audioTrackPopUpChanged: fAudLang1PopUp]; + [self audioTrackPopUpChanged: fAudLang2PopUp]; + [self audioTrackPopUpChanged: fAudLang3PopUp]; + [self audioTrackPopUpChanged: fAudLang4PopUp]; + + [fVidRatePopUp selectItemAtIndex: 0]; + + /* we run the picture size values through calculatePictureSizing to get all picture setting information*/ + [self calculatePictureSizing:nil]; + + /* lets call tableViewSelected to make sure that any preset we have selected is enforced after a title change */ + [self selectPreset:nil]; +} + +- (IBAction) chapterPopUpChanged: (id) sender +{ + + /* If start chapter popup is greater than end chapter popup, + we set the end chapter popup to the same as start chapter popup */ + if ([fSrcChapterStartPopUp indexOfSelectedItem] > [fSrcChapterEndPopUp indexOfSelectedItem]) + { + [fSrcChapterEndPopUp selectItemAtIndex: [fSrcChapterStartPopUp indexOfSelectedItem]]; + } + + + hb_list_t * list = hb_get_titles( fHandle ); + hb_title_t * title = (hb_title_t *) + hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] ); + + hb_chapter_t * chapter; + int64_t duration = 0; + for( int i = [fSrcChapterStartPopUp indexOfSelectedItem]; + i <= [fSrcChapterEndPopUp indexOfSelectedItem]; i++ ) + { + chapter = (hb_chapter_t *) hb_list_item( title->list_chapter, i ); + duration += chapter->duration; + } + + duration /= 90000; /* pts -> seconds */ + [fSrcDuration2Field setStringValue: [NSString stringWithFormat: + @"%02lld:%02lld:%02lld", duration / 3600, ( duration / 60 ) % 60, + duration % 60]]; + + [self calculateBitrate: sender]; + + if ( [fSrcChapterStartPopUp indexOfSelectedItem] == [fSrcChapterEndPopUp indexOfSelectedItem] ) + { + /* Disable chapter markers for any source with less than two chapters as it makes no sense. */ + [fCreateChapterMarkers setEnabled: NO]; + [fCreateChapterMarkers setState: NSOffState]; + } + else + { + [fCreateChapterMarkers setEnabled: YES]; + } +} + +- (IBAction) formatPopUpChanged: (id) sender +{ + NSString * string = [fDstFile2Field stringValue]; + int format = [fDstFormatPopUp indexOfSelectedItem]; + char * ext = NULL; + /* Initially set the large file (64 bit formatting) output checkbox to hidden */ + [fDstMp4LargeFileCheck setHidden: YES]; + [fDstMp4HttpOptFileCheck setHidden: YES]; + [fDstMp4iPodFileCheck setHidden: YES]; + + /* Update the Video Codec PopUp */ + /* lets get the tag of the currently selected item first so we might reset it later */ + int selectedVidEncoderTag; + selectedVidEncoderTag = [[fVidEncoderPopUp selectedItem] tag]; + + /* Note: we now store the video encoder int values from common.c in the tags of each popup for easy retrieval later */ + [fVidEncoderPopUp removeAllItems]; + NSMenuItem *menuItem; + /* These video encoders are available to all of our current muxers, so lets list them once here */ + menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"MPEG-4 (FFmpeg)" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_VCODEC_FFMPEG]; + + switch( format ) + { + case 0: + /*Get Default MP4 File Extension*/ + if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultMpegName"] > 0) + { + ext = "m4v"; + } + else + { + ext = "mp4"; + } + /* Add additional video encoders here */ + menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"H.264 (x264)" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_VCODEC_X264]; + /* We show the mp4 option checkboxes here since we are mp4 */ + [fCreateChapterMarkers setEnabled: YES]; + [fDstMp4LargeFileCheck setHidden: NO]; + [fDstMp4HttpOptFileCheck setHidden: NO]; + [fDstMp4iPodFileCheck setHidden: NO]; + break; + + case 1: + ext = "mkv"; + /* Add additional video encoders here */ + menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"H.264 (x264)" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_VCODEC_X264]; + menuItem = [[fVidEncoderPopUp menu] addItemWithTitle:@"VP3 (Theora)" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_VCODEC_THEORA]; + /* We enable the create chapters checkbox here */ + [fCreateChapterMarkers setEnabled: YES]; + break; + + + } + /* tell fSubtitlesDelegate we have a new video container */ + + [fSubtitlesDelegate containerChanged:[[fDstFormatPopUp selectedItem] tag]]; + [fSubtitlesTable reloadData]; + /* if we have a previously selected vid encoder tag, then try to select it */ + if (selectedVidEncoderTag) + { + [fVidEncoderPopUp selectItemWithTag: selectedVidEncoderTag]; + } + else + { + [fVidEncoderPopUp selectItemAtIndex: 0]; + } + + [self audioAddAudioTrackCodecs: fAudTrack1CodecPopUp]; + [self audioAddAudioTrackCodecs: fAudTrack2CodecPopUp]; + [self audioAddAudioTrackCodecs: fAudTrack3CodecPopUp]; + [self audioAddAudioTrackCodecs: fAudTrack4CodecPopUp]; + + if( format == 0 ) + [self autoSetM4vExtension: sender]; + else + [fDstFile2Field setStringValue: [NSString stringWithFormat:@"%@.%s", [string stringByDeletingPathExtension], ext]]; + + if( SuccessfulScan ) + { + /* Add/replace to the correct extension */ + [self audioTrackPopUpChanged: fAudLang1PopUp]; + [self audioTrackPopUpChanged: fAudLang2PopUp]; + [self audioTrackPopUpChanged: fAudLang3PopUp]; + [self audioTrackPopUpChanged: fAudLang4PopUp]; + + if( [fVidEncoderPopUp selectedItem] == nil ) + { + + [fVidEncoderPopUp selectItemAtIndex:0]; + [self videoEncoderPopUpChanged:nil]; + + /* changing the format may mean that we can / can't offer mono or 6ch, */ + /* so call audioTrackPopUpChanged for both audio tracks to update the mixdown popups */ + + /* We call the method to properly enable/disable turbo 2 pass */ + [self twoPassCheckboxChanged: sender]; + /* We call method method to change UI to reflect whether a preset is used or not*/ + } + } + [self customSettingUsed: sender]; +} + +- (IBAction) autoSetM4vExtension: (id) sender +{ + if ( [fDstFormatPopUp indexOfSelectedItem] ) + return; + + NSString * extension = @"mp4"; + + if( [[fAudTrack1CodecPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack2CodecPopUp selectedItem] tag] == HB_ACODEC_AC3 || + [[fAudTrack3CodecPopUp selectedItem] tag] == HB_ACODEC_AC3 || + [[fAudTrack4CodecPopUp selectedItem] tag] == HB_ACODEC_AC3 || + [fCreateChapterMarkers state] == NSOnState || + [[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultMpegName"] > 0 ) + { + extension = @"m4v"; + } + + if( [extension isEqualTo: [[fDstFile2Field stringValue] pathExtension]] ) + return; + else + [fDstFile2Field setStringValue: [NSString stringWithFormat:@"%@.%@", + [[fDstFile2Field stringValue] stringByDeletingPathExtension], extension]]; +} + +/* Method to determine if we should change the UI +To reflect whether or not a Preset is being used or if +the user is using "Custom" settings by determining the sender*/ +- (IBAction) customSettingUsed: (id) sender +{ + if ([sender stringValue]) + { + /* Deselect the currently selected Preset if there is one*/ + [fPresetsOutlineView deselectRow:[fPresetsOutlineView selectedRow]]; + /* Change UI to show "Custom" settings are being used */ + [fPresetSelectedDisplay setStringValue: @"Custom"]; + + curUserPresetChosenNum = nil; + } +[self calculateBitrate:nil]; +} + + +#pragma mark - +#pragma mark - Video + +- (IBAction) videoEncoderPopUpChanged: (id) sender +{ + hb_job_t * job = fTitle->job; + int videoEncoder = [[fVidEncoderPopUp selectedItem] tag]; + + [fAdvancedOptions setHidden:YES]; + /* If we are using x264 then show the x264 advanced panel*/ + if (videoEncoder == HB_VCODEC_X264) + { + [fAdvancedOptions setHidden:NO]; + [self autoSetM4vExtension: sender]; + } + + /* We need to set loose anamorphic as available depending on whether or not the ffmpeg encoder + is being used as it borks up loose anamorphic . + For convenience lets use the titleOfSelected index. Probably should revisit whether or not we want + to use the index itself but this is easier */ + if (videoEncoder == HB_VCODEC_FFMPEG) + { + if (job->anamorphic.mode == 2) + { + job->anamorphic.mode = 0; + } + [fPictureController setAllowLooseAnamorphic:NO]; + /* We set the iPod atom checkbox to disabled and uncheck it as its only for x264 in the mp4 + container. Format is taken care of in formatPopUpChanged method by hiding and unchecking + anything other than MP4. + */ + [fDstMp4iPodFileCheck setEnabled: NO]; + [fDstMp4iPodFileCheck setState: NSOffState]; + } + else + { + [fPictureController setAllowLooseAnamorphic:YES]; + [fDstMp4iPodFileCheck setEnabled: YES]; + } + [self setupQualitySlider]; + [self calculatePictureSizing: sender]; + [self twoPassCheckboxChanged: sender]; +} + + +- (IBAction) twoPassCheckboxChanged: (id) sender +{ + /* check to see if x264 is chosen */ + if([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_X264) + { + if( [fVidTwoPassCheck state] == NSOnState) + { + [fVidTurboPassCheck setHidden: NO]; + } + else + { + [fVidTurboPassCheck setHidden: YES]; + [fVidTurboPassCheck setState: NSOffState]; + } + /* Make sure Two Pass is checked if Turbo is checked */ + if( [fVidTurboPassCheck state] == NSOnState) + { + [fVidTwoPassCheck setState: NSOnState]; + } + } + else + { + [fVidTurboPassCheck setHidden: YES]; + [fVidTurboPassCheck setState: NSOffState]; + } + + /* We call method method to change UI to reflect whether a preset is used or not*/ + [self customSettingUsed: sender]; +} + +- (IBAction ) videoFrameRateChanged: (id) sender +{ + /* We call method method to calculatePictureSizing to error check detelecine*/ + [self calculatePictureSizing: sender]; + + /* We call method method to change UI to reflect whether a preset is used or not*/ + [self customSettingUsed: sender]; +} +- (IBAction) videoMatrixChanged: (id) sender; +{ + bool target, bitrate, quality; + + target = bitrate = quality = false; + if( [fVidQualityMatrix isEnabled] ) + { + switch( [fVidQualityMatrix selectedRow] ) + { + case 0: + target = true; + break; + case 1: + bitrate = true; + break; + case 2: + quality = true; + break; + } + } + [fVidTargetSizeField setEnabled: target]; + [fVidBitrateField setEnabled: bitrate]; + [fVidQualitySlider setEnabled: quality]; + [fVidQualityRFField setEnabled: quality]; + [fVidQualityRFLabel setEnabled: quality]; + [fVidTwoPassCheck setEnabled: !quality && + [fVidQualityMatrix isEnabled]]; + if( quality ) + { + [fVidTwoPassCheck setState: NSOffState]; + [fVidTurboPassCheck setHidden: YES]; + [fVidTurboPassCheck setState: NSOffState]; + } + + [self qualitySliderChanged: sender]; + [self calculateBitrate: sender]; + [self customSettingUsed: sender]; +} + +/* Use this method to setup the quality slider for cq/rf values depending on + * the video encoder selected. + */ +- (void) setupQualitySlider +{ + /* Get the current slider maxValue to check for a change in slider scale later + * so that we can choose a new similar value on the new slider scale */ + float previousMaxValue = [fVidQualitySlider maxValue]; + float previousPercentOfSliderScale = [fVidQualitySlider floatValue] / ([fVidQualitySlider maxValue] - [fVidQualitySlider minValue] + 1); + NSString * qpRFLabelString = @"QP:"; + /* x264 0-51 */ + if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_X264) + { + [fVidQualitySlider setMinValue:0.0]; + [fVidQualitySlider setMaxValue:51.0]; + /* As x264 allows for qp/rf values that are fractional, we get the value from the preferences */ + int fractionalGranularity = 1 / [[NSUserDefaults standardUserDefaults] floatForKey:@"x264CqSliderFractional"]; + [fVidQualitySlider setNumberOfTickMarks:(([fVidQualitySlider maxValue] - [fVidQualitySlider minValue]) * fractionalGranularity) + 1]; + if ([[NSUserDefaults standardUserDefaults] boolForKey:@"DefaultCrf"] > 0) + { + qpRFLabelString = @"RF:"; + } + } + /* ffmpeg 1-31 */ + if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_FFMPEG ) + { + [fVidQualitySlider setMinValue:1.0]; + [fVidQualitySlider setMaxValue:31.0]; + [fVidQualitySlider setNumberOfTickMarks:31]; + } + /* Theora 0-63 */ + if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_THEORA) + { + [fVidQualitySlider setMinValue:0.0]; + [fVidQualitySlider setMaxValue:63.0]; + [fVidQualitySlider setNumberOfTickMarks:64]; + } + [fVidQualityRFLabel setStringValue:qpRFLabelString]; + + /* check to see if we have changed slider scales */ + if (previousMaxValue != [fVidQualitySlider maxValue]) + { + /* if so, convert the old setting to the new scale as close as possible based on percentages */ + float rf = ([fVidQualitySlider maxValue] - [fVidQualitySlider minValue] + 1) * previousPercentOfSliderScale; + [fVidQualitySlider setFloatValue:rf]; + } + + [self qualitySliderChanged:nil]; +} + +- (IBAction) qualitySliderChanged: (id) sender +{ + /* Our constant quality slider is in a range based + * on each encoders qp/rf values. The range depends + * on the encoder. Also, the range is inverse of quality + * for all of the encoders *except* for theora + * (ie. as the "quality" goes up, the cq or rf value + * actually goes down). Since the IB sliders always set + * their max value at the right end of the slider, we + * will calculate the inverse, so as the slider floatValue + * goes up, we will show the inverse in the rf field + * so, the floatValue at the right for x264 would be 51 + * and our rf field needs to show 0 and vice versa. + */ + + float sliderRfInverse = ([fVidQualitySlider maxValue] - [fVidQualitySlider floatValue]) + [fVidQualitySlider minValue]; + /* If the encoder is theora, use the float, otherwise use the inverse float*/ + float sliderRfToPercent; + if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_THEORA) + { + [fVidQualityRFField setStringValue: [NSString stringWithFormat: @"%.2f", [fVidQualitySlider floatValue]]]; + sliderRfToPercent = [fVidQualityRFField floatValue] / ([fVidQualitySlider maxValue] - [fVidQualitySlider minValue]); + } + else + { + [fVidQualityRFField setStringValue: [NSString stringWithFormat: @"%.2f", sliderRfInverse]]; + sliderRfToPercent = ( ([fVidQualitySlider maxValue] - [fVidQualitySlider minValue]) - ([fVidQualityRFField floatValue] - [fVidQualitySlider minValue])) / ([fVidQualitySlider maxValue] - [fVidQualitySlider minValue]); + } + [fVidConstantCell setTitle: [NSString stringWithFormat: + NSLocalizedString( @"Constant quality: %.2f %%", @"" ), 100 * sliderRfToPercent]]; + + [self customSettingUsed: sender]; +} + +- (void) controlTextDidChange: (NSNotification *) notification +{ + [self calculateBitrate:nil]; +} + +- (IBAction) calculateBitrate: (id) sender +{ + if( !fHandle || [fVidQualityMatrix selectedRow] != 0 || !SuccessfulScan ) + { + return; + } + + hb_list_t * list = hb_get_titles( fHandle ); + hb_title_t * title = (hb_title_t *) hb_list_item( list, + [fSrcTitlePopUp indexOfSelectedItem] ); + hb_job_t * job = title->job; + hb_audio_config_t * audio; + /* For hb_calc_bitrate in addition to the Target Size in MB out of the + * Target Size Field, we also need the job info for the Muxer, the Chapters + * as well as all of the audio track info. + * This used to be accomplished by simply calling prepareJob here, however + * since the resilient queue sets the queue array values instead of the job + * values directly, we duplicate the old prepareJob code here for the variables + * needed + */ + job->chapter_start = [fSrcChapterStartPopUp indexOfSelectedItem] + 1; + job->chapter_end = [fSrcChapterEndPopUp indexOfSelectedItem] + 1; + job->mux = [[fDstFormatPopUp selectedItem] tag]; + + /* Audio goes here */ + int audiotrack_count = hb_list_count(job->list_audio); + for( int i = 0; i < audiotrack_count;i++) + { + hb_audio_t * temp_audio = (hb_audio_t*) hb_list_item( job->list_audio, 0 ); + hb_list_rem(job->list_audio, temp_audio); + } + /* Now we need our audio info here for each track if applicable */ + if ([fAudLang1PopUp indexOfSelectedItem] > 0) + { + audio = (hb_audio_config_t *) calloc(1, sizeof(*audio)); + hb_audio_config_init(audio); + audio->in.track = [fAudLang1PopUp indexOfSelectedItem] - 1; + /* We go ahead and assign values to our audio->out.<properties> */ + audio->out.track = [fAudLang1PopUp indexOfSelectedItem] - 1; + audio->out.codec = [[fAudTrack1CodecPopUp selectedItem] tag]; + audio->out.mixdown = [[fAudTrack1MixPopUp selectedItem] tag]; + audio->out.bitrate = [[fAudTrack1BitratePopUp selectedItem] tag]; + audio->out.samplerate = [[fAudTrack1RatePopUp selectedItem] tag]; + audio->out.dynamic_range_compression = [fAudTrack1DrcField floatValue]; + + hb_audio_add( job, audio ); + free(audio); + } + if ([fAudLang2PopUp indexOfSelectedItem] > 0) + { + audio = (hb_audio_config_t *) calloc(1, sizeof(*audio)); + hb_audio_config_init(audio); + audio->in.track = [fAudLang2PopUp indexOfSelectedItem] - 1; + /* We go ahead and assign values to our audio->out.<properties> */ + audio->out.track = [fAudLang2PopUp indexOfSelectedItem] - 1; + audio->out.codec = [[fAudTrack2CodecPopUp selectedItem] tag]; + audio->out.mixdown = [[fAudTrack2MixPopUp selectedItem] tag]; + audio->out.bitrate = [[fAudTrack2BitratePopUp selectedItem] tag]; + audio->out.samplerate = [[fAudTrack2RatePopUp selectedItem] tag]; + audio->out.dynamic_range_compression = [fAudTrack2DrcField floatValue]; + + hb_audio_add( job, audio ); + free(audio); + + } + + if ([fAudLang3PopUp indexOfSelectedItem] > 0) + { + audio = (hb_audio_config_t *) calloc(1, sizeof(*audio)); + hb_audio_config_init(audio); + audio->in.track = [fAudLang3PopUp indexOfSelectedItem] - 1; + /* We go ahead and assign values to our audio->out.<properties> */ + audio->out.track = [fAudLang3PopUp indexOfSelectedItem] - 1; + audio->out.codec = [[fAudTrack3CodecPopUp selectedItem] tag]; + audio->out.mixdown = [[fAudTrack3MixPopUp selectedItem] tag]; + audio->out.bitrate = [[fAudTrack3BitratePopUp selectedItem] tag]; + audio->out.samplerate = [[fAudTrack3RatePopUp selectedItem] tag]; + audio->out.dynamic_range_compression = [fAudTrack3DrcField floatValue]; + + hb_audio_add( job, audio ); + free(audio); + + } + + if ([fAudLang4PopUp indexOfSelectedItem] > 0) + { + audio = (hb_audio_config_t *) calloc(1, sizeof(*audio)); + hb_audio_config_init(audio); + audio->in.track = [fAudLang4PopUp indexOfSelectedItem] - 1; + /* We go ahead and assign values to our audio->out.<properties> */ + audio->out.track = [fAudLang4PopUp indexOfSelectedItem] - 1; + audio->out.codec = [[fAudTrack4CodecPopUp selectedItem] tag]; + audio->out.mixdown = [[fAudTrack4MixPopUp selectedItem] tag]; + audio->out.bitrate = [[fAudTrack4BitratePopUp selectedItem] tag]; + audio->out.samplerate = [[fAudTrack4RatePopUp selectedItem] tag]; + audio->out.dynamic_range_compression = [fAudTrack4DrcField floatValue]; + + hb_audio_add( job, audio ); + free(audio); + + } + +[fVidBitrateField setIntValue: hb_calc_bitrate( job, [fVidTargetSizeField intValue] )]; +} + +#pragma mark - +#pragma mark - Picture + +/* lets set the picture size back to the max from right after title scan + Lets use an IBAction here as down the road we could always use a checkbox + in the gui to easily take the user back to max. Remember, the compiler + resolves IBActions down to -(void) during compile anyway */ +- (IBAction) revertPictureSizeToMax: (id) sender +{ + hb_job_t * job = fTitle->job; + /* Here we apply the title source and height */ + job->width = fTitle->width; + job->height = fTitle->height; + + [self calculatePictureSizing: sender]; + /* We call method to change UI to reflect whether a preset is used or not*/ + [self customSettingUsed: sender]; +} + +/** + * Registers changes made in the Picture Settings Window. + */ + +- (void)pictureSettingsDidChange +{ + [self calculatePictureSizing:nil]; +} + +/* Get and Display Current Pic Settings in main window */ +- (IBAction) calculatePictureSizing: (id) sender +{ + if (fTitle->job->anamorphic.mode > 0) + { + fTitle->job->keep_ratio = 0; + } + + [fPictureSizeField setStringValue: [NSString stringWithFormat:@"Picture Size: %@", [fPictureController getPictureSizeInfoString]]]; + + NSString *picCropping; + /* Set the display field for crop as per boolean */ + if (![fPictureController autoCrop]) + { + picCropping = @"Custom"; + } + else + { + picCropping = @"Auto"; + } + picCropping = [picCropping stringByAppendingString:[NSString stringWithFormat:@" %d/%d/%d/%d",fTitle->job->crop[0],fTitle->job->crop[1],fTitle->job->crop[2],fTitle->job->crop[3]]]; + + [fPictureCroppingField setStringValue: [NSString stringWithFormat:@"Picture Cropping: %@",picCropping]]; + + NSString *videoFilters; + videoFilters = @""; + /* Detelecine */ + if ([fPictureController detelecine] == 2) + { + videoFilters = [videoFilters stringByAppendingString:@" - Detelecine (Default)"]; + } + else if ([fPictureController detelecine] == 1) + { + videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Detelecine (%@)",[fPictureController detelecineCustomString]]]; + } + + + if ([fPictureController useDecomb] == 1) + { + /* Decomb */ + if ([fPictureController decomb] == 2) + { + videoFilters = [videoFilters stringByAppendingString:@" - Decomb (Default)"]; + } + else if ([fPictureController decomb] == 1) + { + videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Decomb (%@)",[fPictureController decombCustomString]]]; + } + } + else + { + /* Deinterlace */ + if ([fPictureController deinterlace] > 0) + { + fTitle->job->deinterlace = 1; + } + else + { + fTitle->job->deinterlace = 0; + } + + if ([fPictureController deinterlace] == 2) + { + videoFilters = [videoFilters stringByAppendingString:@" - Deinterlace (Fast)"]; + } + else if ([fPictureController deinterlace] == 3) + { + videoFilters = [videoFilters stringByAppendingString:@" - Deinterlace (Slow)"]; + } + else if ([fPictureController deinterlace] == 4) + { + videoFilters = [videoFilters stringByAppendingString:@" - Deinterlace (Slower)"]; + } + else if ([fPictureController deinterlace] == 1) + { + videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Deinterlace (%@)",[fPictureController deinterlaceCustomString]]]; + } + } + + + /* Denoise */ + if ([fPictureController denoise] == 2) + { + videoFilters = [videoFilters stringByAppendingString:@" - Denoise (Weak)"]; + } + else if ([fPictureController denoise] == 3) + { + videoFilters = [videoFilters stringByAppendingString:@" - Denoise (Medium)"]; + } + else if ([fPictureController denoise] == 4) + { + videoFilters = [videoFilters stringByAppendingString:@" - Denoise (Strong)"]; + } + else if ([fPictureController denoise] == 1) + { + videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Denoise (%@)",[fPictureController denoiseCustomString]]]; + } + + /* Deblock */ + if ([fPictureController deblock] > 0) + { + videoFilters = [videoFilters stringByAppendingString:[NSString stringWithFormat:@" - Deblock (%d)",[fPictureController deblock]]]; + } + + /* Grayscale */ + if ([fPictureController grayscale]) + { + videoFilters = [videoFilters stringByAppendingString:@" - Grayscale"]; + } + [fVideoFiltersField setStringValue: [NSString stringWithFormat:@"Video Filters: %@", videoFilters]]; + + //[fPictureController reloadStillPreview]; +} + + +#pragma mark - +#pragma mark - Audio and Subtitles +- (IBAction) audioCodecsPopUpChanged: (id) sender +{ + + NSPopUpButton * audiotrackPopUp; + NSPopUpButton * sampleratePopUp; + NSPopUpButton * bitratePopUp; + NSPopUpButton * audiocodecPopUp; + if (sender == fAudTrack1CodecPopUp) + { + audiotrackPopUp = fAudLang1PopUp; + audiocodecPopUp = fAudTrack1CodecPopUp; + sampleratePopUp = fAudTrack1RatePopUp; + bitratePopUp = fAudTrack1BitratePopUp; + } + else if (sender == fAudTrack2CodecPopUp) + { + audiotrackPopUp = fAudLang2PopUp; + audiocodecPopUp = fAudTrack2CodecPopUp; + sampleratePopUp = fAudTrack2RatePopUp; + bitratePopUp = fAudTrack2BitratePopUp; + } + else if (sender == fAudTrack3CodecPopUp) + { + audiotrackPopUp = fAudLang3PopUp; + audiocodecPopUp = fAudTrack3CodecPopUp; + sampleratePopUp = fAudTrack3RatePopUp; + bitratePopUp = fAudTrack3BitratePopUp; + } + else + { + audiotrackPopUp = fAudLang4PopUp; + audiocodecPopUp = fAudTrack4CodecPopUp; + sampleratePopUp = fAudTrack4RatePopUp; + bitratePopUp = fAudTrack4BitratePopUp; + } + + /* changing the codecs on offer may mean that we can / can't offer mono or 6ch, */ + /* so call audioTrackPopUpChanged for both audio tracks to update the mixdown popups */ + [self audioTrackPopUpChanged: audiotrackPopUp]; + +} + +- (IBAction) setEnabledStateOfAudioMixdownControls: (id) sender +{ + /* We will be setting the enabled/disabled state of each tracks audio controls based on + * the settings of the source audio for that track. We leave the samplerate and bitrate + * to audiotrackMixdownChanged + */ + + /* We will first verify that a lower track number has been selected before enabling each track + * for example, make sure a track is selected for track 1 before enabling track 2, etc. + */ + if ([fAudLang1PopUp indexOfSelectedItem] == 0) + { + [fAudLang2PopUp setEnabled: NO]; + [fAudLang2PopUp selectItemAtIndex: 0]; + } + else + { + [fAudLang2PopUp setEnabled: YES]; + } + + if ([fAudLang2PopUp indexOfSelectedItem] == 0) + { + [fAudLang3PopUp setEnabled: NO]; + [fAudLang3PopUp selectItemAtIndex: 0]; + } + else + { + [fAudLang3PopUp setEnabled: YES]; + } + if ([fAudLang3PopUp indexOfSelectedItem] == 0) + { + [fAudLang4PopUp setEnabled: NO]; + [fAudLang4PopUp selectItemAtIndex: 0]; + } + else + { + [fAudLang4PopUp setEnabled: YES]; + } + /* enable/disable the mixdown text and popupbutton for audio track 1 */ + [fAudTrack1CodecPopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES]; + [fAudTrack1MixPopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES]; + [fAudTrack1RatePopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES]; + [fAudTrack1BitratePopUp setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES]; + [fAudTrack1DrcSlider setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES]; + [fAudTrack1DrcField setEnabled: ([fAudLang1PopUp indexOfSelectedItem] == 0) ? NO : YES]; + if ([fAudLang1PopUp indexOfSelectedItem] == 0) + { + [fAudTrack1CodecPopUp removeAllItems]; + [fAudTrack1MixPopUp removeAllItems]; + [fAudTrack1RatePopUp removeAllItems]; + [fAudTrack1BitratePopUp removeAllItems]; + [fAudTrack1DrcSlider setFloatValue: 1.00]; + [self audioDRCSliderChanged: fAudTrack1DrcSlider]; + } + else if ([[fAudTrack1MixPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack1MixPopUp selectedItem] tag] == HB_ACODEC_DCA) + { + [fAudTrack1RatePopUp setEnabled: NO]; + [fAudTrack1BitratePopUp setEnabled: NO]; + [fAudTrack1DrcSlider setEnabled: NO]; + [fAudTrack1DrcField setEnabled: NO]; + } + + /* enable/disable the mixdown text and popupbutton for audio track 2 */ + [fAudTrack2CodecPopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES]; + [fAudTrack2MixPopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES]; + [fAudTrack2RatePopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES]; + [fAudTrack2BitratePopUp setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES]; + [fAudTrack2DrcSlider setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES]; + [fAudTrack2DrcField setEnabled: ([fAudLang2PopUp indexOfSelectedItem] == 0) ? NO : YES]; + if ([fAudLang2PopUp indexOfSelectedItem] == 0) + { + [fAudTrack2CodecPopUp removeAllItems]; + [fAudTrack2MixPopUp removeAllItems]; + [fAudTrack2RatePopUp removeAllItems]; + [fAudTrack2BitratePopUp removeAllItems]; + [fAudTrack2DrcSlider setFloatValue: 1.00]; + [self audioDRCSliderChanged: fAudTrack2DrcSlider]; + } + else if ([[fAudTrack2MixPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack2MixPopUp selectedItem] tag] == HB_ACODEC_DCA) + { + [fAudTrack2RatePopUp setEnabled: NO]; + [fAudTrack2BitratePopUp setEnabled: NO]; + [fAudTrack2DrcSlider setEnabled: NO]; + [fAudTrack2DrcField setEnabled: NO]; + } + + /* enable/disable the mixdown text and popupbutton for audio track 3 */ + [fAudTrack3CodecPopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES]; + [fAudTrack3MixPopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES]; + [fAudTrack3RatePopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES]; + [fAudTrack3BitratePopUp setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES]; + [fAudTrack3DrcSlider setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES]; + [fAudTrack3DrcField setEnabled: ([fAudLang3PopUp indexOfSelectedItem] == 0) ? NO : YES]; + if ([fAudLang3PopUp indexOfSelectedItem] == 0) + { + [fAudTrack3CodecPopUp removeAllItems]; + [fAudTrack3MixPopUp removeAllItems]; + [fAudTrack3RatePopUp removeAllItems]; + [fAudTrack3BitratePopUp removeAllItems]; + [fAudTrack3DrcSlider setFloatValue: 1.00]; + [self audioDRCSliderChanged: fAudTrack3DrcSlider]; + } + else if ([[fAudTrack3MixPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack3MixPopUp selectedItem] tag] == HB_ACODEC_DCA) + { + [fAudTrack3RatePopUp setEnabled: NO]; + [fAudTrack3BitratePopUp setEnabled: NO]; + [fAudTrack3DrcSlider setEnabled: NO]; + [fAudTrack3DrcField setEnabled: NO]; + } + + /* enable/disable the mixdown text and popupbutton for audio track 4 */ + [fAudTrack4CodecPopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES]; + [fAudTrack4MixPopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES]; + [fAudTrack4RatePopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES]; + [fAudTrack4BitratePopUp setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES]; + [fAudTrack4DrcSlider setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES]; + [fAudTrack4DrcField setEnabled: ([fAudLang4PopUp indexOfSelectedItem] == 0) ? NO : YES]; + if ([fAudLang4PopUp indexOfSelectedItem] == 0) + { + [fAudTrack4CodecPopUp removeAllItems]; + [fAudTrack4MixPopUp removeAllItems]; + [fAudTrack4RatePopUp removeAllItems]; + [fAudTrack4BitratePopUp removeAllItems]; + [fAudTrack4DrcSlider setFloatValue: 1.00]; + [self audioDRCSliderChanged: fAudTrack4DrcSlider]; + } + else if ([[fAudTrack4MixPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[fAudTrack4MixPopUp selectedItem] tag] == HB_ACODEC_DCA) + { + [fAudTrack4RatePopUp setEnabled: NO]; + [fAudTrack4BitratePopUp setEnabled: NO]; + [fAudTrack4DrcSlider setEnabled: NO]; + [fAudTrack4DrcField setEnabled: NO]; + } + +} + +- (IBAction) addAllAudioTracksToPopUp: (id) sender +{ + + hb_list_t * list = hb_get_titles( fHandle ); + hb_title_t * title = (hb_title_t*) + hb_list_item( list, [fSrcTitlePopUp indexOfSelectedItem] ); + + hb_audio_config_t * audio; + + [sender removeAllItems]; + [sender addItemWithTitle: NSLocalizedString( @"None", @"" )]; + for( int i = 0; i < hb_list_count( title->list_audio ); i++ ) + { + audio = (hb_audio_config_t *) hb_list_audio_config_item( title->list_audio, i ); + [[sender menu] addItemWithTitle: + [NSString stringWithCString: audio->lang.description] + action: NULL keyEquivalent: @""]; + } + [sender selectItemAtIndex: 0]; + +} + +- (IBAction) selectAudioTrackInPopUp: (id) sender searchPrefixString: (NSString *) searchPrefixString selectIndexIfNotFound: (int) selectIndexIfNotFound +{ + + /* this method can be used to find a language, or a language-and-source-format combination, by passing in the appropriate string */ + /* e.g. to find the first French track, pass in an NSString * of "Francais" */ + /* e.g. to find the first English 5.1 AC3 track, pass in an NSString * of "English (AC3) (5.1 ch)" */ + /* if no matching track is found, then selectIndexIfNotFound is used to choose which track to select instead */ + + if (searchPrefixString) + { + + for( int i = 0; i < [sender numberOfItems]; i++ ) + { + /* Try to find the desired search string */ + if ([[[sender itemAtIndex: i] title] hasPrefix:searchPrefixString]) + { + [sender selectItemAtIndex: i]; + return; + } + } + /* couldn't find the string, so select the requested "search string not found" item */ + /* index of 0 means select the "none" item */ + /* index of 1 means select the first audio track */ + [sender selectItemAtIndex: selectIndexIfNotFound]; + } + else + { + /* if no search string is provided, then select the selectIndexIfNotFound item */ + [sender selectItemAtIndex: selectIndexIfNotFound]; + } + +} +- (IBAction) audioAddAudioTrackCodecs: (id)sender +{ + int format = [fDstFormatPopUp indexOfSelectedItem]; + + /* setup pointers to the appropriate popups for the correct track */ + NSPopUpButton * audiocodecPopUp; + NSPopUpButton * audiotrackPopUp; + if (sender == fAudTrack1CodecPopUp) + { + audiotrackPopUp = fAudLang1PopUp; + audiocodecPopUp = fAudTrack1CodecPopUp; + } + else if (sender == fAudTrack2CodecPopUp) + { + audiotrackPopUp = fAudLang2PopUp; + audiocodecPopUp = fAudTrack2CodecPopUp; + } + else if (sender == fAudTrack3CodecPopUp) + { + audiotrackPopUp = fAudLang3PopUp; + audiocodecPopUp = fAudTrack3CodecPopUp; + } + else + { + audiotrackPopUp = fAudLang4PopUp; + audiocodecPopUp = fAudTrack4CodecPopUp; + } + + [audiocodecPopUp removeAllItems]; + /* Make sure "None" isnt selected in the source track */ + if ([audiotrackPopUp indexOfSelectedItem] > 0) + { + [audiocodecPopUp setEnabled:YES]; + NSMenuItem *menuItem; + /* We setup our appropriate popups for codecs and put the int value in the popup tag for easy retrieval */ + switch( format ) + { + case 0: + /* MP4 */ + // FAAC + menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (faac)" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_ACODEC_FAAC]; + + // CA_AAC + menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (CoreAudio)" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_ACODEC_CA_AAC]; + + // AC3 Passthru + menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AC3 Passthru" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_ACODEC_AC3]; + break; + + case 1: + /* MKV */ + // FAAC + menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (faac)" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_ACODEC_FAAC]; + // CA_AAC + menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AAC (CoreAudio)" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_ACODEC_CA_AAC]; + // AC3 Passthru + menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AC3 Passthru" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_ACODEC_AC3]; + // DTS Passthru + menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"DTS Passthru" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_ACODEC_DCA]; + // MP3 + menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"MP3 (lame)" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_ACODEC_LAME]; + // Vorbis + menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"Vorbis (vorbis)" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_ACODEC_VORBIS]; + break; + + case 2: + /* AVI */ + // MP3 + menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"MP3 (lame)" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_ACODEC_LAME]; + // AC3 Passthru + menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"AC3 Passthru" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_ACODEC_AC3]; + break; + + case 3: + /* OGM */ + // Vorbis + menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"Vorbis (vorbis)" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_ACODEC_VORBIS]; + // MP3 + menuItem = [[audiocodecPopUp menu] addItemWithTitle:@"MP3 (lame)" action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_ACODEC_LAME]; + break; + } + [audiocodecPopUp selectItemAtIndex:0]; + } + else + { + [audiocodecPopUp setEnabled:NO]; + } +} + +- (IBAction) audioTrackPopUpChanged: (id) sender +{ + /* utility function to call audioTrackPopUpChanged without passing in a mixdown-to-use */ + [self audioTrackPopUpChanged: sender mixdownToUse: 0]; +} + +- (IBAction) audioTrackPopUpChanged: (id) sender mixdownToUse: (int) mixdownToUse +{ + + /* make sure we have a selected title before continuing */ + if (fTitle == NULL) return; + /* if the sender is the lanaguage popup and there is nothing in the codec popup, lets call + * audioAddAudioTrackCodecs on the codec popup to populate it properly before moving on + */ + if (sender == fAudLang1PopUp && [[fAudTrack1CodecPopUp menu] numberOfItems] == 0) + { + [self audioAddAudioTrackCodecs: fAudTrack1CodecPopUp]; + } + if (sender == fAudLang2PopUp && [[fAudTrack2CodecPopUp menu] numberOfItems] == 0) + { + [self audioAddAudioTrackCodecs: fAudTrack2CodecPopUp]; + } + if (sender == fAudLang3PopUp && [[fAudTrack3CodecPopUp menu] numberOfItems] == 0) + { + [self audioAddAudioTrackCodecs: fAudTrack3CodecPopUp]; + } + if (sender == fAudLang4PopUp && [[fAudTrack4CodecPopUp menu] numberOfItems] == 0) + { + [self audioAddAudioTrackCodecs: fAudTrack4CodecPopUp]; + } + + /* Now lets make the sender the appropriate Audio Track popup from this point on */ + if (sender == fAudTrack1CodecPopUp || sender == fAudTrack1MixPopUp) + { + sender = fAudLang1PopUp; + } + if (sender == fAudTrack2CodecPopUp || sender == fAudTrack2MixPopUp) + { + sender = fAudLang2PopUp; + } + if (sender == fAudTrack3CodecPopUp || sender == fAudTrack3MixPopUp) + { + sender = fAudLang3PopUp; + } + if (sender == fAudTrack4CodecPopUp || sender == fAudTrack4MixPopUp) + { + sender = fAudLang4PopUp; + } + + /* pointer to this track's mixdown, codec, sample rate and bitrate NSPopUpButton's */ + NSPopUpButton * mixdownPopUp; + NSPopUpButton * audiocodecPopUp; + NSPopUpButton * sampleratePopUp; + NSPopUpButton * bitratePopUp; + if (sender == fAudLang1PopUp) + { + mixdownPopUp = fAudTrack1MixPopUp; + audiocodecPopUp = fAudTrack1CodecPopUp; + sampleratePopUp = fAudTrack1RatePopUp; + bitratePopUp = fAudTrack1BitratePopUp; + } + else if (sender == fAudLang2PopUp) + { + mixdownPopUp = fAudTrack2MixPopUp; + audiocodecPopUp = fAudTrack2CodecPopUp; + sampleratePopUp = fAudTrack2RatePopUp; + bitratePopUp = fAudTrack2BitratePopUp; + } + else if (sender == fAudLang3PopUp) + { + mixdownPopUp = fAudTrack3MixPopUp; + audiocodecPopUp = fAudTrack3CodecPopUp; + sampleratePopUp = fAudTrack3RatePopUp; + bitratePopUp = fAudTrack3BitratePopUp; + } + else + { + mixdownPopUp = fAudTrack4MixPopUp; + audiocodecPopUp = fAudTrack4CodecPopUp; + sampleratePopUp = fAudTrack4RatePopUp; + bitratePopUp = fAudTrack4BitratePopUp; + } + + /* get the index of the selected audio Track*/ + int thisAudioIndex = [sender indexOfSelectedItem] - 1; + + /* pointer for the hb_audio_s struct we will use later on */ + hb_audio_config_t * audio; + + int acodec; + /* check if the audio mixdown controls need their enabled state changing */ + [self setEnabledStateOfAudioMixdownControls:nil]; + + if (thisAudioIndex != -1) + { + + /* get the audio */ + audio = (hb_audio_config_t *) hb_list_audio_config_item( fTitle->list_audio, thisAudioIndex );// Should "fTitle" be title and be setup ? + + /* actually manipulate the proper mixdowns here */ + /* delete the previous audio mixdown options */ + [mixdownPopUp removeAllItems]; + + acodec = [[audiocodecPopUp selectedItem] tag]; + + if (audio != NULL) + { + + /* find out if our selected output audio codec supports mono and / or 6ch */ + /* we also check for an input codec of AC3 or DCA, + as they are the only libraries able to do the mixdown to mono / conversion to 6-ch */ + /* audioCodecsSupportMono and audioCodecsSupport6Ch are the same for now, + but this may change in the future, so they are separated for flexibility */ + int audioCodecsSupportMono = + (audio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)) && + (acodec != HB_ACODEC_LAME); + int audioCodecsSupport6Ch = + (audio->in.codec & (HB_ACODEC_AC3|HB_ACODEC_DCA)) && + (acodec != HB_ACODEC_LAME); + + /* check for AC-3 passthru */ + if (audio->in.codec == HB_ACODEC_AC3 && acodec == HB_ACODEC_AC3) + { + + NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle: + [NSString stringWithCString: "AC3 Passthru"] + action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_ACODEC_AC3]; + } + else if (audio->in.codec == HB_ACODEC_DCA && acodec == HB_ACODEC_DCA) + { + NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle: + [NSString stringWithCString: "DTS Passthru"] + action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_ACODEC_DCA]; + } + else + { + + /* add the appropriate audio mixdown menuitems to the popupbutton */ + /* in each case, we set the new menuitem's tag to be the amixdown value for that mixdown, + so that we can reference the mixdown later */ + + /* keep a track of the min and max mixdowns we used, so we can select the best match later */ + int minMixdownUsed = 0; + int maxMixdownUsed = 0; + + /* get the input channel layout without any lfe channels */ + int layout = audio->in.channel_layout & HB_INPUT_CH_LAYOUT_DISCRETE_NO_LFE_MASK; + + /* do we want to add a mono option? */ + if (audioCodecsSupportMono == 1) + { + NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle: + [NSString stringWithCString: hb_audio_mixdowns[0].human_readable_name] + action: NULL keyEquivalent: @""]; + [menuItem setTag: hb_audio_mixdowns[0].amixdown]; + if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[0].amixdown; + maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[0].amixdown); + } + + /* do we want to add a stereo option? */ + /* offer stereo if we have a mono source and non-mono-supporting codecs, as otherwise we won't have a mixdown at all */ + /* also offer stereo if we have a stereo-or-better source */ + if ((layout == HB_INPUT_CH_LAYOUT_MONO && audioCodecsSupportMono == 0) || layout >= HB_INPUT_CH_LAYOUT_STEREO) + { + NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle: + [NSString stringWithCString: hb_audio_mixdowns[1].human_readable_name] + action: NULL keyEquivalent: @""]; + [menuItem setTag: hb_audio_mixdowns[1].amixdown]; + if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[1].amixdown; + maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[1].amixdown); + } + + /* do we want to add a dolby surround (DPL1) option? */ + if (layout == HB_INPUT_CH_LAYOUT_3F1R || layout == HB_INPUT_CH_LAYOUT_3F2R || layout == HB_INPUT_CH_LAYOUT_DOLBY) + { + NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle: + [NSString stringWithCString: hb_audio_mixdowns[2].human_readable_name] + action: NULL keyEquivalent: @""]; + [menuItem setTag: hb_audio_mixdowns[2].amixdown]; + if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[2].amixdown; + maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[2].amixdown); + } + + /* do we want to add a dolby pro logic 2 (DPL2) option? */ + if (layout == HB_INPUT_CH_LAYOUT_3F2R) + { + NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle: + [NSString stringWithCString: hb_audio_mixdowns[3].human_readable_name] + action: NULL keyEquivalent: @""]; + [menuItem setTag: hb_audio_mixdowns[3].amixdown]; + if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[3].amixdown; + maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[3].amixdown); + } + + /* do we want to add a 6-channel discrete option? */ + if (audioCodecsSupport6Ch == 1 && layout == HB_INPUT_CH_LAYOUT_3F2R && (audio->in.channel_layout & HB_INPUT_CH_LAYOUT_HAS_LFE)) + { + NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle: + [NSString stringWithCString: hb_audio_mixdowns[4].human_readable_name] + action: NULL keyEquivalent: @""]; + [menuItem setTag: hb_audio_mixdowns[4].amixdown]; + if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[4].amixdown; + maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[4].amixdown); + } + + /* do we want to add an AC-3 passthrough option? */ + if (audio->in.codec == HB_ACODEC_AC3 && acodec == HB_ACODEC_AC3) + { + NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle: + [NSString stringWithCString: hb_audio_mixdowns[5].human_readable_name] + action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_ACODEC_AC3]; + if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[5].amixdown; + maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[5].amixdown); + } + + /* do we want to add a DTS Passthru option ? HB_ACODEC_DCA*/ + if (audio->in.codec == HB_ACODEC_DCA && acodec == HB_ACODEC_DCA) + { + NSMenuItem *menuItem = [[mixdownPopUp menu] addItemWithTitle: + [NSString stringWithCString: hb_audio_mixdowns[5].human_readable_name] + action: NULL keyEquivalent: @""]; + [menuItem setTag: HB_ACODEC_DCA]; + if (minMixdownUsed == 0) minMixdownUsed = hb_audio_mixdowns[5].amixdown; + maxMixdownUsed = MAX(maxMixdownUsed, hb_audio_mixdowns[5].amixdown); + } + + /* auto-select the best mixdown based on our saved mixdown preference */ + + /* for now, this is hard-coded to a "best" mixdown of HB_AMIXDOWN_DOLBYPLII */ + /* ultimately this should be a prefs option */ + int useMixdown; + + /* if we passed in a mixdown to use - in order to load a preset - then try and use it */ + if (mixdownToUse > 0) + { + useMixdown = mixdownToUse; + } + else + { + useMixdown = HB_AMIXDOWN_DOLBYPLII; + } + + /* if useMixdown > maxMixdownUsed, then use maxMixdownUsed */ + if (useMixdown > maxMixdownUsed) + { + useMixdown = maxMixdownUsed; + } + + /* if useMixdown < minMixdownUsed, then use minMixdownUsed */ + if (useMixdown < minMixdownUsed) + { + useMixdown = minMixdownUsed; + } + + /* select the (possibly-amended) preferred mixdown */ + [mixdownPopUp selectItemWithTag: useMixdown]; + + } + /* In the case of a source track that is not AC3 and the user tries to use AC3 Passthru (which does not work) + * we force the Audio Codec choice back to a workable codec. We use MP3 for avi and aac for all + * other containers. + */ + if (audio->in.codec != HB_ACODEC_AC3 && [[audiocodecPopUp selectedItem] tag] == HB_ACODEC_AC3) + { + /* If we are using the avi container, we select MP3 as there is no aac available*/ + if ([[fDstFormatPopUp selectedItem] tag] == HB_MUX_AVI) + { + [audiocodecPopUp selectItemWithTag: HB_ACODEC_LAME]; + } + else + { + [audiocodecPopUp selectItemWithTag: HB_ACODEC_FAAC]; + } + } + + /* In the case of a source track that is not DTS and the user tries to use DTS Passthru (which does not work) + * we force the Audio Codec choice back to a workable codec. We use MP3 for avi and aac for all + * other containers. + */ + if (audio->in.codec != HB_ACODEC_DCA && [[audiocodecPopUp selectedItem] tag] == HB_ACODEC_DCA) + { + /* If we are using the avi container, we select MP3 as there is no aac available*/ + if ([[fDstFormatPopUp selectedItem] tag] == HB_MUX_AVI) + { + [audiocodecPopUp selectItemWithTag: HB_ACODEC_LAME]; + } + else + { + [audiocodecPopUp selectItemWithTag: HB_ACODEC_FAAC]; + } + } + + /* Setup our samplerate and bitrate popups we will need based on mixdown */ + [self audioTrackMixdownChanged: mixdownPopUp]; + } + + } + if( [fDstFormatPopUp indexOfSelectedItem] == 0 ) + { + [self autoSetM4vExtension: sender]; + } +} + +- (IBAction) audioTrackMixdownChanged: (id) sender +{ + + int acodec; + /* setup pointers to all of the other audio track controls + * we will need later + */ + NSPopUpButton * mixdownPopUp; + NSPopUpButton * sampleratePopUp; + NSPopUpButton * bitratePopUp; + NSPopUpButton * audiocodecPopUp; + NSPopUpButton * audiotrackPopUp; + NSSlider * drcSlider; + NSTextField * drcField; + if (sender == fAudTrack1MixPopUp) + { + audiotrackPopUp = fAudLang1PopUp; + audiocodecPopUp = fAudTrack1CodecPopUp; + mixdownPopUp = fAudTrack1MixPopUp; + sampleratePopUp = fAudTrack1RatePopUp; + bitratePopUp = fAudTrack1BitratePopUp; + drcSlider = fAudTrack1DrcSlider; + drcField = fAudTrack1DrcField; + } + else if (sender == fAudTrack2MixPopUp) + { + audiotrackPopUp = fAudLang2PopUp; + audiocodecPopUp = fAudTrack2CodecPopUp; + mixdownPopUp = fAudTrack2MixPopUp; + sampleratePopUp = fAudTrack2RatePopUp; + bitratePopUp = fAudTrack2BitratePopUp; + drcSlider = fAudTrack2DrcSlider; + drcField = fAudTrack2DrcField; + } + else if (sender == fAudTrack3MixPopUp) + { + audiotrackPopUp = fAudLang3PopUp; + audiocodecPopUp = fAudTrack3CodecPopUp; + mixdownPopUp = fAudTrack3MixPopUp; + sampleratePopUp = fAudTrack3RatePopUp; + bitratePopUp = fAudTrack3BitratePopUp; + drcSlider = fAudTrack3DrcSlider; + drcField = fAudTrack3DrcField; + } + else + { + audiotrackPopUp = fAudLang4PopUp; + audiocodecPopUp = fAudTrack4CodecPopUp; + mixdownPopUp = fAudTrack4MixPopUp; + sampleratePopUp = fAudTrack4RatePopUp; + bitratePopUp = fAudTrack4BitratePopUp; + drcSlider = fAudTrack4DrcSlider; + drcField = fAudTrack4DrcField; + } + acodec = [[audiocodecPopUp selectedItem] tag]; + /* storage variable for the min and max bitrate allowed for this codec */ + int minbitrate; + int maxbitrate; + + switch( acodec ) + { + case HB_ACODEC_FAAC: + /* check if we have a 6ch discrete conversion in either audio track */ + if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH) + { + /* FAAC is happy using our min bitrate of 32 kbps, even for 6ch */ + minbitrate = 32; + /* If either mixdown popup includes 6-channel discrete, then allow up to 384 kbps */ + maxbitrate = 384; + break; + } + else + { + /* FAAC is happy using our min bitrate of 32 kbps for stereo or mono */ + minbitrate = 32; + /* FAAC won't honour anything more than 160 for stereo, so let's not offer it */ + /* note: haven't dealt with mono separately here, FAAC will just use the max it can */ + maxbitrate = 160; + break; + } + + case HB_ACODEC_CA_AAC: + /* check if we have a 6ch discrete conversion in either audio track */ + if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH) + { + minbitrate = 128; + maxbitrate = 768; + break; + } + else + { + minbitrate = 64; + maxbitrate = 320; + break; + } + + case HB_ACODEC_LAME: + /* Lame is happy using our min bitrate of 32 kbps */ + minbitrate = 32; + /* Lame won't encode if the bitrate is higher than 320 kbps */ + maxbitrate = 320; + break; + + case HB_ACODEC_VORBIS: + if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH) + { + /* Vorbis causes a crash if we use a bitrate below 192 kbps with 6 channel */ + minbitrate = 192; + /* If either mixdown popup includes 6-channel discrete, then allow up to 384 kbps */ + maxbitrate = 384; + break; + } + else + { + /* Vorbis causes a crash if we use a bitrate below 48 kbps */ + minbitrate = 48; + /* Vorbis can cope with 384 kbps quite happily, even for stereo */ + maxbitrate = 384; + break; + } + + default: + /* AC3 passthru disables the bitrate dropdown anyway, so we might as well just use the min and max bitrate */ + minbitrate = 32; + maxbitrate = 384; + + } + + /* make sure we have a selected title before continuing */ + if (fTitle == NULL) return; + /* get the audio so we can find out what input rates are*/ + hb_audio_config_t * audio; + audio = (hb_audio_config_t *) hb_list_audio_config_item( fTitle->list_audio, [audiotrackPopUp indexOfSelectedItem] - 1 ); + int inputbitrate = audio->in.bitrate / 1000; + int inputsamplerate = audio->in.samplerate; + + if ([[mixdownPopUp selectedItem] tag] != HB_ACODEC_AC3 && [[mixdownPopUp selectedItem] tag] != HB_ACODEC_DCA) + { + [bitratePopUp removeAllItems]; + + for( int i = 0; i < hb_audio_bitrates_count; i++ ) + { + if (hb_audio_bitrates[i].rate >= minbitrate && hb_audio_bitrates[i].rate <= maxbitrate) + { + /* add a new menuitem for this bitrate */ + NSMenuItem *menuItem = [[bitratePopUp menu] addItemWithTitle: + [NSString stringWithCString: hb_audio_bitrates[i].string] + action: NULL keyEquivalent: @""]; + /* set its tag to be the actual bitrate as an integer, so we can retrieve it later */ + [menuItem setTag: hb_audio_bitrates[i].rate]; + } + } + + /* select the default bitrate (but use 384 for 6-ch AAC) */ + if ([[mixdownPopUp selectedItem] tag] == HB_AMIXDOWN_6CH) + { + [bitratePopUp selectItemWithTag: 384]; + } + else + { + [bitratePopUp selectItemWithTag: hb_audio_bitrates[hb_audio_bitrates_default].rate]; + } + } + /* populate and set the sample rate popup */ + /* Audio samplerate */ + [sampleratePopUp removeAllItems]; + /* we create a same as source selection (Auto) so that we can choose to use the input sample rate */ + NSMenuItem *menuItem = [[sampleratePopUp menu] addItemWithTitle: @"Auto" action: NULL keyEquivalent: @""]; + [menuItem setTag: inputsamplerate]; + + for( int i = 0; i < hb_audio_rates_count; i++ ) + { + NSMenuItem *menuItem = [[sampleratePopUp menu] addItemWithTitle: + [NSString stringWithCString: hb_audio_rates[i].string] + action: NULL keyEquivalent: @""]; + [menuItem setTag: hb_audio_rates[i].rate]; + } + /* We use the input sample rate as the default sample rate as downsampling just makes audio worse + * and there is no compelling reason to use anything else as default, though the users default + * preset will likely override any setting chosen here. + */ + [sampleratePopUp selectItemWithTag: inputsamplerate]; + + + /* Since AC3 Pass Thru and DTS Pass Thru uses the input bitrate and sample rate, we get the input tracks + * bitrate and display it in the bitrate popup even though libhb happily ignores any bitrate input from + * the gui. We do this for better user feedback in the audio tab as well as the queue for the most part + */ + if ([[mixdownPopUp selectedItem] tag] == HB_ACODEC_AC3 || [[mixdownPopUp selectedItem] tag] == HB_ACODEC_DCA) + { + + /* lets also set the bitrate popup to the input bitrate as thats what passthru will use */ + [bitratePopUp removeAllItems]; + NSMenuItem *menuItem = [[bitratePopUp menu] addItemWithTitle: + [NSString stringWithFormat:@"%d", inputbitrate] + action: NULL keyEquivalent: @""]; + [menuItem setTag: inputbitrate]; + /* For ac3 passthru we disable the sample rate and bitrate popups as well as the drc slider*/ + [bitratePopUp setEnabled: NO]; + [sampleratePopUp setEnabled: NO]; + + [drcSlider setFloatValue: 1.00]; + [self audioDRCSliderChanged: drcSlider]; + [drcSlider setEnabled: NO]; + [drcField setEnabled: NO]; + } + else + { + [sampleratePopUp setEnabled: YES]; + [bitratePopUp setEnabled: YES]; + [drcSlider setEnabled: YES]; + [drcField setEnabled: YES]; + } +[self calculateBitrate:nil]; +} + +- (IBAction) audioDRCSliderChanged: (id) sender +{ + NSSlider * drcSlider; + NSTextField * drcField; + if (sender == fAudTrack1DrcSlider) + { + drcSlider = fAudTrack1DrcSlider; + drcField = fAudTrack1DrcField; + } + else if (sender == fAudTrack2DrcSlider) + { + drcSlider = fAudTrack2DrcSlider; + drcField = fAudTrack2DrcField; + } + else if (sender == fAudTrack3DrcSlider) + { + drcSlider = fAudTrack3DrcSlider; + drcField = fAudTrack3DrcField; + } + else + { + drcSlider = fAudTrack4DrcSlider; + drcField = fAudTrack4DrcField; + } + + /* If we are between 0.0 and 1.0 on the slider, snap it to 1.0 */ + if ([drcSlider floatValue] > 0.0 && [drcSlider floatValue] < 1.0) + { + [drcSlider setFloatValue:1.0]; + } + + + [drcField setStringValue: [NSString stringWithFormat: @"%.2f", [drcSlider floatValue]]]; + /* For now, do not call this until we have an intelligent way to determine audio track selections + * compared to presets + */ + //[self customSettingUsed: sender]; +} + +#pragma mark - + +- (IBAction) browseImportSrtFile: (id) sender +{ + + NSOpenPanel * panel; + + panel = [NSOpenPanel openPanel]; + [panel setAllowsMultipleSelection: NO]; + [panel setCanChooseFiles: YES]; + [panel setCanChooseDirectories: NO ]; + NSString * sourceDirectory; + if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastSrtImportDirectory"]) + { + sourceDirectory = [[NSUserDefaults standardUserDefaults] stringForKey:@"LastSrtImportDirectory"]; + } + else + { + sourceDirectory = @"~/Desktop"; + sourceDirectory = [sourceDirectory stringByExpandingTildeInPath]; + } + /* we open up the browse srt sheet here and call for browseImportSrtFileDone after the sheet is closed */ + NSArray *fileTypes = [NSArray arrayWithObjects:@"plist", @"srt", nil]; + [panel beginSheetForDirectory: sourceDirectory file: nil types: fileTypes + modalForWindow: fWindow modalDelegate: self + didEndSelector: @selector( browseImportSrtFileDone:returnCode:contextInfo: ) + contextInfo: sender]; +} + +- (void) browseImportSrtFileDone: (NSSavePanel *) sheet + returnCode: (int) returnCode contextInfo: (void *) contextInfo +{ + if( returnCode == NSOKButton ) + { + NSString *importSrtDirectory = [[sheet filename] stringByDeletingLastPathComponent]; + NSString *importSrtFilePath = [sheet filename]; + [[NSUserDefaults standardUserDefaults] setObject:importSrtDirectory forKey:@"LastSrtImportDirectory"]; + + /* now pass the string off to fSubtitlesDelegate to add the srt file to the dropdown */ + [fSubtitlesDelegate createSubtitleSrtTrack:importSrtFilePath]; + + [fSubtitlesTable reloadData]; + + } +} + +#pragma mark - +#pragma mark Open New Windows + +- (IBAction) openHomepage: (id) sender +{ + [[NSWorkspace sharedWorkspace] openURL: [NSURL + URLWithString:@"http://handbrake.fr/"]]; +} + +- (IBAction) openForums: (id) sender +{ + [[NSWorkspace sharedWorkspace] openURL: [NSURL + URLWithString:@"http://handbrake.fr/forum/"]]; +} +- (IBAction) openUserGuide: (id) sender +{ + [[NSWorkspace sharedWorkspace] openURL: [NSURL + URLWithString:@"http://handbrake.fr/trac/wiki/HandBrakeGuide"]]; +} + +/** + * Shows debug output window. + */ +- (IBAction)showDebugOutputPanel:(id)sender +{ + [outputPanel showOutputPanel:sender]; +} + +/** + * Shows preferences window. + */ +- (IBAction) showPreferencesWindow: (id) sender +{ + NSWindow * window = [fPreferencesController window]; + if (![window isVisible]) + [window center]; + + [window makeKeyAndOrderFront: nil]; +} + +/** + * Shows queue window. + */ +- (IBAction) showQueueWindow:(id)sender +{ + [fQueueController showQueueWindow:sender]; +} + + +- (IBAction) toggleDrawer:(id)sender { + [fPresetDrawer toggle:self]; +} + +/** + * Shows Picture Settings Window. + */ + +- (IBAction) showPicturePanel: (id) sender +{ + [fPictureController showPictureWindow:sender]; +} + +- (void) picturePanelFullScreen +{ + [fPictureController setToFullScreenMode]; +} + +- (void) picturePanelWindowed +{ + [fPictureController setToWindowedMode]; +} + +- (IBAction) showPreviewWindow: (id) sender +{ + [fPictureController showPreviewWindow:sender]; +} + +#pragma mark - +#pragma mark Preset Outline View Methods +#pragma mark - Required +/* These are required by the NSOutlineView Datasource Delegate */ + + +/* used to specify the number of levels to show for each item */ +- (int)outlineView:(NSOutlineView *)fPresetsOutlineView numberOfChildrenOfItem:(id)item +{ + /* currently use no levels to test outline view viability */ + if (item == nil) // for an outline view the root level of the hierarchy is always nil + { + return [UserPresets count]; + } + else + { + /* we need to return the count of the array in ChildrenArray for this folder */ + NSArray *children = nil; + children = [item objectForKey:@"ChildrenArray"]; + if ([children count] > 0) + { + return [children count]; + } + else + { + return 0; + } + } +} + +/* We use this to deterimine children of an item */ +- (id)outlineView:(NSOutlineView *)fPresetsOutlineView child:(NSInteger)index ofItem:(id)item +{ + + /* we need to return the count of the array in ChildrenArray for this folder */ + NSArray *children = nil; + if (item == nil) + { + children = UserPresets; + } + else + { + if ([item objectForKey:@"ChildrenArray"]) + { + children = [item objectForKey:@"ChildrenArray"]; + } + } + if ((children == nil) || ( [children count] <= (NSUInteger) index)) + { + return nil; + } + else + { + return [children objectAtIndex:index]; + } + + + // We are only one level deep, so we can't be asked about children + //NSAssert (NO, @"Presets View outlineView:child:ofItem: currently can't handle nested items."); + //return nil; +} + +/* We use this to determine if an item should be expandable */ +- (BOOL)outlineView:(NSOutlineView *)fPresetsOutlineView isItemExpandable:(id)item +{ + + /* we need to return the count of the array in ChildrenArray for this folder */ + NSArray *children= nil; + if (item == nil) + { + children = UserPresets; + } + else + { + if ([item objectForKey:@"ChildrenArray"]) + { + children = [item objectForKey:@"ChildrenArray"]; + } + } + + /* To deterimine if an item should show a disclosure triangle + * we could do it by the children count as so: + * if ([children count] < 1) + * However, lets leave the triangle show even if there are no + * children to help indicate a folder, just like folder in the + * finder can show a disclosure triangle even when empty + */ + + /* We need to determine if the item is a folder */ + if ([[item objectForKey:@"Folder"] intValue] == 1) + { + return YES; + } + else + { + return NO; + } + +} + +- (BOOL)outlineView:(NSOutlineView *)outlineView shouldExpandItem:(id)item +{ + // Our outline view has no levels, but we can still expand every item. Doing so + // just makes the row taller. See heightOfRowByItem below. +//return ![(HBQueueOutlineView*)outlineView isDragging]; + +return YES; +} + + +/* Used to tell the outline view which information is to be displayed per item */ +- (id)outlineView:(NSOutlineView *)fPresetsOutlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item +{ + /* We have two columns right now, icon and PresetName */ + + if ([[tableColumn identifier] isEqualToString:@"PresetName"]) + { + return [item objectForKey:@"PresetName"]; + } + else + { + //return @""; + return nil; + } +} + +- (id)outlineView:(NSOutlineView *)outlineView itemForPersistentObject:(id)object +{ + return [NSKeyedUnarchiver unarchiveObjectWithData:object]; +} +- (id)outlineView:(NSOutlineView *)outlineView persistentObjectForItem:(id)item +{ + return [NSKeyedArchiver archivedDataWithRootObject:item]; +} + +#pragma mark - Added Functionality (optional) +/* Use to customize the font and display characteristics of the title cell */ +- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item +{ + if ([[tableColumn identifier] isEqualToString:@"PresetName"]) + { + NSFont *txtFont; + NSColor *fontColor; + NSColor *shadowColor; + txtFont = [NSFont systemFontOfSize: [NSFont smallSystemFontSize]]; + /*check to see if its a selected row */ + if ([fPresetsOutlineView selectedRow] == [fPresetsOutlineView rowForItem:item]) + { + + fontColor = [NSColor blackColor]; + shadowColor = [NSColor colorWithDeviceRed:(127.0/255.0) green:(140.0/255.0) blue:(160.0/255.0) alpha:1.0]; + } + else + { + if ([[item objectForKey:@"Type"] intValue] == 0) + { + fontColor = [NSColor blueColor]; + } + else // User created preset, use a black font + { + fontColor = [NSColor blackColor]; + } + /* check to see if its a folder */ + //if ([[item objectForKey:@"Folder"] intValue] == 1) + //{ + //fontColor = [NSColor greenColor]; + //} + + + } + /* We use Bold Text for the HB Default */ + if ([[item objectForKey:@"Default"] intValue] == 1)// 1 is HB default + { + txtFont = [NSFont boldSystemFontOfSize: [NSFont smallSystemFontSize]]; + } + /* We use Bold Text for the User Specified Default */ + if ([[item objectForKey:@"Default"] intValue] == 2)// 2 is User default + { + txtFont = [NSFont boldSystemFontOfSize: [NSFont smallSystemFontSize]]; + } + + + [cell setTextColor:fontColor]; + [cell setFont:txtFont]; + + } +} + +/* We use this to edit the name field in the outline view */ +- (void)outlineView:(NSOutlineView *)outlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item +{ + if ([[tableColumn identifier] isEqualToString:@"PresetName"]) + { + id theRecord; + + theRecord = item; + [theRecord setObject:object forKey:@"PresetName"]; + + [self sortPresets]; + + [fPresetsOutlineView reloadData]; + /* We save all of the preset data here */ + [self savePreset]; + } +} +/* We use this to provide tooltips for the items in the presets outline view */ +- (NSString *)outlineView:(NSOutlineView *)fPresetsOutlineView toolTipForCell:(NSCell *)cell rect:(NSRectPointer)rect tableColumn:(NSTableColumn *)tc item:(id)item mouseLocation:(NSPoint)mouseLocation +{ + //if ([[tc identifier] isEqualToString:@"PresetName"]) + //{ + /* initialize the tooltip contents variable */ + NSString *loc_tip; + /* if there is a description for the preset, we show it in the tooltip */ + if ([item objectForKey:@"PresetDescription"]) + { + loc_tip = [item objectForKey:@"PresetDescription"]; + return (loc_tip); + } + else + { + loc_tip = @"No description available"; + } + return (loc_tip); + //} +} + +#pragma mark - +#pragma mark Preset Outline View Methods (dragging related) + + +- (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray *)items toPasteboard:(NSPasteboard *)pboard +{ + // Dragging is only allowed for custom presets. + //[[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Default"] intValue] != 1 + if ([[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Type"] intValue] == 0) // 0 is built in preset + { + return NO; + } + // Don't retain since this is just holding temporaral drag information, and it is + //only used during a drag! We could put this in the pboard actually. + fDraggedNodes = items; + // Provide data for our custom type, and simple NSStrings. + [pboard declareTypes:[NSArray arrayWithObjects: DragDropSimplePboardType, nil] owner:self]; + + // the actual data doesn't matter since DragDropSimplePboardType drags aren't recognized by anyone but us!. + [pboard setData:[NSData data] forType:DragDropSimplePboardType]; + + return YES; +} + +- (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(NSInteger)index +{ + + // Don't allow dropping ONTO an item since they can't really contain any children. + + BOOL isOnDropTypeProposal = index == NSOutlineViewDropOnItemIndex; + if (isOnDropTypeProposal) + return NSDragOperationNone; + + // Don't allow dropping INTO an item since they can't really contain any children as of yet. + if (item != nil) + { + index = [fPresetsOutlineView rowForItem: item] + 1; + item = nil; + } + + // Don't allow dropping into the Built In Presets. + if (index < presetCurrentBuiltInCount) + { + return NSDragOperationNone; + index = MAX (index, presetCurrentBuiltInCount); + } + + [outlineView setDropItem:item dropChildIndex:index]; + return NSDragOperationGeneric; +} + + + +- (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(NSInteger)index +{ + /* first, lets see if we are dropping into a folder */ + if ([[fPresetsOutlineView itemAtRow:index] objectForKey:@"Folder"] && [[[fPresetsOutlineView itemAtRow:index] objectForKey:@"Folder"] intValue] == 1) // if its a folder + { + NSMutableArray *childrenArray = [[NSMutableArray alloc] init]; + childrenArray = [[fPresetsOutlineView itemAtRow:index] objectForKey:@"ChildrenArray"]; + [childrenArray addObject:item]; + [[fPresetsOutlineView itemAtRow:index] setObject:[NSMutableArray arrayWithArray: childrenArray] forKey:@"ChildrenArray"]; + [childrenArray autorelease]; + } + else // We are not, so we just move the preset into the existing array + { + NSMutableIndexSet *moveItems = [NSMutableIndexSet indexSet]; + id obj; + NSEnumerator *enumerator = [fDraggedNodes objectEnumerator]; + while (obj = [enumerator nextObject]) + { + [moveItems addIndex:[UserPresets indexOfObject:obj]]; + } + // Successful drop, lets rearrange the view and save it all + [self moveObjectsInPresetsArray:UserPresets fromIndexes:moveItems toIndex: index]; + } + [fPresetsOutlineView reloadData]; + [self savePreset]; + return YES; +} + +- (void)moveObjectsInPresetsArray:(NSMutableArray *)array fromIndexes:(NSIndexSet *)indexSet toIndex:(NSUInteger)insertIndex +{ + NSUInteger index = [indexSet lastIndex]; + NSUInteger aboveInsertIndexCount = 0; + + NSUInteger removeIndex; + + if (index >= insertIndex) + { + removeIndex = index + aboveInsertIndexCount; + aboveInsertIndexCount++; + } + else + { + removeIndex = index; + insertIndex--; + } + + id object = [[array objectAtIndex:removeIndex] retain]; + [array removeObjectAtIndex:removeIndex]; + [array insertObject:object atIndex:insertIndex]; + [object release]; + + index = [indexSet indexLessThanIndex:index]; +} + + + +#pragma mark - Functional Preset NSOutlineView Methods + +- (IBAction)selectPreset:(id)sender +{ + + if ([fPresetsOutlineView selectedRow] >= 0 && [[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Folder"] intValue] != 1) + { + chosenPreset = [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]]; + [fPresetSelectedDisplay setStringValue:[chosenPreset objectForKey:@"PresetName"]]; + + if ([[chosenPreset objectForKey:@"Default"] intValue] == 1) + { + [fPresetSelectedDisplay setStringValue:[NSString stringWithFormat:@"%@ (Default)", [chosenPreset objectForKey:@"PresetName"]]]; + } + else + { + [fPresetSelectedDisplay setStringValue:[chosenPreset objectForKey:@"PresetName"]]; + } + + /* File Format */ + [fDstFormatPopUp selectItemWithTitle:[chosenPreset objectForKey:@"FileFormat"]]; + [self formatPopUpChanged:nil]; + + /* Chapter Markers*/ + [fCreateChapterMarkers setState:[[chosenPreset objectForKey:@"ChapterMarkers"] intValue]]; + /* check to see if we have only one chapter */ + [self chapterPopUpChanged:nil]; + + /* Allow Mpeg4 64 bit formatting +4GB file sizes */ + [fDstMp4LargeFileCheck setState:[[chosenPreset objectForKey:@"Mp4LargeFile"] intValue]]; + /* Mux mp4 with http optimization */ + [fDstMp4HttpOptFileCheck setState:[[chosenPreset objectForKey:@"Mp4HttpOptimize"] intValue]]; + + /* Video encoder */ + [fVidEncoderPopUp selectItemWithTitle:[chosenPreset objectForKey:@"VideoEncoder"]]; + /* We set the advanced opt string here if applicable*/ + [fAdvancedOptions setOptions:[chosenPreset objectForKey:@"x264Option"]]; + + /* Lets run through the following functions to get variables set there */ + [self videoEncoderPopUpChanged:nil]; + /* Set the state of ipod compatible with Mp4iPodCompatible. Only for x264*/ + [fDstMp4iPodFileCheck setState:[[chosenPreset objectForKey:@"Mp4iPodCompatible"] intValue]]; + [self calculateBitrate:nil]; + + /* Video quality */ + [fVidQualityMatrix selectCellAtRow:[[chosenPreset objectForKey:@"VideoQualityType"] intValue] column:0]; + + [fVidTargetSizeField setStringValue:[chosenPreset objectForKey:@"VideoTargetSize"]]; + [fVidBitrateField setStringValue:[chosenPreset objectForKey:@"VideoAvgBitrate"]]; + + /* Since we are now using RF Values for the slider, we detect if the preset uses an old quality float. + * So, check to see if the quality value is less than 1.0 which should indicate the old ".062" type + * quality preset. Caveat: in the case of x264, where the RF scale starts at 0, it would misinterpret + * a preset that uses 0.0 - 0.99 for RF as an old style preset. Not sure how to get around that one yet, + * though it should be a corner case since it would pretty much be a preset for lossless encoding. */ + if ([[chosenPreset objectForKey:@"VideoQualitySlider"] floatValue] < 1.0) + { + /* For the quality slider we need to convert the old percent's to the new rf scales */ + float rf = (([fVidQualitySlider maxValue] - [fVidQualitySlider minValue]) * [[chosenPreset objectForKey:@"VideoQualitySlider"] floatValue]); + [fVidQualitySlider setFloatValue:rf]; + + } + else + { + /* Since theora's qp value goes up from left to right, we can just set the slider float value */ + if ([[fVidEncoderPopUp selectedItem] tag] == HB_VCODEC_THEORA) + { + [fVidQualitySlider setFloatValue:[[chosenPreset objectForKey:@"VideoQualitySlider"] floatValue]]; + } + else + { + /* since ffmpeg and x264 use an "inverted" slider (lower qp/rf values indicate a higher quality) we invert the value on the slider */ + [fVidQualitySlider setFloatValue:([fVidQualitySlider maxValue] + [fVidQualitySlider minValue]) - [[chosenPreset objectForKey:@"VideoQualitySlider"] floatValue]]; + } + } + + [self videoMatrixChanged:nil]; + + /* Video framerate */ + /* For video preset video framerate, we want to make sure that Same as source does not conflict with the + detected framerate in the fVidRatePopUp so we use index 0*/ + if ([[chosenPreset objectForKey:@"VideoFramerate"] isEqualToString:@"Same as source"]) + { + [fVidRatePopUp selectItemAtIndex: 0]; + } + else + { + [fVidRatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"VideoFramerate"]]; + } + + + /* 2 Pass Encoding */ + [fVidTwoPassCheck setState:[[chosenPreset objectForKey:@"VideoTwoPass"] intValue]]; + [self twoPassCheckboxChanged:nil]; + + /* Turbo 1st pass for 2 Pass Encoding */ + [fVidTurboPassCheck setState:[[chosenPreset objectForKey:@"VideoTurboTwoPass"] intValue]]; + + /*Audio*/ + /* First we check to see if we are using the current audio track layout based on AudioList array */ + if ([chosenPreset objectForKey:@"AudioList"]) + { + /* Populate the audio widgets based on the contents of the AudioList array */ + int i = 0; + NSEnumerator *enumerator = [[chosenPreset objectForKey:@"AudioList"] objectEnumerator]; + id tempObject; + while (tempObject = [enumerator nextObject]) + { + i++; + if( i == 1 ) + { + if ([fAudLang1PopUp indexOfSelectedItem] == 0) + { + [fAudLang1PopUp selectItemAtIndex: 1]; + } + [self audioTrackPopUpChanged: fAudLang1PopUp]; + [fAudTrack1CodecPopUp selectItemWithTitle:[tempObject objectForKey:@"AudioEncoder"]]; + /* check our pref for core audio and use it in place of faac if applicable */ + if ([[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES && + [[tempObject objectForKey:@"AudioEncoder"] isEqualToString: @"AAC (faac)"]) + { + [fAudTrack1CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"]; + } + + [self audioTrackPopUpChanged: fAudTrack1CodecPopUp]; + [fAudTrack1MixPopUp selectItemWithTitle:[tempObject objectForKey:@"AudioMixdown"]]; + /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default + * mixdown*/ + if ([fAudTrack1MixPopUp selectedItem] == nil) + { + [self audioTrackPopUpChanged: fAudTrack1CodecPopUp]; + } + [fAudTrack1RatePopUp selectItemWithTitle:[tempObject objectForKey:@"AudioSamplerate"]]; + /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */ + if (![[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"AC3 Passthru"]) + { + [fAudTrack1BitratePopUp selectItemWithTitle:[tempObject objectForKey:@"AudioBitrate"]]; + } + [fAudTrack1DrcSlider setFloatValue:[[tempObject objectForKey:@"AudioTrackDRCSlider"] floatValue]]; + [self audioDRCSliderChanged: fAudTrack1DrcSlider]; + } + + if( i == 2 ) + { + + if ([fAudLang2PopUp indexOfSelectedItem] == 0) + { + [fAudLang2PopUp selectItemAtIndex: 1]; + } + [self audioTrackPopUpChanged: fAudLang2PopUp]; + [fAudTrack2CodecPopUp selectItemWithTitle:[tempObject objectForKey:@"AudioEncoder"]]; + /* check our pref for core audio and use it in place of faac if applicable */ + if ([[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES && + [[tempObject objectForKey:@"AudioEncoder"] isEqualToString: @"AAC (faac)"]) + { + [fAudTrack2CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"]; + } + [self audioTrackPopUpChanged: fAudTrack2CodecPopUp]; + [fAudTrack2MixPopUp selectItemWithTitle:[tempObject objectForKey:@"AudioMixdown"]]; + /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default + * mixdown*/ + if ([fAudTrack2MixPopUp selectedItem] == nil) + { + [self audioTrackPopUpChanged: fAudTrack2CodecPopUp]; + } + [fAudTrack2RatePopUp selectItemWithTitle:[tempObject objectForKey:@"AudioSamplerate"]]; + /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */ + if (![[tempObject objectForKey:@"AudioEncoder"] isEqualToString:@"AC3 Passthru"]) + { + [fAudTrack2BitratePopUp selectItemWithTitle:[tempObject objectForKey:@"AudioBitrate"]]; + } + [fAudTrack2DrcSlider setFloatValue:[[tempObject objectForKey:@"AudioTrackDRCSlider"] floatValue]]; + [self audioDRCSliderChanged: fAudTrack2DrcSlider]; + + } + + } + + /* We now cleanup any extra audio tracks that may have been previously set if we need to */ + + if (i < 4) + { + [fAudLang4PopUp selectItemAtIndex: 0]; + [self audioTrackPopUpChanged: fAudLang4PopUp]; + + if (i < 3) + { + [fAudLang3PopUp selectItemAtIndex: 0]; + [self audioTrackPopUpChanged: fAudLang3PopUp]; + + if (i < 2) + { + [fAudLang2PopUp selectItemAtIndex: 0]; + [self audioTrackPopUpChanged: fAudLang2PopUp]; + } + } + } + + } + else + { + if ([chosenPreset objectForKey:@"Audio1Track"] > 0) + { + if ([fAudLang1PopUp indexOfSelectedItem] == 0) + { + [fAudLang1PopUp selectItemAtIndex: 1]; + } + [self audioTrackPopUpChanged: fAudLang1PopUp]; + [fAudTrack1CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Encoder"]]; + /* check our pref for core audio and use it in place of faac if applicable */ + if ([[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES && + [[chosenPreset objectForKey:@"Audio1Encoder"] isEqualToString: @"AAC (faac)"]) + { + [fAudTrack1CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"]; + } + [self audioTrackPopUpChanged: fAudTrack1CodecPopUp]; + [fAudTrack1MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Mixdown"]]; + /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default + * mixdown*/ + if ([fAudTrack1MixPopUp selectedItem] == nil) + { + [self audioTrackPopUpChanged: fAudTrack1CodecPopUp]; + } + [fAudTrack1RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Samplerate"]]; + /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */ + if (![[chosenPreset objectForKey:@"Audio1Encoder"] isEqualToString:@"AC3 Passthru"]) + { + [fAudTrack1BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio1Bitrate"]]; + } + [fAudTrack1DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio1TrackDRCSlider"] floatValue]]; + [self audioDRCSliderChanged: fAudTrack1DrcSlider]; + } + if ([chosenPreset objectForKey:@"Audio2Track"] > 0) + { + if ([fAudLang2PopUp indexOfSelectedItem] == 0) + { + [fAudLang2PopUp selectItemAtIndex: 1]; + } + [self audioTrackPopUpChanged: fAudLang2PopUp]; + [fAudTrack2CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Encoder"]]; + /* check our pref for core audio and use it in place of faac if applicable */ + if ([[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES && + [[chosenPreset objectForKey:@"Audio2Encoder"] isEqualToString: @"AAC (faac)"]) + { + [fAudTrack2CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"]; + } + [self audioTrackPopUpChanged: fAudTrack2CodecPopUp]; + [fAudTrack2MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Mixdown"]]; + /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default + * mixdown*/ + if ([fAudTrack2MixPopUp selectedItem] == nil) + { + [self audioTrackPopUpChanged: fAudTrack2CodecPopUp]; + } + [fAudTrack2RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Samplerate"]]; + /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */ + if (![[chosenPreset objectForKey:@"Audio2Encoder"] isEqualToString:@"AC3 Passthru"]) + { + [fAudTrack2BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio2Bitrate"]]; + } + [fAudTrack2DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio2TrackDRCSlider"] floatValue]]; + [self audioDRCSliderChanged: fAudTrack2DrcSlider]; + } + if ([chosenPreset objectForKey:@"Audio3Track"] > 0) + { + if ([fAudLang3PopUp indexOfSelectedItem] == 0) + { + [fAudLang3PopUp selectItemAtIndex: 1]; + } + [self audioTrackPopUpChanged: fAudLang3PopUp]; + [fAudTrack3CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Encoder"]]; + /* check our pref for core audio and use it in place of faac if applicable */ + if ([[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES && + [[chosenPreset objectForKey:@"Audio3Encoder"] isEqualToString: @"AAC (faac)"]) + { + [fAudTrack3CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"]; + } + [self audioTrackPopUpChanged: fAudTrack3CodecPopUp]; + [fAudTrack3MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Mixdown"]]; + /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default + * mixdown*/ + if ([fAudTrack3MixPopUp selectedItem] == nil) + { + [self audioTrackPopUpChanged: fAudTrack3CodecPopUp]; + } + [fAudTrack3RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Samplerate"]]; + /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */ + if (![[chosenPreset objectForKey:@"Audio3Encoder"] isEqualToString: @"AC3 Passthru"]) + { + [fAudTrack3BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio3Bitrate"]]; + } + [fAudTrack3DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio3TrackDRCSlider"] floatValue]]; + [self audioDRCSliderChanged: fAudTrack3DrcSlider]; + } + if ([chosenPreset objectForKey:@"Audio4Track"] > 0) + { + if ([fAudLang4PopUp indexOfSelectedItem] == 0) + { + [fAudLang4PopUp selectItemAtIndex: 1]; + } + [self audioTrackPopUpChanged: fAudLang4PopUp]; + [fAudTrack4CodecPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Encoder"]]; + /* check our pref for core audio and use it in place of faac if applicable */ + if ([[NSUserDefaults standardUserDefaults] boolForKey: @"UseCoreAudio"] == YES && + [[chosenPreset objectForKey:@"Audio4Encoder"] isEqualToString: @"AAC (faac)"]) + { + [fAudTrack4CodecPopUp selectItemWithTitle:@"AAC (CoreAudio)"]; + } + [self audioTrackPopUpChanged: fAudTrack4CodecPopUp]; + [fAudTrack4MixPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Mixdown"]]; + /* check to see if the selections was available, if not, rerun audioTrackPopUpChanged using the codec to just set the default + * mixdown*/ + if ([fAudTrack4MixPopUp selectedItem] == nil) + { + [self audioTrackPopUpChanged: fAudTrack4CodecPopUp]; + } + [fAudTrack4RatePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Samplerate"]]; + /* We set the presets bitrate if it is *not* an AC3 track since that uses the input bitrate */ + if (![[chosenPreset objectForKey:@"Audio4Encoder"] isEqualToString:@"AC3 Passthru"]) + { + [fAudTrack4BitratePopUp selectItemWithTitle:[chosenPreset objectForKey:@"Audio4Bitrate"]]; + } + [fAudTrack4DrcSlider setFloatValue:[[chosenPreset objectForKey:@"Audio4TrackDRCSlider"] floatValue]]; + [self audioDRCSliderChanged: fAudTrack4DrcSlider]; + } + + /* We now cleanup any extra audio tracks that may have been previously set if we need to */ + + if (![chosenPreset objectForKey:@"Audio2Track"] || [chosenPreset objectForKey:@"Audio2Track"] == 0) + { + [fAudLang2PopUp selectItemAtIndex: 0]; + [self audioTrackPopUpChanged: fAudLang2PopUp]; + } + if (![chosenPreset objectForKey:@"Audio3Track"] || [chosenPreset objectForKey:@"Audio3Track"] > 0) + { + [fAudLang3PopUp selectItemAtIndex: 0]; + [self audioTrackPopUpChanged: fAudLang3PopUp]; + } + if (![chosenPreset objectForKey:@"Audio4Track"] || [chosenPreset objectForKey:@"Audio4Track"] > 0) + { + [fAudLang4PopUp selectItemAtIndex: 0]; + [self audioTrackPopUpChanged: fAudLang4PopUp]; + } + } + + /*Subtitles*/ + [fSubPopUp selectItemWithTitle:[chosenPreset objectForKey:@"Subtitles"]]; + /* Forced Subtitles */ + [fSubForcedCheck setState:[[chosenPreset objectForKey:@"SubtitlesForced"] intValue]]; + + /* Picture Settings */ + /* Note: objectForKey:@"UsesPictureSettings" refers to picture size, which encompasses: + * height, width, keep ar, anamorphic and crop settings. + * picture filters are handled separately below. + */ + /* Check to see if the objectForKey:@"UsesPictureSettings is greater than 0, as 0 means use picture sizing "None" + * ( 2 is use max for source and 1 is use exact size when the preset was created ) and the + * preset completely ignores any picture sizing values in the preset. + */ + if ([[chosenPreset objectForKey:@"UsesPictureSettings"] intValue] > 0) + { + hb_job_t * job = fTitle->job; + + /* If Cropping is set to custom, then recall all four crop values from + when the preset was created and apply them */ + if ([[chosenPreset objectForKey:@"PictureAutoCrop"] intValue] == 0) + { + [fPictureController setAutoCrop:NO]; + + /* Here we use the custom crop values saved at the time the preset was saved */ + job->crop[0] = [[chosenPreset objectForKey:@"PictureTopCrop"] intValue]; + job->crop[1] = [[chosenPreset objectForKey:@"PictureBottomCrop"] intValue]; + job->crop[2] = [[chosenPreset objectForKey:@"PictureLeftCrop"] intValue]; + job->crop[3] = [[chosenPreset objectForKey:@"PictureRightCrop"] intValue]; + + } + else /* if auto crop has been saved in preset, set to auto and use post scan auto crop */ + { + [fPictureController setAutoCrop:YES]; + /* Here we use the auto crop values determined right after scan */ + job->crop[0] = AutoCropTop; + job->crop[1] = AutoCropBottom; + job->crop[2] = AutoCropLeft; + job->crop[3] = AutoCropRight; + + } + + + /* Check to see if the objectForKey:@"UsesPictureSettings is 2 which is "Use Max for the source */ + if ([[chosenPreset objectForKey:@"UsesPictureSettings"] intValue] == 2 || [[chosenPreset objectForKey:@"UsesMaxPictureSettings"] intValue] == 1) + { + /* Use Max Picture settings for whatever the dvd is.*/ + [self revertPictureSizeToMax:nil]; + job->keep_ratio = [[chosenPreset objectForKey:@"PictureKeepRatio"] intValue]; + if (job->keep_ratio == 1) + { + hb_fix_aspect( job, HB_KEEP_WIDTH ); + if( job->height > fTitle->height ) + { + job->height = fTitle->height; + hb_fix_aspect( job, HB_KEEP_HEIGHT ); + } + } + job->anamorphic.mode = [[chosenPreset objectForKey:@"PicturePAR"] intValue]; + } + else // /* If not 0 or 2 we assume objectForKey:@"UsesPictureSettings is 1 which is "Use picture sizing from when the preset was set" */ + { + /* we check to make sure the presets width/height does not exceed the sources width/height */ + if (fTitle->width < [[chosenPreset objectForKey:@"PictureWidth"] intValue] || fTitle->height < [[chosenPreset objectForKey:@"PictureHeight"] intValue]) + { + /* if so, then we use the sources height and width to avoid scaling up */ + //job->width = fTitle->width; + //job->height = fTitle->height; + [self revertPictureSizeToMax:nil]; + } + else // source width/height is >= the preset height/width + { + /* we can go ahead and use the presets values for height and width */ + job->width = [[chosenPreset objectForKey:@"PictureWidth"] intValue]; + job->height = [[chosenPreset objectForKey:@"PictureHeight"] intValue]; + } + job->keep_ratio = [[chosenPreset objectForKey:@"PictureKeepRatio"] intValue]; + if (job->keep_ratio == 1) + { + hb_fix_aspect( job, HB_KEEP_WIDTH ); + if( job->height > fTitle->height ) + { + job->height = fTitle->height; + hb_fix_aspect( job, HB_KEEP_HEIGHT ); + } + } + job->anamorphic.mode = [[chosenPreset objectForKey:@"PicturePAR"] intValue]; + + } + + + } + /* If the preset has an objectForKey:@"UsesPictureFilters", and handle the filters here */ + if ([chosenPreset objectForKey:@"UsesPictureFilters"] && [[chosenPreset objectForKey:@"UsesPictureFilters"] intValue] > 0) + { + /* Filters */ + + /* We only allow *either* Decomb or Deinterlace. So check for the PictureDecombDeinterlace key. + * also, older presets may not have this key, in which case we also check to see if that preset had PictureDecomb + * specified, in which case we use decomb and ignore any possible Deinterlace settings as using both was less than + * sane. + */ + [fPictureController setUseDecomb:1]; + [fPictureController setDecomb:0]; + [fPictureController setDeinterlace:0]; + if ([[chosenPreset objectForKey:@"PictureDecombDeinterlace"] intValue] == 1 || [[chosenPreset objectForKey:@"PictureDecomb"] intValue] > 0) + { + /* we are using decomb */ + /* Decomb */ + if ([[chosenPreset objectForKey:@"PictureDecomb"] intValue] > 0) + { + [fPictureController setDecomb:[[chosenPreset objectForKey:@"PictureDecomb"] intValue]]; + + /* if we are using "Custom" in the decomb setting, also set the custom string*/ + if ([[chosenPreset objectForKey:@"PictureDecomb"] intValue] == 1) + { + [fPictureController setDecombCustomString:[chosenPreset objectForKey:@"PictureDecombCustom"]]; + } + } + } + else + { + /* We are using Deinterlace */ + /* Deinterlace */ + if ([[chosenPreset objectForKey:@"PictureDeinterlace"] intValue] > 0) + { + [fPictureController setUseDecomb:0]; + [fPictureController setDeinterlace:[[chosenPreset objectForKey:@"PictureDeinterlace"] intValue]]; + /* if we are using "Custom" in the deinterlace setting, also set the custom string*/ + if ([[chosenPreset objectForKey:@"PictureDeinterlace"] intValue] == 1) + { + [fPictureController setDeinterlaceCustomString:[chosenPreset objectForKey:@"PictureDeinterlaceCustom"]]; + } + } + } + + + /* Detelecine */ + if ([[chosenPreset objectForKey:@"PictureDetelecine"] intValue] > 0) + { + [fPictureController setDetelecine:[[chosenPreset objectForKey:@"PictureDetelecine"] intValue]]; + /* if we are using "Custom" in the detelecine setting, also set the custom string*/ + if ([[chosenPreset objectForKey:@"PictureDetelecine"] intValue] == 1) + { + [fPictureController setDetelecineCustomString:[chosenPreset objectForKey:@"PictureDetelecineCustom"]]; + } + } + else + { + [fPictureController setDetelecine:0]; + } + + /* Denoise */ + if ([[chosenPreset objectForKey:@"PictureDenoise"] intValue] > 0) + { + [fPictureController setDenoise:[[chosenPreset objectForKey:@"PictureDenoise"] intValue]]; + /* if we are using "Custom" in the denoise setting, also set the custom string*/ + if ([[chosenPreset objectForKey:@"PictureDenoise"] intValue] == 1) + { + [fPictureController setDenoiseCustomString:[chosenPreset objectForKey:@"PictureDenoiseCustom"]]; + } + } + else + { + [fPictureController setDenoise:0]; + } + + /* Deblock */ + if ([[chosenPreset objectForKey:@"PictureDeblock"] intValue] == 1) + { + /* if its a one, then its the old on/off deblock, set on to 5*/ + [fPictureController setDeblock:5]; + } + else + { + /* use the settings intValue */ + [fPictureController setDeblock:[[chosenPreset objectForKey:@"PictureDeblock"] intValue]]; + } + + if ([[chosenPreset objectForKey:@"VideoGrayScale"] intValue] == 1) + { + [fPictureController setGrayscale:1]; + } + else + { + [fPictureController setGrayscale:0]; + } + } + /* we call SetTitle: in fPictureController so we get an instant update in the Picture Settings window */ + [fPictureController SetTitle:fTitle]; + [fPictureController SetTitle:fTitle]; + [self calculatePictureSizing:nil]; + } +} + + +#pragma mark - +#pragma mark Manage Presets + +- (void) loadPresets { + /* We declare the default NSFileManager into fileManager */ + NSFileManager * fileManager = [NSFileManager defaultManager]; + /*We define the location of the user presets file */ + UserPresetsFile = @"~/Library/Application Support/HandBrake/UserPresets.plist"; + UserPresetsFile = [[UserPresetsFile stringByExpandingTildeInPath]retain]; + /* We check for the presets.plist */ + if ([fileManager fileExistsAtPath:UserPresetsFile] == 0) + { + [fileManager createFileAtPath:UserPresetsFile contents:nil attributes:nil]; + } + + UserPresets = [[NSMutableArray alloc] initWithContentsOfFile:UserPresetsFile]; + if (nil == UserPresets) + { + UserPresets = [[NSMutableArray alloc] init]; + [self addFactoryPresets:nil]; + } + [fPresetsOutlineView reloadData]; + + [self checkBuiltInsForUpdates]; +} + +- (void) checkBuiltInsForUpdates { + + BOOL updateBuiltInPresets = NO; + int i = 0; + NSEnumerator *enumerator = [UserPresets objectEnumerator]; + id tempObject; + while (tempObject = [enumerator nextObject]) + { + /* iterate through the built in presets to see if any have an old build number */ + NSMutableDictionary *thisPresetDict = tempObject; + /*Key Type == 0 is built in, and key PresetBuildNumber is the build number it was created with */ + if ([[thisPresetDict objectForKey:@"Type"] intValue] == 0) + { + if (![thisPresetDict objectForKey:@"PresetBuildNumber"] || [[thisPresetDict objectForKey:@"PresetBuildNumber"] intValue] < [[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] intValue]) + { + updateBuiltInPresets = YES; + } + } + i++; + } + /* if we have built in presets to update, then do so AlertBuiltInPresetUpdate*/ + if ( updateBuiltInPresets == YES) + { + if( [[NSUserDefaults standardUserDefaults] boolForKey:@"AlertBuiltInPresetUpdate"] == YES) + { + /* Show an alert window that built in presets will be updated */ + /*On Screen Notification*/ + int status; + NSBeep(); + status = NSRunAlertPanel(@"HandBrake has determined your built in presets are out of date...",@"HandBrake will now update your built-in presets.", @"OK", nil, nil); + [NSApp requestUserAttention:NSCriticalRequest]; + } + /* when alert is dismissed, go ahead and update the built in presets */ + [self addFactoryPresets:nil]; + } + +} + + +- (IBAction) showAddPresetPanel: (id) sender +{ + /* Deselect the currently selected Preset if there is one*/ + [fPresetsOutlineView deselectRow:[fPresetsOutlineView selectedRow]]; + + /* Populate the preset picture settings popup here */ + [fPresetNewPicSettingsPopUp removeAllItems]; + [fPresetNewPicSettingsPopUp addItemWithTitle:@"None"]; + [fPresetNewPicSettingsPopUp addItemWithTitle:@"Current"]; + [fPresetNewPicSettingsPopUp addItemWithTitle:@"Source Maximum (post source scan)"]; + [fPresetNewPicSettingsPopUp selectItemAtIndex: 0]; + /* Uncheck the preset use filters checkbox */ + [fPresetNewPicFiltersCheck setState:NSOffState]; + // fPresetNewFolderCheck + [fPresetNewFolderCheck setState:NSOffState]; + /* Erase info from the input fields*/ + [fPresetNewName setStringValue: @""]; + [fPresetNewDesc setStringValue: @""]; + /* Show the panel */ + [NSApp beginSheet:fAddPresetPanel modalForWindow:fWindow modalDelegate:nil didEndSelector:NULL contextInfo:NULL]; +} + +- (IBAction) closeAddPresetPanel: (id) sender +{ + [NSApp endSheet: fAddPresetPanel]; + [fAddPresetPanel orderOut: self]; +} + +- (IBAction)addUserPreset:(id)sender +{ + if (![[fPresetNewName stringValue] length]) + NSRunAlertPanel(@"Warning!", @"You need to insert a name for the preset.", @"OK", nil , nil); + else + { + /* Here we create a custom user preset */ + [UserPresets addObject:[self createPreset]]; + [self addPreset]; + + [self closeAddPresetPanel:nil]; + } +} +- (void)addPreset +{ + + + /* We Reload the New Table data for presets */ + [fPresetsOutlineView reloadData]; + /* We save all of the preset data here */ + [self savePreset]; +} + +- (void)sortPresets +{ + + + /* We Sort the Presets By Factory or Custom */ + NSSortDescriptor * presetTypeDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"Type" + ascending:YES] autorelease]; + /* We Sort the Presets Alphabetically by name We do not use this now as we have drag and drop*/ + /* + NSSortDescriptor * presetNameDescriptor=[[[NSSortDescriptor alloc] initWithKey:@"PresetName" + ascending:YES selector:@selector(caseInsensitiveCompare:)] autorelease]; + //NSArray *sortDescriptors=[NSArray arrayWithObjects:presetTypeDescriptor,presetNameDescriptor,nil]; + + */ + /* Since we can drag and drop our custom presets, lets just sort by type and not name */ + NSArray *sortDescriptors=[NSArray arrayWithObjects:presetTypeDescriptor,nil]; + NSArray *sortedArray=[UserPresets sortedArrayUsingDescriptors:sortDescriptors]; + [UserPresets setArray:sortedArray]; + + +} + +- (IBAction)insertPreset:(id)sender +{ + int index = [fPresetsOutlineView selectedRow]; + [UserPresets insertObject:[self createPreset] atIndex:index]; + [fPresetsOutlineView reloadData]; + [self savePreset]; +} + +- (NSDictionary *)createPreset +{ + NSMutableDictionary *preset = [[NSMutableDictionary alloc] init]; + /* Preset build number */ + [preset setObject:[NSString stringWithFormat: @"%d", [[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] intValue]] forKey:@"PresetBuildNumber"]; + [preset setObject:[fPresetNewName stringValue] forKey:@"PresetName"]; + /* Get the New Preset Name from the field in the AddPresetPanel */ + [preset setObject:[fPresetNewName stringValue] forKey:@"PresetName"]; + /* Set whether or not this is to be a folder fPresetNewFolderCheck*/ + [preset setObject:[NSNumber numberWithBool:[fPresetNewFolderCheck state]] forKey:@"Folder"]; + /*Set whether or not this is a user preset or factory 0 is factory, 1 is user*/ + [preset setObject:[NSNumber numberWithInt:1] forKey:@"Type"]; + /*Set whether or not this is default, at creation set to 0*/ + [preset setObject:[NSNumber numberWithInt:0] forKey:@"Default"]; + if ([fPresetNewFolderCheck state] == YES) + { + /* initialize and set an empty array for children here since we are a new folder */ + NSMutableArray *childrenArray = [[NSMutableArray alloc] init]; + [preset setObject:[NSMutableArray arrayWithArray: childrenArray] forKey:@"ChildrenArray"]; + [childrenArray autorelease]; + } + else // we are not creating a preset folder, so we go ahead with the rest of the preset info + { + /*Get the whether or not to apply pic Size and Cropping (includes Anamorphic)*/ + [preset setObject:[NSNumber numberWithInt:[fPresetNewPicSettingsPopUp indexOfSelectedItem]] forKey:@"UsesPictureSettings"]; + /* Get whether or not to use the current Picture Filter settings for the preset */ + [preset setObject:[NSNumber numberWithInt:[fPresetNewPicFiltersCheck state]] forKey:@"UsesPictureFilters"]; + + /* Get New Preset Description from the field in the AddPresetPanel*/ + [preset setObject:[fPresetNewDesc stringValue] forKey:@"PresetDescription"]; + /* File Format */ + [preset setObject:[fDstFormatPopUp titleOfSelectedItem] forKey:@"FileFormat"]; + /* Chapter Markers fCreateChapterMarkers*/ + [preset setObject:[NSNumber numberWithInt:[fCreateChapterMarkers state]] forKey:@"ChapterMarkers"]; + /* Allow Mpeg4 64 bit formatting +4GB file sizes */ + [preset setObject:[NSNumber numberWithInt:[fDstMp4LargeFileCheck state]] forKey:@"Mp4LargeFile"]; + /* Mux mp4 with http optimization */ + [preset setObject:[NSNumber numberWithInt:[fDstMp4HttpOptFileCheck state]] forKey:@"Mp4HttpOptimize"]; + /* Add iPod uuid atom */ + [preset setObject:[NSNumber numberWithInt:[fDstMp4iPodFileCheck state]] forKey:@"Mp4iPodCompatible"]; + + /* Codecs */ + /* Video encoder */ + [preset setObject:[fVidEncoderPopUp titleOfSelectedItem] forKey:@"VideoEncoder"]; + /* x264 Option String */ + [preset setObject:[fAdvancedOptions optionsString] forKey:@"x264Option"]; + + [preset setObject:[NSNumber numberWithInt:[fVidQualityMatrix selectedRow]] forKey:@"VideoQualityType"]; + [preset setObject:[fVidTargetSizeField stringValue] forKey:@"VideoTargetSize"]; + [preset setObject:[fVidBitrateField stringValue] forKey:@"VideoAvgBitrate"]; + [preset setObject:[NSNumber numberWithFloat:[fVidQualityRFField floatValue]] forKey:@"VideoQualitySlider"]; + + /* Video framerate */ + if ([fVidRatePopUp indexOfSelectedItem] == 0) // Same as source is selected + { + [preset setObject:@"Same as source" forKey:@"VideoFramerate"]; + } + else // we can record the actual titleOfSelectedItem + { + [preset setObject:[fVidRatePopUp titleOfSelectedItem] forKey:@"VideoFramerate"]; + } + + /* 2 Pass Encoding */ + [preset setObject:[NSNumber numberWithInt:[fVidTwoPassCheck state]] forKey:@"VideoTwoPass"]; + /* Turbo 2 pass Encoding fVidTurboPassCheck*/ + [preset setObject:[NSNumber numberWithInt:[fVidTurboPassCheck state]] forKey:@"VideoTurboTwoPass"]; + /*Picture Settings*/ + hb_job_t * job = fTitle->job; + /* Picture Sizing */ + /* Use Max Picture settings for whatever the dvd is.*/ + [preset setObject:[NSNumber numberWithInt:0] forKey:@"UsesMaxPictureSettings"]; + [preset setObject:[NSNumber numberWithInt:fTitle->job->width] forKey:@"PictureWidth"]; + [preset setObject:[NSNumber numberWithInt:fTitle->job->height] forKey:@"PictureHeight"]; + [preset setObject:[NSNumber numberWithInt:fTitle->job->keep_ratio] forKey:@"PictureKeepRatio"]; + [preset setObject:[NSNumber numberWithInt:fTitle->job->anamorphic.mode] forKey:@"PicturePAR"]; + + /* Set crop settings here */ + [preset setObject:[NSNumber numberWithInt:[fPictureController autoCrop]] forKey:@"PictureAutoCrop"]; + [preset setObject:[NSNumber numberWithInt:job->crop[0]] forKey:@"PictureTopCrop"]; + [preset setObject:[NSNumber numberWithInt:job->crop[1]] forKey:@"PictureBottomCrop"]; + [preset setObject:[NSNumber numberWithInt:job->crop[2]] forKey:@"PictureLeftCrop"]; + [preset setObject:[NSNumber numberWithInt:job->crop[3]] forKey:@"PictureRightCrop"]; + + /* Picture Filters */ + [preset setObject:[NSNumber numberWithInt:[fPictureController useDecomb]] forKey:@"PictureDecombDeinterlace"]; + [preset setObject:[NSNumber numberWithInt:[fPictureController deinterlace]] forKey:@"PictureDeinterlace"]; + [preset setObject:[fPictureController deinterlaceCustomString] forKey:@"PictureDeinterlaceCustom"]; + [preset setObject:[NSNumber numberWithInt:[fPictureController detelecine]] forKey:@"PictureDetelecine"]; + [preset setObject:[fPictureController detelecineCustomString] forKey:@"PictureDetelecineCustom"]; + [preset setObject:[NSNumber numberWithInt:[fPictureController denoise]] forKey:@"PictureDenoise"]; + [preset setObject:[fPictureController denoiseCustomString] forKey:@"PictureDenoiseCustom"]; + [preset setObject:[NSNumber numberWithInt:[fPictureController deblock]] forKey:@"PictureDeblock"]; + [preset setObject:[NSNumber numberWithInt:[fPictureController decomb]] forKey:@"PictureDecomb"]; + [preset setObject:[fPictureController decombCustomString] forKey:@"PictureDecombCustom"]; + [preset setObject:[NSNumber numberWithInt:[fPictureController grayscale]] forKey:@"VideoGrayScale"]; + + /*Audio*/ + NSMutableArray *audioListArray = [[NSMutableArray alloc] init]; + /* we actually call the methods for the nests here */ + if ([fAudLang1PopUp indexOfSelectedItem] > 0) + { + NSMutableDictionary *audioTrack1Array = [[NSMutableDictionary alloc] init]; + [audioTrack1Array setObject:[NSNumber numberWithInt:[fAudLang1PopUp indexOfSelectedItem]] forKey:@"AudioTrack"]; + [audioTrack1Array setObject:[fAudLang1PopUp titleOfSelectedItem] forKey:@"AudioTrackDescription"]; + [audioTrack1Array setObject:[fAudTrack1CodecPopUp titleOfSelectedItem] forKey:@"AudioEncoder"]; + [audioTrack1Array setObject:[fAudTrack1MixPopUp titleOfSelectedItem] forKey:@"AudioMixdown"]; + [audioTrack1Array setObject:[fAudTrack1RatePopUp titleOfSelectedItem] forKey:@"AudioSamplerate"]; + [audioTrack1Array setObject:[fAudTrack1BitratePopUp titleOfSelectedItem] forKey:@"AudioBitrate"]; + [audioTrack1Array setObject:[NSNumber numberWithFloat:[fAudTrack1DrcSlider floatValue]] forKey:@"AudioTrackDRCSlider"]; + [audioTrack1Array autorelease]; + [audioListArray addObject:audioTrack1Array]; + } + + if ([fAudLang2PopUp indexOfSelectedItem] > 0) + { + NSMutableDictionary *audioTrack2Array = [[NSMutableDictionary alloc] init]; + [audioTrack2Array setObject:[NSNumber numberWithInt:[fAudLang2PopUp indexOfSelectedItem]] forKey:@"AudioTrack"]; + [audioTrack2Array setObject:[fAudLang2PopUp titleOfSelectedItem] forKey:@"AudioTrackDescription"]; + [audioTrack2Array setObject:[fAudTrack2CodecPopUp titleOfSelectedItem] forKey:@"AudioEncoder"]; + [audioTrack2Array setObject:[fAudTrack2MixPopUp titleOfSelectedItem] forKey:@"AudioMixdown"]; + [audioTrack2Array setObject:[fAudTrack2RatePopUp titleOfSelectedItem] forKey:@"AudioSamplerate"]; + [audioTrack2Array setObject:[fAudTrack2BitratePopUp titleOfSelectedItem] forKey:@"AudioBitrate"]; + [audioTrack2Array setObject:[NSNumber numberWithFloat:[fAudTrack2DrcSlider floatValue]] forKey:@"AudioTrackDRCSlider"]; + [audioTrack2Array autorelease]; + [audioListArray addObject:audioTrack2Array]; + } + + if ([fAudLang3PopUp indexOfSelectedItem] > 0) + { + NSMutableDictionary *audioTrack3Array = [[NSMutableDictionary alloc] init]; + [audioTrack3Array setObject:[NSNumber numberWithInt:[fAudLang3PopUp indexOfSelectedItem]] forKey:@"AudioTrack"]; + [audioTrack3Array setObject:[fAudLang3PopUp titleOfSelectedItem] forKey:@"AudioTrackDescription"]; + [audioTrack3Array setObject:[fAudTrack3CodecPopUp titleOfSelectedItem] forKey:@"AudioEncoder"]; + [audioTrack3Array setObject:[fAudTrack3MixPopUp titleOfSelectedItem] forKey:@"AudioMixdown"]; + [audioTrack3Array setObject:[fAudTrack3RatePopUp titleOfSelectedItem] forKey:@"AudioSamplerate"]; + [audioTrack3Array setObject:[fAudTrack3BitratePopUp titleOfSelectedItem] forKey:@"AudioBitrate"]; + [audioTrack3Array setObject:[NSNumber numberWithFloat:[fAudTrack3DrcSlider floatValue]] forKey:@"AudioTrackDRCSlider"]; + [audioTrack3Array autorelease]; + [audioListArray addObject:audioTrack3Array]; + } + + if ([fAudLang4PopUp indexOfSelectedItem] > 0) + { + NSMutableDictionary *audioTrack4Array = [[NSMutableDictionary alloc] init]; + [audioTrack4Array setObject:[NSNumber numberWithInt:[fAudLang4PopUp indexOfSelectedItem]] forKey:@"AudioTrack"]; + [audioTrack4Array setObject:[fAudLang4PopUp titleOfSelectedItem] forKey:@"AudioTrackDescription"]; + [audioTrack4Array setObject:[fAudTrack4CodecPopUp titleOfSelectedItem] forKey:@"AudioEncoder"]; + [audioTrack4Array setObject:[fAudTrack4MixPopUp titleOfSelectedItem] forKey:@"AudioMixdown"]; + [audioTrack4Array setObject:[fAudTrack4RatePopUp titleOfSelectedItem] forKey:@"AudioSamplerate"]; + [audioTrack4Array setObject:[fAudTrack4BitratePopUp titleOfSelectedItem] forKey:@"AudioBitrate"]; + [audioTrack4Array setObject:[NSNumber numberWithFloat:[fAudTrack4DrcSlider floatValue]] forKey:@"AudioTrackDRCSlider"]; + [audioTrack4Array autorelease]; + [audioListArray addObject:audioTrack4Array]; + } + + + [preset setObject:[NSMutableArray arrayWithArray: audioListArray] forKey:@"AudioList"]; + + + /* Temporarily remove subtitles from creating a new preset as it has to be converted over to use the new + * subititle array code. */ + /* Subtitles*/ + //[preset setObject:[fSubPopUp titleOfSelectedItem] forKey:@"Subtitles"]; + /* Forced Subtitles */ + //[preset setObject:[NSNumber numberWithInt:[fSubForcedCheck state]] forKey:@"SubtitlesForced"]; + } + [preset autorelease]; + return preset; + +} + +- (void)savePreset +{ + [UserPresets writeToFile:UserPresetsFile atomically:YES]; + /* We get the default preset in case it changed */ + [self getDefaultPresets:nil]; + +} + +- (IBAction)deletePreset:(id)sender +{ + + + if ( [fPresetsOutlineView numberOfSelectedRows] == 0 ) + { + return; + } + /* Alert user before deleting preset */ + int status; + status = NSRunAlertPanel(@"Warning!", @"Are you sure that you want to delete the selected preset?", @"OK", @"Cancel", nil); + + if ( status == NSAlertDefaultReturn ) + { + int presetToModLevel = [fPresetsOutlineView levelForItem: [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]]]; + NSDictionary *presetToMod = [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]]; + NSDictionary *presetToModParent = [fPresetsOutlineView parentForItem: presetToMod]; + + NSEnumerator *enumerator; + NSMutableArray *presetsArrayToMod; + NSMutableArray *tempArray; + id tempObject; + /* If we are a root level preset, we are modding the UserPresets array */ + if (presetToModLevel == 0) + { + presetsArrayToMod = UserPresets; + } + else // We have a parent preset, so we modify the chidren array object for key + { + presetsArrayToMod = [presetToModParent objectForKey:@"ChildrenArray"]; + } + + enumerator = [presetsArrayToMod objectEnumerator]; + tempArray = [NSMutableArray array]; + + while (tempObject = [enumerator nextObject]) + { + NSDictionary *thisPresetDict = tempObject; + if (thisPresetDict == presetToMod) + { + [tempArray addObject:tempObject]; + } + } + + [presetsArrayToMod removeObjectsInArray:tempArray]; + [fPresetsOutlineView reloadData]; + [self savePreset]; + } +} + + +#pragma mark - +#pragma mark Import Export Preset(s) + +- (IBAction) browseExportPresetFile: (id) sender +{ + /* Open a panel to let the user choose where and how to save the export file */ + NSSavePanel * panel = [NSSavePanel savePanel]; + /* We get the current file name and path from the destination field here */ + NSString *defaultExportDirectory = [NSString stringWithFormat: @"%@/Desktop/", NSHomeDirectory()]; + + [panel beginSheetForDirectory: defaultExportDirectory file: @"HB_Export.plist" + modalForWindow: fWindow modalDelegate: self + didEndSelector: @selector( browseExportPresetFileDone:returnCode:contextInfo: ) + contextInfo: NULL]; +} + +- (void) browseExportPresetFileDone: (NSSavePanel *) sheet + returnCode: (int) returnCode contextInfo: (void *) contextInfo +{ + if( returnCode == NSOKButton ) + { + NSString *presetExportDirectory = [[sheet filename] stringByDeletingLastPathComponent]; + NSString *exportPresetsFile = [sheet filename]; + [[NSUserDefaults standardUserDefaults] setObject:presetExportDirectory forKey:@"LastPresetExportDirectory"]; + /* We check for the presets.plist */ + if ([[NSFileManager defaultManager] fileExistsAtPath:exportPresetsFile] == 0) + { + [[NSFileManager defaultManager] createFileAtPath:exportPresetsFile contents:nil attributes:nil]; + } + NSMutableArray * presetsToExport = [[NSMutableArray alloc] initWithContentsOfFile:exportPresetsFile]; + if (nil == presetsToExport) + { + presetsToExport = [[NSMutableArray alloc] init]; + + /* now get and add selected presets to export */ + + } + if ([fPresetsOutlineView selectedRow] >= 0 && [[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Folder"] intValue] != 1) + { + [presetsToExport addObject:[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]]]; + [presetsToExport writeToFile:exportPresetsFile atomically:YES]; + + } + + } +} + + +- (IBAction) browseImportPresetFile: (id) sender +{ + + NSOpenPanel * panel; + + panel = [NSOpenPanel openPanel]; + [panel setAllowsMultipleSelection: NO]; + [panel setCanChooseFiles: YES]; + [panel setCanChooseDirectories: NO ]; + NSString * sourceDirectory; + if ([[NSUserDefaults standardUserDefaults] stringForKey:@"LastPresetImportDirectory"]) + { + sourceDirectory = [[NSUserDefaults standardUserDefaults] stringForKey:@"LastPresetImportDirectory"]; + } + else + { + sourceDirectory = @"~/Desktop"; + sourceDirectory = [sourceDirectory stringByExpandingTildeInPath]; + } + /* we open up the browse sources sheet here and call for browseSourcesDone after the sheet is closed + * to evaluate whether we want to specify a title, we pass the sender in the contextInfo variable + */ + /* set this for allowed file types, not sure if we should allow xml or not */ + NSArray *fileTypes = [NSArray arrayWithObjects:@"plist", @"xml", nil]; + [panel beginSheetForDirectory: sourceDirectory file: nil types: fileTypes + modalForWindow: fWindow modalDelegate: self + didEndSelector: @selector( browseImportPresetDone:returnCode:contextInfo: ) + contextInfo: sender]; +} + +- (void) browseImportPresetDone: (NSSavePanel *) sheet + returnCode: (int) returnCode contextInfo: (void *) contextInfo +{ + if( returnCode == NSOKButton ) + { + NSString *importPresetsDirectory = [[sheet filename] stringByDeletingLastPathComponent]; + NSString *importPresetsFile = [sheet filename]; + [[NSUserDefaults standardUserDefaults] setObject:importPresetsDirectory forKey:@"LastPresetImportDirectory"]; + /* NOTE: here we need to do some sanity checking to verify we do not hose up our presets file */ + NSMutableArray * presetsToImport = [[NSMutableArray alloc] initWithContentsOfFile:importPresetsFile]; + /* iterate though the new array of presets to import and add them to our presets array */ + int i = 0; + NSEnumerator *enumerator = [presetsToImport objectEnumerator]; + id tempObject; + while (tempObject = [enumerator nextObject]) + { + /* make any changes to the incoming preset we see fit */ + /* make sure the incoming preset is not tagged as default */ + [tempObject setObject:[NSNumber numberWithInt:0] forKey:@"Default"]; + /* prepend "(imported) to the name of the incoming preset for clarification since it can be changed */ + NSString * prependedName = [@"(import) " stringByAppendingString:[tempObject objectForKey:@"PresetName"]] ; + [tempObject setObject:prependedName forKey:@"PresetName"]; + + /* actually add the new preset to our presets array */ + [UserPresets addObject:tempObject]; + i++; + } + [presetsToImport autorelease]; + [self sortPresets]; + [self addPreset]; + + } +} + +#pragma mark - +#pragma mark Manage Default Preset + +- (IBAction)getDefaultPresets:(id)sender +{ + presetHbDefault = nil; + presetUserDefault = nil; + presetUserDefaultParent = nil; + presetUserDefaultParentParent = nil; + NSMutableDictionary *presetHbDefaultParent = nil; + NSMutableDictionary *presetHbDefaultParentParent = nil; + + int i = 0; + BOOL userDefaultFound = NO; + presetCurrentBuiltInCount = 0; + /* First we iterate through the root UserPresets array to check for defaults */ + NSEnumerator *enumerator = [UserPresets objectEnumerator]; + id tempObject; + while (tempObject = [enumerator nextObject]) + { + NSMutableDictionary *thisPresetDict = tempObject; + if ([[thisPresetDict objectForKey:@"Default"] intValue] == 1) // 1 is HB default + { + presetHbDefault = thisPresetDict; + } + if ([[thisPresetDict objectForKey:@"Default"] intValue] == 2) // 2 is User specified default + { + presetUserDefault = thisPresetDict; + userDefaultFound = YES; + } + if ([[thisPresetDict objectForKey:@"Type"] intValue] == 0) // Type 0 is a built in preset + { + presetCurrentBuiltInCount++; // <--increment the current number of built in presets + } + i++; + + /* if we run into a folder, go to level 1 and iterate through the children arrays for the default */ + if ([thisPresetDict objectForKey:@"ChildrenArray"]) + { + NSMutableDictionary *thisPresetDictParent = thisPresetDict; + NSEnumerator *enumerator = [[thisPresetDict objectForKey:@"ChildrenArray"] objectEnumerator]; + id tempObject; + while (tempObject = [enumerator nextObject]) + { + NSMutableDictionary *thisPresetDict = tempObject; + if ([[thisPresetDict objectForKey:@"Default"] intValue] == 1) // 1 is HB default + { + presetHbDefault = thisPresetDict; + presetHbDefaultParent = thisPresetDictParent; + } + if ([[thisPresetDict objectForKey:@"Default"] intValue] == 2) // 2 is User specified default + { + presetUserDefault = thisPresetDict; + presetUserDefaultParent = thisPresetDictParent; + userDefaultFound = YES; + } + + /* if we run into a folder, go to level 2 and iterate through the children arrays for the default */ + if ([thisPresetDict objectForKey:@"ChildrenArray"]) + { + NSMutableDictionary *thisPresetDictParentParent = thisPresetDict; + NSEnumerator *enumerator = [[thisPresetDict objectForKey:@"ChildrenArray"] objectEnumerator]; + id tempObject; + while (tempObject = [enumerator nextObject]) + { + NSMutableDictionary *thisPresetDict = tempObject; + if ([[thisPresetDict objectForKey:@"Default"] intValue] == 1) // 1 is HB default + { + presetHbDefault = thisPresetDict; + presetHbDefaultParent = thisPresetDictParent; + presetHbDefaultParentParent = thisPresetDictParentParent; + } + if ([[thisPresetDict objectForKey:@"Default"] intValue] == 2) // 2 is User specified default + { + presetUserDefault = thisPresetDict; + presetUserDefaultParent = thisPresetDictParent; + presetUserDefaultParentParent = thisPresetDictParentParent; + userDefaultFound = YES; + } + + } + } + } + } + + } + /* check to see if a user specified preset was found, if not then assign the parents for + * the presetHbDefault so that we can open the parents for the nested presets + */ + if (userDefaultFound == NO) + { + presetUserDefaultParent = presetHbDefaultParent; + presetUserDefaultParentParent = presetHbDefaultParentParent; + } +} + +- (IBAction)setDefaultPreset:(id)sender +{ +/* We need to determine if the item is a folder */ + if ([[[fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]] objectForKey:@"Folder"] intValue] == 1) + { + return; + } + + int i = 0; + NSEnumerator *enumerator = [UserPresets objectEnumerator]; + id tempObject; + /* First make sure the old user specified default preset is removed */ + while (tempObject = [enumerator nextObject]) + { + NSMutableDictionary *thisPresetDict = tempObject; + if ([[tempObject objectForKey:@"Default"] intValue] != 1) // if not the default HB Preset, set to 0 + { + [[UserPresets objectAtIndex:i] setObject:[NSNumber numberWithInt:0] forKey:@"Default"]; + } + + /* if we run into a folder, go to level 1 and iterate through the children arrays for the default */ + if ([thisPresetDict objectForKey:@"ChildrenArray"]) + { + NSEnumerator *enumerator = [[thisPresetDict objectForKey:@"ChildrenArray"] objectEnumerator]; + id tempObject; + int ii = 0; + while (tempObject = [enumerator nextObject]) + { + NSMutableDictionary *thisPresetDict1 = tempObject; + if ([[tempObject objectForKey:@"Default"] intValue] != 1) // if not the default HB Preset, set to 0 + { + [[[thisPresetDict objectForKey:@"ChildrenArray"] objectAtIndex:ii] setObject:[NSNumber numberWithInt:0] forKey:@"Default"]; + } + /* if we run into a folder, go to level 2 and iterate through the children arrays for the default */ + if ([thisPresetDict1 objectForKey:@"ChildrenArray"]) + { + NSEnumerator *enumerator = [[thisPresetDict1 objectForKey:@"ChildrenArray"] objectEnumerator]; + id tempObject; + int iii = 0; + while (tempObject = [enumerator nextObject]) + { + if ([[tempObject objectForKey:@"Default"] intValue] != 1) // if not the default HB Preset, set to 0 + { + [[[thisPresetDict1 objectForKey:@"ChildrenArray"] objectAtIndex:iii] setObject:[NSNumber numberWithInt:0] forKey:@"Default"]; + } + iii++; + } + } + ii++; + } + + } + i++; + } + + + int presetToModLevel = [fPresetsOutlineView levelForItem: [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]]]; + NSDictionary *presetToMod = [fPresetsOutlineView itemAtRow:[fPresetsOutlineView selectedRow]]; + NSDictionary *presetToModParent = [fPresetsOutlineView parentForItem: presetToMod]; + + + NSMutableArray *presetsArrayToMod; + NSMutableArray *tempArray; + + /* If we are a root level preset, we are modding the UserPresets array */ + if (presetToModLevel == 0) + { + presetsArrayToMod = UserPresets; + } + else // We have a parent preset, so we modify the chidren array object for key + { + presetsArrayToMod = [presetToModParent objectForKey:@"ChildrenArray"]; + } + + enumerator = [presetsArrayToMod objectEnumerator]; + tempArray = [NSMutableArray array]; + int iiii = 0; + while (tempObject = [enumerator nextObject]) + { + NSDictionary *thisPresetDict = tempObject; + if (thisPresetDict == presetToMod) + { + if ([[tempObject objectForKey:@"Default"] intValue] != 1) // if not the default HB Preset, set to 2 + { + [[presetsArrayToMod objectAtIndex:iiii] setObject:[NSNumber numberWithInt:2] forKey:@"Default"]; + } + } + iiii++; + } + + + /* We save all of the preset data here */ + [self savePreset]; + /* We Reload the New Table data for presets */ + [fPresetsOutlineView reloadData]; +} + +- (IBAction)selectDefaultPreset:(id)sender +{ + NSMutableDictionary *presetToMod; + /* if there is a user specified default, we use it */ + if (presetUserDefault) + { + presetToMod = presetUserDefault; + } + else if (presetHbDefault) //else we use the built in default presetHbDefault + { + presetToMod = presetHbDefault; + } + else + { + return; + } + + if (presetUserDefaultParent != nil) + { + [fPresetsOutlineView expandItem:presetUserDefaultParent]; + + } + if (presetUserDefaultParentParent != nil) + { + [fPresetsOutlineView expandItem:presetUserDefaultParentParent]; + + } + + [fPresetsOutlineView selectRowIndexes:[NSIndexSet indexSetWithIndex:[fPresetsOutlineView rowForItem: presetToMod]] byExtendingSelection:NO]; + [self selectPreset:nil]; +} + + +#pragma mark - +#pragma mark Manage Built In Presets + + +- (IBAction)deleteFactoryPresets:(id)sender +{ + //int status; + NSEnumerator *enumerator = [UserPresets objectEnumerator]; + id tempObject; + + //NSNumber *index; + NSMutableArray *tempArray; + + + tempArray = [NSMutableArray array]; + /* we look here to see if the preset is we move on to the next one */ + while ( tempObject = [enumerator nextObject] ) + { + /* if the preset is "Factory" then we put it in the array of + presets to delete */ + if ([[tempObject objectForKey:@"Type"] intValue] == 0) + { + [tempArray addObject:tempObject]; + } + } + + [UserPresets removeObjectsInArray:tempArray]; + [fPresetsOutlineView reloadData]; + [self savePreset]; + +} + + /* We use this method to recreate new, updated factory presets */ +- (IBAction)addFactoryPresets:(id)sender +{ + + /* First, we delete any existing built in presets */ + [self deleteFactoryPresets: sender]; + /* Then we generate new built in presets programmatically with fPresetsBuiltin + * which is all setup in HBPresets.h and HBPresets.m*/ + [fPresetsBuiltin generateBuiltinPresets:UserPresets]; + /* update build number for built in presets */ + /* iterate though the new array of presets to import and add them to our presets array */ + int i = 0; + NSEnumerator *enumerator = [UserPresets objectEnumerator]; + id tempObject; + while (tempObject = [enumerator nextObject]) + { + /* Record the apps current build number in the PresetBuildNumber key */ + if ([[tempObject objectForKey:@"Type"] intValue] == 0) // Type 0 is a built in preset + { + /* Preset build number */ + [[UserPresets objectAtIndex:i] setObject:[NSNumber numberWithInt:[[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] intValue]] forKey:@"PresetBuildNumber"]; + } + i++; + } + /* report the built in preset updating to the activity log */ + [self writeToActivityLog: "built in presets updated to build number: %d", [[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] intValue]]; + + [self sortPresets]; + [self addPreset]; + +} + + +@end + +/******************************* + * Subclass of the HBPresetsOutlineView * + *******************************/ + +@implementation HBPresetsOutlineView +- (NSImage *)dragImageForRowsWithIndexes:(NSIndexSet *)dragRows tableColumns:(NSArray *)tableColumns event:(NSEvent*)dragEvent offset:(NSPointPointer)dragImageOffset +{ + fIsDragging = YES; + + // By default, NSTableView only drags an image of the first column. Change this to + // drag an image of the queue's icon and PresetName columns. + NSArray * cols = [NSArray arrayWithObjects: [self tableColumnWithIdentifier:@"PresetName"], nil]; + return [super dragImageForRowsWithIndexes:dragRows tableColumns:cols event:dragEvent offset:dragImageOffset]; +} + + + +- (void) mouseDown:(NSEvent *)theEvent +{ + [super mouseDown:theEvent]; + fIsDragging = NO; +} + + + +- (BOOL) isDragging; +{ + return fIsDragging; +} +@end + + + |