Add new files which should have been in previous commit.
[vlc.git] / modules / misc / osd / simple.c
1 /*****************************************************************************
2  * simple.c - The OSD Menu simple parser code.
3  *****************************************************************************
4  * Copyright (C) 2005-2007 M2X
5  * $Id: $
6  *
7  * Authors: Jean-Paul Saman
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  * Preamble
26  *****************************************************************************/
27
28 #include <vlc/vlc.h>
29 #include <vlc_vout.h>
30 #include <vlc_config.h>
31
32 #include <vlc_keys.h>
33 #include <vlc_image.h>
34 #include <vlc_osd.h>
35 #include <vlc_charset.h>
36
37 /*****************************************************************************
38  * osd_ConfigLoader: Load and parse osd text configurationfile
39  *****************************************************************************/
40 int E_(osd_parser_simpleOpen) ( vlc_object_t *p_this )
41 {
42     osd_menu_t     *p_menu = (osd_menu_t *) p_this->p_menu;
43     osd_button_t   *p_current = NULL; /* button currently processed */
44     osd_button_t   *p_prev = NULL;    /* previous processed button */
45
46 #define MAX_FILE_PATH 256
47     FILE       *fd = NULL;
48     int        result = 0;
49
50     msg_Dbg( p_this, "opening osdmenu definition file %s", p_menu->psz_file );
51     fd = utf8_fopen( p_menu->psz_file, "r" );
52     if( !fd )
53     {
54         msg_Err( p_this, "failed to open osdmenu definition file %s", p_menu->psz_file );
55         return VLC_EGENERIC;
56     }
57
58     /* Read first line */
59     if( !feof( fd ) )
60     {
61         char action[25] = "";
62         char cmd[25] = "";
63         char path[MAX_FILE_PATH] = "";
64         char *psz_path = NULL;
65         size_t i_len = 0;
66         long pos = 0;
67
68         result = fscanf(fd, "%24s %255s", &action[0], &path[0] );
69
70         /* override images path ? */
71         psz_path = config_GetPsz( p_this, "osdmenu-file-path" );
72         if( psz_path )
73         {
74             /* psz_path is not null and therefor &path[0] cannot be NULL
75              * it might be null terminated.
76              */
77             strncpy( &path[0], psz_path, MAX_FILE_PATH );
78             free( psz_path );
79             psz_path = NULL;
80         }
81         /* NULL terminate before asking the length of path[] */
82         path[MAX_FILE_PATH-1] = '\0';
83         i_len = strlen(&path[0]);
84         if( i_len == MAX_FILE_PATH )
85             i_len--; /* truncate to prevent buffer overflow */
86 #if defined(WIN32) || defined(UNDER_CE)
87         if( (i_len > 0) && path[i_len] != '\\' )
88             path[i_len] = '\\';
89 #else
90         if( (i_len > 0) && path[i_len] != '/' )
91             path[i_len] = '/';
92 #endif
93         path[i_len+1] = '\0';
94         if( result == 0 || result == EOF )
95             goto error;
96         msg_Dbg( p_this, "osdmenu dir %s", &path[0] );
97
98         if( i_len == 0 )
99             *p_menu = osd_MenuNew( *p_menu, NULL, 0, 0 );
100         else
101             *p_menu = osd_MenuNew( *p_menu, &path[0], 0, 0 );
102
103         /* Peek for 'style' argument */
104         pos = ftell( fd );
105         if( pos < 0 )
106                 goto error;
107
108         result = fscanf(fd, "%24s %24s", &cmd[0], &action[0] );
109         if( result == 0 || result == EOF )
110             goto error;
111
112         msg_Dbg( p_this, "osdmenu %s %s", &cmd[0], &action[0] );
113         if( strncmp( &cmd[0], "style", 5 ) == 0 )
114         {
115             if( strncmp( &action[0], "default", 7) == 0 )
116             {
117                 (*p_menu)->i_style = OSD_MENU_STYLE_SIMPLE;
118             }
119             else if( strncmp( &action[0], "concat", 6) == 0 )
120             {
121                 (*p_menu)->i_style = OSD_MENU_STYLE_CONCAT;
122             }
123         }
124         else
125         {
126             result = fseek( fd, pos, SEEK_SET );
127             if( result < 0 )
128                 goto error;
129         }
130     }
131
132     if( !*p_menu )
133         goto error;
134
135     /* read successive lines */
136     while( !feof( fd ) )
137     {
138         osd_state_t   *p_state_current = NULL; /* button state currently processed */
139         osd_state_t   *p_state_prev = NULL;    /* previous state processed button */
140
141         char cmd[25] = "";
142         char action[25] = "";
143         char state[25]  = "";
144         char file[256]  = "";
145         char path[512]  = "";
146         int  i_x = 0;
147         int  i_y = 0;
148
149         result = fscanf( fd, "%24s %24s (%d,%d)", &cmd[0], &action[0], &i_x, &i_y );
150         if( result == 0 )
151             goto error;
152         if( strncmp( &cmd[0], "action", 6 ) != 0 )
153             break;
154         msg_Dbg( p_this, " + %s hotkey=%s (%d,%d)", &cmd[0], &action[0], i_x, i_y );
155
156         p_prev = p_current;
157         p_current = osd_ButtonNew( &action[0], i_x, i_y );
158         if( !p_current )
159             goto error;
160
161         if( p_prev )
162             p_prev->p_next = p_current;
163         else
164             (*p_menu)->p_button = p_current;
165         p_current->p_prev = p_prev;
166
167         /* parse all states */
168         while( !feof( fd ) )
169         {
170             char type[25] = "";
171
172             result = fscanf( fd, "\t%24s", &state[0] );
173             if( result == 0 )
174                 goto error;
175
176             /* FIXME: We only parse one level deep now */
177             if( strncmp( &state[0], "action", 6 ) == 0 )
178             {
179                 osd_button_t   *p_up = NULL;
180
181                 result = fscanf( fd, "%24s (%d,%d)", &action[0], &i_x, &i_y );
182                 if( result == 0 )
183                     goto error;
184                 /* create new button */
185                 p_up = osd_ButtonNew( &action[0], i_x, i_y );
186                 if( !p_up )
187                     goto error;
188                 /* Link to list */
189                 p_up->p_down = p_current;
190                 p_current->p_up = p_up;
191                 msg_Dbg( p_this, " + (menu up) hotkey=%s (%d,%d)", &action[0], i_x, i_y );
192                 /* Parse type state */
193                 result = fscanf( fd, "\t%24s %24s", &cmd[0], &type[0] );
194                 if( result == 0 )
195                     goto error;
196                 if( strncmp( &cmd[0], "type", 4 ) == 0 )
197                 {
198                     if( strncmp( &type[0], "volume", 6 ) == 0 )
199                     {
200                         (*p_menu)->p_state->p_volume = p_up;
201                         msg_Dbg( p_this, " + type=%s", &type[0] );
202                     }
203                 }
204                 /* Parse range state */
205                 result = fscanf( fd, "\t%24s", &state[0] );
206                 if( result == 0 )
207                     goto error;
208                 /* Parse the range state */
209                 if( strncmp( &state[0], "range", 5 ) == 0 )
210                 {
211                     osd_state_t   *p_range_current = NULL; /* range state currently processed */
212                     osd_state_t   *p_range_prev = NULL;    /* previous state processed range */
213                     int i_index = 0;
214
215                     p_up->b_range = VLC_TRUE;
216
217                     result = fscanf( fd, "\t%24s", &action[0] );
218                     if( result == 0 )
219                         goto error;
220
221                     result = fscanf( fd, "\t%d", &i_index );
222                     if( result == 0 )
223                         goto error;
224
225                     msg_Dbg( p_this, " + (menu up) hotkey down %s, file=%s%s",
226                              &action[0], (*p_menu)->psz_path, &file[0] );
227
228                     if( p_up->psz_action_down ) free( p_up->psz_action_down );
229                     p_up->psz_action_down = strdup( &action[0] );
230
231                     /* Parse range contstruction :
232                      * range <hotkey>
233                      *      <state1> <file1>
234                      *
235                      *      <stateN> <fileN>
236                      * end
237                      */
238                     while( !feof( fd ) )
239                     {
240                         result = fscanf( fd, "\t%255s", &file[0] );
241                         if( result == 0 )
242                             goto error;
243                         if( strncmp( &file[0], "end", 3 ) == 0 )
244                             break;
245
246                         p_range_prev = p_range_current;
247
248                         if( (*p_menu)->psz_path )
249                         {
250                             size_t i_path_size = strlen( (*p_menu)->psz_path );
251                             size_t i_file_size = strlen( &file[0] );
252
253                             strncpy( &path[0], (*p_menu)->psz_path, i_path_size );
254                             strncpy( &path[i_path_size], &file[0], 512 - (i_path_size + i_file_size) );
255                             path[ i_path_size + i_file_size ] = '\0';
256
257                             p_range_current = osd_StateNew( p_this, &path[0], "pressed" );
258                         }
259                         else /* absolute paths are used. */
260                             p_range_current = osd_StateNew( p_this, &file[0], "pressed" );
261
262                         if( !p_range_current || !p_range_current->p_pic )
263                             goto error;
264
265                         /* increment the number of ranges for this button */
266                         p_up->i_ranges++;
267
268                         if( p_range_prev )
269                             p_range_prev->p_next = p_range_current;
270                         else
271                             p_up->p_states = p_range_current;
272                         p_range_current->p_prev = p_range_prev;
273
274                         msg_Dbg( p_this, "  |- range=%d, file=%s%s",
275                                 p_up->i_ranges,
276                                 (*p_menu)->psz_path, &file[0] );
277                     }
278                     if( i_index > 0 )
279                     {
280                         osd_state_t *p_range = NULL;
281
282                         /* Find the default index for state range */
283                         p_range = p_up->p_states;
284                         while( (--i_index > 0) && p_range->p_next )
285                         {
286                             osd_state_t *p_temp = NULL;
287                             p_temp = p_range->p_next;
288                             p_range = p_temp;
289                         }
290                         p_up->p_current_state = p_range;
291                     }
292                     else p_up->p_current_state = p_up->p_states;
293
294                 }
295                 result = fscanf( fd, "\t%24s", &state[0] );
296                 if( result == 0 )
297                     goto error;
298                 if( strncmp( &state[0], "end", 3 ) != 0 )
299                     goto error;
300
301                 /* Continue at the beginning of the while() */
302                 continue;
303             }
304
305             /* Parse the range state */
306             if( strncmp( &state[0], "range", 5 ) == 0 )
307             {
308                 osd_state_t   *p_range_current = NULL; /* range state currently processed */
309                 osd_state_t   *p_range_prev = NULL;    /* previous state processed range */
310                 int i_index = 0;
311
312                 p_current->b_range = VLC_TRUE;
313
314                 result = fscanf( fd, "\t%24s", &action[0] );
315                 if( result == 0 )
316                     goto error;
317
318                 result = fscanf( fd, "\t%d", &i_index );
319                 if( result == 0 )
320                     goto error;
321
322                 msg_Dbg( p_this, " + hotkey down %s, file=%s%s", &action[0], (*p_menu)->psz_path, &file[0] );
323                 if( p_current->psz_action_down ) free( p_current->psz_action_down );
324                 p_current->psz_action_down = strdup( &action[0] );
325
326                 /* Parse range contstruction :
327                  * range <hotkey>
328                  *      <state1> <file1>
329                  *
330                  *      <stateN> <fileN>
331                  * end
332                  */
333                 while( !feof( fd ) )
334                 {
335                     result = fscanf( fd, "\t%255s", &file[0] );
336                     if( result == 0 )
337                         goto error;
338                     if( strncmp( &file[0], "end", 3 ) == 0 )
339                         break;
340
341                     p_range_prev = p_range_current;
342
343                     if( (*p_menu)->psz_path )
344                     {
345                         size_t i_path_size = strlen( (*p_menu)->psz_path );
346                         size_t i_file_size = strlen( &file[0] );
347
348                         strncpy( &path[0], (*p_menu)->psz_path, i_path_size );
349                         strncpy( &path[i_path_size], &file[0], 512 - (i_path_size + i_file_size) );
350                         path[ i_path_size + i_file_size ] = '\0';
351
352                         p_range_current = osd_StateNew( p_this, &path[0], "pressed" );
353                     }
354                     else /* absolute paths are used. */
355                         p_range_current = osd_StateNew( p_this, &file[0], "pressed" );
356
357                     if( !p_range_current || !p_range_current->p_pic )
358                         goto error;
359
360                     /* increment the number of ranges for this button */
361                     p_current->i_ranges++;
362
363                     if( p_range_prev )
364                         p_range_prev->p_next = p_range_current;
365                     else
366                         p_current->p_states = p_range_current;
367                     p_range_current->p_prev = p_range_prev;
368
369                     msg_Dbg( p_this, "  |- range=%d, file=%s%s",
370                             p_current->i_ranges,
371                             (*p_menu)->psz_path, &file[0] );
372                 }
373                 if( i_index > 0 )
374                 {
375                     osd_state_t *p_range = NULL;
376
377                     /* Find the default index for state range */
378                     p_range = p_current->p_states;
379                     while( (--i_index > 0) && p_range->p_next )
380                     {
381                         osd_state_t *p_temp = NULL;
382                         p_temp = p_range->p_next;
383                         p_range = p_temp;
384                     }
385                     p_current->p_current_state = p_range;
386                 }
387                 else p_current->p_current_state = p_current->p_states;
388                 /* Continue at the beginning of the while() */
389                 continue;
390             }
391             if( strncmp( &state[0], "end", 3 ) == 0 )
392                 break;
393
394             result = fscanf( fd, "\t%255s", &file[0] );
395             if( result == 0 )
396                 goto error;
397
398             p_state_prev = p_state_current;
399
400             if( ( strncmp( ppsz_button_states[0], &state[0], strlen(ppsz_button_states[0]) ) != 0 ) &&
401                 ( strncmp( ppsz_button_states[1], &state[0], strlen(ppsz_button_states[1]) ) != 0 ) &&
402                 ( strncmp( ppsz_button_states[2], &state[0], strlen(ppsz_button_states[2]) ) != 0 ) )
403             {
404                 msg_Err( p_this, "invalid button state %s for button %s "
405                          "expected %u: unselect, select or pressed)",
406                          &state[0], &action[0], (unsigned)strlen(&state[0]));
407                 goto error;
408             }
409
410             if( (*p_menu)->psz_path )
411             {
412                 size_t i_path_size = strlen( (*p_menu)->psz_path );
413                 size_t i_file_size = strlen( &file[0] );
414
415                 strncpy( &path[0], (*p_menu)->psz_path, i_path_size );
416                 strncpy( &path[i_path_size], &file[0], 512 - (i_path_size + i_file_size) );
417                 path[ i_path_size + i_file_size ] = '\0';
418
419                 p_state_current = osd_StateNew( p_this, &path[0], &state[0] );
420             }
421             else /* absolute paths are used. */
422                 p_state_current = osd_StateNew( p_this, &file[0], &state[0] );
423
424             if( !p_state_current || !p_state_current->p_pic )
425                 goto error;
426
427             if( p_state_prev )
428                 p_state_prev->p_next = p_state_current;
429             else
430                 p_current->p_states = p_state_current;
431             p_state_current->p_prev = p_state_prev;
432
433             msg_Dbg( p_this, " |- state=%s, file=%s%s", &state[0], (*p_menu)->psz_path, &file[0] );
434         }
435         p_current->p_current_state = p_current->p_states;
436     }
437
438     /* Find the last button and store its pointer.
439      * The OSD menu behaves like a roundrobin list.
440      */
441     p_current = (*p_menu)->p_button;
442     while( p_current && p_current->p_next )
443     {
444         osd_button_t *p_temp = NULL;
445         p_temp = p_current->p_next;
446         p_current = p_temp;
447     }
448     (*p_menu)->p_last_button = p_current;
449     fclose( fd );
450     return VLC_SUCCESS;
451
452 #undef MAX_FILE_PATH
453 error:
454     msg_Err( p_this, "parsing file failed (returned %d)", result );
455     osd_MenuFree( p_this, *p_menu );
456     fclose( fd );
457     return VLC_EGENERIC;
458 }