]> git.sesse.net Git - vlc/blob - modules/gui/macosx/sfilters.m
bb6c5e251b82445fcd4d6f3b4fc635b2dc08e4b2
[vlc] / modules / gui / macosx / sfilters.m
1 /*****************************************************************************
2  * sfilter.m: MacOS X Subpicture filters dialogue
3  *****************************************************************************
4  * Copyright (C) 2005-2006 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Felix Kühne <fkuehne@users.sf.net>
8  *
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.
13  *
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.
18  *
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  *****************************************************************************/
23
24
25 /*****************************************************************************
26  * Note: 
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  *****************************************************************************/
31
32 #import "sfilters.h"
33 #import "intf.h"
34 #import <vlc_vout.h>
35
36 /* TODO:
37     - check for memory leaks
38     - save the preferences, if requested
39 */
40
41 @implementation VLCsFilters
42
43 static VLCsFilters *_o_sharedInstance = nil;
44
45 + (VLCsFilters *)sharedInstance
46 {
47     return _o_sharedInstance ? _o_sharedInstance : [[self alloc] init];
48 }
49
50 - (id)init
51 {
52     if (_o_sharedInstance) {
53         [self dealloc];
54     } else {
55         _o_sharedInstance = [super init];
56     }
57
58     return _o_sharedInstance;
59 }
60
61 - (void)dealloc
62 {
63     [o_colors release];
64
65     [super dealloc];
66 }
67
68 - (void)initStrings
69 {
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)")];
96 }
97
98 - (void)awakeFromNib
99 {
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 */
103     NSArray * o_default;
104     NSArray * o_black;
105     NSArray * o_gray;
106     NSArray * o_silver;
107     NSArray * o_white;
108     NSArray * o_maroon;
109     NSArray * o_red;
110     NSArray * o_fuchsia;
111     NSArray * o_yellow;
112     NSArray * o_olive;
113     NSArray * o_green;
114     NSArray * o_teal;
115     NSArray * o_lime;
116     NSArray * o_purple;
117     NSArray * o_navy;
118     NSArray * o_blue;
119     NSArray * o_aqua;
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];
140
141     unsigned int x = 0;
142     [o_marq_color_pop removeAllItems];
143     [o_time_color_pop removeAllItems];
144     
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])
148     {
149         [o_marq_color_pop addItemWithTitle: [[o_colors objectAtIndex:x]
150             objectAtIndex:0]];
151         [[o_marq_color_pop lastItem] setTag: x];
152         
153         [o_time_color_pop addItemWithTitle: [[o_colors objectAtIndex:x]
154             objectAtIndex:0]];
155         [[o_time_color_pop lastItem] setTag: x];
156         
157         x = (x + 1);
158     }
159
160     [o_marq_color_pop selectItemAtIndex:0];
161     [o_time_color_pop selectItemAtIndex:0];
162
163     NSArray * o_sizes;
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];
171     [o_sizes release];
172 }
173
174 - (void)showAsPanel
175 {
176     char * psz_temp;
177
178     /* called from intf.m */
179     [o_sfilter_win displayIfNeeded];
180     [o_sfilter_win makeKeyAndOrderFront:nil];
181
182     intf_thread_t * p_intf = VLCIntf;
183
184     /* retrieve the marquee settings */
185     int x = 0;
186     int tempInt = config_GetInt( p_intf, "marq-color" );
187     while( strtol([[[o_colors objectAtIndex:x] objectAtIndex:1] UTF8String],
188         NULL, 0) != tempInt )
189     {
190         x = (x + 1);
191         
192         if( x >= [o_marq_color_pop numberOfItems] )
193         {
194             x = 0;
195             return;
196         }
197     }
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]];
201     else
202         [o_marq_marq_fld setStringValue: _NS("Not Available")];
203     
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
207      * value */
208     [o_marq_size_pop selectItemWithTitle: 
209         [[NSNumber numberWithInt: config_GetInt( p_intf, "marq-size" )]
210             stringValue]];
211     [o_marq_size_pop selectItemAtIndex: x];
212     [o_marq_tmOut_fld setStringValue: [[NSNumber numberWithInt:
213         config_GetInt( p_intf, "marq-timeout" )] stringValue]];
214     
215     /* retrieve the time settings */
216     x = 0;
217     tempInt = config_GetInt( p_intf, "time-color" );
218     while( strtol([[[o_colors objectAtIndex:x] objectAtIndex:1] UTF8String],
219         NULL, 0) != tempInt )
220     {
221         x = (x + 1);
222         
223         if( x >= [o_time_color_pop numberOfItems] )
224         {
225             x = 0;
226             return;
227         }
228     }
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]];
232     else
233         [o_time_stamp_fld setStringValue: _NS("Not Available")];
234
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
237      * value */
238     [o_time_size_pop selectItemWithTitle: 
239         [[NSNumber numberWithInt: config_GetInt( p_intf, "time-size" )]
240             stringValue]];
241     [o_time_pos_radio selectCellWithTag: config_GetInt( p_intf, "time-position" )];    
242
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]];
248     else
249         [o_logo_image_fld setStringValue: @""];
250     [o_logo_pos_radio selectCellWithTag: config_GetInt( p_intf, "logo-position" )];
251     
252     /* enable the requested filters */
253     char * psz_subfilters;
254     psz_subfilters = config_GetPsz( p_intf, "sub-filter" );
255     if( psz_subfilters )
256     {
257         if( strstr( psz_subfilters, "marq") )
258             [o_marq_enabled_ckb setState: YES];
259         else
260             [o_marq_enabled_ckb setState: NO];
261         
262         if( strstr( psz_subfilters, "logo") )
263             [o_logo_enabled_ckb setState: YES];
264         else
265             [o_logo_enabled_ckb setState: NO];
266         
267         if( strstr( psz_subfilters, "time") )
268             [o_time_enabled_ckb setState: YES];
269         else
270             [o_time_enabled_ckb setState: NO];
271     }
272     [self enableMarq];
273     [self enableLogo];
274     [self enableTime];
275 }
276
277 - (IBAction)logo_selectFile:(id)sender
278 {
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];
284 }
285
286 - (void)logo_getFile: (NSOpenPanel *)sheet returnCode:
287     (int)returnCode contextInfo: (void *)contextInfo
288 {
289     if (returnCode == NSOKButton)
290     {
291         [o_logo_image_fld setStringValue: [sheet filename]];
292         /* Make sure we notice that */
293         [self propertyChanged: o_logo_image_fld];
294     }
295 }
296
297 - (IBAction)propertyChanged:(id)sender
298 {
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 );
302
303     vlc_value_t val;
304
305     /* general properties */
306     if( sender == o_sfilter_saveSettings_ckb)
307     {
308         o_save_settings = [o_sfilter_saveSettings_ckb state]; 
309     }
310
311     /* marquee */
312     else if( sender == o_marq_marq_fld )
313     {
314         if( [[o_marq_marq_fld stringValue] length] == 0 )
315         {
316             val.psz_string = (char *)"";
317         }
318         else
319         {
320             val.psz_string = (char *)[[o_marq_marq_fld stringValue] UTF8String];
321         }
322
323         if( p_input )
324             var_Set( p_input->p_libvlc, "marq-marquee", val );
325
326         config_PutPsz( p_intf, "marq-marquee", val.psz_string );
327     }
328
329     else if( sender == o_marq_pos_radio )
330     {
331         val.i_int = [[o_marq_pos_radio selectedCell] tag];
332
333         if( p_input )
334             var_Set( p_input->p_libvlc, "marq-position", val );
335
336         config_PutInt( p_intf, "marq-position", val.i_int );
337     }
338
339     else if( sender == o_marq_color_pop )
340     {
341         val.i_int = strtol( [[[o_colors objectAtIndex: [o_marq_color_pop
342             indexOfSelectedItem]] objectAtIndex: 1] UTF8String], NULL, 0 );
343
344         if( p_input )
345             var_Set( p_input->p_libvlc, "marq-color", val );
346
347         config_PutInt( p_intf, "marq-color", val.i_int );
348     }
349
350     else if( sender == o_marq_opaque_sld )
351     {
352         val.i_int = [o_marq_opaque_sld intValue];
353
354         if( p_input )
355             var_Set( p_input->p_libvlc, "marq-opacity", val );
356
357         config_PutInt( p_intf, "marq-opacity", val.i_int );
358     }
359
360     else if( sender == o_marq_size_pop )
361     {
362         val.i_int = [[o_marq_size_pop titleOfSelectedItem] intValue];
363
364         if( p_input )
365             var_Set( p_input->p_libvlc, "marq-size", val );
366
367         config_PutInt( p_intf, "marq-size", val.i_int );
368     }
369
370     else if( sender == o_marq_tmOut_fld && [[sender stringValue] length] > 0 )
371     {
372         val.i_int = [o_marq_tmOut_fld intValue];
373
374         if( p_input )
375             var_Set( p_input->p_libvlc, "marq-timeout", val );
376
377         config_PutInt( p_intf, "marq-timeout", val.i_int );
378     }
379     
380     /* time */
381     
382     else if( sender == o_time_stamp_fld )
383     {
384         if( [[o_time_stamp_fld stringValue] length] == 0 )
385         {
386             val.psz_string = (char *)"";
387         }
388         else
389         {
390             val.psz_string = (char *)[[o_time_stamp_fld stringValue] UTF8String];
391         }
392
393         if( p_input )
394             var_Set( p_input->p_libvlc, "time-format", val );
395
396         config_PutPsz( p_intf, "time-format", val.psz_string );
397     }
398
399     else if( sender == o_time_pos_radio )
400     {
401         val.i_int = [[o_time_pos_radio selectedCell] tag];
402
403         if( p_input )
404             var_Set( p_input->p_libvlc, "time-position", val );
405
406         config_PutInt( p_intf, "time-position", val.i_int );
407     }
408
409     else if( sender == o_time_color_pop )
410     {
411         val.i_int = strtol( [[[o_colors objectAtIndex: [o_time_color_pop
412             indexOfSelectedItem]] objectAtIndex: 1] UTF8String], NULL, 0 );
413
414         if( p_input )
415             var_Set( p_input->p_libvlc, "time-color", val );
416
417         config_PutInt( p_intf, "time-color", val.i_int );
418     }
419
420     else if( sender == o_time_opaque_sld )
421     {
422         val.i_int = [o_time_opaque_sld intValue];
423
424         if( p_input )
425             var_Set( p_input->p_libvlc, "time-opacity", val );
426
427         config_PutInt( p_intf, "time-opacity", val.i_int );
428     }
429
430     else if( sender == o_time_size_pop )
431     {
432         val.i_int = [[o_time_size_pop titleOfSelectedItem] intValue];
433
434         if( p_input )
435             var_Set( p_input->p_libvlc, "time-size", val );
436
437         config_PutInt( p_intf, "time-size", val.i_int );
438     }
439
440     /* logo */
441     else if( sender == o_logo_opaque_sld )
442     {
443         val.i_int = [o_logo_opaque_sld intValue];
444
445         if( p_input )
446             var_Set( p_input->p_libvlc, "logo-transparency", val );
447
448         config_PutInt( p_intf, "logo-transparency", val.i_int );
449     }
450
451     else if( sender == o_logo_pos_radio )
452     {
453         val.i_int = [[o_logo_pos_radio selectedCell] tag];
454
455         if( p_input )
456             var_Set( p_input->p_libvlc, "logo-position", val );
457
458         config_PutInt( p_intf, "logo-position", val.i_int );
459     }
460     else if ( sender == o_logo_image_fld )
461     {
462         val.psz_string = (char *)[[o_logo_image_fld stringValue] UTF8String];
463
464         if( p_input )
465             var_Set( p_input->p_libvlc, "logo-file", val );
466
467         config_PutPsz( p_intf, "logo-file", val.psz_string );
468     }
469     else
470     {
471         /* just in case */
472         msg_Err( p_intf, "couldn't find any action for sender" );
473     }
474
475     /* clean up */
476     if ( p_input )
477     {
478         o_config_changed = YES;
479         vlc_object_release( p_input );
480     }
481 }
482
483 - (IBAction)enableFilter:(id)sender
484 {
485     if( sender == o_marq_enabled_ckb )
486     {
487         if( [o_marq_enabled_ckb state] == NSOnState )
488         {
489             [self changeFiltersString:(char *)"marq" onOrOff:VLC_TRUE];
490         }
491         else
492         {
493             [self changeFiltersString:(char *)"marq" onOrOff:VLC_FALSE];
494         }
495         [self enableMarq];
496     }
497     if( sender == o_logo_enabled_ckb )
498     {
499         if( [o_logo_enabled_ckb state] == NSOnState )
500         {
501             [self changeFiltersString:(char *)"logo" onOrOff:VLC_TRUE];
502         }
503         else
504         {
505             [self changeFiltersString:(char *)"logo" onOrOff:VLC_FALSE];
506         }
507         [self enableLogo];
508     }
509     if( sender == o_time_enabled_ckb )
510     {
511         if( [o_time_enabled_ckb state] == NSOnState )
512         {
513             [self changeFiltersString:(char *)"time" onOrOff:VLC_TRUE];
514         }
515         else
516         {
517             [self changeFiltersString:(char *)"time" onOrOff:VLC_FALSE];
518         }
519         [self enableTime];
520     }    
521 }
522
523 - (void)enableMarq
524 {
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]];
531 }
532
533 - (void)enableTime
534 {
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]];
540 }
541
542 - (void)enableLogo
543 {
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]];
548 }
549
550 - (void)changeFiltersString:(char *)psz_name onOrOff:(vlc_bool_t )b_add
551 {
552     /* copied from ../wxwidgets/extrapanel.cpp
553      * renamed to conform with Cocoa's rules
554      * and slightly modified to suit our needs */
555
556     intf_thread_t * p_intf = VLCIntf;
557     
558     char *psz_parser, *psz_string;
559     psz_string = config_GetPsz( p_intf, "sub-filter" );
560     
561     if( !psz_string ) psz_string = strdup("");
562
563     psz_parser = strstr( psz_string, psz_name );
564
565     if( b_add )
566     {
567         if( !psz_parser )
568         {
569             psz_parser = psz_string;
570             asprintf( &psz_string, (*psz_string) ? "%s:%s" : "%s%s",
571                             psz_string, psz_name );
572             free( psz_parser );
573         }
574         else
575         {
576             return;
577         }
578     }
579     else
580     {
581         if( psz_parser )
582         {
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 );
586
587             /* Remove trailing : : */
588             if( *(psz_string+strlen(psz_string ) -1 ) == ':' )
589             {
590                 *(psz_string+strlen(psz_string ) -1 ) = '\0';
591             }
592          }
593          else
594          {
595              free( psz_string );
596              return;
597          }
598     }
599     
600     config_PutPsz( p_intf, "sub-filter", psz_string );
601     
602     /* Try to set on the fly */
603     /* FIXME: enable this once we support on-the-fly addition of this kind of
604      * filters... */
605     vout_thread_t *p_vout;
606     p_vout = (vout_thread_t *)vlc_object_find( p_intf, VLC_OBJECT_VOUT,
607                                               FIND_ANYWHERE );
608     if( p_vout )
609     {
610         var_SetString( p_vout, "sub-filter", psz_string );
611         vlc_object_release( p_vout );
612     }
613
614     free( psz_string );
615
616     o_config_changed = YES;
617 }
618 @end