]> git.sesse.net Git - vlc/blob - modules/gui/macosx/sfilters.m
Merge branch 'master' of git@git.videolan.org:vlc
[vlc] / modules / gui / macosx / sfilters.m
1 /*****************************************************************************
2  * sfilter.m: MacOS X Subpicture filters dialogue
3  *****************************************************************************
4  * Copyright (C) 2005-2008 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Felix Paul Kühne <fkuehne -at -videolan -dot- org>
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 <vlc_vout.h>
34
35 /* TODO:
36     - check for memory leaks
37     - save the preferences, if requested
38 */
39
40 @implementation VLCsFilters
41
42 static VLCsFilters *_o_sharedInstance = nil;
43
44 + (VLCsFilters *)sharedInstance
45 {
46     return _o_sharedInstance ? _o_sharedInstance : [[self alloc] init];
47 }
48
49 - (id)init
50 {
51     if (_o_sharedInstance) {
52         [self dealloc];
53     } else {
54         _o_sharedInstance = [super init];
55     }
56
57     return _o_sharedInstance;
58 }
59
60 - (void)dealloc
61 {
62     [o_colors release];
63
64     [super dealloc];
65 }
66
67 - (void)initStrings
68 {
69     [o_sfilter_win setTitle: _NS("Subpicture Filters")];
70     [[o_sfilter_tabView tabViewItemAtIndex: 0] setLabel: _NS("Logo")];
71     [[o_sfilter_tabView tabViewItemAtIndex: 1] setLabel: _NS("Time")];
72     [[o_sfilter_tabView tabViewItemAtIndex: 2] setLabel: _NS("Marquee")];
73     [o_sfilter_saveSettings_ckb setTitle: _NS("Save settings")];
74     [o_logo_image_btn setTitle: _NS("Browse...")];
75     [o_logo_enabled_ckb setTitle: _NS("Enabled")];
76     [o_logo_image_lbl setStringValue: _NS("Image:")];
77     [o_logo_pos_lbl setStringValue: _NS("Position:")];
78     [o_logo_opaque_lbl setStringValue: _NS("Opaqueness")];
79     [o_time_enabled_ckb setTitle: _NS("Enabled")];
80     [o_time_stamp_lbl setStringValue: _NS("Timestamp:")];
81     [o_time_size_lbl setStringValue: _NS("Size:")];
82     [o_time_color_lbl setStringValue: _NS("Color:")];
83     [o_time_opaque_lbl setStringValue: _NS("Opaqueness:")];
84     [o_time_pos_lbl setStringValue: _NS("Position:")];
85     [o_time_size_inPx_lbl setStringValue: _NS("(in pixels)")];
86     [o_marq_enabled_ckb setTitle: _NS("Enabled")];
87     [o_marq_color_lbl setStringValue: _NS("Color:")];
88     [o_marq_marq_lbl setStringValue: _NS("Marquee:")];
89     [o_marq_opaque_lbl setStringValue: _NS("Opaqueness")];
90     [o_marq_tmOut_lbl setStringValue: _NS("Timeout:")];
91     [o_marq_tmOut_ms_lbl setStringValue: _NS("ms")];
92     [o_marq_pos_lbl setStringValue: _NS("Position:")];
93     [o_marq_size_lbl setStringValue: _NS("Size:")];
94     [o_time_color_lbl setStringValue: _NS("(in pixels)")];
95 }
96
97 - (void)awakeFromNib
98 {
99     /* colors as implemented at the beginning of marq.c and time.c
100      * feel free to add more colors, but remember to add them to these files
101      * as well to keep a certain level of consistency across the interfaces */
102     NSArray * o_default;
103     NSArray * o_black;
104     NSArray * o_gray;
105     NSArray * o_silver;
106     NSArray * o_white;
107     NSArray * o_maroon;
108     NSArray * o_red;
109     NSArray * o_fuchsia;
110     NSArray * o_yellow;
111     NSArray * o_olive;
112     NSArray * o_green;
113     NSArray * o_teal;
114     NSArray * o_lime;
115     NSArray * o_purple;
116     NSArray * o_navy;
117     NSArray * o_blue;
118     NSArray * o_aqua;
119     o_default = [NSArray arrayWithObjects: _NS("Default"), @"-1", nil];
120     o_black = [NSArray arrayWithObjects: _NS("Black"), @"0x000000", nil];
121     o_gray = [NSArray arrayWithObjects: _NS("Gray"), @"0x808080", nil];
122     o_silver = [NSArray arrayWithObjects: _NS("Silver"), @"0xC0C0C0", nil];
123     o_white = [NSArray arrayWithObjects: _NS("White"), @"0xFFFFFF", nil];
124     o_maroon = [NSArray arrayWithObjects: _NS("Maroon"), @"0x800000", nil];
125     o_red = [NSArray arrayWithObjects: _NS("Red"), @"0xFF0000", nil];
126     o_fuchsia = [NSArray arrayWithObjects: _NS("Fuchsia"), @"0xFF00FF", nil];
127     o_yellow = [NSArray arrayWithObjects: _NS("Yellow"), @"0xFFFF00", nil];
128     o_olive = [NSArray arrayWithObjects: _NS("Olive"), @"0x808000", nil];
129     o_green = [NSArray arrayWithObjects: _NS("Green"), @"0x008000", nil];
130     o_teal = [NSArray arrayWithObjects: _NS("Teal"), @"0x008080", nil];
131     o_lime = [NSArray arrayWithObjects: _NS("Lime"), @"0x00FF00", nil];
132     o_purple = [NSArray arrayWithObjects: _NS("Purple"), @"0x800080", nil];
133     o_navy = [NSArray arrayWithObjects: _NS("Navy"), @"0x000080", nil];
134     o_blue = [NSArray arrayWithObjects: _NS("Blue"), @"0x0000FF", nil];
135     o_aqua = [NSArray arrayWithObjects: _NS("Aqua"), @"0x00FFFF", nil];
136     o_colors = [[NSArray alloc] initWithObjects: o_default, o_black, o_gray,
137         o_silver, o_white, o_maroon, o_red, o_fuchsia, o_yellow, o_olive,
138         o_green, o_teal, o_lime, o_purple, o_navy, o_blue, o_aqua, nil];
139
140     unsigned int x = 0;
141     [o_marq_color_pop removeAllItems];
142     [o_time_color_pop removeAllItems];
143  
144     /* we are adding tags to the items, so we can easily identify them even if
145      * the menu was sorted */
146     while (x != [o_colors count])
147     {
148         [o_marq_color_pop addItemWithTitle: [[o_colors objectAtIndex:x]
149             objectAtIndex:0]];
150         [[o_marq_color_pop lastItem] setTag: x];
151  
152         [o_time_color_pop addItemWithTitle: [[o_colors objectAtIndex:x]
153             objectAtIndex:0]];
154         [[o_time_color_pop lastItem] setTag: x];
155  
156         x = (x + 1);
157     }
158
159     [o_marq_color_pop selectItemAtIndex:0];
160     [o_time_color_pop selectItemAtIndex:0];
161
162     NSArray * o_sizes;
163     o_sizes = [[NSArray alloc] initWithObjects: @"6", @"8", @"10", @"11", @"12",\
164         @"13", @"14", @"16", @"18", @"24", @"36", @"48", @"64", @"72", @"96",
165         @"144", @"288", nil];
166     [o_marq_size_pop removeAllItems];
167     [o_marq_size_pop addItemsWithTitles: o_sizes];
168     [o_time_size_pop removeAllItems];
169     [o_time_size_pop addItemsWithTitles: o_sizes];
170     [o_sizes release];
171 }
172
173 - (void)showAsPanel
174 {
175     char * psz_temp;
176
177     /* called from intf.m */
178     [o_sfilter_win displayIfNeeded];
179     [o_sfilter_win makeKeyAndOrderFront:nil];
180
181     intf_thread_t * p_intf = VLCIntf;
182
183     /* retrieve the marquee settings */
184     int x = 0;
185     int tempInt = config_GetInt( p_intf, "marq-color" );
186     while( strtol([[[o_colors objectAtIndex:x] objectAtIndex:1] UTF8String],
187         NULL, 0) != tempInt )
188     {
189         x = (x + 1);
190  
191         if( x >= [o_marq_color_pop numberOfItems] )
192         {
193             x = 0;
194             return;
195         }
196     }
197     [o_marq_color_pop selectItemAtIndex: x];
198     if( psz_temp = config_GetPsz( p_intf, "time-format" ) )
199         [o_marq_marq_fld setStringValue: [NSString stringWithUTF8String: psz_temp]];
200     else
201         [o_marq_marq_fld setStringValue: _NS("Not Available")];
202  
203     [o_marq_opaque_sld setIntValue: config_GetInt( p_intf, "marq-opacity")];
204     [o_marq_pos_radio selectCellWithTag: config_GetInt( p_intf, "marq-position" )];
205     /* FIXME: the following line doesn't work with "-1", which is the default
206      * value */
207     [o_marq_size_pop selectItemWithTitle:
208         [[NSNumber numberWithInt: config_GetInt( p_intf, "marq-size" )]
209             stringValue]];
210     [o_marq_size_pop selectItemAtIndex: x];
211     [o_marq_tmOut_fld setStringValue: [[NSNumber numberWithInt:
212         config_GetInt( p_intf, "marq-timeout" )] stringValue]];
213  
214     /* retrieve the time settings */
215     x = 0;
216     tempInt = config_GetInt( p_intf, "time-color" );
217     while( strtol([[[o_colors objectAtIndex:x] objectAtIndex:1] UTF8String], NULL, 0) != tempInt )
218     {
219         x = (x + 1);
220  
221         if( x >= [o_time_color_pop numberOfItems] )
222         {
223             x = 0;
224             return;
225         }
226     }
227     [o_time_color_pop selectItemAtIndex: x];
228     if( psz_temp = config_GetPsz( p_intf, "time-format" ) )
229         [o_time_stamp_fld setStringValue: [NSString stringWithUTF8String: psz_temp]];
230     else
231         [o_time_stamp_fld setStringValue: _NS("Not Available")];
232
233     [o_time_opaque_sld setIntValue: config_GetInt( p_intf, "time-opacity")];
234     /* FIXME: the following line doesn't work with "-1", which is the default
235      * value */
236     [o_time_size_pop selectItemWithTitle:
237         [[NSNumber numberWithInt: config_GetInt( p_intf, "time-size" )]
238             stringValue]];
239     [o_time_pos_radio selectCellWithTag: config_GetInt( p_intf, "time-position" )];
240
241     /* retrieve the logo settings */
242     [o_logo_opaque_sld setIntValue: config_GetInt( p_intf, "logo-transparency")];
243     /* in case that no path has been saved yet */
244     if( psz_temp = config_GetPsz( p_intf, "logo-file" ) )
245         [o_logo_image_fld setStringValue: [NSString stringWithUTF8String: psz_temp]];
246     else
247         [o_logo_image_fld setStringValue: @""];
248     [o_logo_pos_radio selectCellWithTag: config_GetInt( p_intf, "logo-position" )];
249  
250     /* enable the requested filters */
251     char * psz_subfilters;
252     psz_subfilters = config_GetPsz( p_intf, "sub-filter" );
253     if( psz_subfilters )
254     {
255         if( strstr( psz_subfilters, "marq") )
256             [o_marq_enabled_ckb setState: YES];
257         else
258             [o_marq_enabled_ckb setState: NO];
259  
260         if( strstr( psz_subfilters, "logo") )
261             [o_logo_enabled_ckb setState: YES];
262         else
263             [o_logo_enabled_ckb setState: NO];
264  
265         if( strstr( psz_subfilters, "time") )
266             [o_time_enabled_ckb setState: YES];
267         else
268             [o_time_enabled_ckb setState: NO];
269     }
270     [self enableMarq];
271     [self enableLogo];
272     [self enableTime];
273 }
274
275 - (IBAction)logo_selectFile:(id)sender
276 {
277     NSOpenPanel * openPanel = [NSOpenPanel openPanel];
278     SEL sel = @selector(logo_getFile:returnCode:contextInfo:);
279     [openPanel beginSheetForDirectory:nil file:nil types: [NSArray
280         arrayWithObjects: @"png", @"PNG", @"'PNGf'", nil] modalForWindow:
281         o_sfilter_win modalDelegate:self didEndSelector:sel contextInfo:nil];
282 }
283
284 - (void)logo_getFile: (NSOpenPanel *)sheet returnCode:
285     (int)returnCode contextInfo: (void *)contextInfo
286 {
287     if (returnCode == NSOKButton)
288     {
289         [o_logo_image_fld setStringValue: [sheet filename]];
290         /* Make sure we notice that */
291         [self propertyChanged: o_logo_image_fld];
292     }
293 }
294
295 - (IBAction)propertyChanged:(id)sender
296 {
297     intf_thread_t * p_intf = VLCIntf;
298     input_thread_t * p_input = (input_thread_t *)vlc_object_find( p_intf,
299         VLC_OBJECT_INPUT, FIND_ANYWHERE );
300
301     vlc_value_t val;
302
303     /* general properties */
304     if( sender == o_sfilter_saveSettings_ckb)
305     {
306         o_save_settings = [o_sfilter_saveSettings_ckb state];
307     }
308
309     /* marquee */
310     else if( sender == o_marq_marq_fld )
311     {
312         if( [[o_marq_marq_fld stringValue] length] == 0 )
313         {
314             val.psz_string = (char *)"";
315         }
316         else
317         {
318             val.psz_string = (char *)[[o_marq_marq_fld stringValue] UTF8String];
319         }
320
321         if( p_input )
322             var_Set( p_input->p_libvlc, "marq-marquee", val );
323
324         config_PutPsz( p_intf, "marq-marquee", val.psz_string );
325     }
326
327     else if( sender == o_marq_pos_radio )
328     {
329         val.i_int = [[o_marq_pos_radio selectedCell] tag];
330
331         if( p_input )
332             var_Set( p_input->p_libvlc, "marq-position", val );
333
334         config_PutInt( p_intf, "marq-position", val.i_int );
335     }
336
337     else if( sender == o_marq_color_pop )
338     {
339         val.i_int = strtol( [[[o_colors objectAtIndex: [o_marq_color_pop
340             indexOfSelectedItem]] objectAtIndex: 1] UTF8String], NULL, 0 );
341
342         if( p_input )
343             var_Set( p_input->p_libvlc, "marq-color", val );
344
345         config_PutInt( p_intf, "marq-color", val.i_int );
346     }
347
348     else if( sender == o_marq_opaque_sld )
349     {
350         val.i_int = [o_marq_opaque_sld intValue];
351
352         if( p_input )
353             var_Set( p_input->p_libvlc, "marq-opacity", val );
354
355         config_PutInt( p_intf, "marq-opacity", val.i_int );
356     }
357
358     else if( sender == o_marq_size_pop )
359     {
360         val.i_int = [[o_marq_size_pop titleOfSelectedItem] intValue];
361
362         if( p_input )
363             var_Set( p_input->p_libvlc, "marq-size", val );
364
365         config_PutInt( p_intf, "marq-size", val.i_int );
366     }
367
368     else if( sender == o_marq_tmOut_fld && [[sender stringValue] length] > 0 )
369     {
370         val.i_int = [o_marq_tmOut_fld intValue];
371
372         if( p_input )
373             var_Set( p_input->p_libvlc, "marq-timeout", val );
374
375         config_PutInt( p_intf, "marq-timeout", val.i_int );
376     }
377  
378     /* time */
379  
380     else if( sender == o_time_stamp_fld )
381     {
382         if( [[o_time_stamp_fld stringValue] length] == 0 )
383         {
384             val.psz_string = (char *)"";
385         }
386         else
387         {
388             val.psz_string = (char *)[[o_time_stamp_fld stringValue] UTF8String];
389         }
390
391         if( p_input )
392             var_Set( p_input->p_libvlc, "time-format", val );
393
394         config_PutPsz( p_intf, "time-format", val.psz_string );
395     }
396
397     else if( sender == o_time_pos_radio )
398     {
399         val.i_int = [[o_time_pos_radio selectedCell] tag];
400
401         if( p_input )
402             var_Set( p_input->p_libvlc, "time-position", val );
403
404         config_PutInt( p_intf, "time-position", val.i_int );
405     }
406
407     else if( sender == o_time_color_pop )
408     {
409         val.i_int = strtol( [[[o_colors objectAtIndex: [o_time_color_pop
410             indexOfSelectedItem]] objectAtIndex: 1] UTF8String], NULL, 0 );
411
412         if( p_input )
413             var_Set( p_input->p_libvlc, "time-color", val );
414
415         config_PutInt( p_intf, "time-color", val.i_int );
416     }
417
418     else if( sender == o_time_opaque_sld )
419     {
420         val.i_int = [o_time_opaque_sld intValue];
421
422         if( p_input )
423             var_Set( p_input->p_libvlc, "time-opacity", val );
424
425         config_PutInt( p_intf, "time-opacity", val.i_int );
426     }
427
428     else if( sender == o_time_size_pop )
429     {
430         val.i_int = [[o_time_size_pop titleOfSelectedItem] intValue];
431
432         if( p_input )
433             var_Set( p_input->p_libvlc, "time-size", val );
434
435         config_PutInt( p_intf, "time-size", val.i_int );
436     }
437
438     /* logo */
439     else if( sender == o_logo_opaque_sld )
440     {
441         val.i_int = [o_logo_opaque_sld intValue];
442
443         if( p_input )
444             var_Set( p_input->p_libvlc, "logo-transparency", val );
445
446         config_PutInt( p_intf, "logo-transparency", val.i_int );
447     }
448
449     else if( sender == o_logo_pos_radio )
450     {
451         val.i_int = [[o_logo_pos_radio selectedCell] tag];
452
453         if( p_input )
454             var_Set( p_input->p_libvlc, "logo-position", val );
455
456         config_PutInt( p_intf, "logo-position", val.i_int );
457     }
458     else if ( sender == o_logo_image_fld )
459     {
460         val.psz_string = (char *)[[o_logo_image_fld stringValue] UTF8String];
461
462         if( p_input )
463             var_Set( p_input->p_libvlc, "logo-file", val );
464
465         config_PutPsz( p_intf, "logo-file", val.psz_string );
466     }
467     else
468     {
469         /* just in case */
470         msg_Err( p_intf, "couldn't find any action for sender" );
471     }
472
473     /* clean up */
474     if ( p_input )
475     {
476         o_config_changed = YES;
477         vlc_object_release( p_input );
478     }
479 }
480
481 - (IBAction)enableFilter:(id)sender
482 {
483     if( sender == o_marq_enabled_ckb )
484     {
485         [self changeFiltersString:"marq" onOrOff: [o_marq_enabled_ckb state]];
486
487         [self enableMarq];
488     }
489     if( sender == o_logo_enabled_ckb )
490     {
491         [self changeFiltersString:"logo" onOrOff: [o_logo_enabled_ckb state]];
492
493         [self enableLogo];
494     }
495     if( sender == o_time_enabled_ckb )
496     {
497         [self changeFiltersString:"time" onOrOff: [o_time_enabled_ckb state]];
498
499         [self enableTime];
500     }
501 }
502
503 - (void)enableMarq
504 {
505     [o_marq_color_pop setEnabled: [o_marq_enabled_ckb state]];
506     [o_marq_marq_fld setEnabled: [o_marq_enabled_ckb state]];
507     [o_marq_opaque_sld setEnabled: [o_marq_enabled_ckb state]];
508     [o_marq_size_pop setEnabled: [o_marq_enabled_ckb state]];
509     [o_marq_tmOut_fld setEnabled: [o_marq_enabled_ckb state]];
510     [o_marq_pos_radio setEnabled: [o_marq_enabled_ckb state]];
511 }
512
513 - (void)enableTime
514 {
515     [o_time_color_pop setEnabled: [o_time_enabled_ckb state]];
516     [o_time_stamp_fld setEnabled: [o_time_enabled_ckb state]];
517     [o_time_opaque_sld setEnabled: [o_time_enabled_ckb state]];
518     [o_time_size_pop setEnabled: [o_time_enabled_ckb state]];
519     [o_time_pos_radio setEnabled: [o_time_enabled_ckb state]];
520 }
521
522 - (void)enableLogo
523 {
524     [o_logo_image_btn setEnabled: [o_logo_enabled_ckb state]];
525     [o_logo_image_fld setEnabled: [o_logo_enabled_ckb state]];
526     [o_logo_opaque_sld setEnabled: [o_logo_enabled_ckb state]];
527     [o_logo_pos_radio setEnabled: [o_logo_enabled_ckb state]];
528 }
529
530 - (void)changeFiltersString:(char *)psz_name onOrOff:(bool )b_add
531 {
532     /* copied from ../wxwidgets/extrapanel.cpp
533      * renamed to conform with Cocoa's rules
534      * and slightly modified to suit our needs */
535
536     intf_thread_t * p_intf = VLCIntf;
537  
538     char *psz_parser, *psz_string;
539     psz_string = config_GetPsz( p_intf, "sub-filter" );
540  
541     if( !psz_string ) psz_string = strdup("");
542
543     psz_parser = strstr( psz_string, psz_name );
544
545     if( b_add )
546     {
547         if( !psz_parser )
548         {
549             psz_parser = psz_string;
550             asprintf( &psz_string, (*psz_string) ? "%s:%s" : "%s%s",
551                             psz_string, psz_name );
552             free( psz_parser );
553         }
554         else
555         {
556             return;
557         }
558     }
559     else
560     {
561         if( psz_parser )
562         {
563             memmove( psz_parser, psz_parser + strlen(psz_name) +
564                             (*(psz_parser + strlen(psz_name)) == ':' ? 1 : 0 ),
565                             strlen(psz_parser + strlen(psz_name)) + 1 );
566
567             /* Remove trailing : : */
568             if( *(psz_string+strlen(psz_string ) -1 ) == ':' )
569             {
570                 *(psz_string+strlen(psz_string ) -1 ) = '\0';
571             }
572          }
573          else
574          {
575              free( psz_string );
576              return;
577          }
578     }
579  
580     config_PutPsz( p_intf, "sub-filter", psz_string );
581  
582     /* Try to set on the fly */
583     /* FIXME: enable this once we support on-the-fly addition of this kind of
584      * filters... */
585     vout_thread_t *p_vout;
586     p_vout = (vout_thread_t *)vlc_object_find( p_intf, VLC_OBJECT_VOUT,
587                                               FIND_ANYWHERE );
588     if( p_vout )
589     {
590         var_SetString( p_vout, "sub-filter", psz_string );
591         vlc_object_release( p_vout );
592     }
593
594     free( psz_string );
595
596     o_config_changed = YES;
597 }
598 @end