]> git.sesse.net Git - vlc/blob - modules/gui/gtk/menu.c
* ALL: changed the prototype of input_AddES() to include enough information so we...
[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.10 2003/05/05 22:23:38 gbazin 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  *          Laurent Aimar <fenrir@via.ecp.fr>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
25  *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30 #include <sys/types.h>                                              /* off_t */
31 #include <stdlib.h>
32
33 #include <vlc/vlc.h>
34 #include <vlc/intf.h>
35 #include <vlc/aout.h>
36 #include <vlc/vout.h>
37
38 #ifdef MODULE_NAME_IS_gnome
39 #   include <gnome.h>
40 #else
41 #   include <gtk/gtk.h>
42 #endif
43
44 #include <string.h>
45
46 #include "gtk_callbacks.h"
47 #include "gtk_interface.h"
48 #include "gtk_support.h"
49
50 #include "playlist.h"
51 #include "common.h"
52
53 /*
54  * Local Prototypes
55  */
56 static gint GtkLanguageMenus( gpointer , GtkWidget *, es_descriptor_t *, gint,
57                         void(*pf_toggle )( GtkCheckMenuItem *, gpointer ) );
58
59 void GtkMenubarAudioToggle   ( GtkCheckMenuItem *, gpointer );
60 void GtkPopupAudioToggle     ( GtkCheckMenuItem *, gpointer );
61 void GtkMenubarSubtitleToggle( GtkCheckMenuItem *, gpointer );
62 void GtkPopupSubtitleToggle  ( GtkCheckMenuItem *, gpointer );
63 static gint GtkTitleMenu( gpointer, GtkWidget *,
64                     void(*pf_toggle )( GtkCheckMenuItem *, gpointer ) );
65 static gint GtkRadioMenu( intf_thread_t *, GtkWidget *, GSList *,
66                           char *, int, int, int,
67                    void( *pf_toggle )( GtkCheckMenuItem *, gpointer ) );
68
69 static void GtkMenubarDeinterlaceToggle( GtkCheckMenuItem * menuitem, gpointer user_data );
70 static void GtkPopupDeinterlaceToggle( GtkCheckMenuItem * menuitem, gpointer user_data );
71 static gint GtkDeinterlaceMenus( gpointer          p_data,
72                                  GtkWidget *       p_root,
73                                  void(*pf_toggle )( GtkCheckMenuItem *, gpointer ) );
74
75 gint GtkSetupMenus( intf_thread_t * p_intf );
76
77 /****************************************************************************
78  * Gtk*Toggle: callbacks to toggle the value of a checkmenuitem
79  ****************************************************************************
80  * We need separate functions for menubar and popup here since we can't use
81  * user_data to transmit intf_* and we need to refresh the other menu.
82  ****************************************************************************/
83
84 #define GTKLANGTOGGLE( window, menu, type, callback, b_update )         \
85     intf_thread_t *         p_intf;                                     \
86     GtkWidget *             p_menu;                                     \
87     es_descriptor_t *       p_es;                                       \
88                                                                         \
89     p_intf = GtkGetIntf( menuitem );                                    \
90                                                                         \
91     if( !p_intf->p_sys->b_update )                                      \
92     {                                                                   \
93         p_menu = GTK_WIDGET( gtk_object_get_data(                       \
94                    GTK_OBJECT( p_intf->p_sys->window ), (menu) ) );     \
95         p_es = (es_descriptor_t*)user_data;                             \
96                                                                         \
97         input_ToggleES( p_intf->p_sys->p_input,                         \
98                         p_es, menuitem->active );                       \
99                                                                         \
100         p_intf->p_sys->b_update = menuitem->active;                     \
101                                                                         \
102         if( p_intf->p_sys->b_update )                                   \
103         {                                                               \
104             GtkLanguageMenus( p_intf, p_menu, p_es, type, callback );   \
105         }                                                               \
106                                                                         \
107         p_intf->p_sys->b_update = VLC_FALSE;                            \
108     }
109
110 /*
111  * Audio
112  */
113
114 void GtkMenubarAudioToggle( GtkCheckMenuItem * menuitem, gpointer user_data )
115 {
116     GTKLANGTOGGLE( p_popup, "popup_language", AUDIO_ES,
117                    GtkPopupAudioToggle, b_audio_update );
118 }
119
120 void GtkPopupAudioToggle( GtkCheckMenuItem * menuitem, gpointer user_data )
121 {
122     GTKLANGTOGGLE( p_window, "menubar_audio", AUDIO_ES,
123                    GtkMenubarAudioToggle, b_audio_update );
124 }
125
126 /*
127  * Subtitles
128  */
129
130 void GtkMenubarSubtitleToggle( GtkCheckMenuItem * menuitem, gpointer user_data )
131 {
132     GTKLANGTOGGLE( p_popup, "popup_subpictures", SPU_ES,
133                    GtkPopupSubtitleToggle, b_spu_update );
134 }
135
136 void GtkPopupSubtitleToggle( GtkCheckMenuItem * menuitem, gpointer user_data )
137 {
138     GTKLANGTOGGLE( p_window, "menubar_subpictures", SPU_ES,
139                    GtkMenubarSubtitleToggle, b_spu_update );
140 }
141
142 #undef GTKLANGTOGGLE
143
144 /*
145  * Navigation
146  */
147
148 void GtkPopupNavigationToggle( GtkCheckMenuItem * menuitem,
149                                gpointer user_data )
150 {
151     intf_thread_t * p_intf = GtkGetIntf( menuitem );
152
153     if( menuitem->active &&
154         !p_intf->p_sys->b_title_update &&
155         !p_intf->p_sys->b_chapter_update )
156     {
157         input_area_t   *p_area;
158
159         guint i_title   = DATA2TITLE( user_data );
160         guint i_chapter = DATA2CHAPTER( user_data );
161
162         vlc_mutex_lock( &p_intf->p_sys->p_input->stream.stream_lock );
163         p_area = p_intf->p_sys->p_input->stream.p_selected_area;
164
165         i_title = __MIN( i_title,
166                          p_intf->p_sys->p_input->stream.i_area_nb - 1 );
167         i_title = __MAX( i_title, 1 );
168
169         if( p_area != p_intf->p_sys->p_input->stream.pp_areas[i_title] )
170         {
171             p_area = p_intf->p_sys->p_input->stream.pp_areas[i_title];
172             p_intf->p_sys->b_title_update = VLC_TRUE;
173         }
174
175         i_chapter = __MIN( i_chapter, p_area->i_part_nb - 1 );
176         i_chapter = __MAX( i_chapter, 1 );
177         p_area->i_part = i_chapter;
178
179         vlc_mutex_unlock( &p_intf->p_sys->p_input->stream.stream_lock );
180
181         input_ChangeArea( p_intf->p_sys->p_input, (input_area_t*)p_area );
182
183         p_intf->p_sys->b_chapter_update = VLC_TRUE;
184         vlc_mutex_lock( &p_intf->p_sys->p_input->stream.stream_lock );
185         GtkSetupMenus( p_intf );
186         vlc_mutex_unlock( &p_intf->p_sys->p_input->stream.stream_lock );
187
188         input_SetStatus( p_intf->p_sys->p_input, INPUT_STATUS_PLAY );
189     }
190 }
191
192 /*
193  * Program
194  */
195 #define GTKPROGRAMTOGGLE( )                                                 \
196     intf_thread_t * p_intf = GtkGetIntf( menuitem );                        \
197                                                                             \
198     if( menuitem->active && !p_intf->p_sys->b_program_update )              \
199     {                                                                       \
200         int i_program_id = (ptrdiff_t)user_data;                            \
201                                                                             \
202         input_ChangeProgram( p_intf->p_sys->p_input, i_program_id );        \
203                                                                             \
204         p_intf->p_sys->b_program_update = VLC_TRUE;                         \
205                                                                             \
206         vlc_mutex_lock( &p_intf->p_sys->p_input->stream.stream_lock );      \
207         GtkSetupMenus( p_intf );                                            \
208         vlc_mutex_unlock( &p_intf->p_sys->p_input->stream.stream_lock );    \
209                                                                             \
210         p_intf->p_sys->b_program_update = VLC_FALSE;                        \
211                                                                             \
212         input_SetStatus( p_intf->p_sys->p_input, INPUT_STATUS_PLAY );       \
213     }
214
215 void GtkMenubarProgramToggle( GtkCheckMenuItem * menuitem, gpointer user_data )
216 {
217     GTKPROGRAMTOGGLE( );
218 }
219
220 void GtkPopupProgramToggle( GtkCheckMenuItem * menuitem, gpointer user_data )
221 {
222     GTKPROGRAMTOGGLE( );
223 }
224
225 /*
226  * Title
227  */
228
229 void GtkMenubarTitleToggle( GtkCheckMenuItem * menuitem, gpointer user_data )
230 {
231     intf_thread_t * p_intf = GtkGetIntf( menuitem );
232
233     if( menuitem->active && !p_intf->p_sys->b_title_update )
234     {
235         guint i_title = (ptrdiff_t)user_data;
236
237         vlc_mutex_lock( &p_intf->p_sys->p_input->stream.stream_lock );
238         i_title = __MIN( i_title,
239                          p_intf->p_sys->p_input->stream.i_area_nb - 1 );
240         i_title = __MAX( i_title, 1 );
241         vlc_mutex_unlock( &p_intf->p_sys->p_input->stream.stream_lock );
242
243         input_ChangeArea( p_intf->p_sys->p_input,
244                           p_intf->p_sys->p_input->stream.pp_areas[i_title] );
245
246         p_intf->p_sys->b_title_update = VLC_TRUE;
247         vlc_mutex_lock( &p_intf->p_sys->p_input->stream.stream_lock );
248         GtkSetupMenus( p_intf );
249         vlc_mutex_unlock( &p_intf->p_sys->p_input->stream.stream_lock );
250         p_intf->p_sys->b_title_update = VLC_FALSE;
251
252         input_SetStatus( p_intf->p_sys->p_input, INPUT_STATUS_PLAY );
253     }
254 }
255
256 /*
257  * Chapter
258  */
259
260 void GtkMenubarChapterToggle( GtkCheckMenuItem * menuitem, gpointer user_data )
261 {
262     intf_thread_t * p_intf;
263     input_area_t *  p_area;
264     guint           i_chapter;
265     GtkWidget *     p_popup_menu;
266
267     p_intf    = GtkGetIntf( menuitem );
268     p_area    = p_intf->p_sys->p_input->stream.p_selected_area;
269     i_chapter = (ptrdiff_t)user_data;
270
271     if( menuitem->active && !p_intf->p_sys->b_chapter_update )
272     {
273         vlc_mutex_lock( &p_intf->p_sys->p_input->stream.stream_lock );
274         i_chapter = __MIN( i_chapter, p_area->i_part_nb - 1 );
275         i_chapter = __MAX( i_chapter, 1 );
276         p_area->i_part = i_chapter;
277         vlc_mutex_unlock( &p_intf->p_sys->p_input->stream.stream_lock );
278
279         input_ChangeArea( p_intf->p_sys->p_input, (input_area_t*)p_area );
280
281         p_intf->p_sys->b_chapter_update = VLC_TRUE;
282         p_popup_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
283                              p_intf->p_sys->p_popup ), "popup_navigation" ) );
284
285         vlc_mutex_lock( &p_intf->p_sys->p_input->stream.stream_lock );
286         GtkTitleMenu( p_intf, p_popup_menu, GtkPopupNavigationToggle );
287         vlc_mutex_unlock( &p_intf->p_sys->p_input->stream.stream_lock );
288
289         p_intf->p_sys->b_chapter_update = VLC_FALSE;
290
291         input_SetStatus( p_intf->p_sys->p_input, INPUT_STATUS_PLAY );
292     }
293 }
294
295
296 static void GtkPopupObjectToggle( GtkCheckMenuItem * menuitem,
297     gpointer user_data, int i_object_type, char *psz_variable )
298 {
299     intf_thread_t   *p_intf = GtkGetIntf( menuitem );
300     GtkLabel        *p_label;
301
302     p_label = GTK_LABEL( ( GTK_BIN( menuitem )->child ) );
303
304     if( menuitem->active && !p_intf->p_sys->b_aout_update &&
305         !p_intf->p_sys->b_vout_update )
306     {
307         vlc_object_t * p_obj;
308
309         p_obj = (vlc_object_t *)vlc_object_find( p_intf, i_object_type,
310                                                   FIND_ANYWHERE );
311         if( p_obj )
312         {
313             vlc_value_t val;
314
315             if( user_data )
316             {
317                 val = (vlc_value_t)user_data;
318             }
319             else
320             {
321                 gtk_label_get( p_label, &val.psz_string );
322             }
323
324             if( var_Set( p_obj, psz_variable, val ) < 0 )
325             {
326                 msg_Warn( p_obj, "cannot set variable (%s)", val.psz_string );
327             }
328             vlc_object_release( p_obj );
329         }
330     }
331 }
332 static void GtkPopupAoutChannelsToggle( GtkCheckMenuItem * menuitem, gpointer user_data )
333 {
334     GtkPopupObjectToggle( menuitem, user_data, VLC_OBJECT_AOUT, "audio-channels" );
335 }
336
337 static void GtkPopupAoutDeviceToggle( GtkCheckMenuItem * menuitem, gpointer user_data )
338 {
339     GtkPopupObjectToggle( menuitem, user_data, VLC_OBJECT_AOUT, "audio-device" );
340 }
341
342
343 static void GtkPopupVoutDeviceToggle( GtkCheckMenuItem * menuitem, gpointer user_data )
344 {
345     GtkPopupObjectToggle( menuitem, user_data, VLC_OBJECT_VOUT, "video-device" );
346 }
347
348
349 static void GtkDeinterlaceUpdate( intf_thread_t *p_intf, char *psz_mode )
350 {
351     char *psz_filter;
352     unsigned int  i;
353
354     psz_filter = config_GetPsz( p_intf, "filter" );
355
356     if( !strcmp( psz_mode, "None" ) )
357     {
358         config_PutPsz( p_intf, "filter", "" );
359     }
360     else
361     {
362         if( !psz_filter || !*psz_filter )
363         {
364             config_PutPsz( p_intf, "filter", "deinterlace" );
365         }
366         else
367         {
368             if( strstr( psz_filter, "deinterlace" ) == NULL )
369             {
370                 psz_filter = realloc( psz_filter, strlen( psz_filter ) + 20 );
371                 strcat( psz_filter, ",deinterlace" );
372             }
373             config_PutPsz( p_intf, "filter", psz_filter );
374         }
375     }
376
377     if( psz_filter )
378         free( psz_filter );
379
380     /* now restart all video stream */
381     if( p_intf->p_sys->p_input )
382     {
383         vout_thread_t *p_vout;
384         vlc_mutex_lock( &p_intf->p_sys->p_input->stream.stream_lock );
385
386         /* Warn the vout we are about to change the filter chain */
387         p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT,
388                                   FIND_ANYWHERE );
389         if( p_vout )
390         {
391             p_vout->b_filter_change = VLC_TRUE;
392             vlc_object_release( p_vout );
393         }
394
395 #define ES p_intf->p_sys->p_input->stream.pp_es[i]
396         /* create a set of language buttons and append them to the container */
397         for( i = 0 ; i < p_intf->p_sys->p_input->stream.i_es_number ; i++ )
398         {
399             if( ( ES->i_cat == VIDEO_ES ) &&
400                     ES->p_decoder_fifo != NULL )
401             {
402                 input_UnselectES( p_intf->p_sys->p_input, ES );
403                 input_SelectES( p_intf->p_sys->p_input, ES );
404             }
405 #undef ES
406         }
407         vlc_mutex_unlock( &p_intf->p_sys->p_input->stream.stream_lock );
408     }
409
410     if( strcmp( psz_mode, "None" ) )
411     {
412         vout_thread_t *p_vout;
413         p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT,
414                                   FIND_ANYWHERE );
415         if( p_vout )
416         {
417             vlc_value_t val;
418
419             val.psz_string = psz_mode;
420             if( var_Set( p_vout, "deinterlace-mode", val ) != VLC_SUCCESS )
421                 config_PutPsz( p_intf, "deinterlace-mode", psz_mode );
422
423             vlc_object_release( p_vout );
424         }
425         else
426             config_PutPsz( p_intf, "deinterlace-mode", psz_mode );
427
428     }
429 }
430
431 static void GtkMenubarDeinterlaceToggle( GtkCheckMenuItem * menuitem, gpointer user_data )
432 {
433     intf_thread_t   *p_intf = GtkGetIntf( menuitem );
434     GtkLabel        *p_label;
435     char            *psz_mode;
436     GtkWidget       *p_popup_menu;
437
438     p_label = GTK_LABEL( ( GTK_BIN( menuitem )->child ) );
439
440     if( !p_intf->p_sys->b_deinterlace_update && menuitem->active )
441     {
442         gtk_label_get( p_label, &psz_mode );
443         GtkDeinterlaceUpdate( p_intf, psz_mode );
444
445         p_intf->p_sys->b_deinterlace_update = VLC_TRUE;
446
447         p_popup_menu   = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
448                                      p_intf->p_sys->p_popup ), "popup_deinterlace" ) );
449
450         GtkDeinterlaceMenus( p_intf, p_popup_menu, GtkPopupDeinterlaceToggle );
451
452         p_intf->p_sys->b_deinterlace_update = VLC_FALSE;
453
454     }
455 }
456
457 static void GtkPopupDeinterlaceToggle( GtkCheckMenuItem * menuitem, gpointer user_data )
458 {
459     intf_thread_t   *p_intf = GtkGetIntf( menuitem );
460     GtkLabel        *p_label;
461     char            *psz_mode;
462     GtkWidget       *p_menubar_menu;
463
464     p_label = GTK_LABEL( ( GTK_BIN( menuitem )->child ) );
465
466     if( !p_intf->p_sys->b_deinterlace_update && menuitem->active )
467     {
468         gtk_label_get( p_label, &psz_mode );
469         GtkDeinterlaceUpdate( p_intf, psz_mode );
470
471         p_intf->p_sys->b_deinterlace_update = VLC_TRUE;
472
473         p_menubar_menu   = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
474                                      p_intf->p_sys->p_window ), "menubar_deinterlace" ) );
475
476         GtkDeinterlaceMenus( p_intf, p_menubar_menu, GtkMenubarDeinterlaceToggle );
477
478         p_intf->p_sys->b_deinterlace_update = VLC_FALSE;
479     }
480 }
481
482 /****************************************************************************
483  * Functions to generate menus
484  ****************************************************************************/
485
486 /*****************************************************************************
487  * GtkRadioMenu: update interactive menus of the interface
488  *****************************************************************************
489  * Sets up menus with information from input
490  * Warning: since this function is designed to be called by management
491  * function, the interface lock has to be taken
492  *****************************************************************************/
493 static gint GtkRadioMenu( intf_thread_t * p_intf,
494                             GtkWidget * p_root, GSList * p_menu_group,
495                             char * psz_item_name,
496                             int i_start, int i_end, int i_selected,
497                      void( *pf_toggle )( GtkCheckMenuItem *, gpointer ) )
498 {
499     char                psz_name[ GTK_MENU_LABEL_SIZE ];
500     GtkWidget *         p_menu;
501     GtkWidget *         p_submenu;
502     GtkWidget *         p_item_group;
503     GtkWidget *         p_item;
504     GtkWidget *         p_item_selected;
505     GSList *            p_group;
506     gint                i_item;
507
508     /* temporary hack to avoid blank menu when an open menu is removed */
509     if( GTK_MENU_ITEM(p_root)->submenu != NULL )
510     {
511         gtk_menu_popdown( GTK_MENU( GTK_MENU_ITEM(p_root)->submenu ) );
512     }
513     /* removes previous menu */
514     gtk_menu_item_remove_submenu( GTK_MENU_ITEM( p_root ) );
515     gtk_widget_set_sensitive( p_root, FALSE );
516
517     p_item_group = NULL;
518     p_submenu = NULL;
519     p_item_selected = NULL;
520     p_group = p_menu_group;
521
522     p_menu = gtk_menu_new();
523     gtk_object_set_data( GTK_OBJECT( p_menu ), "p_intf", p_intf );
524
525     for( i_item = i_start ; i_item <= i_end ; i_item++ )
526     {
527         /* we group chapters in packets of ten for small screens */
528         if( ( i_item % 10 == i_start ) && ( i_end > i_start + 20 ) )
529         {
530             if( i_item != i_start )
531             {
532                 gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_item_group ),
533                                            p_submenu );
534                 gtk_menu_append( GTK_MENU( p_menu ), p_item_group );
535             }
536
537             snprintf( psz_name, GTK_MENU_LABEL_SIZE,
538                       "%ss %d to %d", psz_item_name, i_item, i_item + 9 );
539             psz_name[ GTK_MENU_LABEL_SIZE - 1 ] = '\0';
540             p_item_group = gtk_menu_item_new_with_label( psz_name );
541             gtk_widget_show( p_item_group );
542             p_submenu = gtk_menu_new();
543             gtk_object_set_data( GTK_OBJECT( p_submenu ), "p_intf", p_intf );
544         }
545
546         snprintf( psz_name, GTK_MENU_LABEL_SIZE, "%s %d",
547                   psz_item_name, i_item );
548         psz_name[ GTK_MENU_LABEL_SIZE - 1 ] = '\0';
549
550         p_item = gtk_radio_menu_item_new_with_label( p_group, psz_name );
551         p_group = gtk_radio_menu_item_group( GTK_RADIO_MENU_ITEM( p_item ) );
552
553         if( i_selected == i_item )
554         {
555             p_item_selected = p_item;
556         }
557
558         gtk_widget_show( p_item );
559
560         /* setup signal hanling */
561         gtk_signal_connect( GTK_OBJECT( p_item ),
562                             "toggled",
563                             GTK_SIGNAL_FUNC( pf_toggle ),
564                             (gpointer)((long)(i_item)) );
565
566         if( i_end > i_start + 20 )
567         {
568             gtk_menu_append( GTK_MENU( p_submenu ), p_item );
569         }
570         else
571         {
572             gtk_menu_append( GTK_MENU( p_menu ), p_item );
573         }
574     }
575
576     if( i_end > i_start + 20 )
577     {
578         gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_item_group ), p_submenu );
579         gtk_menu_append( GTK_MENU( p_menu ), p_item_group );
580     }
581
582     /* link the new menu to the title menu item */
583     gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_root ), p_menu );
584
585     /* toggle currently selected chapter
586      * We have to release the lock since input_ToggleES needs it */
587     if( p_item_selected != NULL )
588     {
589         gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM( p_item_selected ),
590                                         TRUE );
591     }
592
593     /* be sure that menu is sensitive, if there are several items */
594     if( i_end > i_start )
595     {
596         gtk_widget_set_sensitive( p_root, TRUE );
597     }
598
599     return TRUE;
600 }
601
602 /*****************************************************************************
603  * GtkProgramMenu: update the programs menu of the interface
604  *****************************************************************************
605  * Builds the program menu according to what have been found in the PAT
606  * by the input. Usefull for multi-programs streams such as DVB ones.
607  *****************************************************************************/
608 static gint GtkProgramMenu( gpointer            p_data,
609                             GtkWidget *         p_root,
610                             pgrm_descriptor_t * p_pgrm,
611                       void(*pf_toggle )( GtkCheckMenuItem *, gpointer ) )
612 {
613     intf_thread_t *     p_intf;
614     GtkWidget *         p_menu;
615     GtkWidget *         p_item;
616     GtkWidget *         p_item_active;
617     GSList *            p_group;
618     char                psz_name[ GTK_MENU_LABEL_SIZE ];
619     guint               i;
620
621     /* cast */
622     p_intf = (intf_thread_t *)p_data;
623
624     /* temporary hack to avoid blank menu when an open menu is removed */
625     if( GTK_MENU_ITEM(p_root)->submenu != NULL )
626     {
627         gtk_menu_popdown( GTK_MENU( GTK_MENU_ITEM(p_root)->submenu ) );
628     }
629     /* removes previous menu */
630     gtk_menu_item_remove_submenu( GTK_MENU_ITEM( p_root ) );
631     gtk_widget_set_sensitive( p_root, FALSE );
632
633     p_group = NULL;
634
635     /* menu container */
636     p_menu = gtk_menu_new();
637     gtk_object_set_data( GTK_OBJECT( p_menu ), "p_intf", p_intf );
638
639     p_item_active = NULL;
640
641     /* create a set of program buttons and append them to the container */
642     for( i = 0 ; i < p_intf->p_sys->p_input->stream.i_pgrm_number ; i++ )
643     {
644         snprintf( psz_name, GTK_MENU_LABEL_SIZE, "id %d",
645             p_intf->p_sys->p_input->stream.pp_programs[i]->i_number );
646         psz_name[GTK_MENU_LABEL_SIZE-1] = '\0';
647
648         p_item = gtk_radio_menu_item_new_with_label( p_group, psz_name );
649         p_group =
650             gtk_radio_menu_item_group( GTK_RADIO_MENU_ITEM( p_item ) );
651
652         if( p_pgrm == p_intf->p_sys->p_input->stream.pp_programs[i] )
653         {
654             /* don't lose p_item when we append into menu */
655             p_item_active = p_item;
656         }
657
658         gtk_widget_show( p_item );
659
660         /* setup signal hanling */
661         gtk_signal_connect( GTK_OBJECT( p_item ), "toggled",
662                         GTK_SIGNAL_FUNC( pf_toggle ),
663                         (gpointer)(ptrdiff_t)( p_intf->p_sys->p_input->
664                         stream.pp_programs[i]->i_number ) );
665
666         gtk_menu_append( GTK_MENU( p_menu ), p_item );
667     }
668
669     /* link the new menu to the menubar item */
670     gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_root ), p_menu );
671
672     /* activation will call signals so we can only do it
673      * when submenu is attached to menu - to get intf_window
674      * We have to release the lock since input_ToggleES needs it */
675     if( p_item_active != NULL )
676     {
677         gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM( p_item_active ),
678                                         TRUE );
679     }
680
681     /* be sure that menu is sensitive if more than 1 program */
682     if( p_intf->p_sys->p_input->stream.i_pgrm_number > 1 )
683     {
684         gtk_widget_set_sensitive( p_root, TRUE );
685     }
686
687     return TRUE;
688 }
689
690 /*****************************************************************************
691  * GtkLanguageMenus: update interactive menus of the interface
692  *****************************************************************************
693  * Sets up menus with information from input:
694  *  -languages
695  *  -sub-pictures
696  * Warning: since this function is designed to be called by management
697  * function, the interface lock has to be taken
698  *****************************************************************************/
699 static gint GtkLanguageMenus( gpointer          p_data,
700                               GtkWidget *       p_root,
701                               es_descriptor_t * p_es,
702                               gint              i_cat,
703                         void(*pf_toggle )( GtkCheckMenuItem *, gpointer ) )
704 {
705     intf_thread_t *     p_intf;
706     GtkWidget *         p_menu;
707     GtkWidget *         p_separator;
708     GtkWidget *         p_item;
709     GtkWidget *         p_item_active;
710     GSList *            p_group;
711     char                psz_name[ GTK_MENU_LABEL_SIZE ];
712     guint               i_item;
713     guint               i;
714
715     p_intf = (intf_thread_t *)p_data;
716
717     /* temporary hack to avoid blank menu when an open menu is removed */
718     if( GTK_MENU_ITEM(p_root)->submenu != NULL )
719     {
720         gtk_menu_popdown( GTK_MENU( GTK_MENU_ITEM(p_root)->submenu ) );
721     }
722     /* removes previous menu */
723     gtk_menu_item_remove_submenu( GTK_MENU_ITEM( p_root ) );
724     gtk_widget_set_sensitive( p_root, FALSE );
725
726     p_group = NULL;
727
728     /* menu container */
729     p_menu = gtk_menu_new();
730     gtk_object_set_data( GTK_OBJECT( p_menu ), "p_intf", p_intf );
731
732     /* special case for "off" item */
733     snprintf( psz_name, GTK_MENU_LABEL_SIZE, _("None") );
734     psz_name[ GTK_MENU_LABEL_SIZE - 1 ] = '\0';
735
736     p_item = gtk_radio_menu_item_new_with_label( p_group, psz_name );
737     p_group = gtk_radio_menu_item_group( GTK_RADIO_MENU_ITEM( p_item ) );
738
739     gtk_widget_show( p_item );
740
741     /* signal hanling for off */
742     gtk_signal_connect( GTK_OBJECT( p_item ), "toggled",
743                         GTK_SIGNAL_FUNC ( pf_toggle ), NULL );
744
745     gtk_menu_append( GTK_MENU( p_menu ), p_item );
746
747     p_separator = gtk_menu_item_new();
748     gtk_widget_set_sensitive( p_separator, FALSE );
749     gtk_widget_show( p_separator );
750     gtk_menu_append( GTK_MENU( p_menu ), p_separator );
751
752     p_item_active = NULL;
753     i_item = 0;
754
755     vlc_mutex_lock( &p_intf->p_sys->p_input->stream.stream_lock );
756
757 #define ES p_intf->p_sys->p_input->stream.pp_es[i]
758     /* create a set of language buttons and append them to the container */
759     for( i = 0 ; i < p_intf->p_sys->p_input->stream.i_es_number ; i++ )
760     {
761         if( ( ES->i_cat == i_cat ) &&
762             ( !ES->p_pgrm ||
763               ES->p_pgrm ==
764                  p_intf->p_sys->p_input->stream.p_selected_program ) )
765         {
766             i_item++;
767             if( !p_intf->p_sys->p_input->stream.pp_es[i]->psz_desc ||
768                 !*p_intf->p_sys->p_input->stream.pp_es[i]->psz_desc )
769             {
770                 snprintf( psz_name, GTK_MENU_LABEL_SIZE,
771                           "Language %d", i_item );
772                 psz_name[ GTK_MENU_LABEL_SIZE - 1 ] = '\0';
773             }
774             else
775             {
776                 strcpy( psz_name,
777                         p_intf->p_sys->p_input->stream.pp_es[i]->psz_desc );
778             }
779
780             p_item = gtk_radio_menu_item_new_with_label( p_group, psz_name );
781             p_group =
782                 gtk_radio_menu_item_group( GTK_RADIO_MENU_ITEM( p_item ) );
783
784             if( p_es == p_intf->p_sys->p_input->stream.pp_es[i] )
785             {
786                 /* don't lose p_item when we append into menu */
787                 p_item_active = p_item;
788             }
789
790             gtk_widget_show( p_item );
791
792             /* setup signal hanling */
793             gtk_signal_connect( GTK_OBJECT( p_item ), "toggled",
794                             GTK_SIGNAL_FUNC( pf_toggle ),
795                             (gpointer)( p_intf->p_sys->p_input->stream.pp_es[i] ) );
796
797             gtk_menu_append( GTK_MENU( p_menu ), p_item );
798         }
799     }
800
801     vlc_mutex_unlock( &p_intf->p_sys->p_input->stream.stream_lock );
802
803     /* link the new menu to the menubar item */
804     gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_root ), p_menu );
805
806     /* acitvation will call signals so we can only do it
807      * when submenu is attached to menu - to get intf_window
808      * We have to release the lock since input_ToggleES needs it */
809     if( p_item_active != NULL )
810     {
811         gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM( p_item_active ),
812                                         TRUE );
813     }
814
815     /* be sure that menu is sensitive if non empty */
816     if( i_item > 0 )
817     {
818         gtk_widget_set_sensitive( p_root, TRUE );
819     }
820
821     return TRUE;
822 }
823
824 /*****************************************************************************
825  * GtkTitleMenu: sets menus for titles and chapters selection
826  *****************************************************************************
827  * Generates two types of menus:
828  *  -simple list of titles
829  *  -cascaded lists of chapters for each title
830  *****************************************************************************/
831 static gint GtkTitleMenu( gpointer       p_data,
832                           GtkWidget *    p_navigation,
833                           void(*pf_toggle )( GtkCheckMenuItem *, gpointer ) )
834 {
835     intf_thread_t *     p_intf;
836     char                psz_name[ GTK_MENU_LABEL_SIZE ];
837     GtkWidget *         p_title_menu;
838     GtkWidget *         p_title_submenu;
839     GtkWidget *         p_title_item;
840     GtkWidget *         p_item_active;
841     GtkWidget *         p_chapter_menu;
842     GtkWidget *         p_chapter_submenu;
843     GtkWidget *         p_title_menu_item;
844     GtkWidget *         p_chapter_menu_item;
845     GtkWidget *         p_item;
846     GSList *            p_title_group;
847     GSList *            p_chapter_group;
848     guint               i_title;
849     guint               i_chapter;
850     guint               i_title_nb;
851     guint               i_chapter_nb;
852
853     /* cast */
854     p_intf = (intf_thread_t*)p_data;
855
856     /* temporary hack to avoid blank menu when an open menu is removed */
857     if( GTK_MENU_ITEM(p_navigation)->submenu != NULL )
858     {
859         gtk_menu_popdown( GTK_MENU( GTK_MENU_ITEM(p_navigation)->submenu ) );
860     }
861     /* removes previous menu */
862     gtk_menu_item_remove_submenu( GTK_MENU_ITEM( p_navigation ) );
863     gtk_widget_set_sensitive( p_navigation, FALSE );
864
865     p_title_menu = gtk_menu_new();
866     p_title_group = NULL;
867     p_title_submenu = NULL;
868     p_title_menu_item = NULL;
869     p_chapter_group = NULL;
870     p_chapter_submenu = NULL;
871     p_chapter_menu_item = NULL;
872     p_item_active = NULL;
873     i_title_nb = p_intf->p_sys->p_input->stream.i_area_nb - 1;
874
875     gtk_object_set_data( GTK_OBJECT( p_title_menu ), "p_intf", p_intf );
876
877     /* loop on titles */
878     for( i_title = 1 ; i_title <= i_title_nb ; i_title++ )
879     {
880         /* we group titles in packets of ten for small screens */
881         if( ( i_title % 10 == 1 ) && ( i_title_nb > 20 ) )
882         {
883             if( i_title != 1 )
884             {
885                 gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_title_menu_item ),
886                                            p_title_submenu );
887                 gtk_menu_append( GTK_MENU( p_title_menu ), p_title_menu_item );
888             }
889
890             snprintf( psz_name, GTK_MENU_LABEL_SIZE,
891                       "%d - %d", i_title, i_title + 9 );
892             psz_name[ GTK_MENU_LABEL_SIZE - 1 ] = '\0';
893             p_title_menu_item = gtk_menu_item_new_with_label( psz_name );
894             gtk_widget_show( p_title_menu_item );
895             p_title_submenu = gtk_menu_new();
896             gtk_object_set_data( GTK_OBJECT( p_title_submenu ),
897                                  "p_intf", p_intf );
898         }
899
900         snprintf( psz_name, GTK_MENU_LABEL_SIZE, _("Title %d (%d)"), i_title,
901                   p_intf->p_sys->p_input->stream.pp_areas[i_title]->i_part_nb - 1);
902         psz_name[ GTK_MENU_LABEL_SIZE - 1 ] = '\0';
903 #if 0
904         if( pf_toggle == on_menubar_title_toggle )
905         {
906             p_title_item = gtk_radio_menu_item_new_with_label( p_title_group,
907                                                            psz_name );
908             p_title_group =
909               gtk_radio_menu_item_group( GTK_RADIO_MENU_ITEM( p_title_item ) );
910
911             if( p_intf->p_sys->p_input->stream.pp_areas[i_title] ==
912                          p_intf->p_sys->p_input->stream.p_selected_area )
913             {
914                 p_item_active = p_title_item;
915             }
916
917             /* setup signal hanling */
918             gtk_signal_connect( GTK_OBJECT( p_title_item ),
919                      "toggled",
920                      GTK_SIGNAL_FUNC( pf_toggle ),
921                      (gpointer)(p_intf->p_sys->p_input->stream.pp_areas[i_title]) );
922
923             if( p_intf->p_sys->p_input->stream.i_area_nb > 1 )
924             {
925                 /* be sure that menu is sensitive */
926                 gtk_widget_set_sensitive( p_navigation, TRUE );
927             }
928         }
929         else
930 #endif
931         {
932             p_title_item = gtk_menu_item_new_with_label( psz_name );
933
934 #if 1
935             p_chapter_menu = gtk_menu_new();
936             gtk_object_set_data( GTK_OBJECT( p_chapter_menu ),
937                                  "p_intf", p_intf );
938             i_chapter_nb =
939                p_intf->p_sys->p_input->stream.pp_areas[i_title]->i_part_nb - 1;
940
941             for( i_chapter = 1 ; i_chapter <= i_chapter_nb ; i_chapter++ )
942             {
943                 /* we group chapters in packets of ten for small screens */
944                 if( ( i_chapter % 10 == 1 ) && ( i_chapter_nb > 20 ) )
945                 {
946                     if( i_chapter != 1 )
947                     {
948                         gtk_menu_item_set_submenu(
949                                     GTK_MENU_ITEM( p_chapter_menu_item ),
950                                     p_chapter_submenu );
951                         gtk_menu_append( GTK_MENU( p_chapter_menu ),
952                                          p_chapter_menu_item );
953                     }
954
955                     snprintf( psz_name, GTK_MENU_LABEL_SIZE,
956                               "%d - %d", i_chapter, i_chapter + 9 );
957                     psz_name[ GTK_MENU_LABEL_SIZE - 1 ] = '\0';
958                     p_chapter_menu_item =
959                             gtk_menu_item_new_with_label( psz_name );
960                     gtk_widget_show( p_chapter_menu_item );
961                     p_chapter_submenu = gtk_menu_new();
962                     gtk_object_set_data( GTK_OBJECT( p_chapter_submenu ),
963                                          "p_intf", p_intf );
964                 }
965
966                 snprintf( psz_name, GTK_MENU_LABEL_SIZE,
967                           _("Chapter %d"), i_chapter );
968                 psz_name[ GTK_MENU_LABEL_SIZE - 1 ] = '\0';
969
970                 p_item = gtk_radio_menu_item_new_with_label(
971                                                 p_chapter_group, psz_name );
972                 p_chapter_group = gtk_radio_menu_item_group(
973                                                 GTK_RADIO_MENU_ITEM( p_item ) );
974                 gtk_widget_show( p_item );
975
976 #define p_area p_intf->p_sys->p_input->stream.pp_areas[i_title]
977                 if( ( p_area ==
978                         p_intf->p_sys->p_input->stream.p_selected_area ) &&
979                     ( p_area->i_part == i_chapter ) )
980                 {
981                     p_item_active = p_item;
982                 }
983 #undef p_area
984
985                 /* setup signal hanling */
986                 gtk_signal_connect( GTK_OBJECT( p_item ),
987                            "toggled",
988                            GTK_SIGNAL_FUNC( pf_toggle ),
989                            (gpointer)POS2DATA( i_title, i_chapter ) );
990
991                 if( i_chapter_nb > 20 )
992                 {
993                     gtk_menu_append( GTK_MENU( p_chapter_submenu ), p_item );
994                 }
995                 else
996                 {
997                     gtk_menu_append( GTK_MENU( p_chapter_menu ), p_item );
998                 }
999             }
1000
1001             if( i_chapter_nb > 20 )
1002             {
1003                 gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_chapter_menu_item ),
1004                                            p_chapter_submenu );
1005                 gtk_menu_append( GTK_MENU( p_chapter_menu ),
1006                                  p_chapter_menu_item );
1007             }
1008
1009             /* link the new menu to the title menu item */
1010             gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_title_item ),
1011                                        p_chapter_menu );
1012
1013             if( p_intf->p_sys->p_input->stream.pp_areas[i_title]->i_part_nb > 1 )
1014             {
1015                 /* be sure that menu is sensitive */
1016                 gtk_widget_set_sensitive( p_navigation, TRUE );
1017             }
1018 #else
1019             GtkRadioMenu( p_intf, p_title_item, p_chapter_group, _("Chapter"),
1020                 p_intf->p_sys->p_input->stream.pp_areas[i_title]->i_part_nb - 1,
1021                 1, i_title * 100,
1022                 p_intf->p_sys->p_input->stream.p_selected_area->i_part +
1023                 p_intf->p_sys->p_input->stream.p_selected_area->i_id *100,
1024                 pf_toggle );
1025
1026 #endif
1027         }
1028         gtk_widget_show( p_title_item );
1029
1030         if( i_title_nb > 20 )
1031         {
1032             gtk_menu_append( GTK_MENU( p_title_submenu ), p_title_item );
1033         }
1034         else
1035         {
1036             gtk_menu_append( GTK_MENU( p_title_menu ), p_title_item );
1037         }
1038     }
1039
1040     if( i_title_nb > 20 )
1041     {
1042         gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_title_menu_item ),
1043                                    p_title_submenu );
1044         gtk_menu_append( GTK_MENU( p_title_menu ), p_title_menu_item );
1045     }
1046
1047     /* be sure that menu is sensitive */
1048     gtk_widget_set_sensitive( p_title_menu, TRUE );
1049
1050     /* link the new menu to the menubar item */
1051     gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_navigation ), p_title_menu );
1052
1053     /* Default selected chapter
1054      * We have to release the lock since input_ToggleES needs it */
1055     if( p_item_active != NULL )
1056     {
1057         gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM( p_item_active ),
1058                                         TRUE );
1059     }
1060 #if 0
1061     if( p_intf->p_sys->p_input->stream.i_area_nb > 1 )
1062     {
1063         /* be sure that menu is sensitive */
1064         gtk_widget_set_sensitive( p_navigation, TRUE );
1065     }
1066 #endif
1067
1068     return TRUE;
1069 }
1070
1071 /*****************************************************************************
1072  * GtkSetupVarMenu :
1073  *****************************************************************************
1074  *
1075  *****************************************************************************/
1076 static gint GtkSetupVarMenu( intf_thread_t * p_intf,
1077                              vlc_object_t * p_object,
1078                              GtkWidget *p_root,
1079                              char * psz_variable,
1080                              void(*pf_toggle )( GtkCheckMenuItem *, gpointer ) )
1081 {
1082     vlc_value_t         val, text, val_list, text_list;
1083     GtkWidget *         p_menu;
1084     GSList *            p_group = NULL;
1085     GtkWidget *         p_item;
1086     GtkWidget *         p_item_active = NULL;
1087
1088     int                 i_item, i_type;
1089
1090      /* temporary hack to avoid blank menu when an open menu is removed */
1091     if( GTK_MENU_ITEM(p_root)->submenu != NULL )
1092     {
1093         gtk_menu_popdown( GTK_MENU( GTK_MENU_ITEM(p_root)->submenu ) );
1094     }
1095     /* removes previous menu */
1096     gtk_menu_item_remove_submenu( GTK_MENU_ITEM( p_root ) );
1097     gtk_widget_set_sensitive( p_root, FALSE );
1098
1099     /* Check the type of the object variable */
1100     i_type = var_Type( p_object, psz_variable );
1101
1102     /* Make sure we want to display the variable */
1103     if( i_type & VLC_VAR_HASCHOICE )
1104     {
1105         var_Change( p_object, psz_variable, VLC_VAR_CHOICESCOUNT, &val, NULL );
1106         if( val.i_int == 0 ) return FALSE;
1107     }
1108
1109     /* Get the descriptive name of the variable */
1110     var_Change( p_object, psz_variable, VLC_VAR_GETTEXT, &text, NULL );
1111
1112     /* get the current value */
1113     if( var_Get( p_object, psz_variable, &val ) < 0 )
1114     {
1115         return FALSE;
1116     }
1117
1118     if( var_Change( p_object, psz_variable, VLC_VAR_GETLIST,
1119                     &val_list, &text_list ) < 0 )
1120     {
1121         if( i_type == VLC_VAR_STRING ) free( val.psz_string );
1122         return FALSE;
1123     }
1124
1125     /* menu container */
1126     p_menu = gtk_menu_new();
1127     gtk_object_set_data( GTK_OBJECT( p_menu ), "p_intf", p_intf );
1128
1129     for( i_item = 0; i_item < val_list.p_list->i_count; i_item++ )
1130     {
1131         switch( i_type & VLC_VAR_TYPE )
1132         {
1133         case VLC_VAR_STRING:
1134             p_item = gtk_radio_menu_item_new_with_label( p_group,
1135                      text_list.p_list->p_values[i_item].psz_string ?
1136                      text_list.p_list->p_values[i_item].psz_string :
1137                      val_list.p_list->p_values[i_item].psz_string );
1138
1139             /* signal hanling for off */
1140             gtk_signal_connect( GTK_OBJECT( p_item ), "toggled",
1141                 GTK_SIGNAL_FUNC ( pf_toggle ),
1142                 /* FIXME memory leak */
1143                 strdup(val_list.p_list->p_values[i_item].psz_string) );
1144
1145             if( !strcmp( val.psz_string,
1146                          val_list.p_list->p_values[i_item].psz_string ) )
1147             {
1148                 p_item_active = p_item;
1149             }
1150             break;
1151         case VLC_VAR_INTEGER:
1152             p_item = gtk_radio_menu_item_new_with_label( p_group,
1153                      text_list.p_list->p_values[i_item].psz_string ?
1154                      text_list.p_list->p_values[i_item].psz_string :
1155                      NULL /* FIXME */ );
1156
1157             /* signal hanling for off */
1158             gtk_signal_connect( GTK_OBJECT( p_item ), "toggled",
1159                 GTK_SIGNAL_FUNC ( pf_toggle ),
1160                 (gpointer)val_list.p_list->p_values[i_item].i_int );
1161
1162             if( val.i_int == val_list.p_list->p_values[i_item].i_int )
1163             {
1164                 p_item_active = p_item;
1165             }
1166             break;
1167         default:
1168             /* FIXME */
1169             return FALSE;
1170         }
1171
1172         p_group = gtk_radio_menu_item_group( GTK_RADIO_MENU_ITEM( p_item ) );
1173
1174         gtk_widget_show( p_item );
1175
1176         gtk_menu_append( GTK_MENU( p_menu ), p_item );
1177     }
1178
1179     /* link the new menu to the menubar item */
1180     gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_root ), p_menu );
1181
1182     if( p_item_active )
1183     {
1184         gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(p_item_active),
1185                                         TRUE );
1186     }
1187
1188     if( val_list.p_list->i_count > 0 )
1189     {
1190         gtk_widget_set_sensitive( p_root, TRUE );
1191     }
1192
1193     /* clean up everything */
1194     if( i_type == VLC_VAR_STRING ) free( val.psz_string );
1195     var_Change( p_object, psz_variable, VLC_VAR_FREELIST,
1196                 &val_list, &text_list );
1197
1198     return TRUE;
1199 }
1200
1201 /*****************************************************************************
1202  * GtkDeinterlaceMenus: update interactive menus of the interface
1203  *****************************************************************************
1204  *****************************************************************************/
1205 static gint GtkDeinterlaceMenus( gpointer          p_data,
1206                                  GtkWidget *       p_root,
1207                                  void(*pf_toggle )( GtkCheckMenuItem *, gpointer ) )
1208 {
1209     intf_thread_t *     p_intf;
1210     GtkWidget *         p_menu;
1211     GtkWidget *         p_separator;
1212     GtkWidget *         p_item;
1213     GtkWidget *         p_item_active;
1214     GSList *            p_group;
1215     guint               i_item;
1216     guint               i;
1217     char                *ppsz_deinterlace_mode[] = { "discard", "blend", "mean", "bob", "linear", NULL };
1218     char                *psz_deinterlace_option;
1219     char                *psz_filter;
1220
1221     p_intf = (intf_thread_t *)p_data;
1222
1223     /* temporary hack to avoid blank menu when an open menu is removed */
1224     if( GTK_MENU_ITEM(p_root)->submenu != NULL )
1225     {
1226         gtk_menu_popdown( GTK_MENU( GTK_MENU_ITEM(p_root)->submenu ) );
1227     }
1228     /* removes previous menu */
1229     gtk_menu_item_remove_submenu( GTK_MENU_ITEM( p_root ) );
1230     gtk_widget_set_sensitive( p_root, FALSE );
1231
1232     p_group = NULL;
1233
1234     /* menu container */
1235     p_menu = gtk_menu_new();
1236     gtk_object_set_data( GTK_OBJECT( p_menu ), "p_intf", p_intf );
1237
1238     /* special case for "off" item */
1239     p_item = gtk_radio_menu_item_new_with_label( p_group, "None" );
1240     p_group = gtk_radio_menu_item_group( GTK_RADIO_MENU_ITEM( p_item ) );
1241
1242     gtk_widget_show( p_item );
1243
1244     /* signal hanling for off */
1245     gtk_signal_connect( GTK_OBJECT( p_item ), "toggled",
1246                         GTK_SIGNAL_FUNC ( pf_toggle ), NULL );
1247
1248     gtk_menu_append( GTK_MENU( p_menu ), p_item );
1249
1250     p_separator = gtk_menu_item_new();
1251     gtk_widget_set_sensitive( p_separator, FALSE );
1252     gtk_widget_show( p_separator );
1253     gtk_menu_append( GTK_MENU( p_menu ), p_separator );
1254
1255
1256     /* search actual deinterlace mode */
1257     psz_filter = config_GetPsz( p_intf, "filter" );
1258     psz_deinterlace_option = strdup( "None" );
1259
1260     if( psz_filter && *psz_filter )
1261     {
1262        if( strstr ( psz_filter, "deinterlace" ) )
1263        {
1264             vlc_value_t val;
1265             vout_thread_t *p_vout;
1266
1267             p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT,
1268                                       FIND_ANYWHERE );
1269             if( p_vout &&
1270                 var_Get( p_vout, "deinterlace-mode", &val ) == VLC_SUCCESS )
1271             {
1272                 if( val.psz_string && *val.psz_string )
1273                 {
1274                     free( psz_deinterlace_option );
1275                     psz_deinterlace_option = val.psz_string;
1276                 }
1277                 else if( val.psz_string ) free( val.psz_string );
1278             }
1279
1280             if( p_vout ) vlc_object_release( p_vout );
1281        }
1282     }
1283     if( psz_filter )
1284         free( psz_filter );
1285
1286     p_item_active = NULL;
1287     i_item = 0;
1288
1289     /* create a set of deinteralce buttons and append them to the container */
1290     for( i = 0; ppsz_deinterlace_mode[i] != NULL; i++ )
1291     {
1292         i_item++;
1293
1294         p_item = gtk_radio_menu_item_new_with_label( p_group, ppsz_deinterlace_mode[i] );
1295         p_group = gtk_radio_menu_item_group( GTK_RADIO_MENU_ITEM( p_item ) );
1296         gtk_widget_show( p_item );
1297
1298         if( !strcmp( ppsz_deinterlace_mode[i], psz_deinterlace_option ) )
1299         {
1300             p_item_active = p_item;
1301         }
1302         /* setup signal hanling */
1303         gtk_signal_connect( GTK_OBJECT( p_item ), "toggled",
1304                             GTK_SIGNAL_FUNC( pf_toggle ),
1305                             NULL );
1306
1307         gtk_menu_append( GTK_MENU( p_menu ), p_item );
1308
1309     }
1310
1311     /* link the new menu to the menubar item */
1312     gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_root ), p_menu );
1313
1314     /* acitvation will call signals so we can only do it
1315      * when submenu is attached to menu - to get intf_window
1316      * We have to release the lock since input_ToggleES needs it */
1317     if( p_item_active != NULL )
1318     {
1319         gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM( p_item_active ),
1320                                         TRUE );
1321     }
1322
1323     /* be sure that menu is sensitive if non empty */
1324     if( i_item > 0 )
1325     {
1326         gtk_widget_set_sensitive( p_root, TRUE );
1327     }
1328
1329     return TRUE;
1330 }
1331
1332
1333
1334 /*****************************************************************************
1335  * GtkSetupMenus: function that generates title/chapter/audio/subpic
1336  * menus with help from preceding functions
1337  *****************************************************************************
1338  * Function called with the lock on stream
1339  *****************************************************************************/
1340 gint GtkSetupMenus( intf_thread_t * p_intf )
1341 {
1342     es_descriptor_t *   p_audio_es;
1343     es_descriptor_t *   p_spu_es;
1344     GtkWidget *         p_menubar_menu;
1345     GtkWidget *         p_popup_menu;
1346     guint               i;
1347
1348     p_intf->p_sys->b_chapter_update |= p_intf->p_sys->b_title_update;
1349     p_intf->p_sys->b_audio_update |= p_intf->p_sys->b_title_update |
1350                                      p_intf->p_sys->b_program_update;
1351     p_intf->p_sys->b_spu_update |= p_intf->p_sys->b_title_update |
1352                                    p_intf->p_sys->b_program_update;
1353
1354     if( 1 )
1355     {
1356         p_menubar_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
1357                                      p_intf->p_sys->p_window ), "menubar_deinterlace" ) );
1358         p_popup_menu   = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
1359                                      p_intf->p_sys->p_popup ), "popup_deinterlace" ) );
1360
1361         p_intf->p_sys->b_deinterlace_update = VLC_TRUE;
1362         GtkDeinterlaceMenus( p_intf, p_menubar_menu, GtkMenubarDeinterlaceToggle );
1363         p_intf->p_sys->b_deinterlace_update = VLC_TRUE;
1364         GtkDeinterlaceMenus( p_intf, p_popup_menu, GtkPopupDeinterlaceToggle );
1365
1366         p_intf->p_sys->b_deinterlace_update = VLC_FALSE;
1367     }
1368
1369     if( p_intf->p_sys->b_program_update )
1370     {
1371         pgrm_descriptor_t * p_pgrm;
1372
1373         if( p_intf->p_sys->p_input->stream.p_new_program )
1374         {
1375             p_pgrm = p_intf->p_sys->p_input->stream.p_new_program;
1376         }
1377         else
1378         {
1379             p_pgrm = p_intf->p_sys->p_input->stream.p_selected_program;
1380         }
1381
1382         p_menubar_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
1383                             p_intf->p_sys->p_window ), "menubar_program" ) );
1384         GtkProgramMenu( p_intf, p_menubar_menu, p_pgrm,
1385                         GtkMenubarProgramToggle );
1386
1387         p_intf->p_sys->b_program_update = VLC_TRUE;
1388         p_popup_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
1389                             p_intf->p_sys->p_popup ), "popup_program" ) );
1390         GtkProgramMenu( p_intf, p_popup_menu, p_pgrm,
1391                         GtkPopupProgramToggle );
1392
1393         p_intf->p_sys->b_program_update = VLC_FALSE;
1394     }
1395
1396     if( p_intf->p_sys->b_title_update )
1397     {
1398         char            psz_title[5];
1399
1400         p_menubar_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
1401                             p_intf->p_sys->p_window ), "menubar_title" ) );
1402         GtkRadioMenu( p_intf, p_menubar_menu, NULL, _("Title"), 1,
1403                       p_intf->p_sys->p_input->stream.i_area_nb - 1,
1404                       p_intf->p_sys->p_input->stream.p_selected_area->i_id,
1405                       GtkMenubarTitleToggle );
1406
1407         snprintf( psz_title, 4, "%d",
1408                   p_intf->p_sys->p_input->stream.p_selected_area->i_id );
1409         psz_title[ 4 ] = '\0';
1410         gtk_label_set_text( p_intf->p_sys->p_label_title, psz_title );
1411
1412         p_intf->p_sys->b_title_update = VLC_FALSE;
1413     }
1414
1415     if( p_intf->p_sys->b_chapter_update )
1416     {
1417         char            psz_chapter[5];
1418
1419         p_popup_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
1420                              p_intf->p_sys->p_popup ), "popup_navigation" ) );
1421         GtkTitleMenu( p_intf, p_popup_menu, GtkPopupNavigationToggle );
1422 #if 0
1423         GtkRadioMenu( p_intf, p_menubar_menu, NULL, _("Title"), 1,
1424                         p_intf->p_sys->p_input->stream.i_area_nb - 1,
1425                         p_intf->p_sys->p_input->stream.p_selected_area->i_id,
1426                         on_menubar_chapter_toggle );
1427 #endif
1428
1429         p_menubar_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
1430                              p_intf->p_sys->p_window ), "menubar_chapter" ) );
1431
1432         GtkRadioMenu( p_intf, p_menubar_menu, NULL, _("Chapter"), 1,
1433                         p_intf->p_sys->p_input->stream.p_selected_area->i_part_nb - 1,
1434                         p_intf->p_sys->p_input->stream.p_selected_area->i_part,
1435                         GtkMenubarChapterToggle );
1436
1437
1438         snprintf( psz_chapter, 4, "%d",
1439                   p_intf->p_sys->p_input->stream.p_selected_area->i_part );
1440         psz_chapter[ 4 ] = '\0';
1441         gtk_label_set_text( p_intf->p_sys->p_label_chapter, psz_chapter );
1442
1443         p_intf->p_sys->i_part =
1444                 p_intf->p_sys->p_input->stream.p_selected_area->i_part;
1445
1446         p_intf->p_sys->b_chapter_update = VLC_FALSE;
1447     }
1448
1449     /* look for selected ES */
1450     p_audio_es = NULL;
1451     p_spu_es = NULL;
1452
1453     for( i = 0 ; i < p_intf->p_sys->p_input->stream.i_selected_es_number ; i++ )
1454     {
1455         if( p_intf->p_sys->p_input->stream.pp_selected_es[i]->i_cat == AUDIO_ES )
1456         {
1457             p_audio_es = p_intf->p_sys->p_input->stream.pp_selected_es[i];
1458         }
1459
1460         if( p_intf->p_sys->p_input->stream.pp_selected_es[i]->i_cat == SPU_ES )
1461         {
1462             p_spu_es = p_intf->p_sys->p_input->stream.pp_selected_es[i];
1463         }
1464     }
1465
1466     vlc_mutex_unlock( &p_intf->p_sys->p_input->stream.stream_lock );
1467
1468     /* audio menus */
1469     if( p_intf->p_sys->b_audio_update )
1470     {
1471         /* find audio root menu */
1472         p_menubar_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
1473                              p_intf->p_sys->p_window ), "menubar_audio" ) );
1474
1475         p_popup_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
1476                      p_intf->p_sys->p_popup ), "popup_language" ) );
1477
1478         p_intf->p_sys->b_audio_update = VLC_TRUE;
1479         GtkLanguageMenus( p_intf, p_menubar_menu, p_audio_es, AUDIO_ES,
1480                             GtkMenubarAudioToggle );
1481         p_intf->p_sys->b_audio_update = VLC_TRUE;
1482         GtkLanguageMenus( p_intf, p_popup_menu, p_audio_es, AUDIO_ES,
1483                             GtkPopupAudioToggle );
1484
1485         p_intf->p_sys->b_audio_update = VLC_FALSE;
1486     }
1487
1488     /* sub picture menus */
1489     if( p_intf->p_sys->b_spu_update )
1490     {
1491         /* find spu root menu */
1492         p_menubar_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
1493                           p_intf->p_sys->p_window ), "menubar_subpictures" ) );
1494
1495         p_popup_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
1496                      p_intf->p_sys->p_popup ), "popup_subpictures" ) );
1497
1498         p_intf->p_sys->b_spu_update = VLC_TRUE;
1499         GtkLanguageMenus( p_intf, p_menubar_menu, p_spu_es, SPU_ES,
1500                             GtkMenubarSubtitleToggle  );
1501         p_intf->p_sys->b_spu_update = VLC_TRUE;
1502         GtkLanguageMenus( p_intf, p_popup_menu, p_spu_es, SPU_ES,
1503                             GtkPopupSubtitleToggle );
1504
1505         p_intf->p_sys->b_spu_update = VLC_FALSE;
1506     }
1507     /* create audio channels and device menu (in menubar _and_ popup */
1508     if( p_intf->p_sys->b_aout_update )
1509     {
1510         aout_instance_t *p_aout;
1511
1512         p_aout = (aout_instance_t*)vlc_object_find( p_intf, VLC_OBJECT_AOUT, FIND_ANYWHERE );
1513
1514         if( p_aout != NULL )
1515         {
1516             vlc_value_t val;
1517             val.b_bool = VLC_FALSE;
1518
1519             var_Set( (vlc_object_t *)p_aout, "intf-change", val );
1520
1521             /* audio-channels */
1522             p_menubar_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
1523                               p_intf->p_sys->p_window ), "menubar_audio_channels" ) );
1524             p_popup_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
1525                          p_intf->p_sys->p_popup ), "popup_audio_channels" ) );
1526             GtkSetupVarMenu( p_intf, (vlc_object_t *)p_aout, p_popup_menu,
1527                               "audio-channels",  GtkPopupAoutChannelsToggle );
1528             GtkSetupVarMenu( p_intf, (vlc_object_t *)p_aout, p_menubar_menu,
1529                               "audio-channels",  GtkPopupAoutChannelsToggle );
1530
1531             /* audio-device */
1532             p_menubar_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
1533                               p_intf->p_sys->p_window ), "menubar_audio_device" ) );
1534             p_popup_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
1535                          p_intf->p_sys->p_popup ), "popup_audio_device" ) );
1536             GtkSetupVarMenu( p_intf, (vlc_object_t *)p_aout, p_popup_menu,
1537                               "audio-device",  GtkPopupAoutDeviceToggle );
1538             GtkSetupVarMenu( p_intf, (vlc_object_t *)p_aout, p_menubar_menu,
1539                               "audio-device",  GtkPopupAoutDeviceToggle );
1540
1541             vlc_object_release( (vlc_object_t *)p_aout );
1542         }
1543         p_intf->p_sys->b_aout_update = VLC_FALSE;
1544     }
1545
1546     if( p_intf->p_sys->b_vout_update )
1547     {
1548         vout_thread_t *p_vout;
1549
1550         p_vout = (vout_thread_t*)vlc_object_find( p_intf, VLC_OBJECT_VOUT, FIND_ANYWHERE );
1551
1552         if( p_vout != NULL )
1553         {
1554             vlc_value_t val;
1555             val.b_bool = VLC_FALSE;
1556
1557             var_Set( (vlc_object_t *)p_vout, "intf-change", val );
1558
1559             /* video-device */
1560             p_menubar_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
1561                               p_intf->p_sys->p_window ), "menubar_video_device" ) );
1562             p_popup_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
1563                          p_intf->p_sys->p_popup ), "popup_video_device" ) );
1564             GtkSetupVarMenu( p_intf, (vlc_object_t *)p_vout, p_popup_menu,
1565                               "video-device",  GtkPopupVoutDeviceToggle );
1566             GtkSetupVarMenu( p_intf, (vlc_object_t *)p_vout, p_menubar_menu,
1567                               "video-device",  GtkPopupVoutDeviceToggle );
1568
1569
1570             vlc_object_release( (vlc_object_t *)p_vout );
1571         }
1572         p_intf->p_sys->b_vout_update = VLC_FALSE;
1573     }
1574     vlc_mutex_lock( &p_intf->p_sys->p_input->stream.stream_lock );
1575
1576     return TRUE;
1577 }
1578