1 /*****************************************************************************
2 * sfilter.m: MacOS X Subpicture filters dialogue
3 *****************************************************************************
4 * Copyright (C) 2005-2006 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
41 @implementation VLCsFilters
43 static VLCsFilters *_o_sharedInstance = nil;
45 + (VLCsFilters *)sharedInstance
47 return _o_sharedInstance ? _o_sharedInstance : [[self alloc] init];
52 if (_o_sharedInstance) {
55 _o_sharedInstance = [super init];
58 return _o_sharedInstance;
70 [o_sfilter_win setTitle: _NS("Subpicture Filters")];
71 [[o_sfilter_tabView tabViewItemAtIndex: 0] setLabel: _NS("Logo")];
72 [[o_sfilter_tabView tabViewItemAtIndex: 1] setLabel: _NS("Time")];
73 [[o_sfilter_tabView tabViewItemAtIndex: 2] setLabel: _NS("Marquee")];
74 [o_sfilter_saveSettings_ckb setTitle: _NS("Save settings")];
75 [o_logo_image_btn setTitle: _NS("Browse...")];
76 [o_logo_enabled_ckb setTitle: _NS("Enabled")];
77 [o_logo_image_lbl setStringValue: _NS("Image:")];
78 [o_logo_pos_lbl setStringValue: _NS("Position:")];
79 [o_logo_opaque_lbl setStringValue: _NS("Opaqueness")];
80 [o_time_enabled_ckb setTitle: _NS("Enabled")];
81 [o_time_stamp_lbl setStringValue: _NS("Timestamp:")];
82 [o_time_size_lbl setStringValue: _NS("Size:")];
83 [o_time_color_lbl setStringValue: _NS("Color:")];
84 [o_time_opaque_lbl setStringValue: _NS("Opaqueness:")];
85 [o_time_pos_lbl setStringValue: _NS("Position:")];
86 [o_time_size_inPx_lbl setStringValue: _NS("(in pixels)")];
87 [o_marq_enabled_ckb setTitle: _NS("Enabled")];
88 [o_marq_color_lbl setStringValue: _NS("Color:")];
89 [o_marq_marq_lbl setStringValue: _NS("Marquee:")];
90 [o_marq_opaque_lbl setStringValue: _NS("Opaqueness")];
91 [o_marq_tmOut_lbl setStringValue: _NS("Timeout:")];
92 [o_marq_tmOut_ms_lbl setStringValue: _NS("ms")];
93 [o_marq_pos_lbl setStringValue: _NS("Position:")];
94 [o_marq_size_lbl setStringValue: _NS("Size:")];
95 [o_time_color_lbl setStringValue: _NS("(in pixels)")];
100 /* colors as implemented at the beginning of marq.c and time.c
101 * feel free to add more colors, but remember to add them to these files
102 * as well to keep a certain level of consistency across the interfaces */
120 o_default = [NSArray arrayWithObjects: _NS("Default"), @"-1", nil];
121 o_black = [NSArray arrayWithObjects: _NS("Black"), @"0x000000", nil];
122 o_gray = [NSArray arrayWithObjects: _NS("Gray"), @"0x808080", nil];
123 o_silver = [NSArray arrayWithObjects: _NS("Silver"), @"0xC0C0C0", nil];
124 o_white = [NSArray arrayWithObjects: _NS("White"), @"0xFFFFFF", nil];
125 o_maroon = [NSArray arrayWithObjects: _NS("Maroon"), @"0x800000", nil];
126 o_red = [NSArray arrayWithObjects: _NS("Red"), @"0xFF0000", nil];
127 o_fuchsia = [NSArray arrayWithObjects: _NS("Fuchsia"), @"0xFF00FF", nil];
128 o_yellow = [NSArray arrayWithObjects: _NS("Yellow"), @"0xFFFF00", nil];
129 o_olive = [NSArray arrayWithObjects: _NS("Olive"), @"0x808000", nil];
130 o_green = [NSArray arrayWithObjects: _NS("Green"), @"0x008000", nil];
131 o_teal = [NSArray arrayWithObjects: _NS("Teal"), @"0x008080", nil];
132 o_lime = [NSArray arrayWithObjects: _NS("Lime"), @"0x00FF00", nil];
133 o_purple = [NSArray arrayWithObjects: _NS("Purple"), @"0x800080", nil];
134 o_navy = [NSArray arrayWithObjects: _NS("Navy"), @"0x000080", nil];
135 o_blue = [NSArray arrayWithObjects: _NS("Blue"), @"0x0000FF", nil];
136 o_aqua = [NSArray arrayWithObjects: _NS("Aqua"), @"0x00FFFF", nil];
137 o_colors = [[NSArray alloc] initWithObjects: o_default, o_black, o_gray,
138 o_silver, o_white, o_maroon, o_red, o_fuchsia, o_yellow, o_olive,
139 o_green, o_teal, o_lime, o_purple, o_navy, o_blue, o_aqua, nil];
142 [o_marq_color_pop removeAllItems];
143 [o_time_color_pop removeAllItems];
145 /* we are adding tags to the items, so we can easily identify them even if
146 * the menu was sorted */
147 while (x != [o_colors count])
149 [o_marq_color_pop addItemWithTitle: [[o_colors objectAtIndex:x]
151 [[o_marq_color_pop lastItem] setTag: x];
153 [o_time_color_pop addItemWithTitle: [[o_colors objectAtIndex:x]
155 [[o_time_color_pop lastItem] setTag: x];
160 [o_marq_color_pop selectItemAtIndex:0];
161 [o_time_color_pop selectItemAtIndex:0];
164 o_sizes = [[NSArray alloc] initWithObjects: @"6", @"8", @"10", @"11", @"12",\
165 @"13", @"14", @"16", @"18", @"24", @"36", @"48", @"64", @"72", @"96",
166 @"144", @"288", nil];
167 [o_marq_size_pop removeAllItems];
168 [o_marq_size_pop addItemsWithTitles: o_sizes];
169 [o_time_size_pop removeAllItems];
170 [o_time_size_pop addItemsWithTitles: o_sizes];
178 /* called from intf.m */
179 [o_sfilter_win displayIfNeeded];
180 [o_sfilter_win makeKeyAndOrderFront:nil];
182 intf_thread_t * p_intf = VLCIntf;
184 /* retrieve the marquee settings */
186 int tempInt = config_GetInt( p_intf, "marq-color" );
187 while( strtol([[[o_colors objectAtIndex:x] objectAtIndex:1] UTF8String],
188 NULL, 0) != tempInt )
192 if( x >= [o_marq_color_pop numberOfItems] )
198 [o_marq_color_pop selectItemAtIndex: x];
199 if( psz_temp = config_GetPsz( p_intf, "time-format" ) )
200 [o_marq_marq_fld setStringValue: [NSString stringWithUTF8String: psz_temp]];
202 [o_marq_marq_fld setStringValue: _NS("Not Available")];
204 [o_marq_opaque_sld setIntValue: config_GetInt( p_intf, "marq-opacity")];
205 [o_marq_pos_radio selectCellWithTag: config_GetInt( p_intf, "marq-position" )];
206 /* FIXME: the following line doesn't work with "-1", which is the default
208 [o_marq_size_pop selectItemWithTitle:
209 [[NSNumber numberWithInt: config_GetInt( p_intf, "marq-size" )]
211 [o_marq_size_pop selectItemAtIndex: x];
212 [o_marq_tmOut_fld setStringValue: [[NSNumber numberWithInt:
213 config_GetInt( p_intf, "marq-timeout" )] stringValue]];
215 /* retrieve the time settings */
217 tempInt = config_GetInt( p_intf, "time-color" );
218 while( strtol([[[o_colors objectAtIndex:x] objectAtIndex:1] UTF8String],
219 NULL, 0) != tempInt )
223 if( x >= [o_time_color_pop numberOfItems] )
229 [o_time_color_pop selectItemAtIndex: x];
230 if( psz_temp = config_GetPsz( p_intf, "time-format" ) )
231 [o_time_stamp_fld setStringValue: [NSString stringWithUTF8String: psz_temp]];
233 [o_time_stamp_fld setStringValue: _NS("Not Available")];
235 [o_time_opaque_sld setIntValue: config_GetInt( p_intf, "time-opacity")];
236 /* FIXME: the following line doesn't work with "-1", which is the default
238 [o_time_size_pop selectItemWithTitle:
239 [[NSNumber numberWithInt: config_GetInt( p_intf, "time-size" )]
241 [o_time_pos_radio selectCellWithTag: config_GetInt( p_intf, "time-position" )];
243 /* retrieve the logo settings */
244 [o_logo_opaque_sld setIntValue: config_GetInt( p_intf, "logo-transparency")];
245 /* in case that no path has been saved yet */
246 if( psz_temp = config_GetPsz( p_intf, "logo-file" ) )
247 [o_logo_image_fld setStringValue: [NSString stringWithUTF8String: psz_temp]];
249 [o_logo_image_fld setStringValue: @""];
250 [o_logo_pos_radio selectCellWithTag: config_GetInt( p_intf, "logo-position" )];
252 /* enable the requested filters */
253 char * psz_subfilters;
254 psz_subfilters = config_GetPsz( p_intf, "sub-filter" );
257 if( strstr( psz_subfilters, "marq") )
258 [o_marq_enabled_ckb setState: YES];
260 [o_marq_enabled_ckb setState: NO];
262 if( strstr( psz_subfilters, "logo") )
263 [o_logo_enabled_ckb setState: YES];
265 [o_logo_enabled_ckb setState: NO];
267 if( strstr( psz_subfilters, "time") )
268 [o_time_enabled_ckb setState: YES];
270 [o_time_enabled_ckb setState: NO];
277 - (IBAction)logo_selectFile:(id)sender
279 NSOpenPanel * openPanel = [NSOpenPanel openPanel];
280 SEL sel = @selector(logo_getFile:returnCode:contextInfo:);
281 [openPanel beginSheetForDirectory:nil file:nil types: [NSArray
282 arrayWithObjects: @"png", @"PNG", @"'PNGf'", nil] modalForWindow:
283 o_sfilter_win modalDelegate:self didEndSelector:sel contextInfo:nil];
286 - (void)logo_getFile: (NSOpenPanel *)sheet returnCode:
287 (int)returnCode contextInfo: (void *)contextInfo
289 if (returnCode == NSOKButton)
291 [o_logo_image_fld setStringValue: [sheet filename]];
292 /* Make sure we notice that */
293 [self propertyChanged: o_logo_image_fld];
297 - (IBAction)propertyChanged:(id)sender
299 intf_thread_t * p_intf = VLCIntf;
300 input_thread_t * p_input = (input_thread_t *)vlc_object_find( p_intf,
301 VLC_OBJECT_INPUT, FIND_ANYWHERE );
305 /* general properties */
306 if( sender == o_sfilter_saveSettings_ckb)
308 o_save_settings = [o_sfilter_saveSettings_ckb state];
312 else if( sender == o_marq_marq_fld )
314 if( [[o_marq_marq_fld stringValue] length] == 0 )
316 val.psz_string = (char *)"";
320 val.psz_string = (char *)[[o_marq_marq_fld stringValue] UTF8String];
324 var_Set( p_input->p_libvlc, "marq-marquee", val );
326 config_PutPsz( p_intf, "marq-marquee", val.psz_string );
329 else if( sender == o_marq_pos_radio )
331 val.i_int = [[o_marq_pos_radio selectedCell] tag];
334 var_Set( p_input->p_libvlc, "marq-position", val );
336 config_PutInt( p_intf, "marq-position", val.i_int );
339 else if( sender == o_marq_color_pop )
341 val.i_int = strtol( [[[o_colors objectAtIndex: [o_marq_color_pop
342 indexOfSelectedItem]] objectAtIndex: 1] UTF8String], NULL, 0 );
345 var_Set( p_input->p_libvlc, "marq-color", val );
347 config_PutInt( p_intf, "marq-color", val.i_int );
350 else if( sender == o_marq_opaque_sld )
352 val.i_int = [o_marq_opaque_sld intValue];
355 var_Set( p_input->p_libvlc, "marq-opacity", val );
357 config_PutInt( p_intf, "marq-opacity", val.i_int );
360 else if( sender == o_marq_size_pop )
362 val.i_int = [[o_marq_size_pop titleOfSelectedItem] intValue];
365 var_Set( p_input->p_libvlc, "marq-size", val );
367 config_PutInt( p_intf, "marq-size", val.i_int );
370 else if( sender == o_marq_tmOut_fld && [[sender stringValue] length] > 0 )
372 val.i_int = [o_marq_tmOut_fld intValue];
375 var_Set( p_input->p_libvlc, "marq-timeout", val );
377 config_PutInt( p_intf, "marq-timeout", val.i_int );
382 else if( sender == o_time_stamp_fld )
384 if( [[o_time_stamp_fld stringValue] length] == 0 )
386 val.psz_string = (char *)"";
390 val.psz_string = (char *)[[o_time_stamp_fld stringValue] UTF8String];
394 var_Set( p_input->p_libvlc, "time-format", val );
396 config_PutPsz( p_intf, "time-format", val.psz_string );
399 else if( sender == o_time_pos_radio )
401 val.i_int = [[o_time_pos_radio selectedCell] tag];
404 var_Set( p_input->p_libvlc, "time-position", val );
406 config_PutInt( p_intf, "time-position", val.i_int );
409 else if( sender == o_time_color_pop )
411 val.i_int = strtol( [[[o_colors objectAtIndex: [o_time_color_pop
412 indexOfSelectedItem]] objectAtIndex: 1] UTF8String], NULL, 0 );
415 var_Set( p_input->p_libvlc, "time-color", val );
417 config_PutInt( p_intf, "time-color", val.i_int );
420 else if( sender == o_time_opaque_sld )
422 val.i_int = [o_time_opaque_sld intValue];
425 var_Set( p_input->p_libvlc, "time-opacity", val );
427 config_PutInt( p_intf, "time-opacity", val.i_int );
430 else if( sender == o_time_size_pop )
432 val.i_int = [[o_time_size_pop titleOfSelectedItem] intValue];
435 var_Set( p_input->p_libvlc, "time-size", val );
437 config_PutInt( p_intf, "time-size", val.i_int );
441 else if( sender == o_logo_opaque_sld )
443 val.i_int = [o_logo_opaque_sld intValue];
446 var_Set( p_input->p_libvlc, "logo-transparency", val );
448 config_PutInt( p_intf, "logo-transparency", val.i_int );
451 else if( sender == o_logo_pos_radio )
453 val.i_int = [[o_logo_pos_radio selectedCell] tag];
456 var_Set( p_input->p_libvlc, "logo-position", val );
458 config_PutInt( p_intf, "logo-position", val.i_int );
460 else if ( sender == o_logo_image_fld )
462 val.psz_string = (char *)[[o_logo_image_fld stringValue] UTF8String];
465 var_Set( p_input->p_libvlc, "logo-file", val );
467 config_PutPsz( p_intf, "logo-file", val.psz_string );
472 msg_Err( p_intf, "couldn't find any action for sender" );
478 o_config_changed = YES;
479 vlc_object_release( p_input );
483 - (IBAction)enableFilter:(id)sender
485 if( sender == o_marq_enabled_ckb )
487 if( [o_marq_enabled_ckb state] == NSOnState )
489 [self changeFiltersString:(char *)"marq" onOrOff:VLC_TRUE];
493 [self changeFiltersString:(char *)"marq" onOrOff:VLC_FALSE];
497 if( sender == o_logo_enabled_ckb )
499 if( [o_logo_enabled_ckb state] == NSOnState )
501 [self changeFiltersString:(char *)"logo" onOrOff:VLC_TRUE];
505 [self changeFiltersString:(char *)"logo" onOrOff:VLC_FALSE];
509 if( sender == o_time_enabled_ckb )
511 if( [o_time_enabled_ckb state] == NSOnState )
513 [self changeFiltersString:(char *)"time" onOrOff:VLC_TRUE];
517 [self changeFiltersString:(char *)"time" onOrOff:VLC_FALSE];
525 [o_marq_color_pop setEnabled: [o_marq_enabled_ckb state]];
526 [o_marq_marq_fld setEnabled: [o_marq_enabled_ckb state]];
527 [o_marq_opaque_sld setEnabled: [o_marq_enabled_ckb state]];
528 [o_marq_size_pop setEnabled: [o_marq_enabled_ckb state]];
529 [o_marq_tmOut_fld setEnabled: [o_marq_enabled_ckb state]];
530 [o_marq_pos_radio setEnabled: [o_marq_enabled_ckb state]];
535 [o_time_color_pop setEnabled: [o_time_enabled_ckb state]];
536 [o_time_stamp_fld setEnabled: [o_time_enabled_ckb state]];
537 [o_time_opaque_sld setEnabled: [o_time_enabled_ckb state]];
538 [o_time_size_pop setEnabled: [o_time_enabled_ckb state]];
539 [o_time_pos_radio setEnabled: [o_time_enabled_ckb state]];
544 [o_logo_image_btn setEnabled: [o_logo_enabled_ckb state]];
545 [o_logo_image_fld setEnabled: [o_logo_enabled_ckb state]];
546 [o_logo_opaque_sld setEnabled: [o_logo_enabled_ckb state]];
547 [o_logo_pos_radio setEnabled: [o_logo_enabled_ckb state]];
550 - (void)changeFiltersString:(char *)psz_name onOrOff:(vlc_bool_t )b_add
552 /* copied from ../wxwidgets/extrapanel.cpp
553 * renamed to conform with Cocoa's rules
554 * and slightly modified to suit our needs */
556 intf_thread_t * p_intf = VLCIntf;
558 char *psz_parser, *psz_string;
559 psz_string = config_GetPsz( p_intf, "sub-filter" );
561 if( !psz_string ) psz_string = strdup("");
563 psz_parser = strstr( psz_string, psz_name );
569 psz_parser = psz_string;
570 asprintf( &psz_string, (*psz_string) ? "%s:%s" : "%s%s",
571 psz_string, psz_name );
583 memmove( psz_parser, psz_parser + strlen(psz_name) +
584 (*(psz_parser + strlen(psz_name)) == ':' ? 1 : 0 ),
585 strlen(psz_parser + strlen(psz_name)) + 1 );
587 /* Remove trailing : : */
588 if( *(psz_string+strlen(psz_string ) -1 ) == ':' )
590 *(psz_string+strlen(psz_string ) -1 ) = '\0';
600 config_PutPsz( p_intf, "sub-filter", psz_string );
602 /* Try to set on the fly */
603 /* FIXME: enable this once we support on-the-fly addition of this kind of
605 vout_thread_t *p_vout;
606 p_vout = (vout_thread_t *)vlc_object_find( p_intf, VLC_OBJECT_VOUT,
610 var_SetString( p_vout, "sub-filter", psz_string );
611 vlc_object_release( p_vout );
616 o_config_changed = YES;