1 /*****************************************************************************
2 * sfilter.m: MacOS X Subpicture filters dialogue
3 *****************************************************************************
4 * Copyright (C) 2005 the VideoLAN team
7 * Authors: Felix Kühne <fkuehne@users.sf.net>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
25 /*****************************************************************************
27 * the code used to bind with VLC's core is partially based upon the
28 * RC-interface, written by Antoine Cellerier and Mark F. Moriarty
29 * (members of the VideoLAN team)
30 *****************************************************************************/
37 - check for memory leaks
38 - save the preferences, if requested
39 - fix stupid compilation warnings
42 @implementation VLCsFilters
44 static VLCsFilters *_o_sharedInstance = nil;
46 + (VLCsFilters *)sharedInstance
48 return _o_sharedInstance ? _o_sharedInstance : [[self alloc] init];
53 if (_o_sharedInstance) {
56 _o_sharedInstance = [super init];
59 return _o_sharedInstance;
71 [o_sfilter_win setTitle: _NS("Subpicture Filters")];
72 [[o_sfilter_tabView tabViewItemAtIndex: 0] setLabel: _NS("Logo")];
73 [[o_sfilter_tabView tabViewItemAtIndex: 1] setLabel: _NS("Time")];
74 [[o_sfilter_tabView tabViewItemAtIndex: 2] setLabel: _NS("Marquee")];
75 [o_sfilter_saveSettings_ckb setTitle: _NS("Save settings")];
76 [o_logo_image_btn setTitle: _NS("Browse...")];
77 [o_logo_enabled_ckb setTitle: _NS("Enabled")];
78 [o_logo_image_lbl setStringValue: [_NS("Image") \
79 stringByAppendingString: @":"]];
80 [o_logo_pos_lbl setStringValue: [_NS("Position") \
81 stringByAppendingString: @":"]];
82 [o_logo_opaque_lbl setStringValue: [_NS("Opaqueness") \
83 stringByAppendingString: @":"]];
84 [o_time_enabled_ckb setTitle: _NS("Enabled")];
85 [o_time_stamp_lbl setStringValue: [_NS("Timestamp") \
86 stringByAppendingString: @":"]];
87 [o_time_size_lbl setStringValue: [_NS("Size") \
88 stringByAppendingString: @":"]];
89 [o_time_color_lbl setStringValue: [_NS("Color") \
90 stringByAppendingString: @":"]];
91 [o_time_opaque_lbl setStringValue: [_NS("Opaqueness") \
92 stringByAppendingString: @":"]];
93 [o_time_pos_lbl setStringValue: [_NS("Position") \
94 stringByAppendingString: @":"]];
95 [o_time_size_inPx_lbl setStringValue: _NS("(in pixels)")];
96 [o_marq_enabled_ckb setTitle: _NS("Enabled")];
97 [o_marq_color_lbl setStringValue: [_NS("Color") \
98 stringByAppendingString: @":"]];
99 [o_marq_marq_lbl setStringValue: [_NS("Marquee") \
100 stringByAppendingString: @":"]];
101 [o_marq_opaque_lbl setStringValue: [_NS("Opaqueness") \
102 stringByAppendingString: @":"]];
103 [o_marq_tmOut_lbl setStringValue: [_NS("Timeout") \
104 stringByAppendingString: @":"]];
105 [o_marq_tmOut_ms_lbl setStringValue: _NS("ms")];
106 [o_marq_pos_lbl setStringValue: [_NS("Position") \
107 stringByAppendingString: @":"]];
108 [o_marq_size_lbl setStringValue: [_NS("Size") \
109 stringByAppendingString: @":"]];
110 [o_time_color_lbl setStringValue: _NS("(in pixels)")];
115 /* colors as implemented at the beginning of marq.c and time.c
116 * feel free to add more colors, but remember to add them to these files
117 * as well to keep a certain level of consistency across the interfaces */
135 o_default = [NSArray arrayWithObjects: _NS("Default"), @"-1", nil];
136 o_black = [NSArray arrayWithObjects: _NS("Black"), @"0x000000", nil];
137 o_gray = [NSArray arrayWithObjects: _NS("Gray"), @"0x808080", nil];
138 o_silver = [NSArray arrayWithObjects: _NS("Silver"), @"0xC0C0C0", nil];
139 o_white = [NSArray arrayWithObjects: _NS("White"), @"0xFFFFFF", nil];
140 o_maroon = [NSArray arrayWithObjects: _NS("Maroon"), @"0x800000", nil];
141 o_red = [NSArray arrayWithObjects: _NS("Red"), @"0xFF0000", nil];
142 o_fuchsia = [NSArray arrayWithObjects: _NS("Fuchsia"), @"0xFF00FF", nil];
143 o_yellow = [NSArray arrayWithObjects: _NS("Yellow"), @"0xFFFF00", nil];
144 o_olive = [NSArray arrayWithObjects: _NS("Olive"), @"0x808000", nil];
145 o_green = [NSArray arrayWithObjects: _NS("Green"), @"0x008000", nil];
146 o_teal = [NSArray arrayWithObjects: _NS("Teal"), @"0x008080", nil];
147 o_lime = [NSArray arrayWithObjects: _NS("Lime"), @"0x00FF00", nil];
148 o_purple = [NSArray arrayWithObjects: _NS("Purple"), @"0x800080", nil];
149 o_navy = [NSArray arrayWithObjects: _NS("Navy"), @"0x000080", nil];
150 o_blue = [NSArray arrayWithObjects: _NS("Blue"), @"0x0000FF", nil];
151 o_aqua = [NSArray arrayWithObjects: _NS("Aqua"), @"0x00FFFF", nil];
152 o_colors = [[NSArray alloc] initWithObjects: o_default, o_black, o_gray, \
153 o_silver, o_white, o_maroon, o_red, o_fuchsia, o_yellow, o_olive, \
154 o_green, o_teal, o_lime, o_purple, o_navy, o_blue, o_aqua, nil];
157 [o_marq_color_pop removeAllItems];
158 [o_time_color_pop removeAllItems];
160 /* we are adding tags to the items, so we can easily identify them even if
161 * the menu was sorted */
162 while (x != [o_colors count])
164 [o_marq_color_pop addItemWithTitle: [[o_colors objectAtIndex:x] \
166 [[o_marq_color_pop lastItem] setTag: x];
168 [o_time_color_pop addItemWithTitle: [[o_colors objectAtIndex:x] \
170 [[o_time_color_pop lastItem] setTag: x];
175 [o_marq_color_pop selectItemAtIndex:0];
176 [o_time_color_pop selectItemAtIndex:0];
178 /* define the relative positions and copy them to the menues
179 * we can destroy the array afterwards, because we are saving the ints
180 * as tags to the menu-items */
190 NSArray * o_positions;
191 o_cnt_cnt = [NSArray arrayWithObjects: _NS("Center-Center"), @"0", nil];
192 o_lft_cnt = [NSArray arrayWithObjects: _NS("Left-Center"), @"1", nil];
193 o_rht_cnt = [NSArray arrayWithObjects: _NS("Right-Center"), @"2", nil];
194 o_cnt_top = [NSArray arrayWithObjects: _NS("Center-Top"), @"4", nil];
195 o_lft_top = [NSArray arrayWithObjects: _NS("Left-Top"), @"5", nil];
196 o_rht_top = [NSArray arrayWithObjects: _NS("Right-Top"), @"6", nil];
197 o_cnt_btm = [NSArray arrayWithObjects: _NS("Center-Bottom"), @"8", nil];
198 o_lft_btm = [NSArray arrayWithObjects: _NS("Left-Bottom"), @"9", nil];
199 o_rht_btm = [NSArray arrayWithObjects: _NS("Right-Bottom"), @"10", nil];
200 o_positions = [[NSArray alloc] initWithObjects: o_cnt_cnt, o_lft_cnt, \
201 o_rht_cnt, o_cnt_top, o_lft_top, o_rht_top, o_cnt_btm, o_lft_btm, \
205 [o_time_pos_rel_pop removeAllItems];
206 [o_marq_pos_rel_pop removeAllItems];
207 [o_logo_pos_rel_pop removeAllItems];
209 /* we are adding a tag here, so we can easily select an item later on */
210 while ( x != [o_positions count] )
212 [o_time_pos_rel_pop addItemWithTitle: [[o_positions objectAtIndex:x] \
214 [[o_time_pos_rel_pop lastItem] setTag: [[[o_positions objectAtIndex:x] \
215 objectAtIndex:1] intValue]];
216 [o_marq_pos_rel_pop addItemWithTitle: [[o_positions objectAtIndex:x] \
218 [[o_marq_pos_rel_pop lastItem] setTag: [[[o_positions objectAtIndex:x] \
219 objectAtIndex:1] intValue]];
220 [o_logo_pos_rel_pop addItemWithTitle: [[o_positions objectAtIndex:x] \
222 [[o_logo_pos_rel_pop lastItem] setTag: [[[o_positions objectAtIndex:x] \
223 objectAtIndex:1] intValue]];
227 [o_positions release];
230 o_sizes = [[NSArray alloc] initWithObjects: @"6", @"8", @"10", @"11", @"12",\
231 @"14", @"13", @"16", @"18", @"24", @"36", @"48", @"64", @"72", @"96", \
232 @"144", @"288", nil];
233 [o_marq_size_pop removeAllItems];
234 [o_marq_size_pop addItemsWithTitles: o_sizes];
235 [o_time_size_pop removeAllItems];
236 [o_time_size_pop addItemsWithTitles: o_sizes];
242 /* called from intf.m */
243 [o_sfilter_win displayIfNeeded];
244 [o_sfilter_win makeKeyAndOrderFront:nil];
246 intf_thread_t * p_intf = VLCIntf;
248 /* retrieve the marquee settings */
250 int tempInt = config_GetInt( p_intf, "marq-color" );
251 while( strtol([[[o_colors objectAtIndex:x] objectAtIndex:1] UTF8String], \
252 NULL, 0) != tempInt )
256 if( x >= [o_marq_color_pop numberOfItems] )
262 [o_marq_color_pop selectItemAtIndex: x];
263 [o_marq_marq_fld setStringValue: [NSString stringWithUTF8String: \
264 config_GetPsz( p_intf, "marq-marquee" )]];
265 [o_marq_opaque_sld setIntValue: config_GetInt( p_intf, "marq-opacity")];
267 tempInt = config_GetInt( p_intf, "marq-position" );
268 while( tempInt != [[o_marq_pos_rel_pop itemAtIndex:x] tag] )
272 if( x >= [o_marq_pos_rel_pop numberOfItems] )
278 [o_marq_pos_rel_pop selectItemAtIndex:x];
280 tempInt = config_GetInt( p_intf, "marq-size" );
281 while( [[[o_marq_size_pop itemAtIndex: x] title] intValue] != tempInt )
284 if( x >= [o_marq_size_pop numberOfItems] )
289 [o_marq_size_pop selectItemAtIndex: x];
290 [o_marq_tmOut_fld setStringValue: [[NSNumber numberWithInt: \
291 config_GetInt( p_intf, "marq-timeout" )] stringValue]];
293 /* retrieve the time settings */
295 tempInt = config_GetInt( p_intf, "time-color" );
296 while( strtol([[[o_colors objectAtIndex:x] objectAtIndex:1] UTF8String], \
297 NULL, 0) != tempInt )
301 if( x >= [o_time_color_pop numberOfItems] )
307 [o_time_color_pop selectItemAtIndex: x];
308 [o_time_stamp_fld setStringValue: [NSString stringWithUTF8String: \
309 config_GetPsz( p_intf, "time-format" )]];
310 [o_time_opaque_sld setIntValue: config_GetInt( p_intf, "time-opacity")];
312 tempInt = config_GetInt( p_intf, "time-size" );
313 while( [[[o_time_size_pop itemAtIndex: x] title] intValue] != tempInt )
316 if( x >= [o_time_size_pop numberOfItems] )
321 [o_time_size_pop selectItemAtIndex: x];
323 tempInt = config_GetInt( p_intf, "time-position" );
324 while( tempInt != [[o_time_pos_rel_pop itemAtIndex:x] tag] )
328 if( x >= [o_time_pos_rel_pop numberOfItems] )
335 /* retrieve the logo settings */
336 [o_logo_opaque_sld setIntValue: config_GetInt( p_intf, "logo-transparency")];
337 /* in case that no path has been saved yet */
338 NSString * tempString = [[NSString alloc] initWithUTF8String: \
339 config_GetPsz( p_intf, "logo-file" )];
340 if( [tempString length] == 0 )
342 [o_logo_image_fld setStringValue: @""];
346 [o_logo_image_fld setStringValue: tempString ];
348 [tempString release];
350 tempInt = config_GetInt( p_intf, "logo-position" );
351 while( tempInt != [[o_logo_pos_rel_pop itemAtIndex:x] tag] )
355 if( x >= [o_logo_pos_rel_pop numberOfItems] )
362 /* enable the wanted filters */
363 char * psz_subfilters;
364 psz_subfilters = config_GetPsz( p_intf, "sub-filter" );
367 [o_marq_enabled_ckb setState: (int)strstr( psz_subfilters, "marq")];
368 [o_logo_enabled_ckb setState: (int)strstr( psz_subfilters, "logo")];
369 [o_time_enabled_ckb setState: (int)strstr( psz_subfilters, "time")];
376 - (IBAction)logo_selectFile:(id)sender
378 NSOpenPanel * openPanel = [NSOpenPanel openPanel];
379 SEL sel = @selector(logo_getFile:returnCode:contextInfo:);
380 [openPanel beginSheetForDirectory:nil file:nil types: [NSArray \
381 arrayWithObjects: @"png", @"PNG", @"'PNGf'", nil] modalForWindow: \
382 o_sfilter_win modalDelegate:self didEndSelector:sel contextInfo:nil];
385 - (void)logo_getFile: (NSOpenPanel *)sheet returnCode: \
386 (int)returnCode contextInfo: (void *)contextInfo
388 if (returnCode == NSOKButton)
390 [o_logo_image_fld setStringValue: [sheet filename]];
394 - (IBAction)propertyChanged:(id)sender
396 intf_thread_t * p_intf = VLCIntf;
397 input_thread_t * p_input = (input_thread_t *)vlc_object_find( p_intf, \
398 VLC_OBJECT_INPUT, FIND_ANYWHERE );
402 /* general properties */
403 if( sender == o_sfilter_saveSettings_ckb)
405 o_save_settings = [o_sfilter_saveSettings_ckb state];
409 else if( sender == o_marq_marq_fld )
411 if( [[o_marq_marq_fld stringValue] length] == 0 )
417 val.psz_string = [[o_marq_marq_fld stringValue] UTF8String];
421 var_Set( p_input->p_libvlc, "marq-marquee", val );
423 config_PutPsz( p_intf, "marq-marquee", val.psz_string );
426 else if( sender == o_marq_pos_rel_pop )
428 val.i_int = [[o_marq_pos_rel_pop selectedItem] tag];
431 var_Set( p_input->p_libvlc, "marq-position", val );
433 config_PutInt( p_intf, "marq-position", val.i_int );
436 else if( sender == o_marq_color_pop )
438 val.i_int = strtol( [[[o_colors objectAtIndex: [o_marq_color_pop \
439 indexOfSelectedItem]] objectAtIndex: 1] UTF8String], NULL, 0 );
442 var_Set( p_input->p_libvlc, "marq-color", val );
444 config_PutInt( p_intf, "marq-color", val.i_int );
447 else if( sender == o_marq_opaque_sld )
449 val.i_int = [o_marq_opaque_sld intValue];
452 var_Set( p_input->p_libvlc, "marq-opacity", val );
454 config_PutInt( p_intf, "marq-opacity", val.i_int );
457 else if( sender == o_marq_size_pop )
459 val.i_int = [[o_marq_size_pop titleOfSelectedItem] intValue];
462 var_Set( p_input->p_libvlc, "marq-size", val );
464 config_PutInt( p_intf, "marq-size", val.i_int );
467 else if( sender == o_marq_tmOut_fld && [[sender stringValue] length] > 0 )
469 val.i_int = [o_marq_tmOut_fld intValue];
472 var_Set( p_input->p_libvlc, "marq-timeout", val );
474 config_PutInt( p_intf, "marq-timeout", val.i_int );
479 else if( sender == o_time_stamp_fld )
481 if( [[o_time_stamp_fld stringValue] length] == 0 )
487 val.psz_string = [[o_time_stamp_fld stringValue] UTF8String];
491 var_Set( p_input->p_libvlc, "time-format", val );
493 config_PutPsz( p_intf, "time-format", val.psz_string );
496 else if( sender == o_time_pos_rel_pop )
498 val.i_int = [[o_time_pos_rel_pop selectedItem] tag];
501 var_Set( p_input->p_libvlc, "time-position", val );
503 config_PutInt( p_intf, "time-position", val.i_int );
506 else if( sender == o_time_color_pop )
508 val.i_int = strtol( [[[o_colors objectAtIndex: [o_time_color_pop \
509 indexOfSelectedItem]] objectAtIndex: 1] UTF8String], NULL, 0 );
512 var_Set( p_input->p_libvlc, "time-color", val );
514 config_PutInt( p_intf, "time-color", val.i_int );
517 else if( sender == o_time_opaque_sld )
519 val.i_int = [o_time_opaque_sld intValue];
522 var_Set( p_input->p_libvlc, "time-opacity", val );
524 config_PutInt( p_intf, "time-opacity", val.i_int );
527 else if( sender == o_time_size_pop )
529 val.i_int = [[o_time_size_pop titleOfSelectedItem] intValue];
532 var_Set( p_input->p_libvlc, "time-size", val );
534 config_PutInt( p_intf, "time-size", val.i_int );
538 else if( sender == o_logo_opaque_sld )
540 val.i_int = [o_logo_opaque_sld intValue];
543 var_Set( p_input->p_libvlc, "logo-transparency", val );
545 config_PutInt( p_intf, "logo-transparency", val.i_int );
548 else if( sender == o_logo_pos_rel_pop )
550 val.i_int = [[o_logo_pos_rel_pop selectedItem] tag];
553 var_Set( p_input->p_libvlc, "logo-position", val );
555 config_PutInt( p_intf, "logo-position", val.i_int );
561 o_config_changed = YES;
562 vlc_object_release( p_input );
566 - (IBAction)enableFilter:(id)sender
568 if( sender == o_marq_enabled_ckb )
570 if( [o_marq_enabled_ckb state] == NSOnState )
572 [self changeFiltersString:"marq" onOrOff:VLC_TRUE];
576 [self changeFiltersString:"marq" onOrOff:VLC_FALSE];
580 if( sender == o_logo_enabled_ckb )
582 if( [o_logo_enabled_ckb state] == NSOnState )
584 [self changeFiltersString:"logo" onOrOff:VLC_TRUE];
588 [self changeFiltersString:"logo" onOrOff:VLC_FALSE];
592 if( sender == o_time_enabled_ckb )
594 if( [o_time_enabled_ckb state] == NSOnState )
596 [self changeFiltersString:"time" onOrOff:VLC_TRUE];
600 [self changeFiltersString:"time" onOrOff:VLC_FALSE];
608 [o_marq_color_pop setEnabled: [o_marq_enabled_ckb state]];
609 [o_marq_marq_fld setEnabled: [o_marq_enabled_ckb state]];
610 [o_marq_opaque_sld setEnabled: [o_marq_enabled_ckb state]];
611 [o_marq_size_pop setEnabled: [o_marq_enabled_ckb state]];
612 [o_marq_tmOut_fld setEnabled: [o_marq_enabled_ckb state]];
613 [o_marq_pos_rel_pop setEnabled: [o_marq_enabled_ckb state]];
618 [o_time_color_pop setEnabled: [o_time_enabled_ckb state]];
619 [o_time_stamp_fld setEnabled: [o_time_enabled_ckb state]];
620 [o_time_opaque_sld setEnabled: [o_time_enabled_ckb state]];
621 [o_time_size_pop setEnabled: [o_time_enabled_ckb state]];
622 [o_time_pos_rel_pop setEnabled: [o_time_enabled_ckb state]];
627 [o_logo_image_btn setEnabled: [o_logo_enabled_ckb state]];
628 [o_logo_image_fld setEnabled: [o_logo_enabled_ckb state]];
629 [o_logo_opaque_sld setEnabled: [o_logo_enabled_ckb state]];
630 [o_logo_pos_rel_pop setEnabled: [o_logo_enabled_ckb state]];
633 - (void)changeFiltersString:(char *)psz_name onOrOff:(vlc_bool_t )b_add
635 /* copied from ../wxwidgets/extrapanel.cpp
636 * renamed to conform with Cocoa's rules
637 * and slightly modified to suit our needs */
639 intf_thread_t * p_intf = VLCIntf;
641 char *psz_parser, *psz_string;
642 psz_string = config_GetPsz( p_intf, "sub-filter" );
644 if( !psz_string ) psz_string = strdup("");
646 psz_parser = strstr( psz_string, psz_name );
652 psz_parser = psz_string;
653 asprintf( &psz_string, (*psz_string) ? "%s:%s" : "%s%s",
654 psz_string, psz_name );
666 memmove( psz_parser, psz_parser + strlen(psz_name) +
667 (*(psz_parser + strlen(psz_name)) == ':' ? 1 : 0 ),
668 strlen(psz_parser + strlen(psz_name)) + 1 );
670 /* Remove trailing : : */
671 if( *(psz_string+strlen(psz_string ) -1 ) == ':' )
673 *(psz_string+strlen(psz_string ) -1 ) = '\0';
683 config_PutPsz( p_intf, "sub-filter", psz_string );
685 /* Try to set on the fly */
686 /* FIXME: enable this once we support on-the-fly addition of this kind of
688 vout_thread_t *p_vout;
689 p_vout = (vout_thread_t *)vlc_object_find( p_intf, VLC_OBJECT_VOUT,
693 var_SetString( p_vout, "sub-filter", psz_string );
694 vlc_object_release( p_vout );
699 vlc_object_release( p_intf );
701 o_config_changed = YES;