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 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 [subtitleArray removeObjectAtIndex: rowIndex];
334 [aTableView reloadData];
338 /* Gives fine grained control over the final drawing of the widget, including widget status via the controlling array */
339 - (void)tableView:(NSTableView *)aTableView willDisplayCell:(id)aCell forTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
341 /* we setup whats displayed given the column identifier */
342 if ([[aTableColumn identifier] isEqualToString:@"track"])
344 /* Set the index of the recorded source track here */
345 [aCell selectItemAtIndex:[[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleSourceTrackNum"] intValue]];
346 /* now that we have actually selected our track, we can grok the titleOfSelectedItem for that track */
347 [[subtitleArray objectAtIndex:rowIndex] setObject:[[aTableColumn dataCellForRow:rowIndex] titleOfSelectedItem] forKey:@"subtitleSourceTrackName"];
353 [aCell setAlignment:NSCenterTextAlignment];
354 /* If the Track is None, we disable the other cells as None is an empty track */
355 if ([[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleSourceTrackNum"] intValue] == 0)
358 [aCell setEnabled:NO];
362 /* Since we have a valid track, we go ahead and enable the rest of the widgets and set them according to the controlling array */
363 [aCell setEnabled:YES];
366 if ([[aTableColumn identifier] isEqualToString:@"forced"])
368 [aCell setState:[[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleTrackForced"] intValue]];
370 else if ([[aTableColumn identifier] isEqualToString:@"burned"])
372 [aCell setState:[[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleTrackBurned"] intValue]];
373 /* Disable the "Burned-In" checkbox if a) the track is "None", b) the subtitle track is text (we do not support burning in
374 * text subs, or c) we are mp4 and the track is a vobsub (picture sub) */
375 if ([[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleSourceTrackNum"] intValue] == 0 ||
376 [[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleSourceTrackisPictureSub"] intValue] == 0 ||
377 (container == HB_MUX_MP4 && [[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleSourceTrackisPictureSub"] intValue] == 1))
379 [aCell setEnabled:NO];
383 [aCell setEnabled:YES];
386 else if ([[aTableColumn identifier] isEqualToString:@"default"])
388 [aCell setState:[[[subtitleArray objectAtIndex:rowIndex] objectForKey:@"subtitleTrackDefault"] intValue]];
393 BOOL useMp4VobsubDelete = YES;
394 if (useMp4VobsubDelete == YES)
396 if (container == HB_MUX_MP4)
398 /* now remove any other tracks that are set as burned and are picturesubs */
400 int removedTracks = 0;
401 NSEnumerator *enumerator = [subtitleArray objectEnumerator];
403 NSMutableArray *tempArrayToDelete = [NSMutableArray array];
404 BOOL removeTrack = NO;
405 while ( tempObject = [enumerator nextObject] )
408 if ([[tempObject objectForKey:@"subtitleSourceTrackisPictureSub"] intValue] == 1)
410 /* if this is the first vobsub mark it. if not, remove it */
411 if (removeTrack == NO)
413 /* make sure that this is set to be burned in */
414 [tempObject setObject:[NSNumber numberWithInt:1] forKey:@"subtitleTrackBurned"];
419 [tempArrayToDelete addObject:tempObject];
426 /* check to see if there are tracks to remove from the array */
427 if ([tempArrayToDelete count] > 0)
429 /* Popup a warning that hb only support one pic sub being burned in with mp4 */
432 status = NSRunAlertPanel(@"More than one vobsub is not supported in an mp4...",@"Your first vobsub track will now be used.", @"OK", nil, nil);
433 [NSApp requestUserAttention:NSCriticalRequest];
435 [subtitleArray removeObjectsInArray:tempArrayToDelete];
436 [aTableView reloadData];