1 /* $Id: HBSubtitles.m,v 1.35 2005/08/01 14:29:50 titer Exp $
3 This file is part of the HandBrake source code.
4 Homepage: <http://handbrake.fr/>.
5 It may be used under the terms of the GNU General Public License. */
8 #import "HBSubtitles.h"
11 @implementation HBSubtitles
24 - (void)resetWithTitle:(hb_title_t *)title
35 [subtitleArray release];
37 subtitleArray = [[NSMutableArray alloc] init];
38 [self addSubtitleTrack];
42 #pragma mark Create new Subtitles
44 - (void)addSubtitleTrack
46 [subtitleArray addObject:[self createSubtitleTrack]];
49 /* Creates a new subtitle track and stores it in an NSMutableDictionary */
50 - (NSDictionary *)createSubtitleTrack
52 NSMutableDictionary *newSubtitleTrack = [[NSMutableDictionary alloc] init];
53 /* Subtitle Source track popup index */
54 [newSubtitleTrack setObject:[NSNumber numberWithInt:0] forKey:@"subtitleSourceTrackNum"];
55 /* Subtitle Source track popup language */
56 [newSubtitleTrack setObject:@"None" forKey:@"subtitleSourceTrackName"];
57 /* Subtitle Source track popup isPictureSub */
58 [newSubtitleTrack setObject:[NSNumber numberWithInt:0] forKey:@"subtitleSourceTrackisPictureSub"];
59 /* Subtitle track forced state */
60 [newSubtitleTrack setObject:[NSNumber numberWithInt:0] forKey:@"subtitleTrackForced"];
61 /* Subtitle track burned state */
62 [newSubtitleTrack setObject:[NSNumber numberWithInt:0] forKey:@"subtitleTrackBurned"];
63 /* Subtitle track default state */
64 [newSubtitleTrack setObject:[NSNumber numberWithInt:0] forKey:@"subtitleTrackDefault"];
66 [newSubtitleTrack autorelease];
67 return newSubtitleTrack;
70 - (NSMutableArray*) getSubtitleArray: (NSMutableArray *) subtitlesArray
72 //NSMutableArray *returnSubtitlesArray = [[NSMutableArray alloc] init];
73 //[returnSubtitlesArray initWithArray:subtitleArray];
74 //[returnSubtitlesArray autorelease];
78 - (void)containerChanged:(int) newContainer
80 container = newContainer;
84 #pragma mark Subtitle Table Delegate Methods
85 /* Table View delegate methods */
86 /* Returns the number of tracks displayed
87 * NOTE: we return one more than the actual number of tracks
88 * specified as we always keep one track set to "None" which is ignored
89 * for setting up tracks, but is used to add tracks.
91 - (int)numberOfRowsInTableView:(NSTableView *)aTableView
93 if( fTitle == NULL || ![subtitleArray count])
99 return [subtitleArray count];
103 /* Used to tell the Table view which information is to be displayed per item */
104 - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
106 NSString *cellEntry = @"__DATA ERROR__";
108 /* we setup whats displayed given the column identifier */
109 if ([[aTableColumn identifier] isEqualToString:@"track"])
111 /* 'track' is a popup of all available source subtitle tracks for the given title */
114 NSPopUpButtonCell *cellTrackPopup = [[NSPopUpButtonCell alloc] init];
115 [cellTrackPopup autorelease];
116 /* Set the Popups properties */
117 /* Following two lines can be used to show kind of a text field with indicator arrows which
118 * will popup when clicked on. Comment out for a standard style popup */
119 //[cellTrackPopup setShowsBorderOnlyWhileMouseInside:YES];
120 //[cellTrackPopup setBordered:NO];
122 [cellTrackPopup setControlSize:NSSmallControlSize];
123 [cellTrackPopup setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]];
126 /* Add our initial "None" track which we use to add source tracks or remove tracks.
127 * "None" is always index 0.
129 [[cellTrackPopup menu] addItemWithTitle: @"None" action: NULL keyEquivalent: @""];
131 /* Foreign Audio Search (index 1 in the popup) is only available for the first track */
134 [[cellTrackPopup menu] addItemWithTitle: @"Foreign Audio Search - (Bitmap)" action: NULL keyEquivalent: @""];
139 hb_subtitle_t *subtitle;
140 hb_subtitle_config_t sub_config;
142 for(i = 0; i < hb_list_count( fTitle->list_subtitle ); i++ )
144 NSString * trackTypeString = @"";
145 subtitle = (hb_subtitle_t *) hb_list_item( fTitle->list_subtitle, i );
146 sub_config = subtitle->config;
148 if (subtitle->format == PICTURESUB)
150 trackTypeString = @"- (Bitmap)";
154 trackTypeString = @"- (Text)";
158 NSString *popupMenuItemDescription = [NSString stringWithFormat:@"%d - %@ %@",i,[NSString stringWithUTF8String:subtitle->lang],trackTypeString];
160 [[cellTrackPopup menu] addItemWithTitle: popupMenuItemDescription action: NULL keyEquivalent: @""];
164 [aTableColumn setDataCell:cellTrackPopup];
167 else if ([[aTableColumn identifier] isEqualToString:@"forced"])
169 /* 'forced' is a checkbox to determine if a given source track is only to be forced */
170 NSButtonCell *cellForcedCheckBox = [[NSButtonCell alloc] init];
171 [cellForcedCheckBox autorelease];
172 [cellForcedCheckBox setButtonType:NSSwitchButton];
173 [cellForcedCheckBox setImagePosition:NSImageOnly];
174 [cellForcedCheckBox setAllowsMixedState:NO];
175 [aTableColumn setDataCell:cellForcedCheckBox];
178 else if ([[aTableColumn identifier] isEqualToString:@"burned"])
180 /* 'burned' is a checkbox to determine if a given source track is only to be burned */
181 NSButtonCell *cellBurnedCheckBox = [[NSButtonCell alloc] init];
182 [cellBurnedCheckBox autorelease];
183 [cellBurnedCheckBox setButtonType:NSSwitchButton];
184 [cellBurnedCheckBox setImagePosition:NSImageOnly];
185 [cellBurnedCheckBox setAllowsMixedState:NO];
186 [aTableColumn setDataCell:cellBurnedCheckBox];
188 else if ([[aTableColumn identifier] isEqualToString:@"default"])
190 NSButtonCell *cellDefaultCheckBox = [[NSButtonCell alloc] init];
191 [cellDefaultCheckBox autorelease];
192 [cellDefaultCheckBox setButtonType:NSSwitchButton];
193 [cellDefaultCheckBox setImagePosition:NSImageOnly];
194 [cellDefaultCheckBox setAllowsMixedState:NO];
195 [aTableColumn setDataCell:cellDefaultCheckBox];
205 /* Called whenever a widget in the table is edited or changed, we use it to record the change in the controlling array
206 * including removing and adding new tracks via the "None" ("track" index of 0) */
207 - (void)tableView:(NSTableView *)aTableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
210 if ([[aTableColumn identifier] isEqualToString:@"track"])
212 [[subtitleArray objectAtIndex:rowIndex] setObject:[NSNumber numberWithInt:[anObject intValue]] forKey:@"subtitleSourceTrackNum"];
213 /* Set the array to track if we are vobsub (picture sub) */
214 if ([anObject intValue] != 0)
216 int sourceSubtitleIndex;
217 bool isPictureSub = FALSE;
220 sourceSubtitleIndex = [anObject intValue] - 2;
224 sourceSubtitleIndex = [anObject intValue] - 1;
227 if (rowIndex == 0 && [anObject intValue] == 1)// we are Foreign Launguage Search, which is inherently bitmap
233 hb_subtitle_t * subtitle;
234 hb_subtitle_config_t sub_config;
235 subtitle = (hb_subtitle_t *) hb_list_item( fTitle->list_subtitle, sourceSubtitleIndex );
236 sub_config = subtitle->config;
237 if (subtitle->format == PICTURESUB)
242 if (isPictureSub == TRUE)
244 [[subtitleArray objectAtIndex:rowIndex] setObject:[NSNumber numberWithInt:1] forKey:@"subtitleSourceTrackisPictureSub"];
248 [[subtitleArray objectAtIndex:rowIndex] setObject:[NSNumber numberWithInt:0] forKey:@"subtitleSourceTrackisPictureSub"];
249 /* if we are not picture sub, then we must be a text sub, handbrake does not support burning in text subs */
250 [[subtitleArray objectAtIndex:rowIndex] setObject:[NSNumber numberWithInt:0] forKey:@"subtitleTrackBurned"];
254 else if ([[aTableColumn identifier] isEqualToString:@"forced"])
256 [[subtitleArray objectAtIndex:rowIndex] setObject:[NSNumber numberWithInt:[anObject intValue]] forKey:@"subtitleTrackForced"];
258 else if ([[aTableColumn identifier] isEqualToString:@"burned"])
260 [[subtitleArray objectAtIndex:rowIndex] setObject:[NSNumber numberWithInt:[anObject intValue]] forKey:@"subtitleTrackBurned"];
261 /* now we need to make sure no other tracks are set to burned if we have set burned*/
262 if ([anObject intValue] == 1)
265 NSEnumerator *enumerator = [subtitleArray objectEnumerator];
267 while ( tempObject = [enumerator nextObject] )
271 [tempObject setObject:[NSNumber numberWithInt:0] forKey:@"subtitleTrackBurned"];
277 else if ([[aTableColumn identifier] isEqualToString:@"default"])
279 [[subtitleArray objectAtIndex:rowIndex] setObject:[NSNumber numberWithInt:[anObject intValue]] forKey:@"subtitleTrackDefault"];
280 /* now we need to make sure no other tracks are set to default */
281 if ([anObject intValue] == 1)
284 NSEnumerator *enumerator = [subtitleArray objectEnumerator];
286 while ( tempObject = [enumerator nextObject] )
290 [tempObject setObject:[NSNumber numberWithInt:0] forKey:@"subtitleTrackDefault"];
299 /* now lets do a bit of logic to add / remove tracks as necessary via the "None" track (index 0) */
300 if ([[aTableColumn identifier] isEqualToString:@"track"])
303 /* since mp4 only supports burned in vobsubs (bitmap) we need to make sure burned in is specified */
304 if (container == HB_MUX_MP4 && [anObject intValue] != 0)
306 /* so, if isPictureSub = TRUE and we are mp4, we now have to A) set burned-in to 1 and b) remove any other
307 * tracks specified that are burned in */
308 if ([[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleSourceTrackisPictureSub"] intValue] == 1)
310 [[subtitleArray objectAtIndex:rowIndex] setObject:[NSNumber numberWithInt:1] forKey:@"subtitleTrackBurned"];
315 /* We use the track popup index number (presumes index 0 is "None" which is ignored and only used to remove tracks if need be)
316 * to determine whether to 1 modify an existing track, 2. add a new empty "None" track or 3. remove an existing track.
319 if ([anObject intValue] != 0 && rowIndex == [subtitleArray count] - 1) // if we have a last track which != "None"
321 /* add a new empty None track */
322 [self addSubtitleTrack];
324 else if ([anObject intValue] == 0 && rowIndex != ([subtitleArray count] -1))// if this track is set to "None" and not the last track displayed
326 /* we know the user chose to remove this track by setting it to None, so remove it from the array */
327 /* 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
328 * the first track, which has to account for the extra "Foreign Language Search" index. */
329 if (rowIndex == 0 && [[[subtitleArray objectAtIndex: 1] objectForKey: @"subtitleSourceTrackNum"] intValue] != 0)
331 /* get the index of the selection in row one (which is track two) */
332 int trackOneSelectedIndex = [[[subtitleArray objectAtIndex: 1] objectForKey: @"subtitleSourceTrackNum"] intValue];
333 /* increment the index of the subtitle menu item by one, to account for Foreign Language Search which is unique to the first track */
334 [[subtitleArray objectAtIndex: 1] setObject:[NSNumber numberWithInt:trackOneSelectedIndex + 1] forKey:@"subtitleSourceTrackNum"];
336 /* now that we have made the adjustment for track one (index 0) go ahead and delete the track */
337 [subtitleArray removeObjectAtIndex: rowIndex];
344 [aTableView reloadData];
348 /* Gives fine grained control over the final drawing of the widget, including widget status via the controlling array */
349 - (void)tableView:(NSTableView *)aTableView willDisplayCell:(id)aCell forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
351 /* we setup whats displayed given the column identifier */
352 if ([[aTableColumn identifier] isEqualToString:@"track"])
354 /* Set the index of the recorded source track here */
355 [aCell selectItemAtIndex:[[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleSourceTrackNum"] intValue]];
356 /* now that we have actually selected our track, we can grok the titleOfSelectedItem for that track */
357 [[subtitleArray objectAtIndex:rowIndex] setObject:[[aTableColumn dataCellForRow:rowIndex] titleOfSelectedItem] forKey:@"subtitleSourceTrackName"];
363 [aCell setAlignment:NSCenterTextAlignment];
364 /* If the Track is None, we disable the other cells as None is an empty track */
365 if ([[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleSourceTrackNum"] intValue] == 0)
368 [aCell setEnabled:NO];
372 /* Since we have a valid track, we go ahead and enable the rest of the widgets and set them according to the controlling array */
373 [aCell setEnabled:YES];
376 if ([[aTableColumn identifier] isEqualToString:@"forced"])
378 [aCell setState:[[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleTrackForced"] intValue]];
380 else if ([[aTableColumn identifier] isEqualToString:@"burned"])
382 [aCell setState:[[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleTrackBurned"] intValue]];
383 /* Disable the "Burned-In" checkbox if a) the track is "None", b) the subtitle track is text (we do not support burning in
384 * text subs, or c) we are mp4 and the track is a vobsub (picture sub) */
385 if ([[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleSourceTrackNum"] intValue] == 0 ||
386 [[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleSourceTrackisPictureSub"] intValue] == 0 ||
387 (container == HB_MUX_MP4 && [[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleSourceTrackisPictureSub"] intValue] == 1))
389 [aCell setEnabled:NO];
393 [aCell setEnabled:YES];
396 else if ([[aTableColumn identifier] isEqualToString:@"default"])
398 [aCell setState:[[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleTrackDefault"] intValue]];
404 if (container == HB_MUX_MP4)
406 /* now remove any other tracks that are set as burned and are picturesubs */
408 int removedTracks = 0;
409 NSEnumerator *enumerator = [subtitleArray objectEnumerator];
411 NSMutableArray *tempArrayToDelete = [NSMutableArray array];
412 BOOL removeTrack = NO;
413 while ( tempObject = [enumerator nextObject] )
416 if ([[tempObject objectForKey:@"subtitleSourceTrackisPictureSub"] intValue] == 1)
418 /* if this is the first vobsub mark it. if not, remove it */
419 if (removeTrack == NO)
421 /* make sure that this is set to be burned in */
422 [tempObject setObject:[NSNumber numberWithInt:1] forKey:@"subtitleTrackBurned"];
427 [tempArrayToDelete addObject:tempObject];
434 /* check to see if there are tracks to remove from the array */
435 if ([tempArrayToDelete count] > 0)
437 /* Popup a warning that hb only support one pic sub being burned in with mp4 */
439 status = NSRunAlertPanel(@"More than one vobsub is not supported in an mp4...",@"Your first vobsub track will now be used.", @"OK", nil, nil);
440 [NSApp requestUserAttention:NSCriticalRequest];
442 [subtitleArray removeObjectsInArray:tempArrayToDelete];
443 [aTableView reloadData];