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