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