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