]> git.sesse.net Git - vlc/blob - modules/gui/gtk/menu.c
* src/misc/variables.c, ALL: improvements to the object variables api.
[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.9 2003/05/04 22:42:15 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             strcpy( psz_name,
768                     p_intf->p_sys->p_input->stream.pp_es[i]->psz_desc );
769             if( psz_name[0] == '\0' )
770             {
771                 snprintf( psz_name, GTK_MENU_LABEL_SIZE,
772                           "Language %d", i_item );
773                 psz_name[ GTK_MENU_LABEL_SIZE - 1 ] = '\0';
774             }
775
776             p_item = gtk_radio_menu_item_new_with_label( p_group, psz_name );
777             p_group =
778                 gtk_radio_menu_item_group( GTK_RADIO_MENU_ITEM( p_item ) );
779
780             if( p_es == p_intf->p_sys->p_input->stream.pp_es[i] )
781             {
782                 /* don't lose p_item when we append into menu */
783                 p_item_active = p_item;
784             }
785
786             gtk_widget_show( p_item );
787
788             /* setup signal hanling */
789             gtk_signal_connect( GTK_OBJECT( p_item ), "toggled",
790                             GTK_SIGNAL_FUNC( pf_toggle ),
791                             (gpointer)( p_intf->p_sys->p_input->stream.pp_es[i] ) );
792
793             gtk_menu_append( GTK_MENU( p_menu ), p_item );
794         }
795     }
796
797     vlc_mutex_unlock( &p_intf->p_sys->p_input->stream.stream_lock );
798
799     /* link the new menu to the menubar item */
800     gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_root ), p_menu );
801
802     /* acitvation will call signals so we can only do it
803      * when submenu is attached to menu - to get intf_window
804      * We have to release the lock since input_ToggleES needs it */
805     if( p_item_active != NULL )
806     {
807         gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM( p_item_active ),
808                                         TRUE );
809     }
810
811     /* be sure that menu is sensitive if non empty */
812     if( i_item > 0 )
813     {
814         gtk_widget_set_sensitive( p_root, TRUE );
815     }
816
817     return TRUE;
818 }
819
820 /*****************************************************************************
821  * GtkTitleMenu: sets menus for titles and chapters selection
822  *****************************************************************************
823  * Generates two types of menus:
824  *  -simple list of titles
825  *  -cascaded lists of chapters for each title
826  *****************************************************************************/
827 static gint GtkTitleMenu( gpointer       p_data,
828                           GtkWidget *    p_navigation,
829                           void(*pf_toggle )( GtkCheckMenuItem *, gpointer ) )
830 {
831     intf_thread_t *     p_intf;
832     char                psz_name[ GTK_MENU_LABEL_SIZE ];
833     GtkWidget *         p_title_menu;
834     GtkWidget *         p_title_submenu;
835     GtkWidget *         p_title_item;
836     GtkWidget *         p_item_active;
837     GtkWidget *         p_chapter_menu;
838     GtkWidget *         p_chapter_submenu;
839     GtkWidget *         p_title_menu_item;
840     GtkWidget *         p_chapter_menu_item;
841     GtkWidget *         p_item;
842     GSList *            p_title_group;
843     GSList *            p_chapter_group;
844     guint               i_title;
845     guint               i_chapter;
846     guint               i_title_nb;
847     guint               i_chapter_nb;
848
849     /* cast */
850     p_intf = (intf_thread_t*)p_data;
851
852     /* temporary hack to avoid blank menu when an open menu is removed */
853     if( GTK_MENU_ITEM(p_navigation)->submenu != NULL )
854     {
855         gtk_menu_popdown( GTK_MENU( GTK_MENU_ITEM(p_navigation)->submenu ) );
856     }
857     /* removes previous menu */
858     gtk_menu_item_remove_submenu( GTK_MENU_ITEM( p_navigation ) );
859     gtk_widget_set_sensitive( p_navigation, FALSE );
860
861     p_title_menu = gtk_menu_new();
862     p_title_group = NULL;
863     p_title_submenu = NULL;
864     p_title_menu_item = NULL;
865     p_chapter_group = NULL;
866     p_chapter_submenu = NULL;
867     p_chapter_menu_item = NULL;
868     p_item_active = NULL;
869     i_title_nb = p_intf->p_sys->p_input->stream.i_area_nb - 1;
870
871     gtk_object_set_data( GTK_OBJECT( p_title_menu ), "p_intf", p_intf );
872
873     /* loop on titles */
874     for( i_title = 1 ; i_title <= i_title_nb ; i_title++ )
875     {
876         /* we group titles in packets of ten for small screens */
877         if( ( i_title % 10 == 1 ) && ( i_title_nb > 20 ) )
878         {
879             if( i_title != 1 )
880             {
881                 gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_title_menu_item ),
882                                            p_title_submenu );
883                 gtk_menu_append( GTK_MENU( p_title_menu ), p_title_menu_item );
884             }
885
886             snprintf( psz_name, GTK_MENU_LABEL_SIZE,
887                       "%d - %d", i_title, i_title + 9 );
888             psz_name[ GTK_MENU_LABEL_SIZE - 1 ] = '\0';
889             p_title_menu_item = gtk_menu_item_new_with_label( psz_name );
890             gtk_widget_show( p_title_menu_item );
891             p_title_submenu = gtk_menu_new();
892             gtk_object_set_data( GTK_OBJECT( p_title_submenu ),
893                                  "p_intf", p_intf );
894         }
895
896         snprintf( psz_name, GTK_MENU_LABEL_SIZE, _("Title %d (%d)"), i_title,
897                   p_intf->p_sys->p_input->stream.pp_areas[i_title]->i_part_nb - 1);
898         psz_name[ GTK_MENU_LABEL_SIZE - 1 ] = '\0';
899 #if 0
900         if( pf_toggle == on_menubar_title_toggle )
901         {
902             p_title_item = gtk_radio_menu_item_new_with_label( p_title_group,
903                                                            psz_name );
904             p_title_group =
905               gtk_radio_menu_item_group( GTK_RADIO_MENU_ITEM( p_title_item ) );
906
907             if( p_intf->p_sys->p_input->stream.pp_areas[i_title] ==
908                          p_intf->p_sys->p_input->stream.p_selected_area )
909             {
910                 p_item_active = p_title_item;
911             }
912
913             /* setup signal hanling */
914             gtk_signal_connect( GTK_OBJECT( p_title_item ),
915                      "toggled",
916                      GTK_SIGNAL_FUNC( pf_toggle ),
917                      (gpointer)(p_intf->p_sys->p_input->stream.pp_areas[i_title]) );
918
919             if( p_intf->p_sys->p_input->stream.i_area_nb > 1 )
920             {
921                 /* be sure that menu is sensitive */
922                 gtk_widget_set_sensitive( p_navigation, TRUE );
923             }
924         }
925         else
926 #endif
927         {
928             p_title_item = gtk_menu_item_new_with_label( psz_name );
929
930 #if 1
931             p_chapter_menu = gtk_menu_new();
932             gtk_object_set_data( GTK_OBJECT( p_chapter_menu ),
933                                  "p_intf", p_intf );
934             i_chapter_nb =
935                p_intf->p_sys->p_input->stream.pp_areas[i_title]->i_part_nb - 1;
936
937             for( i_chapter = 1 ; i_chapter <= i_chapter_nb ; i_chapter++ )
938             {
939                 /* we group chapters in packets of ten for small screens */
940                 if( ( i_chapter % 10 == 1 ) && ( i_chapter_nb > 20 ) )
941                 {
942                     if( i_chapter != 1 )
943                     {
944                         gtk_menu_item_set_submenu(
945                                     GTK_MENU_ITEM( p_chapter_menu_item ),
946                                     p_chapter_submenu );
947                         gtk_menu_append( GTK_MENU( p_chapter_menu ),
948                                          p_chapter_menu_item );
949                     }
950
951                     snprintf( psz_name, GTK_MENU_LABEL_SIZE,
952                               "%d - %d", i_chapter, i_chapter + 9 );
953                     psz_name[ GTK_MENU_LABEL_SIZE - 1 ] = '\0';
954                     p_chapter_menu_item =
955                             gtk_menu_item_new_with_label( psz_name );
956                     gtk_widget_show( p_chapter_menu_item );
957                     p_chapter_submenu = gtk_menu_new();
958                     gtk_object_set_data( GTK_OBJECT( p_chapter_submenu ),
959                                          "p_intf", p_intf );
960                 }
961
962                 snprintf( psz_name, GTK_MENU_LABEL_SIZE,
963                           _("Chapter %d"), i_chapter );
964                 psz_name[ GTK_MENU_LABEL_SIZE - 1 ] = '\0';
965
966                 p_item = gtk_radio_menu_item_new_with_label(
967                                                 p_chapter_group, psz_name );
968                 p_chapter_group = gtk_radio_menu_item_group(
969                                                 GTK_RADIO_MENU_ITEM( p_item ) );
970                 gtk_widget_show( p_item );
971
972 #define p_area p_intf->p_sys->p_input->stream.pp_areas[i_title]
973                 if( ( p_area ==
974                         p_intf->p_sys->p_input->stream.p_selected_area ) &&
975                     ( p_area->i_part == i_chapter ) )
976                 {
977                     p_item_active = p_item;
978                 }
979 #undef p_area
980
981                 /* setup signal hanling */
982                 gtk_signal_connect( GTK_OBJECT( p_item ),
983                            "toggled",
984                            GTK_SIGNAL_FUNC( pf_toggle ),
985                            (gpointer)POS2DATA( i_title, i_chapter ) );
986
987                 if( i_chapter_nb > 20 )
988                 {
989                     gtk_menu_append( GTK_MENU( p_chapter_submenu ), p_item );
990                 }
991                 else
992                 {
993                     gtk_menu_append( GTK_MENU( p_chapter_menu ), p_item );
994                 }
995             }
996
997             if( i_chapter_nb > 20 )
998             {
999                 gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_chapter_menu_item ),
1000                                            p_chapter_submenu );
1001                 gtk_menu_append( GTK_MENU( p_chapter_menu ),
1002                                  p_chapter_menu_item );
1003             }
1004
1005             /* link the new menu to the title menu item */
1006             gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_title_item ),
1007                                        p_chapter_menu );
1008
1009             if( p_intf->p_sys->p_input->stream.pp_areas[i_title]->i_part_nb > 1 )
1010             {
1011                 /* be sure that menu is sensitive */
1012                 gtk_widget_set_sensitive( p_navigation, TRUE );
1013             }
1014 #else
1015             GtkRadioMenu( p_intf, p_title_item, p_chapter_group, _("Chapter"),
1016                 p_intf->p_sys->p_input->stream.pp_areas[i_title]->i_part_nb - 1,
1017                 1, i_title * 100,
1018                 p_intf->p_sys->p_input->stream.p_selected_area->i_part +
1019                 p_intf->p_sys->p_input->stream.p_selected_area->i_id *100,
1020                 pf_toggle );
1021
1022 #endif
1023         }
1024         gtk_widget_show( p_title_item );
1025
1026         if( i_title_nb > 20 )
1027         {
1028             gtk_menu_append( GTK_MENU( p_title_submenu ), p_title_item );
1029         }
1030         else
1031         {
1032             gtk_menu_append( GTK_MENU( p_title_menu ), p_title_item );
1033         }
1034     }
1035
1036     if( i_title_nb > 20 )
1037     {
1038         gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_title_menu_item ),
1039                                    p_title_submenu );
1040         gtk_menu_append( GTK_MENU( p_title_menu ), p_title_menu_item );
1041     }
1042
1043     /* be sure that menu is sensitive */
1044     gtk_widget_set_sensitive( p_title_menu, TRUE );
1045
1046     /* link the new menu to the menubar item */
1047     gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_navigation ), p_title_menu );
1048
1049     /* Default selected chapter
1050      * We have to release the lock since input_ToggleES needs it */
1051     if( p_item_active != NULL )
1052     {
1053         gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM( p_item_active ),
1054                                         TRUE );
1055     }
1056 #if 0
1057     if( p_intf->p_sys->p_input->stream.i_area_nb > 1 )
1058     {
1059         /* be sure that menu is sensitive */
1060         gtk_widget_set_sensitive( p_navigation, TRUE );
1061     }
1062 #endif
1063
1064     return TRUE;
1065 }
1066
1067 /*****************************************************************************
1068  * GtkSetupVarMenu :
1069  *****************************************************************************
1070  *
1071  *****************************************************************************/
1072 static gint GtkSetupVarMenu( intf_thread_t * p_intf,
1073                              vlc_object_t * p_object,
1074                              GtkWidget *p_root,
1075                              char * psz_variable,
1076                              void(*pf_toggle )( GtkCheckMenuItem *, gpointer ) )
1077 {
1078     vlc_value_t         val, text, val_list, text_list;
1079     GtkWidget *         p_menu;
1080     GSList *            p_group = NULL;
1081     GtkWidget *         p_item;
1082     GtkWidget *         p_item_active = NULL;
1083
1084     int                 i_item, i_type;
1085
1086      /* temporary hack to avoid blank menu when an open menu is removed */
1087     if( GTK_MENU_ITEM(p_root)->submenu != NULL )
1088     {
1089         gtk_menu_popdown( GTK_MENU( GTK_MENU_ITEM(p_root)->submenu ) );
1090     }
1091     /* removes previous menu */
1092     gtk_menu_item_remove_submenu( GTK_MENU_ITEM( p_root ) );
1093     gtk_widget_set_sensitive( p_root, FALSE );
1094
1095     /* Check the type of the object variable */
1096     i_type = var_Type( p_object, psz_variable );
1097
1098     /* Make sure we want to display the variable */
1099     if( i_type & VLC_VAR_HASCHOICE )
1100     {
1101         var_Change( p_object, psz_variable, VLC_VAR_CHOICESCOUNT, &val, NULL );
1102         if( val.i_int == 0 ) return FALSE;
1103     }
1104
1105     /* Get the descriptive name of the variable */
1106     var_Change( p_object, psz_variable, VLC_VAR_GETTEXT, &text, NULL );
1107
1108     /* get the current value */
1109     if( var_Get( p_object, psz_variable, &val ) < 0 )
1110     {
1111         return FALSE;
1112     }
1113
1114     if( var_Change( p_object, psz_variable, VLC_VAR_GETLIST,
1115                     &val_list, &text_list ) < 0 )
1116     {
1117         if( i_type == VLC_VAR_STRING ) free( val.psz_string );
1118         return FALSE;
1119     }
1120
1121     /* menu container */
1122     p_menu = gtk_menu_new();
1123     gtk_object_set_data( GTK_OBJECT( p_menu ), "p_intf", p_intf );
1124
1125     for( i_item = 0; i_item < val_list.p_list->i_count; i_item++ )
1126     {
1127         switch( i_type & VLC_VAR_TYPE )
1128         {
1129         case VLC_VAR_STRING:
1130             p_item = gtk_radio_menu_item_new_with_label( p_group,
1131                      text_list.p_list->p_values[i_item].psz_string ?
1132                      text_list.p_list->p_values[i_item].psz_string :
1133                      val_list.p_list->p_values[i_item].psz_string );
1134
1135             /* signal hanling for off */
1136             gtk_signal_connect( GTK_OBJECT( p_item ), "toggled",
1137                 GTK_SIGNAL_FUNC ( pf_toggle ),
1138                 /* FIXME memory leak */
1139                 strdup(val_list.p_list->p_values[i_item].psz_string) );
1140
1141             if( !strcmp( val.psz_string,
1142                          val_list.p_list->p_values[i_item].psz_string ) )
1143             {
1144                 p_item_active = p_item;
1145             }
1146             break;
1147         case VLC_VAR_INTEGER:
1148             p_item = gtk_radio_menu_item_new_with_label( p_group,
1149                      text_list.p_list->p_values[i_item].psz_string ?
1150                      text_list.p_list->p_values[i_item].psz_string :
1151                      NULL /* FIXME */ );
1152
1153             /* signal hanling for off */
1154             gtk_signal_connect( GTK_OBJECT( p_item ), "toggled",
1155                 GTK_SIGNAL_FUNC ( pf_toggle ),
1156                 (gpointer)val_list.p_list->p_values[i_item].i_int );
1157
1158             if( val.i_int == val_list.p_list->p_values[i_item].i_int )
1159             {
1160                 p_item_active = p_item;
1161             }
1162             break;
1163         default:
1164             /* FIXME */
1165             return FALSE;
1166         }
1167
1168         p_group = gtk_radio_menu_item_group( GTK_RADIO_MENU_ITEM( p_item ) );
1169
1170         gtk_widget_show( p_item );
1171
1172         gtk_menu_append( GTK_MENU( p_menu ), p_item );
1173     }
1174
1175     /* link the new menu to the menubar item */
1176     gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_root ), p_menu );
1177
1178     if( p_item_active )
1179     {
1180         gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(p_item_active),
1181                                         TRUE );
1182     }
1183
1184     if( val_list.p_list->i_count > 0 )
1185     {
1186         gtk_widget_set_sensitive( p_root, TRUE );
1187     }
1188
1189     /* clean up everything */
1190     if( i_type == VLC_VAR_STRING ) free( val.psz_string );
1191     var_Change( p_object, psz_variable, VLC_VAR_FREELIST,
1192                 &val_list, &text_list );
1193
1194     return TRUE;
1195 }
1196
1197 /*****************************************************************************
1198  * GtkDeinterlaceMenus: update interactive menus of the interface
1199  *****************************************************************************
1200  *****************************************************************************/
1201 static gint GtkDeinterlaceMenus( gpointer          p_data,
1202                                  GtkWidget *       p_root,
1203                                  void(*pf_toggle )( GtkCheckMenuItem *, gpointer ) )
1204 {
1205     intf_thread_t *     p_intf;
1206     GtkWidget *         p_menu;
1207     GtkWidget *         p_separator;
1208     GtkWidget *         p_item;
1209     GtkWidget *         p_item_active;
1210     GSList *            p_group;
1211     guint               i_item;
1212     guint               i;
1213     char                *ppsz_deinterlace_mode[] = { "discard", "blend", "mean", "bob", "linear", NULL };
1214     char                *psz_deinterlace_option;
1215     char                *psz_filter;
1216
1217     p_intf = (intf_thread_t *)p_data;
1218
1219     /* temporary hack to avoid blank menu when an open menu is removed */
1220     if( GTK_MENU_ITEM(p_root)->submenu != NULL )
1221     {
1222         gtk_menu_popdown( GTK_MENU( GTK_MENU_ITEM(p_root)->submenu ) );
1223     }
1224     /* removes previous menu */
1225     gtk_menu_item_remove_submenu( GTK_MENU_ITEM( p_root ) );
1226     gtk_widget_set_sensitive( p_root, FALSE );
1227
1228     p_group = NULL;
1229
1230     /* menu container */
1231     p_menu = gtk_menu_new();
1232     gtk_object_set_data( GTK_OBJECT( p_menu ), "p_intf", p_intf );
1233
1234     /* special case for "off" item */
1235     p_item = gtk_radio_menu_item_new_with_label( p_group, "None" );
1236     p_group = gtk_radio_menu_item_group( GTK_RADIO_MENU_ITEM( p_item ) );
1237
1238     gtk_widget_show( p_item );
1239
1240     /* signal hanling for off */
1241     gtk_signal_connect( GTK_OBJECT( p_item ), "toggled",
1242                         GTK_SIGNAL_FUNC ( pf_toggle ), NULL );
1243
1244     gtk_menu_append( GTK_MENU( p_menu ), p_item );
1245
1246     p_separator = gtk_menu_item_new();
1247     gtk_widget_set_sensitive( p_separator, FALSE );
1248     gtk_widget_show( p_separator );
1249     gtk_menu_append( GTK_MENU( p_menu ), p_separator );
1250
1251
1252     /* search actual deinterlace mode */
1253     psz_filter = config_GetPsz( p_intf, "filter" );
1254     psz_deinterlace_option = strdup( "None" );
1255
1256     if( psz_filter && *psz_filter )
1257     {
1258        if( strstr ( psz_filter, "deinterlace" ) )
1259        {
1260             vlc_value_t val;
1261             vout_thread_t *p_vout;
1262
1263             p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT,
1264                                       FIND_ANYWHERE );
1265             if( p_vout &&
1266                 var_Get( p_vout, "deinterlace-mode", &val ) == VLC_SUCCESS )
1267             {
1268                 if( val.psz_string && *val.psz_string )
1269                 {
1270                     free( psz_deinterlace_option );
1271                     psz_deinterlace_option = val.psz_string;
1272                 }
1273                 else if( val.psz_string ) free( val.psz_string );
1274             }
1275
1276             if( p_vout ) vlc_object_release( p_vout );
1277        }
1278     }
1279     if( psz_filter )
1280         free( psz_filter );
1281
1282     p_item_active = NULL;
1283     i_item = 0;
1284
1285     /* create a set of deinteralce buttons and append them to the container */
1286     for( i = 0; ppsz_deinterlace_mode[i] != NULL; i++ )
1287     {
1288         i_item++;
1289
1290         p_item = gtk_radio_menu_item_new_with_label( p_group, ppsz_deinterlace_mode[i] );
1291         p_group = gtk_radio_menu_item_group( GTK_RADIO_MENU_ITEM( p_item ) );
1292         gtk_widget_show( p_item );
1293
1294         if( !strcmp( ppsz_deinterlace_mode[i], psz_deinterlace_option ) )
1295         {
1296             p_item_active = p_item;
1297         }
1298         /* setup signal hanling */
1299         gtk_signal_connect( GTK_OBJECT( p_item ), "toggled",
1300                             GTK_SIGNAL_FUNC( pf_toggle ),
1301                             NULL );
1302
1303         gtk_menu_append( GTK_MENU( p_menu ), p_item );
1304
1305     }
1306
1307     /* link the new menu to the menubar item */
1308     gtk_menu_item_set_submenu( GTK_MENU_ITEM( p_root ), p_menu );
1309
1310     /* acitvation will call signals so we can only do it
1311      * when submenu is attached to menu - to get intf_window
1312      * We have to release the lock since input_ToggleES needs it */
1313     if( p_item_active != NULL )
1314     {
1315         gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM( p_item_active ),
1316                                         TRUE );
1317     }
1318
1319     /* be sure that menu is sensitive if non empty */
1320     if( i_item > 0 )
1321     {
1322         gtk_widget_set_sensitive( p_root, TRUE );
1323     }
1324
1325     return TRUE;
1326 }
1327
1328
1329
1330 /*****************************************************************************
1331  * GtkSetupMenus: function that generates title/chapter/audio/subpic
1332  * menus with help from preceding functions
1333  *****************************************************************************
1334  * Function called with the lock on stream
1335  *****************************************************************************/
1336 gint GtkSetupMenus( intf_thread_t * p_intf )
1337 {
1338     es_descriptor_t *   p_audio_es;
1339     es_descriptor_t *   p_spu_es;
1340     GtkWidget *         p_menubar_menu;
1341     GtkWidget *         p_popup_menu;
1342     guint               i;
1343
1344     p_intf->p_sys->b_chapter_update |= p_intf->p_sys->b_title_update;
1345     p_intf->p_sys->b_audio_update |= p_intf->p_sys->b_title_update |
1346                                      p_intf->p_sys->b_program_update;
1347     p_intf->p_sys->b_spu_update |= p_intf->p_sys->b_title_update |
1348                                    p_intf->p_sys->b_program_update;
1349
1350     if( 1 )
1351     {
1352         p_menubar_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
1353                                      p_intf->p_sys->p_window ), "menubar_deinterlace" ) );
1354         p_popup_menu   = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
1355                                      p_intf->p_sys->p_popup ), "popup_deinterlace" ) );
1356
1357         p_intf->p_sys->b_deinterlace_update = VLC_TRUE;
1358         GtkDeinterlaceMenus( p_intf, p_menubar_menu, GtkMenubarDeinterlaceToggle );
1359         p_intf->p_sys->b_deinterlace_update = VLC_TRUE;
1360         GtkDeinterlaceMenus( p_intf, p_popup_menu, GtkPopupDeinterlaceToggle );
1361
1362         p_intf->p_sys->b_deinterlace_update = VLC_FALSE;
1363     }
1364
1365     if( p_intf->p_sys->b_program_update )
1366     {
1367         pgrm_descriptor_t * p_pgrm;
1368
1369         if( p_intf->p_sys->p_input->stream.p_new_program )
1370         {
1371             p_pgrm = p_intf->p_sys->p_input->stream.p_new_program;
1372         }
1373         else
1374         {
1375             p_pgrm = p_intf->p_sys->p_input->stream.p_selected_program;
1376         }
1377
1378         p_menubar_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
1379                             p_intf->p_sys->p_window ), "menubar_program" ) );
1380         GtkProgramMenu( p_intf, p_menubar_menu, p_pgrm,
1381                         GtkMenubarProgramToggle );
1382
1383         p_intf->p_sys->b_program_update = VLC_TRUE;
1384         p_popup_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
1385                             p_intf->p_sys->p_popup ), "popup_program" ) );
1386         GtkProgramMenu( p_intf, p_popup_menu, p_pgrm,
1387                         GtkPopupProgramToggle );
1388
1389         p_intf->p_sys->b_program_update = VLC_FALSE;
1390     }
1391
1392     if( p_intf->p_sys->b_title_update )
1393     {
1394         char            psz_title[5];
1395
1396         p_menubar_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
1397                             p_intf->p_sys->p_window ), "menubar_title" ) );
1398         GtkRadioMenu( p_intf, p_menubar_menu, NULL, _("Title"), 1,
1399                       p_intf->p_sys->p_input->stream.i_area_nb - 1,
1400                       p_intf->p_sys->p_input->stream.p_selected_area->i_id,
1401                       GtkMenubarTitleToggle );
1402
1403         snprintf( psz_title, 4, "%d",
1404                   p_intf->p_sys->p_input->stream.p_selected_area->i_id );
1405         psz_title[ 4 ] = '\0';
1406         gtk_label_set_text( p_intf->p_sys->p_label_title, psz_title );
1407
1408         p_intf->p_sys->b_title_update = VLC_FALSE;
1409     }
1410
1411     if( p_intf->p_sys->b_chapter_update )
1412     {
1413         char            psz_chapter[5];
1414
1415         p_popup_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
1416                              p_intf->p_sys->p_popup ), "popup_navigation" ) );
1417         GtkTitleMenu( p_intf, p_popup_menu, GtkPopupNavigationToggle );
1418 #if 0
1419         GtkRadioMenu( p_intf, p_menubar_menu, NULL, _("Title"), 1,
1420                         p_intf->p_sys->p_input->stream.i_area_nb - 1,
1421                         p_intf->p_sys->p_input->stream.p_selected_area->i_id,
1422                         on_menubar_chapter_toggle );
1423 #endif
1424
1425         p_menubar_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
1426                              p_intf->p_sys->p_window ), "menubar_chapter" ) );
1427
1428         GtkRadioMenu( p_intf, p_menubar_menu, NULL, _("Chapter"), 1,
1429                         p_intf->p_sys->p_input->stream.p_selected_area->i_part_nb - 1,
1430                         p_intf->p_sys->p_input->stream.p_selected_area->i_part,
1431                         GtkMenubarChapterToggle );
1432
1433
1434         snprintf( psz_chapter, 4, "%d",
1435                   p_intf->p_sys->p_input->stream.p_selected_area->i_part );
1436         psz_chapter[ 4 ] = '\0';
1437         gtk_label_set_text( p_intf->p_sys->p_label_chapter, psz_chapter );
1438
1439         p_intf->p_sys->i_part =
1440                 p_intf->p_sys->p_input->stream.p_selected_area->i_part;
1441
1442         p_intf->p_sys->b_chapter_update = VLC_FALSE;
1443     }
1444
1445     /* look for selected ES */
1446     p_audio_es = NULL;
1447     p_spu_es = NULL;
1448
1449     for( i = 0 ; i < p_intf->p_sys->p_input->stream.i_selected_es_number ; i++ )
1450     {
1451         if( p_intf->p_sys->p_input->stream.pp_selected_es[i]->i_cat == AUDIO_ES )
1452         {
1453             p_audio_es = p_intf->p_sys->p_input->stream.pp_selected_es[i];
1454         }
1455
1456         if( p_intf->p_sys->p_input->stream.pp_selected_es[i]->i_cat == SPU_ES )
1457         {
1458             p_spu_es = p_intf->p_sys->p_input->stream.pp_selected_es[i];
1459         }
1460     }
1461
1462     vlc_mutex_unlock( &p_intf->p_sys->p_input->stream.stream_lock );
1463
1464     /* audio menus */
1465     if( p_intf->p_sys->b_audio_update )
1466     {
1467         /* find audio root menu */
1468         p_menubar_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
1469                              p_intf->p_sys->p_window ), "menubar_audio" ) );
1470
1471         p_popup_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
1472                      p_intf->p_sys->p_popup ), "popup_language" ) );
1473
1474         p_intf->p_sys->b_audio_update = VLC_TRUE;
1475         GtkLanguageMenus( p_intf, p_menubar_menu, p_audio_es, AUDIO_ES,
1476                             GtkMenubarAudioToggle );
1477         p_intf->p_sys->b_audio_update = VLC_TRUE;
1478         GtkLanguageMenus( p_intf, p_popup_menu, p_audio_es, AUDIO_ES,
1479                             GtkPopupAudioToggle );
1480
1481         p_intf->p_sys->b_audio_update = VLC_FALSE;
1482     }
1483
1484     /* sub picture menus */
1485     if( p_intf->p_sys->b_spu_update )
1486     {
1487         /* find spu root menu */
1488         p_menubar_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
1489                           p_intf->p_sys->p_window ), "menubar_subpictures" ) );
1490
1491         p_popup_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
1492                      p_intf->p_sys->p_popup ), "popup_subpictures" ) );
1493
1494         p_intf->p_sys->b_spu_update = VLC_TRUE;
1495         GtkLanguageMenus( p_intf, p_menubar_menu, p_spu_es, SPU_ES,
1496                             GtkMenubarSubtitleToggle  );
1497         p_intf->p_sys->b_spu_update = VLC_TRUE;
1498         GtkLanguageMenus( p_intf, p_popup_menu, p_spu_es, SPU_ES,
1499                             GtkPopupSubtitleToggle );
1500
1501         p_intf->p_sys->b_spu_update = VLC_FALSE;
1502     }
1503     /* create audio channels and device menu (in menubar _and_ popup */
1504     if( p_intf->p_sys->b_aout_update )
1505     {
1506         aout_instance_t *p_aout;
1507
1508         p_aout = (aout_instance_t*)vlc_object_find( p_intf, VLC_OBJECT_AOUT, FIND_ANYWHERE );
1509
1510         if( p_aout != NULL )
1511         {
1512             vlc_value_t val;
1513             val.b_bool = VLC_FALSE;
1514
1515             var_Set( (vlc_object_t *)p_aout, "intf-change", val );
1516
1517             /* audio-channels */
1518             p_menubar_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
1519                               p_intf->p_sys->p_window ), "menubar_audio_channels" ) );
1520             p_popup_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
1521                          p_intf->p_sys->p_popup ), "popup_audio_channels" ) );
1522             GtkSetupVarMenu( p_intf, (vlc_object_t *)p_aout, p_popup_menu,
1523                               "audio-channels",  GtkPopupAoutChannelsToggle );
1524             GtkSetupVarMenu( p_intf, (vlc_object_t *)p_aout, p_menubar_menu,
1525                               "audio-channels",  GtkPopupAoutChannelsToggle );
1526
1527             /* audio-device */
1528             p_menubar_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
1529                               p_intf->p_sys->p_window ), "menubar_audio_device" ) );
1530             p_popup_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
1531                          p_intf->p_sys->p_popup ), "popup_audio_device" ) );
1532             GtkSetupVarMenu( p_intf, (vlc_object_t *)p_aout, p_popup_menu,
1533                               "audio-device",  GtkPopupAoutDeviceToggle );
1534             GtkSetupVarMenu( p_intf, (vlc_object_t *)p_aout, p_menubar_menu,
1535                               "audio-device",  GtkPopupAoutDeviceToggle );
1536
1537             vlc_object_release( (vlc_object_t *)p_aout );
1538         }
1539         p_intf->p_sys->b_aout_update = VLC_FALSE;
1540     }
1541
1542     if( p_intf->p_sys->b_vout_update )
1543     {
1544         vout_thread_t *p_vout;
1545
1546         p_vout = (vout_thread_t*)vlc_object_find( p_intf, VLC_OBJECT_VOUT, FIND_ANYWHERE );
1547
1548         if( p_vout != NULL )
1549         {
1550             vlc_value_t val;
1551             val.b_bool = VLC_FALSE;
1552
1553             var_Set( (vlc_object_t *)p_vout, "intf-change", val );
1554
1555             /* video-device */
1556             p_menubar_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
1557                               p_intf->p_sys->p_window ), "menubar_video_device" ) );
1558             p_popup_menu = GTK_WIDGET( gtk_object_get_data( GTK_OBJECT(
1559                          p_intf->p_sys->p_popup ), "popup_video_device" ) );
1560             GtkSetupVarMenu( p_intf, (vlc_object_t *)p_vout, p_popup_menu,
1561                               "video-device",  GtkPopupVoutDeviceToggle );
1562             GtkSetupVarMenu( p_intf, (vlc_object_t *)p_vout, p_menubar_menu,
1563                               "video-device",  GtkPopupVoutDeviceToggle );
1564
1565
1566             vlc_object_release( (vlc_object_t *)p_vout );
1567         }
1568         p_intf->p_sys->b_vout_update = VLC_FALSE;
1569     }
1570     vlc_mutex_lock( &p_intf->p_sys->p_input->stream.stream_lock );
1571
1572     return TRUE;
1573 }
1574