]> git.sesse.net Git - vlc/blob - modules/gui/gtk/menu.c
* ./modules/*: moved plugins to the new tree. Yet untested builds include
[vlc] / modules / gui / gtk / menu.c
1 /*****************************************************************************
2  * menu.c : functions to handle menu items.
3  *****************************************************************************
4  * Copyright (C) 2000, 2001 VideoLAN
5  * $Id: menu.c,v 1.1 2002/08/04 17:23:43 sam Exp $
6  *
7  * Authors: Samuel Hocevar <sam@zoy.org>
8  *          Stéphane Borel <stef@via.ecp.fr>
9  *          Johan Bilien <jobi@via.ecp.fr>
10  *      
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  * 
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <sys/types.h>                                              /* off_t */
30 #include <stdlib.h>
31
32 #include <vlc/vlc.h>
33 #include <vlc/intf.h>
34
35 #ifdef MODULE_NAME_IS_gnome
36 #   include <gnome.h>
37 #else
38 #   include <gtk/gtk.h>
39 #endif
40
41 #include <string.h>
42
43 #include "gtk_callbacks.h"
44 #include "gtk_interface.h"
45 #include "gtk_support.h"
46
47 #include "playlist.h"
48 #include "common.h"
49
50 /*
51  * Local Prototypes
52  */
53 static gint GtkLanguageMenus( gpointer , GtkWidget *, es_descriptor_t *, gint,
54                         void(*pf_toggle )( GtkCheckMenuItem *, gpointer ) );
55
56 void GtkMenubarAudioToggle   ( GtkCheckMenuItem *, gpointer );
57 void GtkPopupAudioToggle     ( GtkCheckMenuItem *, gpointer );
58 void GtkMenubarSubtitleToggle( GtkCheckMenuItem *, gpointer );
59 void GtkPopupSubtitleToggle  ( GtkCheckMenuItem *, gpointer );
60 static gint GtkTitleMenu( gpointer, GtkWidget *, 
61                     void(*pf_toggle )( GtkCheckMenuItem *, gpointer ) );
62 static gint GtkRadioMenu( intf_thread_t *, GtkWidget *, GSList *,
63                           char *, int, int, int,
64                    void( *pf_toggle )( GtkCheckMenuItem *, gpointer ) );
65
66 gint GtkSetupMenus( intf_thread_t * p_intf );
67
68 /****************************************************************************
69  * Gtk*Toggle: callbacks to toggle the value of a checkmenuitem
70  ****************************************************************************
71  * We need separate functions for menubar and popup here since we can't use
72  * user_data to transmit intf_* and we need to refresh the other menu.
73  ****************************************************************************/
74
75 #define GTKLANGTOGGLE( window, menu, type, callback, b_update )         \
76     intf_thread_t *         p_intf;                                     \
77     GtkWidget *             p_menu;                                     \
78     es_descriptor_t *       p_es;                                       \
79                                                                         \
80     p_intf = GtkGetIntf( menuitem );                                    \
81                                                                         \
82     if( !p_intf->p_sys->b_update )                                      \
83     {                                                                   \
84         p_menu = GTK_WIDGET( gtk_object_get_data(                       \
85                    GTK_OBJECT( p_intf->p_sys->window ), (menu) ) );     \
86         p_es = (es_descriptor_t*)user_data;                             \
87                                                                         \
88         input_ToggleES( p_intf->p_sys->p_input,                         \
89                         p_es, menuitem->active );                       \
90                                                                         \
91         p_intf->p_sys->b_update = menuitem->active;                     \
92                                                                         \
93         if( p_intf->p_sys->b_update )                                   \
94         {                                                               \
95             GtkLanguageMenus( p_intf, p_menu, p_es, type, callback );   \
96         }                                                               \
97                                                                         \
98         p_intf->p_sys->b_update = 0;                                    \
99     }
100
101 /*
102  * Audio
103  */ 
104
105 void GtkMenubarAudioToggle( GtkCheckMenuItem * menuitem, gpointer user_data )
106 {
107     GTKLANGTOGGLE( p_popup, "popup_audio", AUDIO_ES,
108                    GtkPopupAudioToggle, b_audio_update );
109 }
110
111 void GtkPopupAudioToggle( GtkCheckMenuItem * menuitem, gpointer user_data )
112 {
113     GTKLANGTOGGLE( p_window, "menubar_audio", AUDIO_ES,
114                    GtkMenubarAudioToggle, b_audio_update );
115 }
116
117 /* 
118  * Subtitles
119  */ 
120
121 void GtkMenubarSubtitleToggle( GtkCheckMenuItem * menuitem, gpointer user_data )
122 {
123     GTKLANGTOGGLE( p_popup, "popup_subpictures", SPU_ES,
124                    GtkPopupSubtitleToggle, b_spu_update );
125 }
126
127 void GtkPopupSubtitleToggle( GtkCheckMenuItem * menuitem, gpointer user_data )
128 {
129     GTKLANGTOGGLE( p_window, "menubar_subpictures", SPU_ES,
130                    GtkMenubarSubtitleToggle, b_spu_update );
131 }
132
133 #undef GTKLANGTOGGLE
134
135 /*
136  * Navigation
137  */
138
139 void GtkPopupNavigationToggle( GtkCheckMenuItem * menuitem,
140                                gpointer user_data )
141 {
142     intf_thread_t * p_intf = GtkGetIntf( menuitem );
143
144     if( menuitem->active &&
145         !p_intf->p_sys->b_title_update &&
146         !p_intf->p_sys->b_chapter_update )
147     {
148         input_area_t   *p_area;
149
150         gint i_title   = DATA2TITLE( user_data );
151         gint i_chapter = DATA2CHAPTER( user_data );
152
153         p_area = p_intf->p_sys->p_input->stream.p_selected_area;
154
155         if( p_area != p_intf->p_sys->p_input->stream.pp_areas[i_title] )
156         {
157             p_area = p_intf->p_sys->p_input->stream.pp_areas[i_title];
158             p_intf->p_sys->b_title_update = 1;
159         }
160
161         p_area->i_part = i_chapter;
162
163         input_ChangeArea( p_intf->p_sys->p_input, (input_area_t*)p_area );
164
165         p_intf->p_sys->b_chapter_update = 1;
166         vlc_mutex_lock( &p_intf->p_sys->p_input->stream.stream_lock );
167         GtkSetupMenus( p_intf );
168         vlc_mutex_unlock( &p_intf->p_sys->p_input->stream.stream_lock );
169
170         input_SetStatus( p_intf->p_sys->p_input, INPUT_STATUS_PLAY );
171     }
172 }
173
174 /*
175  * Program
176  */
177 #define GTKPROGRAMTOGGLE( )                                                 \
178     intf_thread_t * p_intf = GtkGetIntf( menuitem );                        \
179                                                                             \
180     if( menuitem->active && !p_intf->p_sys->b_program_update )              \
181     {                                                                       \
182         u16 i_program_id = (ptrdiff_t)user_data;                            \
183                                                                             \
184         input_ChangeProgram( p_intf->p_sys->p_input, i_program_id );        \
185                                                                             \
186         p_intf->p_sys->b_program_update = 1;                                \
187                                                                             \
188         vlc_mutex_lock( &p_intf->p_sys->p_input->stream.stream_lock );      \
189         GtkSetupMenus( p_intf );                                            \
190         vlc_mutex_unlock( &p_intf->p_sys->p_input->stream.stream_lock );    \
191                                                                             \
192         p_intf->p_sys->b_program_update = 0;                                \
193                                                                             \
194         input_SetStatus( p_intf->p_sys->p_input, INPUT_STATUS_PLAY );       \
195     }
196
197 void GtkMenubarProgramToggle( GtkCheckMenuItem * menuitem, gpointer user_data )
198 {
199     GTKPROGRAMTOGGLE( );
200 }
201
202 void GtkPopupProgramToggle( GtkCheckMenuItem * menuitem, gpointer user_data )
203 {
204     GTKPROGRAMTOGGLE( );
205 }
206
207 /*
208  * Title
209  */
210
211 void GtkMenubarTitleToggle( GtkCheckMenuItem * menuitem, gpointer user_data )
212 {
213     intf_thread_t * p_intf = GtkGetIntf( menuitem );
214
215     if( menuitem->active && !p_intf->p_sys->b_title_update )
216     {
217         gint i_title = (gint)((long)user_data);
218         input_ChangeArea( p_intf->p_sys->p_input,
219                           p_intf->p_sys->p_input->stream.pp_areas[i_title] );
220
221         p_intf->p_sys->b_title_update = 1;
222         vlc_mutex_lock( &p_intf->p_sys->p_input->stream.stream_lock );
223         GtkSetupMenus( p_intf );
224         vlc_mutex_unlock( &p_intf->p_sys->p_input->stream.stream_lock );
225         p_intf->p_sys->b_title_update = 0;
226
227         input_SetStatus( p_intf->p_sys->p_input, INPUT_STATUS_PLAY );
228
229     }
230 }
231
232 /*
233  * Chapter
234  */
235
236 void GtkMenubarChapterToggle( GtkCheckMenuItem * menuitem, gpointer user_data )
237 {
238     intf_thread_t * p_intf;
239     input_area_t *  p_area;
240     gint            i_chapter;
241     GtkWidget *     p_popup_menu;
242
243     p_intf    = GtkGetIntf( menuitem );
244     p_area    = p_intf->p_sys->p_input->stream.p_selected_area;
245     i_chapter = (gint)((long)user_data);
246
247     if( menuitem->active && !p_intf->p_sys->b_chapter_update )
248     {
249         p_area->i_part = i_chapter;
250         input_ChangeArea( p_intf->p_sys->p_input, (input_area_t*)p_area );
251
252         p_intf->p_sys->b_chapter_update = 1;
253         p_popup_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT( 
254                              p_intf->p_sys->p_popup ), "popup_navigation" ) );
255
256         vlc_mutex_lock( &p_intf->p_sys->p_input->stream.stream_lock );
257         GtkTitleMenu( p_intf, p_popup_menu, GtkPopupNavigationToggle );
258         vlc_mutex_unlock( &p_intf->p_sys->p_input->stream.stream_lock );
259
260         p_intf->p_sys->b_chapter_update = 0;
261
262         input_SetStatus( p_intf->p_sys->p_input, INPUT_STATUS_PLAY );
263     }
264 }
265
266
267 /****************************************************************************
268  * Functions to generate menus
269  ****************************************************************************/
270
271 /*****************************************************************************
272  * GtkRadioMenu: update interactive menus of the interface
273  *****************************************************************************
274  * Sets up menus with information from input
275  * Warning: since this function is designed to be called by management
276  * function, the interface lock has to be taken
277  *****************************************************************************/
278 static gint GtkRadioMenu( intf_thread_t * p_intf,
279                             GtkWidget * p_root, GSList * p_menu_group,
280                             char * psz_item_name,
281                             int i_start, int i_end, int i_selected,
282                      void( *pf_toggle )( GtkCheckMenuItem *, gpointer ) )
283 {
284     char                psz_name[ GTK_MENU_LABEL_SIZE ];
285     GtkWidget *         p_menu;
286     GtkWidget *         p_submenu;
287     GtkWidget *         p_item_group;
288     GtkWidget *         p_item;
289     GtkWidget *         p_item_selected;
290     GSList *            p_group;
291     gint                i_item;
292
293     /* temporary hack to avoid blank menu when an open menu is removed */
294     if( GTK_MENU_ITEM(p_root)->submenu != NULL )
295     {
296         gtk_menu_popdown( GTK_MENU( GTK_MENU_ITEM(p_root)->submenu ) );
297     }
298     /* removes previous menu */
299     gtk_menu_item_remove_submenu( GTK_MENU_ITEM( p_root ) );
300     gtk_widget_set_sensitive( p_root, FALSE );
301
302     p_item_group = NULL;
303     p_submenu = NULL;
304     p_item_selected = NULL;
305     p_group = p_menu_group;
306
307     p_menu = gtk_menu_new();
308     gtk_object_set_data( GTK_OBJECT( p_menu ), "p_intf", p_intf );
309
310     for( i_item = i_start ; i_item <= i_end ; i_item++ )
311     {
312         /* we group chapters in packets of ten for small screens */
313         if( ( i_item % 10 == i_start ) && ( i_end > 20 ) )
314         {
315             if( i_item != i_start )
316             {
317                 gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_item_group ),
318                                            p_submenu );
319                 gtk_menu_append( GTK_MENU( p_menu ), p_item_group );
320             }
321
322             snprintf( psz_name, GTK_MENU_LABEL_SIZE,
323                       "%ss %d to %d", psz_item_name, i_item, i_item + 9 );
324             psz_name[ GTK_MENU_LABEL_SIZE - 1 ] = '\0';
325             p_item_group = gtk_menu_item_new_with_label( psz_name );
326             gtk_widget_show( p_item_group );
327             p_submenu = gtk_menu_new();
328             gtk_object_set_data( GTK_OBJECT( p_submenu ), "p_intf", p_intf );
329         }
330
331         snprintf( psz_name, GTK_MENU_LABEL_SIZE, "%s %d",
332                   psz_item_name, i_item );
333         psz_name[ GTK_MENU_LABEL_SIZE - 1 ] = '\0';
334
335         p_item = gtk_radio_menu_item_new_with_label( p_group, psz_name );
336         p_group = gtk_radio_menu_item_group( GTK_RADIO_MENU_ITEM( p_item ) );
337
338         if( i_selected == i_item )
339         {
340             p_item_selected = p_item;
341         }
342         
343         gtk_widget_show( p_item );
344
345         /* setup signal hanling */
346         gtk_signal_connect( GTK_OBJECT( p_item ),
347                             "toggled",
348                             GTK_SIGNAL_FUNC( pf_toggle ),
349                             (gpointer)((long)(i_item)) );
350
351         if( i_end > 20 )
352         {
353             gtk_menu_append( GTK_MENU( p_submenu ), p_item );
354         }
355         else
356         {
357             gtk_menu_append( GTK_MENU( p_menu ), p_item );
358         }
359     }
360
361     if( i_end > 20 )
362     {
363         gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_item_group ), p_submenu );
364         gtk_menu_append( GTK_MENU( p_menu ), p_item_group );
365     }
366
367     /* link the new menu to the title menu item */
368     gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_root ), p_menu );
369
370     /* toggle currently selected chapter
371      * We have to release the lock since input_ToggleES needs it */
372     if( p_item_selected != NULL )
373     {
374         gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM( p_item_selected ),
375                                         TRUE );
376     }
377
378     /* be sure that menu is sensitive, if there are several items */
379     if( i_end > i_start )
380     {
381         gtk_widget_set_sensitive( p_root, TRUE );
382     }
383
384     return TRUE;
385 }
386
387 /*****************************************************************************
388  * GtkProgramMenu: update the programs menu of the interface 
389  *****************************************************************************
390  * Builds the program menu according to what have been found in the PAT 
391  * by the input. Usefull for multi-programs streams such as DVB ones.
392  *****************************************************************************/
393 static gint GtkProgramMenu( gpointer            p_data,
394                             GtkWidget *         p_root,
395                             pgrm_descriptor_t * p_pgrm,
396                       void(*pf_toggle )( GtkCheckMenuItem *, gpointer ) )
397 {
398     intf_thread_t *     p_intf;
399     GtkWidget *         p_menu;
400     GtkWidget *         p_item;
401     GtkWidget *         p_item_active;
402     GSList *            p_group;
403     char                psz_name[ GTK_MENU_LABEL_SIZE ];
404     gint                i;
405
406     /* cast */
407     p_intf = (intf_thread_t *)p_data;
408
409     /* temporary hack to avoid blank menu when an open menu is removed */
410     if( GTK_MENU_ITEM(p_root)->submenu != NULL )
411     {
412         gtk_menu_popdown( GTK_MENU( GTK_MENU_ITEM(p_root)->submenu ) );
413     }
414     /* removes previous menu */
415     gtk_menu_item_remove_submenu( GTK_MENU_ITEM( p_root ) );
416     gtk_widget_set_sensitive( p_root, FALSE );
417
418     p_group = NULL;
419
420     /* menu container */
421     p_menu = gtk_menu_new();
422     gtk_object_set_data( GTK_OBJECT( p_menu ), "p_intf", p_intf );
423
424     p_item_active = NULL;
425
426     /* create a set of program buttons and append them to the container */
427     for( i = 0 ; i < p_intf->p_sys->p_input->stream.i_pgrm_number ; i++ )
428     {
429         snprintf( psz_name, GTK_MENU_LABEL_SIZE, "id %d",
430             p_intf->p_sys->p_input->stream.pp_programs[i]->i_number );
431         psz_name[GTK_MENU_LABEL_SIZE-1] = '\0';
432             
433         p_item = gtk_radio_menu_item_new_with_label( p_group, psz_name );
434         p_group =
435             gtk_radio_menu_item_group( GTK_RADIO_MENU_ITEM( p_item ) );
436
437         if( p_pgrm == p_intf->p_sys->p_input->stream.pp_programs[i] )
438         {
439             /* don't lose p_item when we append into menu */
440             p_item_active = p_item;
441         }
442
443         gtk_widget_show( p_item );
444
445         /* setup signal hanling */
446         gtk_signal_connect( GTK_OBJECT( p_item ), "toggled",
447                         GTK_SIGNAL_FUNC( pf_toggle ),
448                         (gpointer)(ptrdiff_t)( p_intf->p_sys->p_input->
449                         stream.pp_programs[i]->i_number ) );
450
451         gtk_menu_append( GTK_MENU( p_menu ), p_item );
452     }
453
454     /* link the new menu to the menubar item */
455     gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_root ), p_menu );
456
457     /* activation will call signals so we can only do it
458      * when submenu is attached to menu - to get intf_window 
459      * We have to release the lock since input_ToggleES needs it */
460     if( p_item_active != NULL )
461     {
462         gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM( p_item_active ),
463                                         TRUE );
464     }
465
466     /* be sure that menu is sensitive if more than 1 program */
467     if( p_intf->p_sys->p_input->stream.i_pgrm_number > 1 )
468     {
469         gtk_widget_set_sensitive( p_root, TRUE );
470     }
471
472     return TRUE;
473 }
474
475 /*****************************************************************************
476  * GtkLanguageMenus: update interactive menus of the interface
477  *****************************************************************************
478  * Sets up menus with information from input:
479  *  -languages
480  *  -sub-pictures
481  * Warning: since this function is designed to be called by management
482  * function, the interface lock has to be taken
483  *****************************************************************************/
484 static gint GtkLanguageMenus( gpointer          p_data,
485                               GtkWidget *       p_root,
486                               es_descriptor_t * p_es,
487                               gint              i_cat,
488                         void(*pf_toggle )( GtkCheckMenuItem *, gpointer ) )
489 {
490     intf_thread_t *     p_intf;
491     GtkWidget *         p_menu;
492     GtkWidget *         p_separator;
493     GtkWidget *         p_item;
494     GtkWidget *         p_item_active;
495     GSList *            p_group;
496     char                psz_name[ GTK_MENU_LABEL_SIZE ];
497     gint                i_item;
498     gint                i;
499
500     p_intf = (intf_thread_t *)p_data;
501
502     /* temporary hack to avoid blank menu when an open menu is removed */
503     if( GTK_MENU_ITEM(p_root)->submenu != NULL )
504     {
505         gtk_menu_popdown( GTK_MENU( GTK_MENU_ITEM(p_root)->submenu ) );
506     }
507     /* removes previous menu */
508     gtk_menu_item_remove_submenu( GTK_MENU_ITEM( p_root ) );
509     gtk_widget_set_sensitive( p_root, FALSE );
510
511     p_group = NULL;
512
513     /* menu container */
514     p_menu = gtk_menu_new();
515     gtk_object_set_data( GTK_OBJECT( p_menu ), "p_intf", p_intf );
516
517     /* special case for "off" item */
518     snprintf( psz_name, GTK_MENU_LABEL_SIZE, _("None") );
519     psz_name[ GTK_MENU_LABEL_SIZE - 1 ] = '\0';
520
521     p_item = gtk_radio_menu_item_new_with_label( p_group, psz_name );
522     p_group = gtk_radio_menu_item_group( GTK_RADIO_MENU_ITEM( p_item ) );
523
524     gtk_widget_show( p_item );
525
526     /* signal hanling for off */
527     gtk_signal_connect( GTK_OBJECT( p_item ), "toggled",
528                         GTK_SIGNAL_FUNC ( pf_toggle ), NULL );
529
530     gtk_menu_append( GTK_MENU( p_menu ), p_item );
531
532     p_separator = gtk_menu_item_new();
533     gtk_widget_set_sensitive( p_separator, FALSE );
534     gtk_widget_show( p_separator );
535     gtk_menu_append( GTK_MENU( p_menu ), p_separator );
536
537     p_item_active = NULL;
538     i_item = 0;
539
540     vlc_mutex_lock( &p_intf->p_sys->p_input->stream.stream_lock );
541
542 #define ES p_intf->p_sys->p_input->stream.pp_es[i]
543     /* create a set of language buttons and append them to the container */
544     for( i = 0 ; i < p_intf->p_sys->p_input->stream.i_es_number ; i++ )
545     {
546         if( ( ES->i_cat == i_cat ) &&
547             ( !ES->p_pgrm ||
548               ES->p_pgrm ==
549                  p_intf->p_sys->p_input->stream.p_selected_program ) )
550         {
551             i_item++;
552             strcpy( psz_name,
553                     p_intf->p_sys->p_input->stream.pp_es[i]->psz_desc );
554             if( psz_name[0] == '\0' )
555             {
556                 snprintf( psz_name, GTK_MENU_LABEL_SIZE,
557                           "Language %d", i_item );
558                 psz_name[ GTK_MENU_LABEL_SIZE - 1 ] = '\0';
559             }
560
561             p_item = gtk_radio_menu_item_new_with_label( p_group, psz_name );
562             p_group =
563                 gtk_radio_menu_item_group( GTK_RADIO_MENU_ITEM( p_item ) );
564
565             if( p_es == p_intf->p_sys->p_input->stream.pp_es[i] )
566             {
567                 /* don't lose p_item when we append into menu */
568                 p_item_active = p_item;
569             }
570
571             gtk_widget_show( p_item );
572
573             /* setup signal hanling */
574             gtk_signal_connect( GTK_OBJECT( p_item ), "toggled",
575                             GTK_SIGNAL_FUNC( pf_toggle ),
576                             (gpointer)( p_intf->p_sys->p_input->stream.pp_es[i] ) );
577
578             gtk_menu_append( GTK_MENU( p_menu ), p_item );
579         }
580     }
581
582     vlc_mutex_unlock( &p_intf->p_sys->p_input->stream.stream_lock );
583
584     /* link the new menu to the menubar item */
585     gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_root ), p_menu );
586
587     /* acitvation will call signals so we can only do it
588      * when submenu is attached to menu - to get intf_window 
589      * We have to release the lock since input_ToggleES needs it */
590     if( p_item_active != NULL )
591     {
592         gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM( p_item_active ),
593                                         TRUE );
594     }
595
596     /* be sure that menu is sensitive if non empty */
597     if( i_item > 0 )
598     {
599         gtk_widget_set_sensitive( p_root, TRUE );
600     }
601
602     return TRUE;
603 }
604
605 /*****************************************************************************
606  * GtkTitleMenu: sets menus for titles and chapters selection
607  *****************************************************************************
608  * Generates two types of menus:
609  *  -simple list of titles
610  *  -cascaded lists of chapters for each title
611  *****************************************************************************/
612 static gint GtkTitleMenu( gpointer       p_data,
613                             GtkWidget *    p_navigation, 
614                             void(*pf_toggle )( GtkCheckMenuItem *, gpointer ) )
615 {
616     intf_thread_t *     p_intf;
617     char                psz_name[ GTK_MENU_LABEL_SIZE ];
618     GtkWidget *         p_title_menu;
619     GtkWidget *         p_title_submenu;
620     GtkWidget *         p_title_item;
621     GtkWidget *         p_item_active;
622     GtkWidget *         p_chapter_menu;
623     GtkWidget *         p_chapter_submenu;
624     GtkWidget *         p_title_menu_item;
625     GtkWidget *         p_chapter_menu_item;
626     GtkWidget *         p_item;
627     GSList *            p_title_group;
628     GSList *            p_chapter_group;
629     gint                i_title;
630     gint                i_chapter;
631     gint                i_title_nb;
632     gint                i_chapter_nb;
633
634     /* cast */
635     p_intf = (intf_thread_t*)p_data;
636
637     /* temporary hack to avoid blank menu when an open menu is removed */
638     if( GTK_MENU_ITEM(p_navigation)->submenu != NULL )
639     {
640         gtk_menu_popdown( GTK_MENU( GTK_MENU_ITEM(p_navigation)->submenu ) );
641     }
642     /* removes previous menu */
643     gtk_menu_item_remove_submenu( GTK_MENU_ITEM( p_navigation ) );
644     gtk_widget_set_sensitive( p_navigation, FALSE );
645
646     p_title_menu = gtk_menu_new();
647     p_title_group = NULL;
648     p_title_submenu = NULL;
649     p_title_menu_item = NULL;
650     p_chapter_group = NULL;
651     p_chapter_submenu = NULL;
652     p_chapter_menu_item = NULL;
653     p_item_active = NULL;
654     i_title_nb = p_intf->p_sys->p_input->stream.i_area_nb;
655
656     gtk_object_set_data( GTK_OBJECT( p_title_menu ), "p_intf", p_intf );
657
658     /* loop on titles */
659     for( i_title = 0 ; i_title < i_title_nb ; i_title++ )
660     {
661         /* we group titles in packets of ten for small screens */
662         if( ( i_title % 10 == 0 ) && ( i_title_nb > 20 ) )
663         {
664             if( i_title != 0 )
665             {
666                 gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_title_menu_item ),
667                                            p_title_submenu );
668                 gtk_menu_append( GTK_MENU( p_title_menu ), p_title_menu_item );
669             }
670
671             snprintf( psz_name, GTK_MENU_LABEL_SIZE,
672                       "%d - %d", i_title, i_title + 9 );
673             psz_name[ GTK_MENU_LABEL_SIZE - 1 ] = '\0';
674             p_title_menu_item = gtk_menu_item_new_with_label( psz_name );
675             gtk_widget_show( p_title_menu_item );
676             p_title_submenu = gtk_menu_new();
677             gtk_object_set_data( GTK_OBJECT( p_title_submenu ),
678                                  "p_intf", p_intf );
679         }
680
681         snprintf( psz_name, GTK_MENU_LABEL_SIZE, _("Title %d (%d)"), i_title,
682                   p_intf->p_sys->p_input->stream.pp_areas[i_title]->i_part_nb );
683         psz_name[ GTK_MENU_LABEL_SIZE - 1 ] = '\0';
684 #if 0
685         if( pf_toggle == on_menubar_title_toggle )
686         {
687             p_title_item = gtk_radio_menu_item_new_with_label( p_title_group,
688                                                            psz_name );
689             p_title_group =
690               gtk_radio_menu_item_group( GTK_RADIO_MENU_ITEM( p_title_item ) );
691
692             if( p_intf->p_sys->p_input->stream.pp_areas[i_title] ==
693                          p_intf->p_sys->p_input->stream.p_selected_area )
694             {
695                 p_item_active = p_title_item;
696             }
697
698             /* setup signal hanling */
699             gtk_signal_connect( GTK_OBJECT( p_title_item ),
700                      "toggled",
701                      GTK_SIGNAL_FUNC( pf_toggle ),
702                      (gpointer)(p_intf->p_sys->p_input->stream.pp_areas[i_title]) );
703
704             if( p_intf->p_sys->p_input->stream.i_area_nb > 1 )
705             {
706                 /* be sure that menu is sensitive */
707                 gtk_widget_set_sensitive( p_navigation, TRUE );
708             }
709         }
710         else
711 #endif
712         {
713             p_title_item = gtk_menu_item_new_with_label( psz_name );
714
715 #if 1    
716             p_chapter_menu = gtk_menu_new();
717             gtk_object_set_data( GTK_OBJECT( p_chapter_menu ),
718                                  "p_intf", p_intf );
719             i_chapter_nb =
720                     p_intf->p_sys->p_input->stream.pp_areas[i_title]->i_part_nb;
721     
722             for( i_chapter = 0 ; i_chapter < i_chapter_nb ; i_chapter++ )
723             {
724                 /* we group chapters in packets of ten for small screens */
725                 if( ( i_chapter % 10 == 0 ) && ( i_chapter_nb > 20 ) )
726                 {
727                     if( i_chapter != 0 )
728                     {
729                         gtk_menu_item_set_submenu(
730                                     GTK_MENU_ITEM( p_chapter_menu_item ),
731                                     p_chapter_submenu );
732                         gtk_menu_append( GTK_MENU( p_chapter_menu ),
733                                          p_chapter_menu_item );
734                     }
735
736                     snprintf( psz_name, GTK_MENU_LABEL_SIZE,
737                               "%d - %d", i_chapter + 1, i_chapter + 10 );
738                     psz_name[ GTK_MENU_LABEL_SIZE - 1 ] = '\0';
739                     p_chapter_menu_item =
740                             gtk_menu_item_new_with_label( psz_name );
741                     gtk_widget_show( p_chapter_menu_item );
742                     p_chapter_submenu = gtk_menu_new();
743                     gtk_object_set_data( GTK_OBJECT( p_chapter_submenu ),
744                                          "p_intf", p_intf );
745                 }
746
747                 snprintf( psz_name, GTK_MENU_LABEL_SIZE,
748                           _("Chapter %d"), i_chapter + 1 );
749                 psz_name[ GTK_MENU_LABEL_SIZE - 1 ] = '\0';
750     
751                 p_item = gtk_radio_menu_item_new_with_label(
752                                                 p_chapter_group, psz_name );
753                 p_chapter_group = gtk_radio_menu_item_group(
754                                                 GTK_RADIO_MENU_ITEM( p_item ) );
755                 gtk_widget_show( p_item );
756
757 #define p_area p_intf->p_sys->p_input->stream.pp_areas[i_title]
758                 if( ( p_area ==
759                         p_intf->p_sys->p_input->stream.p_selected_area ) &&
760                     ( p_area->i_part == i_chapter + 1 ) )
761                 {
762                     p_item_active = p_item;
763                 }
764 #undef p_area
765
766                 /* setup signal hanling */
767                 gtk_signal_connect( GTK_OBJECT( p_item ),
768                            "toggled",
769                            GTK_SIGNAL_FUNC( pf_toggle ),
770                            (gpointer)POS2DATA( i_title, i_chapter + 1) );
771
772                 if( i_chapter_nb > 20 )
773                 {
774                     gtk_menu_append( GTK_MENU( p_chapter_submenu ), p_item );
775                 }
776                 else
777                 {
778                     gtk_menu_append( GTK_MENU( p_chapter_menu ), p_item );
779                 }
780             }
781
782             if( i_chapter_nb > 20 )
783             {
784                 gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_chapter_menu_item ),
785                                            p_chapter_submenu );
786                 gtk_menu_append( GTK_MENU( p_chapter_menu ),
787                                  p_chapter_menu_item );
788             }
789
790             /* link the new menu to the title menu item */
791             gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_title_item ),
792                                        p_chapter_menu );
793
794             if( p_intf->p_sys->p_input->stream.pp_areas[i_title]->i_part_nb > 1 )
795             {
796                 /* be sure that menu is sensitive */
797                 gtk_widget_set_sensitive( p_navigation, TRUE );
798             }
799 #else
800             GtkRadioMenu( p_intf, p_title_item, p_chapter_group, _("Chapter"),
801                 p_intf->p_sys->p_input->stream.pp_areas[i_title]->i_part_nb,
802                 1, i_title * 100,
803                 p_intf->p_sys->p_input->stream.p_selected_area->i_part +
804                 p_intf->p_sys->p_input->stream.p_selected_area->i_id *100,
805                 pf_toggle );
806
807 #endif
808         }
809         gtk_widget_show( p_title_item );
810
811         if( i_title_nb > 20 )
812         {
813             gtk_menu_append( GTK_MENU( p_title_submenu ), p_title_item );
814         }
815         else
816         {
817             gtk_menu_append( GTK_MENU( p_title_menu ), p_title_item );
818         }
819     }
820
821     if( i_title_nb > 20 )
822     {
823         gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_title_menu_item ),
824                                    p_title_submenu );
825         gtk_menu_append( GTK_MENU( p_title_menu ), p_title_menu_item );
826     }
827
828     /* be sure that menu is sensitive */
829     gtk_widget_set_sensitive( p_title_menu, TRUE );
830
831     /* link the new menu to the menubar item */
832     gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_navigation ), p_title_menu );
833
834     /* Default selected chapter
835      * We have to release the lock since input_ToggleES needs it */
836     if( p_item_active != NULL )
837     {
838         gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM( p_item_active ),
839                                         TRUE );
840     }
841 #if 0
842     if( p_intf->p_sys->p_input->stream.i_area_nb > 1 )
843     {
844         /* be sure that menu is sensitive */
845         gtk_widget_set_sensitive( p_navigation, TRUE );
846     }
847 #endif
848
849     return TRUE;
850 }
851
852 /*****************************************************************************
853  * GtkSetupMenus: function that generates title/chapter/audio/subpic
854  * menus with help from preceding functions
855  *****************************************************************************
856  * Function called with the lock on stream
857  *****************************************************************************/
858 gint GtkSetupMenus( intf_thread_t * p_intf )
859 {
860     es_descriptor_t *   p_audio_es;
861     es_descriptor_t *   p_spu_es;
862     GtkWidget *         p_menubar_menu;
863     GtkWidget *         p_popup_menu;
864     gint                i;
865
866     p_intf->p_sys->b_chapter_update |= p_intf->p_sys->b_title_update;
867     p_intf->p_sys->b_audio_update |= p_intf->p_sys->b_title_update |
868                                      p_intf->p_sys->b_program_update;
869     p_intf->p_sys->b_spu_update |= p_intf->p_sys->b_title_update |
870                                    p_intf->p_sys->b_program_update;
871
872     if( p_intf->p_sys->b_program_update )
873     { 
874         pgrm_descriptor_t * p_pgrm;
875         
876         if( p_intf->p_sys->p_input->stream.p_new_program )
877         {
878             p_pgrm = p_intf->p_sys->p_input->stream.p_new_program;
879         }
880         else
881         {
882             p_pgrm = p_intf->p_sys->p_input->stream.p_selected_program;
883         }
884
885         p_menubar_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT( 
886                             p_intf->p_sys->p_window ), "menubar_program" ) );
887         GtkProgramMenu( p_intf, p_menubar_menu, p_pgrm,
888                         GtkMenubarProgramToggle );
889         
890         p_intf->p_sys->b_program_update = 1;
891         p_popup_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT( 
892                             p_intf->p_sys->p_popup ), "popup_program" ) );
893         GtkProgramMenu( p_intf, p_popup_menu, p_pgrm,
894                         GtkPopupProgramToggle );
895
896         p_intf->p_sys->b_program_update = 0;
897     }
898     
899     if( p_intf->p_sys->b_title_update )
900     { 
901         char            psz_title[5];
902
903         p_menubar_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT( 
904                             p_intf->p_sys->p_window ), "menubar_title" ) );
905         GtkRadioMenu( p_intf, p_menubar_menu, NULL, _("Title"), 0,
906                       p_intf->p_sys->p_input->stream.i_area_nb - 1,
907                       p_intf->p_sys->p_input->stream.p_selected_area->i_id,
908                       GtkMenubarTitleToggle );
909
910         snprintf( psz_title, 4, "%d",
911                   p_intf->p_sys->p_input->stream.p_selected_area->i_id );
912         psz_title[ 4 ] = '\0';
913         gtk_label_set_text( p_intf->p_sys->p_label_title, psz_title );
914
915         p_intf->p_sys->b_title_update = 0;
916     }
917
918     if( p_intf->p_sys->b_chapter_update )
919     {
920         char            psz_chapter[5];
921
922         p_popup_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT( 
923                              p_intf->p_sys->p_popup ), "popup_navigation" ) );
924         GtkTitleMenu( p_intf, p_popup_menu, GtkPopupNavigationToggle );
925 #if 0
926         GtkRadioMenu( p_intf, p_menubar_menu, NULL, _("Title"), 0,
927                         p_intf->p_sys->p_input->stream.i_area_nb - 1,
928                         p_intf->p_sys->p_input->stream.p_selected_area->i_id,
929                         on_menubar_chapter_toggle );
930 #endif
931
932         p_menubar_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT( 
933                              p_intf->p_sys->p_window ), "menubar_chapter" ) );
934
935         GtkRadioMenu( p_intf, p_menubar_menu, NULL, _("Chapter"), 1,
936                         p_intf->p_sys->p_input->stream.p_selected_area->i_part_nb,
937                         p_intf->p_sys->p_input->stream.p_selected_area->i_part,
938                         GtkMenubarChapterToggle );
939
940
941         snprintf( psz_chapter, 4, "%d", 
942                   p_intf->p_sys->p_input->stream.p_selected_area->i_part );
943         psz_chapter[ 4 ] = '\0';
944         gtk_label_set_text( p_intf->p_sys->p_label_chapter, psz_chapter );
945
946         p_intf->p_sys->i_part =
947                 p_intf->p_sys->p_input->stream.p_selected_area->i_part;
948
949         p_intf->p_sys->b_chapter_update = 0;
950     }
951
952     /* look for selected ES */
953     p_audio_es = NULL;
954     p_spu_es = NULL;
955
956     for( i = 0 ; i < p_intf->p_sys->p_input->stream.i_selected_es_number ; i++ )
957     {
958         if( p_intf->p_sys->p_input->stream.pp_selected_es[i]->i_cat == AUDIO_ES )
959         {
960             p_audio_es = p_intf->p_sys->p_input->stream.pp_selected_es[i];
961         }
962
963         if( p_intf->p_sys->p_input->stream.pp_selected_es[i]->i_cat == SPU_ES )
964         {
965             p_spu_es = p_intf->p_sys->p_input->stream.pp_selected_es[i];
966         }
967     }
968
969     vlc_mutex_unlock( &p_intf->p_sys->p_input->stream.stream_lock );
970
971     /* audio menus */
972     if( p_intf->p_sys->b_audio_update )
973     {
974         /* find audio root menu */
975         p_menubar_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
976                              p_intf->p_sys->p_window ), "menubar_audio" ) );
977     
978         p_popup_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT( 
979                      p_intf->p_sys->p_popup ), "popup_audio" ) );
980     
981         p_intf->p_sys->b_audio_update = 1;
982         GtkLanguageMenus( p_intf, p_menubar_menu, p_audio_es, AUDIO_ES,
983                             GtkMenubarAudioToggle );
984         p_intf->p_sys->b_audio_update = 1;
985         GtkLanguageMenus( p_intf, p_popup_menu, p_audio_es, AUDIO_ES,
986                             GtkPopupAudioToggle );
987     
988         p_intf->p_sys->b_audio_update = 0;
989     }
990     
991     /* sub picture menus */
992     if( p_intf->p_sys->b_spu_update )
993     {
994         /* find spu root menu */
995         p_menubar_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
996                           p_intf->p_sys->p_window ), "menubar_subpictures" ) );
997     
998         p_popup_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT( 
999                      p_intf->p_sys->p_popup ), "popup_subpictures" ) );
1000     
1001         p_intf->p_sys->b_spu_update = 1;
1002         GtkLanguageMenus( p_intf, p_menubar_menu, p_spu_es, SPU_ES,
1003                             GtkMenubarSubtitleToggle  );
1004         p_intf->p_sys->b_spu_update = 1;
1005         GtkLanguageMenus( p_intf, p_popup_menu, p_spu_es, SPU_ES,
1006                             GtkPopupSubtitleToggle );
1007     
1008         p_intf->p_sys->b_spu_update = 0;
1009     }
1010
1011     vlc_mutex_lock( &p_intf->p_sys->p_input->stream.stream_lock );
1012
1013     return TRUE;
1014 }
1015