diff options
Diffstat (limited to 'macosx/HBSubtitlesController.m')
-rw-r--r-- | macosx/HBSubtitlesController.m | 928 |
1 files changed, 928 insertions, 0 deletions
diff --git a/macosx/HBSubtitlesController.m b/macosx/HBSubtitlesController.m new file mode 100644 index 000000000..43bed5034 --- /dev/null +++ b/macosx/HBSubtitlesController.m @@ -0,0 +1,928 @@ +/* $Id: HBSubtitles.m + + 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 "HBSubtitlesController.h" +#import "Controller.h" +#include "hb.h" +#include "lang.h" + +@interface HBSubtitlesController () <NSTableViewDataSource, NSTableViewDelegate> +{ + NSMutableArray *subtitleArray; // contains the output subtitle track info + NSMutableArray *subtitleSourceArray;// contains the source subtitle track info + NSString *foreignAudioSearchTrackName; + NSMutableArray *languagesArray; // array of languages taken from lang.c + NSInteger languagesArrayDefIndex; + NSMutableArray *charCodeArray; // array of character codes + int charCodeArrayDefIndex; + int container; +} + +@property (assign) IBOutlet NSButton *fBrowseSrtFileButton; +@property (assign) IBOutlet NSTableView *fTableView; + +@end + +@implementation HBSubtitlesController + +- (instancetype)init +{ + self = [super initWithNibName:@"Subtitles" bundle:nil]; + if (self) + { + /* setup our array of languages */ + const iso639_lang_t *lang; + languagesArray = [[NSMutableArray alloc] init]; + for (lang = lang_get_next(NULL); lang != NULL; lang = lang_get_next(lang)) + { + [languagesArray addObject:[NSArray arrayWithObjects: + [NSString stringWithUTF8String:lang->eng_name], + [NSString stringWithUTF8String:lang->iso639_2], + nil]]; + if (!strcasecmp(lang->eng_name, "English")) + { + languagesArrayDefIndex = [languagesArray count] - 1; + } + } + + /* populate the charCodeArray */ + charCodeArray = [[NSMutableArray alloc] init]; + [charCodeArray addObject:@"ANSI_X3.4-1968"]; + [charCodeArray addObject:@"ANSI_X3.4-1986"]; + [charCodeArray addObject:@"ANSI_X3.4"]; + [charCodeArray addObject:@"ANSI_X3.110-1983"]; + [charCodeArray addObject:@"ANSI_X3.110"]; + [charCodeArray addObject:@"ASCII"]; + [charCodeArray addObject:@"ECMA-114"]; + [charCodeArray addObject:@"ECMA-118"]; + [charCodeArray addObject:@"ECMA-128"]; + [charCodeArray addObject:@"ECMA-CYRILLIC"]; + [charCodeArray addObject:@"IEC_P27-1"]; + [charCodeArray addObject:@"ISO-8859-1"]; + [charCodeArray addObject:@"ISO-8859-2"]; + [charCodeArray addObject:@"ISO-8859-3"]; + [charCodeArray addObject:@"ISO-8859-4"]; + [charCodeArray addObject:@"ISO-8859-5"]; + [charCodeArray addObject:@"ISO-8859-6"]; + [charCodeArray addObject:@"ISO-8859-7"]; + [charCodeArray addObject:@"ISO-8859-8"]; + [charCodeArray addObject:@"ISO-8859-9"]; + [charCodeArray addObject:@"ISO-8859-9E"]; + [charCodeArray addObject:@"ISO-8859-10"]; + [charCodeArray addObject:@"ISO-8859-11"]; + [charCodeArray addObject:@"ISO-8859-13"]; + [charCodeArray addObject:@"ISO-8859-14"]; + [charCodeArray addObject:@"ISO-8859-15"]; + [charCodeArray addObject:@"ISO-8859-16"]; + [charCodeArray addObject:@"UTF-7"]; + [charCodeArray addObject:@"UTF-8"]; + [charCodeArray addObject:@"UTF-16"]; + [charCodeArray addObject:@"UTF-16LE"]; + [charCodeArray addObject:@"UTF-16BE"]; + [charCodeArray addObject:@"UTF-32"]; + [charCodeArray addObject:@"UTF-32LE"]; + [charCodeArray addObject:@"UTF-32BE"]; + + charCodeArrayDefIndex = 11; + + [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(containerChanged:) name: HBContainerChangedNotification object: nil]; + [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(titleChanged:) name: HBTitleChangedNotification object: nil]; + } + + return self; +} + +- (void)titleChanged:(NSNotification *)aNotification +{ + NSDictionary *notDict = [aNotification userInfo]; + NSData *theData = [notDict objectForKey: keyTitleTag]; + hb_title_t *title = NULL; + + [theData getBytes: &title length: sizeof(title)]; + if (title) + { + /* reset the subtitle source array */ + if (subtitleSourceArray) + { + [subtitleSourceArray release]; + } + subtitleSourceArray = [[NSMutableArray alloc] init]; + + /* now populate the array with the source subtitle track info */ + int i; + hb_subtitle_t *subtitle; + NSMutableArray *forcedSourceNamesArray = [[NSMutableArray alloc] init]; + for (i = 0; i < hb_list_count(title->list_subtitle); i++) + { + subtitle = (hb_subtitle_t*)hb_list_item(title->list_subtitle, i); + + /* Human-readable representation of subtitle->source */ + NSString *bitmapOrText = subtitle->format == PICTURESUB ? @"Bitmap" : @"Text"; + NSString *subSourceName = [NSString stringWithUTF8String:hb_subsource_name(subtitle->source)]; + /* if the subtitle track can be forced, add its source name to the array */ + if (hb_subtitle_can_force(subtitle->source) && + [forcedSourceNamesArray containsObject:subSourceName] == NO) + { + [forcedSourceNamesArray addObject:subSourceName]; + } + + /* create a dictionary of source subtitle information to store in our array */ + NSMutableDictionary *newSubtitleSourceTrack = [[NSMutableDictionary alloc] init]; + /* Subtitle Source track name */ + [newSubtitleSourceTrack setObject:[NSString stringWithFormat:@"%d - %@ - (%@) (%@)", + i, [NSString stringWithUTF8String:subtitle->lang], + bitmapOrText,subSourceName] + forKey:@"sourceTrackName"]; + /* Subtitle Source track number, type and features */ + [newSubtitleSourceTrack setObject:[NSNumber numberWithInt:i] forKey:@"sourceTrackNum"]; + [newSubtitleSourceTrack setObject:[NSNumber numberWithInt:subtitle->source] forKey:@"sourceTrackType"]; + [subtitleSourceArray addObject:newSubtitleSourceTrack]; + [newSubtitleSourceTrack autorelease]; + } + + /* now set the name of the Foreign Audio Search track */ + if ([forcedSourceNamesArray count]) + { + [forcedSourceNamesArray sortUsingComparator:^(id obj1, id obj2) + { + return [((NSString*)obj1) compare:((NSString*)obj2)]; + }]; + NSString *tempString; + NSString *tempList = @""; + NSEnumerator *enumerator = [forcedSourceNamesArray objectEnumerator]; + while (tempString = (NSString*)[enumerator nextObject]) + { + if ([tempList length]) + { + tempList = [tempList stringByAppendingString:@", "]; + } + tempList = [tempList stringByAppendingString:tempString]; + } + [foreignAudioSearchTrackName release]; + foreignAudioSearchTrackName = [[NSString stringWithFormat:@"Foreign Audio Search - (Bitmap) (%@)", tempList] + retain]; + } + else + { + [foreignAudioSearchTrackName release]; + foreignAudioSearchTrackName = @"Foreign Audio Search - (Bitmap)"; + } + [forcedSourceNamesArray release]; + + /* reset the subtitle output array */ + if (subtitleArray) + { + [subtitleArray release]; + } + subtitleArray = [[NSMutableArray alloc] init]; + [self addSubtitleTrack]; + } + else + { + [subtitleArray removeAllObjects]; + [subtitleSourceArray removeAllObjects]; + } + + [self.fTableView reloadData]; +} + +- (void)enableUI:(BOOL)b +{ + [self.fBrowseSrtFileButton setEnabled:b]; + [self.fTableView setEnabled:b]; +} + +#pragma mark - +#pragma mark Create new Subtitles + +- (void)addSubtitleTrack +{ + [subtitleArray addObject:[self createSubtitleTrack]]; +} + +/* Creates a new subtitle track and stores it in an NSMutableDictionary */ +- (NSDictionary *)createSubtitleTrack +{ + NSMutableDictionary *newSubtitleTrack = [[NSMutableDictionary alloc] init]; + /* Subtitle Source track popup index */ + [newSubtitleTrack setObject:[NSNumber numberWithInt:0] forKey:@"subtitleSourceTrackNum"]; + /* Subtitle Source track popup language */ + [newSubtitleTrack setObject:@"None" forKey:@"subtitleSourceTrackName"]; + /* Subtitle track forced state */ + [newSubtitleTrack setObject:[NSNumber numberWithInt:0] forKey:@"subtitleTrackForced"]; + /* Subtitle track burned state */ + [newSubtitleTrack setObject:[NSNumber numberWithInt:0] forKey:@"subtitleTrackBurned"]; + /* Subtitle track default state */ + [newSubtitleTrack setObject:[NSNumber numberWithInt:0] forKey:@"subtitleTrackDefault"]; + + [newSubtitleTrack autorelease]; + return newSubtitleTrack; +} + +- (void)createSubtitleSrtTrack:(NSURL *)fileURL +{ + /* Create a new entry for the subtitle source array so it shows up in our subtitle source list */ + NSString *displayname = [fileURL lastPathComponent];// grok an appropriate display name from the srt subtitle */ + /* create a dictionary of source subtitle information to store in our array */ + NSMutableDictionary *newSubtitleSourceTrack = [[NSMutableDictionary alloc] init]; + /* Subtitle Source track popup index */ + [newSubtitleSourceTrack setObject:[NSNumber numberWithInteger:[subtitleSourceArray count]+1] forKey:@"sourceTrackNum"]; + /* Subtitle Source track name */ + [newSubtitleSourceTrack setObject:displayname forKey:@"sourceTrackName"]; + /* Subtitle Source track type (VobSub, Srt, etc.) */ + [newSubtitleSourceTrack setObject:[NSNumber numberWithInt:SRTSUB] forKey:@"sourceTrackType"]; + [newSubtitleSourceTrack setObject:[NSNumber numberWithInt:SRTSUB] forKey:@"subtitleSourceTrackType"]; + /* Subtitle Source file path */ + [newSubtitleSourceTrack setObject:[fileURL path] forKey:@"sourceSrtFilePath"]; + + [subtitleSourceArray addObject:newSubtitleSourceTrack]; + [newSubtitleSourceTrack autorelease]; + + /* Now create a new srt subtitle dictionary assuming the user wants to add it to their list + * Note: the subtitle array always has last entry for "None", so we need to replace its + * position in the array and tack a "None" track back on the end of the list */ + [subtitleArray removeObjectAtIndex:[subtitleArray count] - 1]; + + + NSMutableDictionary *newSubtitleSrtTrack = [[NSMutableDictionary alloc] init]; + /* Subtitle Source track popup index */ + if ([subtitleArray count] == 0) // we now have an empty array so this will be our first track + { + [newSubtitleSrtTrack setObject:[NSNumber numberWithInteger:[subtitleSourceArray count] + 1] forKey:@"subtitleSourceTrackNum"]; + } + else + { + [newSubtitleSrtTrack setObject:[NSNumber numberWithInteger:[subtitleSourceArray count]] forKey:@"subtitleSourceTrackNum"]; + } + + [newSubtitleSrtTrack setObject:[NSNumber numberWithInt:SRTSUB] forKey:@"sourceTrackType"]; + [newSubtitleSrtTrack setObject:[NSNumber numberWithInt:SRTSUB] forKey:@"subtitleSourceTrackType"]; + /* Subtitle Source track popup language */ + [newSubtitleSrtTrack setObject:displayname forKey:@"subtitleSourceTrackName"]; + /* Subtitle track forced state */ + [newSubtitleSrtTrack setObject:[NSNumber numberWithInt:0] forKey:@"subtitleTrackForced"]; + /* Subtitle track burned state */ + [newSubtitleSrtTrack setObject:[NSNumber numberWithInt:0] forKey:@"subtitleTrackBurned"]; + /* Subtitle track default state */ + [newSubtitleSrtTrack setObject:[NSNumber numberWithInt:0] forKey:@"subtitleTrackDefault"]; + + /* now the srt only info, Language, Chart Code and offset */ + [newSubtitleSrtTrack setObject:[fileURL path] forKey:@"subtitleSourceSrtFilePath"]; + [newSubtitleSrtTrack setObject:[NSNumber numberWithInteger:languagesArrayDefIndex] forKey:@"subtitleTrackSrtLanguageIndex"]; + [newSubtitleSrtTrack setObject:[[languagesArray objectAtIndex:languagesArrayDefIndex] objectAtIndex:0] forKey:@"subtitleTrackSrtLanguageLong"]; + [newSubtitleSrtTrack setObject:[[languagesArray objectAtIndex:languagesArrayDefIndex] objectAtIndex:1] forKey:@"subtitleTrackSrtLanguageIso3"]; + + [newSubtitleSrtTrack setObject:[NSNumber numberWithInt:charCodeArrayDefIndex] forKey:@"subtitleTrackSrtCharCodeIndex"]; + [newSubtitleSrtTrack setObject:[charCodeArray objectAtIndex:charCodeArrayDefIndex] forKey:@"subtitleTrackSrtCharCode"]; + + [newSubtitleSrtTrack setObject:[NSNumber numberWithInt:0] forKey:@"subtitleTrackSrtOffset"]; + + + [subtitleArray addObject:newSubtitleSrtTrack]; + [newSubtitleSrtTrack release]; + + /* now add back the none track to the end of the array */ + [self addSubtitleTrack]; +} + +/* used to return the current subtitleArray to controller.m */ +- (NSMutableArray *) subtitleArray +{ + return subtitleArray; +} + +// This gets called whenever the video container changes. +- (void) containerChanged: (NSNotification *) aNotification + +{ + NSDictionary *notDict = [aNotification userInfo]; + + container = [[notDict objectForKey: keyContainerTag] intValue]; + [self.fTableView reloadData]; +} + +- (void)addTracksFromQueue:(NSMutableArray *)newSubtitleArray +{ + /* Note: we need to look for external subtitles so it can be added to the source array track. + * Remember the source container subs are already loaded with resetTitle which is already called + * so any external sub sources need to be added to our source subs here + */ + + int i = 0; + for ( id tempObject in newSubtitleArray ) + { + /* We have an srt track */ + if ([[tempObject objectForKey:@"subtitleSourceTrackType"] intValue] == SRTSUB) + { + NSString *filePath = [tempObject objectForKey:@"subtitleSourceSrtFilePath"]; + /* Start replicate the add new srt code above */ + /* Create a new entry for the subtitle source array so it shows up in our subtitle source list */ + NSString *displayname = [filePath lastPathComponent];// grok an appropriate display name from the srt subtitle */ + /* create a dictionary of source subtitle information to store in our array */ + NSMutableDictionary *newSubtitleSourceTrack = [[NSMutableDictionary alloc] init]; + /* Subtitle Source track popup index */ + [newSubtitleSourceTrack setObject:[NSNumber numberWithInteger:[subtitleSourceArray count]+1] forKey:@"sourceTrackNum"]; + /* Subtitle Source track name */ + [newSubtitleSourceTrack setObject:displayname forKey:@"sourceTrackName"]; + /* Subtitle Source track type (VobSub, Srt, etc.) */ + [newSubtitleSourceTrack setObject:[NSNumber numberWithInt:SRTSUB] forKey:@"sourceTrackType"]; + [newSubtitleSourceTrack setObject:[NSNumber numberWithInt:SRTSUB] forKey:@"subtitleSourceTrackType"]; + /* Subtitle Source file path */ + [newSubtitleSourceTrack setObject:filePath forKey:@"sourceSrtFilePath"]; + + [subtitleSourceArray addObject:newSubtitleSourceTrack]; + [newSubtitleSourceTrack autorelease]; + /* END replicate the add new srt code above */ + } + i++; + } + + /*Set the subtitleArray to the newSubtitleArray */ + [subtitleArray setArray:newSubtitleArray]; + [self.fTableView reloadData]; +} + +#pragma mark - Srt import + +- (IBAction)browseImportSrtFile:(id)sender +{ + NSOpenPanel *panel = [NSOpenPanel openPanel]; + [panel setAllowsMultipleSelection:NO]; + [panel setCanChooseFiles:YES]; + [panel setCanChooseDirectories:NO]; + + NSURL *sourceDirectory; + if ([[NSUserDefaults standardUserDefaults] URLForKey:@"LastSrtImportDirectoryURL"]) + { + sourceDirectory = [[NSUserDefaults standardUserDefaults] URLForKey:@"LastSrtImportDirectoryURL"]; + } + else + { + sourceDirectory = [[NSURL fileURLWithPath:NSHomeDirectory()] URLByAppendingPathComponent:@"Desktop"]; + } + + /* 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 setDirectoryURL:sourceDirectory]; + [panel setAllowedFileTypes:fileTypes]; + [panel beginSheetModalForWindow:[[self view] window] completionHandler:^(NSInteger result) { + if (result == NSOKButton) + { + NSURL *importSrtFileURL = [panel URL]; + NSURL *importSrtDirectory = [importSrtFileURL URLByDeletingLastPathComponent]; + [[NSUserDefaults standardUserDefaults] setURL:importSrtDirectory forKey:@"LastSrtImportDirectoryURL"]; + + /* now pass the string off to fSubtitlesDelegate to add the srt file to the dropdown */ + [self createSubtitleSrtTrack:importSrtFileURL]; + + [self.fTableView reloadData]; + } + }]; +} + +#pragma mark - +#pragma mark Subtitle Table Delegate Methods +/* Table View delegate methods */ +/* Returns the number of tracks displayed + * NOTE: we return one more than the actual number of tracks + * specified as we always keep one track set to "None" which is ignored + * for setting up tracks, but is used to add tracks. + */ +- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView +{ + return [subtitleArray count]; +} + +/* Used to tell the Table view which information is to be displayed per item */ +- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex +{ + NSString *cellEntry = @"__DATA ERROR__"; + + /* we setup whats displayed given the column identifier */ + if ([[aTableColumn identifier] isEqualToString:@"track"]) + { + /* 'track' is a popup of all available source subtitle tracks for the given title */ + NSPopUpButtonCell *cellTrackPopup = [[NSPopUpButtonCell alloc] init]; + [cellTrackPopup autorelease]; + /* Set the Popups properties */ + [cellTrackPopup setControlSize:NSSmallControlSize]; + [cellTrackPopup setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]]; + + + /* Add our initial "None" track which we use to add source tracks or remove tracks. + * "None" is always index 0. + */ + [[cellTrackPopup menu] addItemWithTitle: @"None" action: NULL keyEquivalent: @""]; + + /* Foreign Audio Search (index 1 in the popup) is only available for the first track */ + if (rowIndex == 0) + { + // TODO: hide the track when no force-able subtitles are present in the source + [[cellTrackPopup menu] addItemWithTitle:foreignAudioSearchTrackName + action:NULL + keyEquivalent:@""]; + } + + int i; + for(i = 0; i < [subtitleSourceArray count]; i++ ) + { + [[cellTrackPopup menu] addItemWithTitle: [[subtitleSourceArray objectAtIndex:i] objectForKey: @"sourceTrackName"] action: NULL keyEquivalent: @""]; + } + + + [aTableColumn setDataCell:cellTrackPopup]; + + } + else if ([[aTableColumn identifier] isEqualToString:@"forced"]) + { + /* 'forced' is a checkbox to determine if a given source track is only to be forced */ + NSButtonCell *cellForcedCheckBox = [[NSButtonCell alloc] init]; + [cellForcedCheckBox autorelease]; + [cellForcedCheckBox setButtonType:NSSwitchButton]; + [cellForcedCheckBox setImagePosition:NSImageOnly]; + [cellForcedCheckBox setAllowsMixedState:NO]; + [aTableColumn setDataCell:cellForcedCheckBox]; + + } + else if ([[aTableColumn identifier] isEqualToString:@"burned"]) + { + /* 'burned' is a checkbox to determine if a given source track is only to be burned */ + NSButtonCell *cellBurnedCheckBox = [[NSButtonCell alloc] init]; + [cellBurnedCheckBox autorelease]; + [cellBurnedCheckBox setButtonType:NSSwitchButton]; + [cellBurnedCheckBox setImagePosition:NSImageOnly]; + [cellBurnedCheckBox setAllowsMixedState:NO]; + [aTableColumn setDataCell:cellBurnedCheckBox]; + } + else if ([[aTableColumn identifier] isEqualToString:@"default"]) + { + NSButtonCell *cellDefaultCheckBox = [[NSButtonCell alloc] init]; + [cellDefaultCheckBox autorelease]; + [cellDefaultCheckBox setButtonType:NSSwitchButton]; + [cellDefaultCheckBox setImagePosition:NSImageOnly]; + [cellDefaultCheckBox setAllowsMixedState:NO]; + [aTableColumn setDataCell:cellDefaultCheckBox]; + } + /* These next three columns only apply to srt's. they are disabled for source subs */ + else if ([[aTableColumn identifier] isEqualToString:@"srt_lang"]) + { + /* 'srt_lang' is a popup of commonly used languages to be matched to the source srt file */ + NSPopUpButtonCell *cellSrtLangPopup = [[NSPopUpButtonCell alloc] init]; + [cellSrtLangPopup autorelease]; + /* Set the Popups properties */ + [cellSrtLangPopup setControlSize:NSSmallControlSize]; + [cellSrtLangPopup setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]]; + /* list our languages as per the languagesArray */ + int i; + for(i = 0; i < [languagesArray count]; i++ ) + { + [[cellSrtLangPopup menu] addItemWithTitle: [[languagesArray objectAtIndex:i] objectAtIndex:0] action: NULL keyEquivalent: @""]; + } + [aTableColumn setDataCell:cellSrtLangPopup]; + } + else if ([[aTableColumn identifier] isEqualToString:@"srt_charcode"]) + { + /* 'srt_charcode' is a popup of commonly used character codes to be matched to the source srt file */ + NSPopUpButtonCell *cellSrtCharCodePopup = [[NSPopUpButtonCell alloc] init]; + [cellSrtCharCodePopup autorelease]; + /* Set the Popups properties */ + [cellSrtCharCodePopup setControlSize:NSSmallControlSize]; + [cellSrtCharCodePopup setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]]; + /* list our character codes, as per charCodeArray */ + + int i; + for(i = 0; i < [charCodeArray count]; i++ ) + { + [[cellSrtCharCodePopup menu] addItemWithTitle: [charCodeArray objectAtIndex:i] action: NULL keyEquivalent: @""]; + } + [aTableColumn setDataCell:cellSrtCharCodePopup]; + + } + else if ([[aTableColumn identifier] isEqualToString:@"srt_offset"]) + { + if ([[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleTrackSrtOffset"]) + { + cellEntry = [NSString stringWithFormat:@"%d",[[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleTrackSrtOffset"] intValue]]; + } + else + { + cellEntry = [NSString stringWithFormat:@"%d",0]; + } + } + else + { + cellEntry = nil; + } + + return cellEntry; +} + +/* Called whenever a widget in the table is edited or changed, we use it to record the change in the controlling array + * including removing and adding new tracks via the "None" ("track" index of 0) */ +- (void)tableView:(NSTableView *)aTableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex +{ + + if ([[aTableColumn identifier] isEqualToString:@"track"]) + { + [[subtitleArray objectAtIndex:rowIndex] setObject:[NSNumber numberWithInt:[anObject intValue]] forKey:@"subtitleSourceTrackNum"]; + /* Set the array to track if we are vobsub (picture sub) */ + if ([anObject intValue] != 0) + { + /* The first row has an additional track (Foreign Audio Search) */ + int sourceSubtitleIndex = [anObject intValue] - 1 - (rowIndex == 0); + + if(rowIndex == 0 && [anObject intValue] == 1) + { + /* + * we are foreign lang search, which is inherently bitmap + * + * since it can be either VOBSUB or PGS and the latter can't be + * passed through to MP4, we need to know whether there are any + * PGS tracks in the source - otherwise we can just set the + * source track type to VOBSUB + */ + int subtitleTrackType = VOBSUB; + if ([foreignAudioSearchTrackName rangeOfString: + [NSString stringWithUTF8String: + hb_subsource_name(PGSSUB)]].location != NSNotFound) + { + subtitleTrackType = PGSSUB; + } + // now set the track type + [[subtitleArray objectAtIndex:rowIndex] setObject:[NSNumber numberWithInt:subtitleTrackType] forKey:@"subtitleSourceTrackType"]; + // foreign lang search is most useful when combined w/Forced Only - make it default + [[subtitleArray objectAtIndex:rowIndex] setObject:[NSNumber numberWithInt:1] forKey:@"subtitleTrackForced"]; + } + /* check to see if we are an srt, in which case set our file path and source track type kvp's*/ + else if ([[[subtitleSourceArray objectAtIndex:sourceSubtitleIndex] objectForKey:@"sourceTrackType"] intValue] == SRTSUB) + { + [[subtitleArray objectAtIndex:rowIndex] setObject:[NSNumber numberWithInt:SRTSUB] + forKey:@"subtitleSourceTrackType"]; + [[subtitleArray objectAtIndex:rowIndex] setObject:[[subtitleSourceArray objectAtIndex:sourceSubtitleIndex] objectForKey:@"sourceSrtFilePath"] + forKey:@"subtitleSourceSrtFilePath"]; + } + else + { + [[subtitleArray objectAtIndex:rowIndex] setObject:[[subtitleSourceArray objectAtIndex:sourceSubtitleIndex] objectForKey:@"sourceTrackType"] + forKey:@"subtitleSourceTrackType"]; + } + + if (!hb_subtitle_can_burn([[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleSourceTrackType"] intValue])) + { + /* the source track cannot be burned in, so uncheck the widget */ + [[subtitleArray objectAtIndex:rowIndex] setObject:[NSNumber numberWithInt:0] forKey:@"subtitleTrackBurned"]; + } + + if (!hb_subtitle_can_force([[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleSourceTrackType"] intValue])) + { + /* the source track does not support forced flags, so uncheck the widget */ + [[subtitleArray objectAtIndex:rowIndex] setObject:[NSNumber numberWithInt:0] forKey:@"subtitleTrackForced"]; + } + } + } + else if ([[aTableColumn identifier] isEqualToString:@"forced"]) + { + [[subtitleArray objectAtIndex:rowIndex] setObject:[NSNumber numberWithInt:[anObject intValue]] forKey:@"subtitleTrackForced"]; + } + else if ([[aTableColumn identifier] isEqualToString:@"burned"]) + { + [[subtitleArray objectAtIndex:rowIndex] setObject:[NSNumber numberWithInt:[anObject intValue]] forKey:@"subtitleTrackBurned"]; + if([anObject intValue] == 1) + { + /* Burned In and Default are mutually exclusive */ + [[subtitleArray objectAtIndex:rowIndex] setObject:[NSNumber numberWithInt:0] forKey:@"subtitleTrackDefault"]; + } + /* now we need to make sure no other tracks are set to burned if we have set burned */ + if ([anObject intValue] == 1) + { + int i = 0; + NSEnumerator *enumerator = [subtitleArray objectEnumerator]; + id tempObject; + while ( tempObject = [enumerator nextObject] ) + { + if (i != rowIndex ) + { + [tempObject setObject:[NSNumber numberWithInt:0] forKey:@"subtitleTrackBurned"]; + } + i++; + } + } + } + else if ([[aTableColumn identifier] isEqualToString:@"default"]) + { + [[subtitleArray objectAtIndex:rowIndex] setObject:[NSNumber numberWithInt:[anObject intValue]] forKey:@"subtitleTrackDefault"]; + if([anObject intValue] == 1) + { + /* Burned In and Default are mutually exclusive */ + [[subtitleArray objectAtIndex:rowIndex] setObject:[NSNumber numberWithInt:0] forKey:@"subtitleTrackBurned"]; + } + /* now we need to make sure no other tracks are set to default */ + if ([anObject intValue] == 1) + { + int i = 0; + NSEnumerator *enumerator = [subtitleArray objectEnumerator]; + id tempObject; + while ( tempObject = [enumerator nextObject] ) + { + if (i != rowIndex) + { + [tempObject setObject:[NSNumber numberWithInt:0] forKey:@"subtitleTrackDefault"]; + } + i++; + } + } + + } + /* These next three columns only apply to srt's. they are disabled for source subs */ + else if ([[aTableColumn identifier] isEqualToString:@"srt_lang"]) + { + + [[subtitleArray objectAtIndex:rowIndex] setObject:[NSNumber numberWithInt:[anObject intValue]] forKey:@"subtitleTrackSrtLanguageIndex"]; + [[subtitleArray objectAtIndex:rowIndex] setObject:[[languagesArray objectAtIndex:[anObject intValue]] objectAtIndex:0] forKey:@"subtitleTrackSrtLanguageLong"]; + [[subtitleArray objectAtIndex:rowIndex] setObject:[[languagesArray objectAtIndex:[anObject intValue]] objectAtIndex:1] forKey:@"subtitleTrackSrtLanguageIso3"]; + + } + else if ([[aTableColumn identifier] isEqualToString:@"srt_charcode"]) + { + /* charCodeArray */ + [[subtitleArray objectAtIndex:rowIndex] setObject:[NSNumber numberWithInt:[anObject intValue]] forKey:@"subtitleTrackSrtCharCodeIndex"]; + [[subtitleArray objectAtIndex:rowIndex] setObject:[charCodeArray objectAtIndex:[anObject intValue]] forKey:@"subtitleTrackSrtCharCode"]; + } + else if ([[aTableColumn identifier] isEqualToString:@"srt_offset"]) + { + [[subtitleArray objectAtIndex:rowIndex] setObject:anObject forKey:@"subtitleTrackSrtOffset"]; + } + + + /* now lets do a bit of logic to add / remove tracks as necessary via the "None" track (index 0) */ + if ([[aTableColumn identifier] isEqualToString:@"track"]) + { + + /* Since currently no quicktime based playback devices support soft vobsubs in mp4, we make sure "burned in" is specified + * by default to avoid massive confusion and anarchy. However we also want to guard against multiple burned in subtitle tracks + * as libhb would ignore all but the first one anyway. Plus it would probably be stupid. + */ + if ((container & HB_MUX_MASK_MP4) && ([anObject intValue] != 0)) + { + if ([[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleSourceTrackType"] intValue] == VOBSUB) + { + /* lets see if there are currently any burned in subs specified */ + NSEnumerator *enumerator = [subtitleArray objectEnumerator]; + id tempObject; + BOOL subtrackBurnedInFound = NO; + while ( tempObject = [enumerator nextObject] ) + { + if ([[tempObject objectForKey:@"subtitleTrackBurned"] intValue] == 1) + { + subtrackBurnedInFound = YES; + } + } + /* if we have no current vobsub set to burn it in ... burn it in by default */ + if (!subtrackBurnedInFound) + { + [[subtitleArray objectAtIndex:rowIndex] setObject:[NSNumber numberWithInt:1] forKey:@"subtitleTrackBurned"]; + /* Burned In and Default are mutually exclusive */ + [[subtitleArray objectAtIndex:rowIndex] setObject:[NSNumber numberWithInt:0] forKey:@"subtitleTrackDefault"]; + } + } + } + + /* We use the track popup index number (presumes index 0 is "None" which is ignored and only used to remove tracks if need be) + * to determine whether to 1 modify an existing track, 2. add a new empty "None" track or 3. remove an existing track. + */ + + if ([anObject intValue] != 0 && rowIndex == [subtitleArray count] - 1) // if we have a last track which != "None" + { + /* add a new empty None track */ + [self addSubtitleTrack]; + + } + else if ([anObject intValue] == 0 && rowIndex != ([subtitleArray count] -1))// if this track is set to "None" and not the last track displayed + { + /* we know the user chose to remove this track by setting it to None, so remove it from the array */ + /* However,if this is the first track we have to reset the selected index of the next track by + 1, since it will now become + * the first track, which has to account for the extra "Foreign Language Search" index. */ + if (rowIndex == 0 && [[[subtitleArray objectAtIndex: 1] objectForKey: @"subtitleSourceTrackNum"] intValue] != 0) + { + /* get the index of the selection in row one (which is track two) */ + int trackOneSelectedIndex = [[[subtitleArray objectAtIndex: 1] objectForKey: @"subtitleSourceTrackNum"] intValue]; + /* increment the index of the subtitle menu item by one, to account for Foreign Language Search which is unique to the first track */ + [[subtitleArray objectAtIndex: 1] setObject:[NSNumber numberWithInt:trackOneSelectedIndex + 1] forKey:@"subtitleSourceTrackNum"]; + } + /* now that we have made the adjustment for track one (index 0) go ahead and delete the track */ + [subtitleArray removeObjectAtIndex: rowIndex]; + } + + + + } + + [aTableView reloadData]; +} + + +/* Gives fine grained control over the final drawing of the widget, including widget status via the controlling array */ +- (void)tableView:(NSTableView *)aTableView willDisplayCell:(id)aCell forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex +{ + /* we setup whats displayed given the column identifier */ + if ([[aTableColumn identifier] isEqualToString:@"track"]) + { + /* Set the index of the recorded source track here */ + [aCell selectItemAtIndex:[[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleSourceTrackNum"] intValue]]; + /* now that we have actually selected our track, we can grok the titleOfSelectedItem for that track */ + [[subtitleArray objectAtIndex:rowIndex] setObject:[[aTableColumn dataCellForRow:rowIndex] titleOfSelectedItem] forKey:@"subtitleSourceTrackName"]; + + } + else + { + + [aCell setAlignment:NSCenterTextAlignment]; + /* If the Track is None, we disable the other cells as None is an empty track */ + if ([[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleSourceTrackNum"] intValue] == 0) + { + [aCell setState:0]; + [aCell setEnabled:NO]; + } + else + { + /* Since we have a valid track, we go ahead and enable the rest of the widgets and set them according to the controlling array */ + [aCell setEnabled:YES]; + } + + if ([[aTableColumn identifier] isEqualToString:@"forced"]) + { + [aCell setState:[[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleTrackForced"] intValue]]; + /* Disable the "Forced Only" checkbox if a) the track is "None" or b) the subtitle track doesn't support forced flags */ + if (![[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleSourceTrackNum"] intValue] || + !hb_subtitle_can_force([[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleSourceTrackType"] intValue])) + { + [aCell setEnabled:NO]; + } + else + { + [aCell setEnabled:YES]; + } + } + else if ([[aTableColumn identifier] isEqualToString:@"burned"]) + { + [aCell setState:[[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleTrackBurned"] intValue]]; + /* + * Disable the "Burned In" checkbox if: + * a) the track is "None" OR + * b) the subtitle track can't be burned in OR + * c) the subtitle track can't be passed through (e.g. PGS w/MP4) + */ + int subtitleTrackType = [[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleSourceTrackType"] intValue]; + if (![[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleSourceTrackNum"] intValue] || + !hb_subtitle_can_burn(subtitleTrackType) || !hb_subtitle_can_pass(subtitleTrackType, container)) + { + [aCell setEnabled:NO]; + } + else + { + [aCell setEnabled:YES]; + } + } + else if ([[aTableColumn identifier] isEqualToString:@"default"]) + { + /* + * Disable the "Default" checkbox if: + * a) the track is "None" OR + * b) the subtitle track can't be passed through (e.g. PGS w/MP4) + */ + if (![[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleSourceTrackNum"] intValue] || + !hb_subtitle_can_pass([[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleSourceTrackType"] intValue], container)) + { + [aCell setState:NSOffState]; + [aCell setEnabled:NO]; + } + else + { + [aCell setState:[[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleTrackDefault"] intValue]]; + [aCell setEnabled:YES]; + } + } + /* These next three columns only apply to srt's. they are disabled for source subs */ + else if ([[aTableColumn identifier] isEqualToString:@"srt_lang"]) + { + /* We have an srt file so set the track type (Source or SRT, and the srt file path ) kvp's*/ + if ([[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleSourceTrackType"] intValue] == SRTSUB) + { + [aCell setEnabled:YES]; + if([[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleTrackSrtLanguageIndex"]) + { + [aCell selectItemAtIndex:[[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleTrackSrtLanguageIndex"] intValue]]; + } + else + { + [aCell selectItemAtIndex:languagesArrayDefIndex]; // English + [[subtitleArray objectAtIndex:rowIndex] setObject:[NSNumber numberWithInteger:languagesArrayDefIndex] forKey:@"subtitleTrackSrtLanguageIndex"]; + [[subtitleArray objectAtIndex:rowIndex] setObject:[[languagesArray objectAtIndex:languagesArrayDefIndex] objectAtIndex:0] forKey:@"subtitleTrackSrtLanguageLong"]; + [[subtitleArray objectAtIndex:rowIndex] setObject:[[languagesArray objectAtIndex:languagesArrayDefIndex] objectAtIndex:1] forKey:@"subtitleTrackSrtLanguageIso3"]; + + } + } + else + { + [aCell setEnabled:NO]; + } + } + else if ([[aTableColumn identifier] isEqualToString:@"srt_charcode"]) + { + /* We have an srt file so set the track type (Source or SRT, and the srt file path ) kvp's*/ + if ([[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleSourceTrackType"] intValue] == SRTSUB) + { + [aCell setEnabled:YES]; + if ([[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleTrackSrtCharCodeIndex"]) + { + [aCell selectItemAtIndex:[[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleTrackSrtCharCodeIndex"] intValue]]; + } + else + { + [aCell selectItemAtIndex:charCodeArrayDefIndex]; // ISO-8859-1 + [[subtitleArray objectAtIndex:rowIndex] setObject:[NSNumber numberWithInt:charCodeArrayDefIndex] forKey:@"subtitleTrackSrtCharCodeIndex"]; + [[subtitleArray objectAtIndex:rowIndex] setObject:[charCodeArray objectAtIndex:charCodeArrayDefIndex] forKey:@"subtitleTrackSrtCharCode"]; + } + } + else + { + [aCell setEnabled:NO]; + } + } + else if ([[aTableColumn identifier] isEqualToString:@"srt_offset"]) + { + if ([[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleSourceTrackType"] intValue] == SRTSUB) + { + [aCell setEnabled:YES]; + } + else + { + [aCell setEnabled:NO]; + } + } + + /* + * Let's check whether any subtitles in the list cannot be passed through. + * Set the first of any such subtitles to burned-in, remove the others. + */ + id tempObject; + int subtitleTrackType; + BOOL convertToBurnInUsed = NO; + NSMutableArray *tracksToDelete = [[NSMutableArray alloc] init]; + NSEnumerator *enumerator = [subtitleArray objectEnumerator]; + /* convert any non-None incompatible tracks to burn-in or remove them */ + while ((tempObject = [enumerator nextObject]) && + [tempObject objectForKey:@"subtitleSourceTrackType"]) + { + subtitleTrackType = [[tempObject objectForKey:@"subtitleSourceTrackType"] intValue]; + if (!hb_subtitle_can_pass(subtitleTrackType, container)) + { + if (convertToBurnInUsed == NO) + { + /* we haven't set any track to burned-in yet, so we can */ + [tempObject setObject:[NSNumber numberWithInt:1] forKey:@"subtitleTrackBurned"]; + convertToBurnInUsed = YES; //remove any additional tracks + } + else + { + /* we already have a burned-in track, we must remove others */ + [tracksToDelete addObject:tempObject]; + } + } + } + /* if we converted a track to burned-in, unset it for tracks that support passthru */ + if (convertToBurnInUsed == YES) + { + enumerator = [subtitleArray objectEnumerator]; + while ((tempObject = [enumerator nextObject]) && + [tempObject objectForKey:@"subtitleSourceTrackType"]) + { + subtitleTrackType = [[tempObject objectForKey:@"subtitleSourceTrackType"] intValue]; + if (hb_subtitle_can_pass(subtitleTrackType, container)) + { + [tempObject setObject:[NSNumber numberWithInt:0] forKey:@"subtitleTrackBurned"]; + } + } + } + /* this is where the actual removal takes place */ + if ([tracksToDelete count] > 0) + { + [subtitleArray removeObjectsInArray:tracksToDelete]; + [aTableView reloadData]; + /* this must be called after reloadData so as to not block the UI */ + [[NSAlert alertWithMessageText:@"Subtitle tack(s) removed" + defaultButton:@"OK" + alternateButton:nil + otherButton:nil + informativeTextWithFormat:@"%lu subtitle %@ could neither be converted to burn-in nor passed through", + (unsigned long)[tracksToDelete count], + [tracksToDelete count] > 1 ? @"tracks" : @"track"] runModal]; + } + [tracksToDelete release]; + } +} + +@end |