]> git.sesse.net Git - vlc/blob - plugins/gnome/intf_gnome.c
-Various bug fixes in dvd_netlist. Some bugs seem to remain though.
[vlc] / plugins / gnome / intf_gnome.c
1 /*****************************************************************************
2  * intf_gnome.c: Gnome interface
3  *****************************************************************************
4  * Copyright (C) 1999, 2000 VideoLAN
5  * $Id: intf_gnome.c,v 1.24 2001/04/01 07:31:38 stef Exp $
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *          Stéphane Borel <stef@via.ecp.fr>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  * 
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 #define MODULE_NAME gnome
26 #include "modules_inner.h"
27
28 /*****************************************************************************
29  * Preamble
30  *****************************************************************************/
31 #include "defs.h"
32
33 #include <errno.h>                                                 /* ENOMEM */
34 #include <stdlib.h>                                                /* free() */
35 #include <string.h>                                            /* strerror() */
36 #include <stdio.h>
37
38 #include <gnome.h>
39
40 #include "config.h"
41 #include "common.h"
42 #include "threads.h"
43 #include "mtime.h"
44 #include "tests.h"
45 #include "modules.h"
46
47 #include "stream_control.h"
48 #include "input_ext-intf.h"
49
50 #include "intf_msg.h"
51 #include "interface.h"
52
53 #include "gnome_callbacks.h"
54 #include "gnome_interface.h"
55 #include "gnome_support.h"
56 #include "intf_gnome.h"
57
58 #include "main.h"
59
60 /*****************************************************************************
61  * Local prototypes.
62  *****************************************************************************/
63 static int  intf_Probe     ( probedata_t *p_data );
64 static int  intf_Open      ( intf_thread_t *p_intf );
65 static void intf_Close     ( intf_thread_t *p_intf );
66 static void intf_Run       ( intf_thread_t *p_intf );
67
68 static gint GnomeManage    ( gpointer p_data );
69 static gint GnomeLanguageMenus( gpointer, GtkWidget *, es_descriptor_t *, gint,
70                               void (*pf_toggle)(GtkCheckMenuItem *, gpointer) );
71 static gint GnomeChapterMenu  ( gpointer, GtkWidget *,
72                               void (*pf_toggle)(GtkCheckMenuItem *, gpointer) );
73 static gint GnomeTitleMenu    ( gpointer, GtkWidget *, 
74                               void (*pf_toggle)(GtkCheckMenuItem *, gpointer) );
75 static gint GnomeSetupMenu    ( intf_thread_t * p_intf );
76 static void GnomeDisplayDate  ( GtkAdjustment *p_adj );
77
78 /*****************************************************************************
79  * g_atexit: kludge to avoid the Gnome thread to segfault at exit
80  *****************************************************************************
81  * gtk_init() makes several calls to g_atexit() which calls atexit() to
82  * register tidying callbacks to be called at program exit. Since the Gnome
83  * plugin is likely to be unloaded at program exit, we have to export this
84  * symbol to intercept the g_atexit() calls. Talk about crude hack.
85  *****************************************************************************/
86 void g_atexit( GVoidFunc func )
87 {
88     intf_thread_t *p_intf = p_main->p_intf;
89
90     if( p_intf->p_sys->pf_gdk_callback == NULL )
91     {
92         p_intf->p_sys->pf_gdk_callback = func;
93     }
94     else if( p_intf->p_sys->pf_gtk_callback == NULL )
95     {
96         p_intf->p_sys->pf_gtk_callback = func;
97     }
98     /* else nothing, but we could do something here */
99     return;
100 }
101
102 /*****************************************************************************
103  * Functions exported as capabilities. They are declared as static so that
104  * we don't pollute the namespace too much.
105  *****************************************************************************/
106 void _M( intf_getfunctions )( function_list_t * p_function_list )
107 {
108     p_function_list->pf_probe = intf_Probe;
109     p_function_list->functions.intf.pf_open  = intf_Open;
110     p_function_list->functions.intf.pf_close = intf_Close;
111     p_function_list->functions.intf.pf_run   = intf_Run;
112 }
113
114 /*****************************************************************************
115  * intf_Probe: probe the interface and return a score
116  *****************************************************************************
117  * This function tries to initialize Gnome and returns a score to the
118  * plugin manager so that it can select the best plugin.
119  *****************************************************************************/
120 static int intf_Probe( probedata_t *p_data )
121 {
122     if( TestMethod( INTF_METHOD_VAR, "gnome" ) )
123     {
124         return( 999 );
125     }
126
127     return( 100 );
128 }
129
130 /*****************************************************************************
131  * intf_Open: initialize and create window
132  *****************************************************************************/
133 static int intf_Open( intf_thread_t *p_intf )
134 {
135     /* Allocate instance and initialize some members */
136     p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
137     if( p_intf->p_sys == NULL )
138     {
139         intf_ErrMsg("error: %s", strerror(ENOMEM));
140         return( 1 );
141     }
142
143     /* Initialize Gnome thread */
144     p_intf->p_sys->b_popup_changed = 0;
145     p_intf->p_sys->b_window_changed = 0;
146     p_intf->p_sys->b_playlist_changed = 0;
147     p_intf->p_sys->b_menus_update = 1;
148     p_intf->p_sys->b_menus_ready = 0;
149
150     p_intf->p_sys->b_slider_free = 1;
151
152     p_intf->p_sys->pf_gtk_callback = NULL;
153     p_intf->p_sys->pf_gdk_callback = NULL;
154
155     return( 0 );
156 }
157
158 /*****************************************************************************
159  * intf_Close: destroy interface window
160  *****************************************************************************/
161 static void intf_Close( intf_thread_t *p_intf )
162 {
163     /* Destroy structure */
164     free( p_intf->p_sys );
165 }
166
167 /*****************************************************************************
168  * intf_Run: Gnome thread
169  *****************************************************************************
170  * this part of the interface is in a separate thread so that we can call
171  * gtk_main() from within it without annoying the rest of the program.
172  * XXX: the approach may look kludgy, and probably is, but I could not find
173  * a better way to dynamically load a Gnome interface at runtime.
174  *****************************************************************************/
175 static void intf_Run( intf_thread_t *p_intf )
176 {
177     /* gnome_init needs to know the command line. We don't care, so we
178      * give it an empty one */
179     char *p_args[] = { "" };
180     int   i_args   = 1;
181
182     /* The data types we are allowed to receive */
183     static GtkTargetEntry target_table[] =
184     {
185         { "text/uri-list", 0, DROP_ACCEPT_TEXT_URI_LIST },
186         { "text/plain",    0, DROP_ACCEPT_TEXT_PLAIN }
187     };
188
189     /* intf_Manage callback timeout */
190     int i_timeout;
191
192     /* Initialize Gnome */
193     gnome_init( p_main->psz_arg0, VERSION, i_args, p_args );
194
195     /* Create some useful widgets that will certainly be used */
196     p_intf->p_sys->p_window = create_intf_window( );
197     p_intf->p_sys->p_popup = create_intf_popup( );
198     p_intf->p_sys->p_disc = create_intf_disc( );
199     p_intf->p_sys->p_network = create_intf_network( );
200
201     /* Set the title of the main window */
202     gtk_window_set_title( GTK_WINDOW(p_intf->p_sys->p_window),
203                           VOUT_TITLE " (Gnome interface)");
204
205     /* Accept file drops on the main window */
206     gtk_drag_dest_set( GTK_WIDGET( p_intf->p_sys->p_window ),
207                        GTK_DEST_DEFAULT_ALL, target_table,
208                        1, GDK_ACTION_COPY );
209
210     /* Get the interface labels */
211     #define P_LABEL( name ) GTK_LABEL( gtk_object_get_data( \
212                          GTK_OBJECT( p_intf->p_sys->p_window ), name ) )
213     p_intf->p_sys->p_label_date = P_LABEL( "label_date" );
214     p_intf->p_sys->p_label_status = P_LABEL( "label_status" );
215     #undef P_LABEL
216
217     /* Connect the date display to the slider */
218     #define P_SLIDER GTK_RANGE( gtk_object_get_data( \
219                          GTK_OBJECT( p_intf->p_sys->p_window ), "slider" ) )
220     p_intf->p_sys->p_adj = gtk_range_get_adjustment( P_SLIDER );
221
222     gtk_signal_connect ( GTK_OBJECT( p_intf->p_sys->p_adj ), "value_changed",
223                          GTK_SIGNAL_FUNC( GnomeDisplayDate ), NULL );
224     p_intf->p_sys->f_adj_oldvalue = 0;
225     #undef P_SLIDER
226
227     /* We don't create these ones yet because we perhaps won't need them */
228     p_intf->p_sys->p_about = NULL;
229     p_intf->p_sys->p_playlist = NULL;
230     p_intf->p_sys->p_modules = NULL;
231     p_intf->p_sys->p_fileopen = NULL;
232
233     /* Store p_intf to keep an eye on it */
234     gtk_object_set_data( GTK_OBJECT(p_intf->p_sys->p_window),
235                          "p_intf", p_intf );
236
237     gtk_object_set_data( GTK_OBJECT(p_intf->p_sys->p_popup),
238                          "p_intf", p_intf );
239
240     gtk_object_set_data( GTK_OBJECT(p_intf->p_sys->p_disc),
241                          "p_intf", p_intf );
242
243     gtk_object_set_data( GTK_OBJECT(p_intf->p_sys->p_network),
244                          "p_intf", p_intf );
245
246     gtk_object_set_data( GTK_OBJECT(p_intf->p_sys->p_adj),
247                          "p_intf", p_intf );
248
249     /* Show the control window */
250     gtk_widget_show( p_intf->p_sys->p_window );
251
252     /* Sleep to avoid using all CPU - since some interfaces needs to access
253      * keyboard events, a 100ms delay is a good compromise */
254     i_timeout = gtk_timeout_add( INTF_IDLE_SLEEP / 1000, GnomeManage, p_intf );
255
256     /* Enter gnome mode */
257     gtk_main();
258
259     /* Remove the timeout */
260     gtk_timeout_remove( i_timeout );
261
262     /* Get rid of stored callbacks so we can unload the plugin */
263     if( p_intf->p_sys->pf_gtk_callback != NULL )
264     {
265         p_intf->p_sys->pf_gtk_callback( );
266         p_intf->p_sys->pf_gtk_callback = NULL;
267
268     }
269
270     if( p_intf->p_sys->pf_gdk_callback != NULL )
271     {
272         p_intf->p_sys->pf_gdk_callback( );
273         p_intf->p_sys->pf_gdk_callback = NULL;
274     }
275 }
276
277 /* following functions are local */
278
279 /*****************************************************************************
280  * GnomeManage: manage main thread messages
281  *****************************************************************************
282  * In this function, called approx. 10 times a second, we check what the
283  * main program wanted to tell us.
284  *****************************************************************************/
285 static gint GnomeManage( gpointer p_data )
286 {
287 #define p_intf ((intf_thread_t *)p_data)
288
289     vlc_mutex_lock( &p_intf->change_lock );
290
291     /* If the "display popup" flag has changed */
292     if( p_intf->b_menu_change )
293     {
294         gnome_popup_menu_do_popup( p_intf->p_sys->p_popup,
295                                    NULL, NULL, NULL, NULL );
296         p_intf->b_menu_change = 0;
297     }
298
299     /* Update language/chapter menus after user request */
300     if( p_intf->p_input != NULL && p_intf->p_sys->p_window != NULL &&
301         p_intf->p_sys->b_menus_update )
302     {
303         p_intf->p_sys->b_menus_ready = 0;
304         GnomeSetupMenu( p_intf );
305         p_intf->p_sys->b_menus_ready = 1;
306     }
307
308     /* Manage the slider */
309     if( p_intf->p_input != NULL )
310     {
311         float newvalue = p_intf->p_sys->p_adj->value;
312
313 #define p_area p_intf->p_input->stream.p_selected_area
314         /* If the user hasn't touched the slider since the last time,
315          * then the input can safely change it */
316         if( newvalue == p_intf->p_sys->f_adj_oldvalue )
317         {
318             /* Update the value */
319             p_intf->p_sys->p_adj->value = p_intf->p_sys->f_adj_oldvalue =
320                 ( 100. * p_area->i_tell ) / p_area->i_size;
321
322             gtk_signal_emit_by_name( GTK_OBJECT( p_intf->p_sys->p_adj ),
323                                      "value_changed" );
324         }
325         /* Otherwise, send message to the input if the user has
326          * finished dragging the slider */
327         else if( p_intf->p_sys->b_slider_free )
328         {
329             off_t i_seek = ( newvalue * p_area->i_size ) / 100;
330
331             input_Seek( p_intf->p_input, i_seek );
332
333             /* Update the old value */
334             p_intf->p_sys->f_adj_oldvalue = newvalue;
335         }
336 #undef p_area
337     }
338
339     /* Manage core vlc functions through the callback */
340     p_intf->pf_manage( p_intf );
341
342     if( p_intf->b_die )
343     {
344         vlc_mutex_unlock( &p_intf->change_lock );
345
346         /* Prepare to die, young Skywalker */
347         gtk_main_quit();
348
349         /* Just in case */
350         return( FALSE );
351     }
352
353     vlc_mutex_unlock( &p_intf->change_lock );
354
355     return( TRUE );
356
357 #undef p_intf
358 }
359
360 /*****************************************************************************
361  * GnomeLanguageMenus: update interactive menus of the interface
362  *****************************************************************************
363  * Sets up menus with information from input:
364  *  -languages
365  *  -sub-pictures
366  * Warning: since this function is designed to be called by management
367  * function, the interface lock has to be taken
368  *****************************************************************************/
369 static gint GnomeLanguageMenus( gpointer          p_data,
370                                 GtkWidget *       p_root,
371                                 es_descriptor_t * p_es,
372                                 gint              i_type,
373                           void(*pf_toggle )( GtkCheckMenuItem *, gpointer ) )
374 {
375     intf_thread_t *     p_intf;
376     GtkWidget *         p_menu;
377     GtkWidget *         p_separator;
378     GtkWidget *         p_item;
379     GtkWidget *         p_item_active;
380     GSList *            p_group;
381     char *              psz_name;
382     gint                b_audio;
383     gint                b_spu;
384     gint                i_item;
385     gint                i;
386
387     
388
389     /* cast */
390     p_intf = (intf_thread_t *)p_data;
391
392     /* removes previous menu */
393     gtk_menu_item_remove_submenu( GTK_MENU_ITEM( p_root ) );
394     gtk_widget_set_sensitive( p_root, FALSE );
395
396     p_group = NULL;
397
398     /* menu container */
399     p_menu = gtk_menu_new();
400
401     /* special case for "off" item */
402     psz_name = "Off";
403
404     p_item = gtk_radio_menu_item_new_with_label( p_group, psz_name );
405     p_group = gtk_radio_menu_item_group( GTK_RADIO_MENU_ITEM( p_item ) );
406
407     gtk_widget_show( p_item );
408
409     /* signal hanling for off */
410     gtk_signal_connect( GTK_OBJECT( p_item ), "toggled",
411                         GTK_SIGNAL_FUNC ( pf_toggle ), NULL );
412
413     gtk_menu_append( GTK_MENU( p_menu ), p_item );
414
415     p_separator = gtk_menu_item_new();
416     gtk_widget_set_sensitive( p_separator, FALSE );
417     gtk_widget_show( p_separator );
418     gtk_menu_append( GTK_MENU( p_menu ), p_separator );
419
420     vlc_mutex_lock( &p_intf->p_input->stream.stream_lock );
421     p_item_active = NULL;
422     i_item = 0;
423
424     /* create a set of language buttons and append them to the container */
425     for( i = 0 ; i < p_intf->p_input->stream.i_es_number ; i++ )
426     {
427
428         b_audio = ( i_type == 1 ) && p_intf->p_input->stream.pp_es[i]->b_audio;
429         b_spu   = ( i_type == 2 ) && p_intf->p_input->stream.pp_es[i]->b_spu;
430
431         if( b_audio || b_spu )
432         {
433             i_item++;
434             psz_name = p_intf->p_input->stream.pp_es[i]->psz_desc;
435             if( psz_name[0] == '\0' )
436             {
437                 sprintf( psz_name, "Language %d", i_item );
438             }
439
440             p_item = gtk_radio_menu_item_new_with_label( p_group, psz_name );
441             p_group =
442                 gtk_radio_menu_item_group( GTK_RADIO_MENU_ITEM( p_item ) );
443
444             if( p_es == p_intf->p_input->stream.pp_es[i] )
445             {
446                 /* don't lose p_item when we append into menu */
447                 p_item_active = p_item;
448                 gtk_object_ref( GTK_OBJECT( p_item_active ) );
449             }
450
451             gtk_widget_show( p_item );
452
453             /* setup signal hanling */
454             gtk_signal_connect( GTK_OBJECT( p_item ), "toggled",
455                             GTK_SIGNAL_FUNC( pf_toggle ),
456                             (gpointer)( p_intf->p_input->stream.pp_es[i] ) );
457
458             gtk_menu_append( GTK_MENU( p_menu ), p_item );
459         }
460     }
461
462     vlc_mutex_unlock( &p_intf->p_input->stream.stream_lock );
463
464     /* link the new menu to the menubar item */
465     gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_root ), p_menu );
466
467     /* acitvation will call signals so we can only do it
468      * when submenu is attached to menu - to get intf_window */
469     if( p_item_active != NULL )
470     {
471         gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM( p_item_active ),
472                                         TRUE );
473         gtk_object_unref( GTK_OBJECT( p_item_active ) );
474     }
475
476     /* be sure that menu is sensitive if non empty */
477     if( i_item > 0 )
478     {
479         gtk_widget_set_sensitive( p_root, TRUE );
480     }
481
482     return TRUE;
483 }
484
485 /*****************************************************************************
486  * GnomeChapterMenu: generate chapter menu for current title
487  *****************************************************************************/
488 static gint GnomeChapterMenu( gpointer p_data, GtkWidget * p_chapter,
489                         void(*pf_toggle )( GtkCheckMenuItem *, gpointer ) )
490 {
491     intf_thread_t *     p_intf;
492     char                psz_name[10];
493     GtkWidget *         p_chapter_menu;
494     GtkWidget *         p_chapter_submenu;
495     GtkWidget *         p_menu_item;
496     GtkWidget *         p_item;
497     GtkWidget *         p_item_selected;
498     GSList *            p_chapter_group;
499     gint                i_title;
500     gint                i_chapter;
501     gint                i_nb;
502
503     /* cast */
504     p_intf = (intf_thread_t*)p_data;
505
506     /* removes previous menu */
507     gtk_menu_item_remove_submenu( GTK_MENU_ITEM( p_chapter ) );
508     gtk_widget_set_sensitive( p_chapter, FALSE );
509
510     p_chapter_submenu = NULL;
511     p_chapter_group = NULL;
512     p_item_selected = NULL;
513     p_menu_item = NULL;
514
515     i_title = p_intf->p_input->stream.p_selected_area->i_id;
516     p_chapter_menu = gtk_menu_new();
517     i_nb = p_intf->p_input->stream.pp_areas[i_title]->i_part_nb;
518
519     for( i_chapter = 0 ; i_chapter < i_nb ; i_chapter++ )
520     {
521         /* we group chapters in packets of ten for small screens */
522         if( ( i_chapter % 10 == 0 ) )// && ( i_nb > 20 ) )
523         {
524             if( i_chapter != 0 )
525             {
526                 gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_menu_item ),
527                                            p_chapter_submenu );
528                 gtk_menu_append( GTK_MENU( p_chapter_menu ), p_menu_item );
529             }
530
531             sprintf( psz_name, "%d - %d", i_chapter + 1, i_chapter + 10);
532             p_menu_item = gtk_menu_item_new_with_label( psz_name );
533             gtk_widget_show( p_menu_item );
534             p_chapter_submenu = gtk_menu_new();
535         }
536
537         sprintf( psz_name, "Chapter %d", i_chapter + 1 );
538
539         p_item = gtk_radio_menu_item_new_with_label( p_chapter_group,
540                                                      psz_name );
541         p_chapter_group =
542             gtk_radio_menu_item_group( GTK_RADIO_MENU_ITEM( p_item ) );
543
544         if( p_intf->p_input->stream.pp_areas[i_title]->i_part
545                      == i_chapter + 1 )
546         {
547             p_item_selected = p_item;
548             gtk_object_ref( GTK_OBJECT( p_item_selected ) );
549         }
550         
551         gtk_widget_show( p_item );
552
553         /* setup signal hanling */
554         gtk_signal_connect( GTK_OBJECT( p_item ),
555                         "toggled",
556                         GTK_SIGNAL_FUNC( pf_toggle ),
557                         (gpointer)(i_chapter + 1) );
558
559         gtk_menu_append( GTK_MENU( p_chapter_submenu ), p_item );
560     }
561
562     gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_menu_item ),
563                                p_chapter_submenu );
564     gtk_menu_append( GTK_MENU( p_chapter_menu ), p_menu_item );
565
566
567     /* link the new menu to the title menu item */
568     gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_chapter ),
569                                p_chapter_menu );
570
571     /* toggle currently selected chapter */
572     if( p_item_selected != NULL )
573     {
574         gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM( p_item_selected ),
575                                         TRUE );
576         gtk_object_unref( GTK_OBJECT( p_item_selected ) );
577     }
578
579     /* be sure that chapter menu is sensitive, if there are several items */
580     if( p_intf->p_input->stream.pp_areas[i_title]->i_part_nb > 1 )
581     {
582         gtk_widget_set_sensitive( p_chapter, TRUE );
583     }
584
585     return TRUE;
586 }
587
588 /*****************************************************************************
589  * GnomeTitleMenu: sets menus for titles and chapters selection
590  *****************************************************************************
591  * Generates two types of menus:
592  *  -simple list of titles
593  *  -cascaded lists of chapters for each title
594  *****************************************************************************/
595 static gint GnomeTitleMenu( gpointer       p_data,
596                             GtkWidget *    p_navigation, 
597                             void(*pf_toggle )( GtkCheckMenuItem *, gpointer ) )
598 {
599     intf_thread_t *     p_intf;
600     char                psz_name[10];
601     GtkWidget *         p_title_menu;
602     GtkWidget *         p_title_submenu;
603     GtkWidget *         p_title_item;
604     GtkWidget *         p_item_active;
605     GtkWidget *         p_chapter_menu;
606     GtkWidget *         p_chapter_submenu;
607     GtkWidget *         p_title_menu_item;
608     GtkWidget *         p_chapter_menu_item;
609     GtkWidget *         p_item;
610     GSList *            p_title_group;
611     GSList *            p_chapter_group;
612     gint                i_title;
613     gint                i_chapter;
614     gint                i_nb;
615
616     /* cast */
617     p_intf = (intf_thread_t*)p_data;
618
619     p_title_menu = gtk_menu_new();
620     p_title_group = NULL;
621     p_title_submenu = NULL;
622     p_title_menu_item = NULL;
623     p_chapter_group = NULL;
624     p_chapter_submenu = NULL;
625     p_chapter_menu_item = NULL;
626     p_item_active = NULL;
627     i_nb = p_intf->p_input->stream.i_area_nb;
628
629     /* loop on titles */
630     for( i_title = 1 ; i_title < i_nb ; i_title++ )
631     {
632         /* we group titles in packets of ten for small screens */
633         if( ( i_title % 10 == 1 ))// && ( i_nb > 20 ) )
634         {
635             if( i_title != 1 )
636             {
637                 gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_title_menu_item ),
638                                            p_title_submenu );
639                 gtk_menu_append( GTK_MENU( p_title_menu ), p_title_menu_item );
640             }
641
642             sprintf( psz_name, "%d - %d", i_title, i_title + 9 );
643             p_title_menu_item = gtk_menu_item_new_with_label( psz_name );
644             gtk_widget_show( p_title_menu_item );
645             p_title_submenu = gtk_menu_new();
646         }
647
648         sprintf( psz_name, "Title %d", i_title );
649
650         if( pf_toggle == on_menubar_title_toggle )
651         {
652             p_title_item = gtk_radio_menu_item_new_with_label( p_title_group,
653                                                            psz_name );
654             p_title_group =
655               gtk_radio_menu_item_group( GTK_RADIO_MENU_ITEM( p_title_item ) );
656
657             if( p_intf->p_input->stream.pp_areas[i_title] ==
658                          p_intf->p_input->stream.p_selected_area )
659             {
660                 p_item_active = p_title_item;
661                 gtk_object_ref( GTK_OBJECT( p_item_active ) );
662             }
663
664             /* setup signal hanling */
665             gtk_signal_connect( GTK_OBJECT( p_title_item ),
666                      "toggled",
667                      GTK_SIGNAL_FUNC( pf_toggle ),
668                      (gpointer)(p_intf->p_input->stream.pp_areas[i_title]) );
669
670             if( p_intf->p_input->stream.i_area_nb > 1 )
671             {
672                 /* be sure that menu is sensitive */
673                 gtk_widget_set_sensitive( p_navigation, TRUE );
674             }
675         }
676         else
677         {
678     
679             p_title_item = gtk_menu_item_new_with_label( psz_name );
680             p_chapter_menu = gtk_menu_new();
681     
682             for( i_chapter = 0;
683                  i_chapter <
684                         p_intf->p_input->stream.pp_areas[i_title]->i_part_nb ;
685                  i_chapter++ )
686             {
687                 /* we group chapters in packets of ten for small screens */
688                 if( ( i_chapter % 10 == 0 ) )// && ( i_nb > 20 ) )
689                 {
690                     if( i_chapter != 0 )
691                     {
692                         gtk_menu_item_set_submenu(
693                                     GTK_MENU_ITEM( p_chapter_menu_item ),
694                                     p_chapter_submenu );
695                         gtk_menu_append( GTK_MENU( p_chapter_menu ),
696                                          p_chapter_menu_item );
697                     }
698
699                     sprintf( psz_name, "%d - %d", i_chapter + 1,
700                                                   i_chapter + 10);
701                     p_chapter_menu_item =
702                             gtk_menu_item_new_with_label( psz_name );
703                     gtk_widget_show( p_chapter_menu_item );
704                     p_chapter_submenu = gtk_menu_new();
705                 }
706
707                 sprintf( psz_name, "Chapter %d", i_chapter + 1 );
708     
709                 p_item = gtk_radio_menu_item_new_with_label(
710                                                 p_chapter_group, psz_name );
711                 p_chapter_group = gtk_radio_menu_item_group(
712                                                 GTK_RADIO_MENU_ITEM( p_item ) );
713                 gtk_widget_show( p_item );
714
715                 if( p_intf->p_input->stream.pp_areas[i_title]->i_part
716                              == i_chapter + 1 )
717                 {
718                     p_item_active = p_item;
719                     gtk_object_ref( GTK_OBJECT( p_item_active ) );
720                 }
721
722                 /* setup signal hanling */
723                 gtk_signal_connect( GTK_OBJECT( p_item ),
724                            "toggled",
725                            GTK_SIGNAL_FUNC( pf_toggle ),
726                            (gpointer)( ( i_title * 100 ) + ( i_chapter + 1) ) );
727
728                 gtk_menu_append( GTK_MENU( p_chapter_submenu ), p_item );
729             }
730
731             gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_chapter_menu_item ),
732                                        p_chapter_submenu );
733             gtk_menu_append( GTK_MENU( p_chapter_menu ), p_chapter_menu_item );
734
735             /* link the new menu to the title menu item */
736             gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_title_item ),
737                                        p_chapter_menu );
738
739             if( p_intf->p_input->stream.pp_areas[i_title]->i_part_nb > 1 )
740             {
741                 /* be sure that menu is sensitive */
742                 gtk_widget_set_sensitive( p_navigation, TRUE );
743             }
744         }
745         gtk_widget_show( p_title_item );
746
747         gtk_menu_append( GTK_MENU( p_title_submenu ), p_title_item );
748     }
749
750     gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_title_menu_item ),
751                                p_title_submenu );
752     gtk_menu_append( GTK_MENU( p_title_menu ), p_title_menu_item );
753
754     /* be sure that menu is sensitive */
755     gtk_widget_set_sensitive( p_title_menu, TRUE );
756
757     /* link the new menu to the menubar item */
758     gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_navigation ), p_title_menu );
759
760     if( p_item_active != NULL )
761     {
762         gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM( p_item_active ),
763                                         TRUE );
764         gtk_object_unref( GTK_OBJECT( p_item_active ) );
765     }
766
767
768     return TRUE;
769 }
770
771 /*****************************************************************************
772  * GnomeSetupMenu: function that generates title/chapter/audio/subpic
773  * menus with help from preceding functions
774  *****************************************************************************/
775 static gint GnomeSetupMenu( intf_thread_t * p_intf )
776 {
777     es_descriptor_t *   p_audio_es;
778     es_descriptor_t *   p_spu_es;
779     GtkWidget *         p_menubar_menu;
780     GtkWidget *         p_popup_menu;
781     gint                i;
782
783     vlc_mutex_lock( &p_intf->p_input->stream.stream_lock );
784
785     if( p_intf->p_input->stream.i_area_nb > 1 )
786     {
787         p_menubar_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT( 
788                             p_intf->p_sys->p_window ), "menubar_title" ) );
789         GnomeTitleMenu( p_intf, p_menubar_menu, on_menubar_title_toggle );
790
791         p_popup_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT( 
792                              p_intf->p_sys->p_popup ), "popup_navigation" ) );
793         GnomeTitleMenu( p_intf, p_popup_menu, on_popup_navigation_toggle );
794     
795         p_menubar_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT( 
796                              p_intf->p_sys->p_window ), "menubar_chapter" ) );
797         GnomeChapterMenu( p_intf, p_menubar_menu, on_menubar_chapter_toggle );
798     }
799
800     /* look for selected ES */
801     p_audio_es = NULL;
802     p_spu_es = NULL;
803
804     for( i = 0 ; i < p_intf->p_input->stream.i_selected_es_number ; i++ )
805     {
806         if( p_intf->p_input->stream.pp_selected_es[i]->b_audio )
807         {
808             p_audio_es = p_intf->p_input->stream.pp_selected_es[i];
809         }
810
811         if( p_intf->p_input->stream.pp_selected_es[i]->b_spu )
812         {
813             p_spu_es = p_intf->p_input->stream.pp_selected_es[i];
814         }
815     }
816
817     vlc_mutex_unlock( &p_intf->p_input->stream.stream_lock );
818
819     /* audio menus */
820
821     /* find audio root menu */
822     p_menubar_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
823                          p_intf->p_sys->p_window ), "menubar_audio" ) );
824
825     p_popup_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT( 
826                  p_intf->p_sys->p_popup ), "popup_audio" ) );
827
828     GnomeLanguageMenus( p_intf, p_menubar_menu, p_audio_es, 1,
829                         on_menubar_audio_toggle );
830     GnomeLanguageMenus( p_intf, p_popup_menu, p_audio_es, 1,
831                         on_popup_audio_toggle );
832
833     /* sub picture menus */
834
835     /* find spu root menu */
836     p_menubar_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
837                       p_intf->p_sys->p_window ), "menubar_subtitle" ) );
838
839     p_popup_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT( 
840                  p_intf->p_sys->p_popup ), "popup_subtitle" ) );
841
842     GnomeLanguageMenus( p_intf, p_menubar_menu, p_spu_es, 2,
843                         on_menubar_subtitle_toggle  );
844     GnomeLanguageMenus( p_intf, p_popup_menu, p_spu_es, 2,
845                         on_popup_subtitle_toggle );
846
847     /* everything is ready */
848     p_intf->p_sys->b_menus_update = 0;
849
850     return TRUE;
851 }
852
853 /*****************************************************************************
854  * GnomeDisplayDate: display stream date
855  *****************************************************************************
856  * This function displays the current date related to the position in
857  * the stream. It is called whenever the slider changes its value.
858  *****************************************************************************/
859 void GnomeDisplayDate( GtkAdjustment *p_adj )
860 {
861     intf_thread_t *p_intf;
862    
863     p_intf = gtk_object_get_data( GTK_OBJECT( p_adj ), "p_intf" );
864
865     if( p_intf->p_input != NULL )
866     {
867 #define p_area p_intf->p_input->stream.p_selected_area
868         char psz_time[ OFFSETTOTIME_MAX_SIZE ];
869
870         vlc_mutex_lock( &p_intf->p_input->stream.stream_lock );
871
872         gtk_label_set_text( p_intf->p_sys->p_label_date,
873                             input_OffsetToTime( p_intf->p_input, psz_time,
874                                    ( p_area->i_size * p_adj->value ) / 100 ) );
875
876         vlc_mutex_unlock( &p_intf->p_input->stream.stream_lock );
877 #undef p_area
878      }
879 }
880
881