]> git.sesse.net Git - vlc/blob - modules/gui/win32/menu.cpp
* ./include/vlc_common.h: fixed win32 plugin compilation.
[vlc] / modules / gui / win32 / menu.cpp
1 /*****************************************************************************
2  * menu.cpp: functions to handle menu items
3  *****************************************************************************
4  * Copyright (C) 2002-2003 VideoLAN
5  * $Id: menu.cpp,v 1.5 2003/01/16 09:02:46 sam Exp $
6  *
7  * Authors: Olivier Teuliere <ipkiss@via.ecp.fr>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 #include <vcl.h>
25
26 #include <vlc/vlc.h>
27 #include <vlc/intf.h>
28
29 #include "menu.h"
30 #include "win32_common.h"
31
32 /*****************************************************************************
33  * TMenusGen::*Click: callbacks for the menuitems
34  ****************************************************************************/
35
36 /*
37  * Variables
38  */
39
40 /* variables of the audio output */
41 void __fastcall TMenusGen::AoutVarClick( TObject *Sender )
42 {
43     TMenuItem * Item = (TMenuItem *)Sender;
44
45     vlc_object_t * p_aout;
46     p_aout = (vlc_object_t *)vlc_object_find( p_intf, VLC_OBJECT_AOUT,
47                                               FIND_ANYWHERE );
48     if( p_aout == NULL )
49     {
50         msg_Warn( p_intf, "cannot set variable (%s)", Item->Caption.c_str() );
51         return;
52     }
53
54     if( Item->Parent == MenuADevice || Item->Parent == PopupADevice )
55     {
56         VarChange( p_aout, "audio-device", MenuADevice, PopupADevice, Item );
57     }
58     else if( Item->Parent == MenuChannel || Item->Parent == PopupChannel )
59     {
60         VarChange( p_aout, "audio-channels", MenuChannel, PopupChannel, Item );
61     }
62
63     vlc_object_release( p_aout );
64 }
65
66 /* variables of the video output */
67 void __fastcall TMenusGen::VoutVarClick( TObject *Sender )
68 {
69     TMenuItem * Item = (TMenuItem *)Sender;
70
71     vlc_object_t * p_vout;
72     p_vout = (vlc_object_t *)vlc_object_find( p_intf, VLC_OBJECT_VOUT,
73                                               FIND_ANYWHERE );
74     if( p_vout == NULL )
75     {
76         msg_Warn( p_intf, "cannot set variable (%s)", Item->Caption.c_str() );
77         return;
78     }
79
80     if( Item->Parent == MenuVDevice || Item->Parent == PopupVDevice )
81     {
82         VarChange( p_vout, "video-device", MenuVDevice, PopupVDevice, Item );
83     }
84
85     vlc_object_release( p_vout );
86 }
87
88 /*
89  * Audio
90  */
91
92 void __fastcall TMenusGen::MenuLanguageClick( TObject *Sender )
93 {
94     LangChange( MenuLanguage, (TMenuItem *)Sender, PopupLanguage, AUDIO_ES );
95 }
96
97 void __fastcall TMenusGen::PopupLanguageClick( TObject *Sender )
98 {
99     LangChange( PopupLanguage, (TMenuItem *)Sender, MenuLanguage, AUDIO_ES );
100 }
101
102 /*
103  * Subtitles
104  */
105
106 void __fastcall TMenusGen::MenuSubtitleClick( TObject *Sender )
107 {
108     LangChange( MenuSubtitles, (TMenuItem *)Sender, PopupSubtitles, SPU_ES );
109 }
110
111 void __fastcall TMenusGen::PopupSubtitleClick( TObject *Sender )
112 {
113     LangChange( PopupSubtitles, (TMenuItem *)Sender, MenuSubtitles, SPU_ES );
114 }
115
116 /*
117  * Program
118  */
119
120 void __fastcall TMenusGen::MenuProgramClick( TObject *Sender )
121 {
122    ProgramChange( (TMenuItem *)Sender, PopupProgram );
123 }
124
125 void __fastcall TMenusGen::PopupProgramClick( TObject *Sender )
126 {
127     ProgramChange( (TMenuItem *)Sender, MenuProgram );
128 }
129
130 /*
131  * Title
132  */
133
134 void __fastcall TMenusGen::MenuTitleClick( TObject *Sender )
135 {
136     TMenuItem     * Item = (TMenuItem *)Sender;
137     TMenuItem     * ItemTitle;
138     int             i_title = Item->Tag;
139
140     input_ChangeArea( p_intf->p_sys->p_input,
141                       p_intf->p_sys->p_input->stream.pp_areas[i_title] );
142     Item->Checked = true;
143     ItemTitle = Index2Item( PopupNavigation, i_title - 1, false );
144     Index2Item( ItemTitle, 0, false )->Checked = true;
145
146     input_SetStatus( p_intf->p_sys->p_input, INPUT_STATUS_PLAY );
147 }
148
149 /*
150  * Chapter
151  */
152
153 void __fastcall TMenusGen::MenuChapterClick( TObject *Sender )
154 {
155     TMenuItem     * Item = (TMenuItem *)Sender;
156     TMenuItem     * ItemTitle;
157     input_area_t  * p_area;
158     int             i_title;
159     int             i_chapter = Item->Tag;
160
161     p_area = p_intf->p_sys->p_input->stream.p_selected_area;
162     p_area->i_part = i_chapter;
163
164     input_ChangeArea( p_intf->p_sys->p_input, (input_area_t*)p_area );
165
166     i_title = p_intf->p_sys->p_input->stream.p_selected_area->i_id;
167     ItemTitle = Index2Item( PopupNavigation, i_title - 1, false );
168     Index2Item( ItemTitle, i_chapter - 1, false )->Checked = true;
169
170     input_SetStatus( p_intf->p_sys->p_input, INPUT_STATUS_PLAY );
171 }
172
173 /*
174  * Navigation
175  */
176
177 void __fastcall TMenusGen::PopupNavigationClick( TObject *Sender )
178 {
179     TMenuItem     * Item = (TMenuItem *)Sender;
180     TMenuItem     * ItemTitle;
181     input_area_t  * p_area;
182     int             i_title   = Data2Title( Item->Tag );
183     int             i_chapter = Data2Chapter( Item->Tag );
184
185     p_area = p_intf->p_sys->p_input->stream.pp_areas[i_title];
186     p_area->i_part = i_chapter;
187
188     input_ChangeArea( p_intf->p_sys->p_input, (input_area_t*)p_area );
189
190     Item->Checked = true;
191     ItemTitle = Index2Item( MenuTitle, i_title - 1, false );
192     if( ItemTitle->Checked )
193     {
194         /* same title, new chapter */
195         Index2Item( MenuChapter, i_chapter - 1, false )->Checked = true;
196     }
197     else
198     {
199         /* new title => we must rebuild the chapter menu */
200         vlc_mutex_lock( &p_intf->p_sys->p_input->stream.stream_lock );
201         RadioMenu(
202             MenuChapter, "Chapter",
203             p_intf->p_sys->p_input->stream.p_selected_area->i_part_nb,
204             i_chapter, MenuChapterClick );
205         vlc_mutex_unlock( &p_intf->p_sys->p_input->stream.stream_lock );
206     }
207
208     input_SetStatus( p_intf->p_sys->p_input, INPUT_STATUS_PLAY );
209 }
210
211
212 __fastcall TMenusGen::TMenusGen( intf_thread_t *_p_intf ) : TObject()
213 {
214     p_intf = _p_intf;
215
216     /* Initialize local pointers to menu items of the main window */
217     TMainFrameDlg * p_window = p_intf->p_sys->p_window;
218     if( p_window == NULL )
219     {
220         msg_Warn( p_intf, "Main window wasn't created, expect problems..." );
221         return;
222     }
223
224     MenuChannel = p_window->MenuChannel;
225     PopupChannel = p_window->PopupChannel;
226     MenuADevice = p_window->MenuADevice;
227     PopupADevice = p_window->PopupADevice;
228     MenuVDevice = p_window->MenuVDevice;
229     PopupVDevice = p_window->PopupVDevice;
230     MenuLanguage = p_window->MenuLanguage;
231     PopupLanguage = p_window->PopupLanguage;
232     MenuSubtitles = p_window->MenuSubtitles;
233     PopupSubtitles = p_window->PopupSubtitles;
234     MenuProgram = p_window->MenuProgram;
235     PopupProgram = p_window->PopupProgram;
236     MenuTitle = p_window->MenuTitle;
237     MenuChapter = p_window->MenuChapter;
238     PopupNavigation = p_window->PopupNavigation;
239 }
240
241
242 /*****************************************************************************
243  * SetupMenus: This function dynamically generates some menus
244  *****************************************************************************
245  * The lock on p_input->stream must be taken before you call this function
246  *****************************************************************************/
247 void __fastcall TMenusGen::SetupMenus()
248 {
249     TMainFrameDlg  * p_window = p_intf->p_sys->p_window;
250     input_thread_t * p_input  = p_intf->p_sys->p_input;
251     es_descriptor_t   * p_audio_es;
252     es_descriptor_t   * p_spu_es;
253
254     p_intf->p_sys->b_chapter_update |= p_intf->p_sys->b_title_update;
255     p_intf->p_sys->b_audio_update |= p_intf->p_sys->b_program_update |
256                                      p_intf->p_sys->b_title_update;
257     p_intf->p_sys->b_spu_update |= p_intf->p_sys->b_program_update |
258                                    p_intf->p_sys->b_title_update;
259
260     if( p_intf->p_sys->b_program_update )
261     {
262         pgrm_descriptor_t * p_pgrm;
263
264         if( p_input->stream.p_new_program )
265         {
266             p_pgrm = p_input->stream.p_new_program;
267         }
268         else
269         {
270             p_pgrm = p_input->stream.p_selected_program;
271         }
272
273         ProgramMenu( MenuProgram, p_pgrm, MenuProgramClick );
274         ProgramMenu( PopupProgram, p_pgrm, PopupProgramClick );
275
276         p_intf->p_sys->b_program_update = 0;
277     }
278
279     if( p_intf->p_sys->b_title_update )
280     {
281 // why "-1" ?
282         RadioMenu( MenuTitle, "Title",
283                    p_input->stream.i_area_nb - 1,
284                    p_input->stream.p_selected_area->i_id,
285                    MenuTitleClick );
286
287         AnsiString CurrentTitle;
288         CurrentTitle.sprintf( "%d", p_input->stream.p_selected_area->i_id );
289         p_window->LabelTitleCurrent->Caption = CurrentTitle;
290
291         p_intf->p_sys->b_title_update = 0;
292     }
293
294     if( p_intf->p_sys->b_chapter_update )
295     {
296         RadioMenu( MenuChapter, "Chapter",
297                    p_input->stream.p_selected_area->i_part_nb,
298                    p_input->stream.p_selected_area->i_part,
299                    MenuChapterClick );
300
301         NavigationMenu( PopupNavigation, PopupNavigationClick );
302
303         AnsiString CurrentChapter;
304         CurrentChapter.sprintf( "%d", p_input->stream.p_selected_area->i_part );
305         p_window->LabelChapterCurrent->Caption = CurrentChapter;
306
307         p_intf->p_sys->i_part = p_input->stream.p_selected_area->i_part;
308
309         p_intf->p_sys->b_chapter_update = 0;
310     }
311
312     /* look for selected ES */
313     p_audio_es = NULL;
314     p_spu_es = NULL;
315
316     for( unsigned int i = 0; i < p_input->stream.i_selected_es_number; i++ )
317     {
318         if( p_input->stream.pp_selected_es[i]->i_cat == AUDIO_ES )
319         {
320             p_audio_es = p_input->stream.pp_selected_es[i];
321         }
322
323         if( p_input->stream.pp_selected_es[i]->i_cat == SPU_ES )
324         {
325             p_spu_es = p_input->stream.pp_selected_es[i];
326         }
327     }
328     this->p_audio_es_old = p_audio_es;
329     this->p_spu_es_old = p_spu_es;
330
331     vlc_mutex_unlock( &p_input->stream.stream_lock );
332
333     /* audio menus */
334     if( p_intf->p_sys->b_audio_update )
335     {
336         LanguageMenu( MenuLanguage, p_audio_es, AUDIO_ES, MenuLanguageClick );
337         LanguageMenu( PopupLanguage, p_audio_es, AUDIO_ES, PopupLanguageClick );
338
339         p_intf->p_sys->b_audio_update = 0;
340     }
341
342     /* sub picture menus */
343     if( p_intf->p_sys->b_spu_update )
344     {
345         LanguageMenu( PopupSubtitles, p_spu_es, SPU_ES, PopupSubtitleClick );
346         LanguageMenu( MenuSubtitles, p_spu_es, SPU_ES, MenuSubtitleClick );
347
348         p_intf->p_sys->b_spu_update = 0;
349     }
350
351     if( p_intf->p_sys->b_aout_update )
352     {
353         aout_instance_t * p_aout;
354         p_aout = (aout_instance_t *)vlc_object_find( p_intf, VLC_OBJECT_AOUT,
355                                                      FIND_ANYWHERE );
356
357         if( p_aout != NULL )
358         {
359             vlc_value_t val;
360             val.b_bool = 0;
361
362             var_Set( (vlc_object_t *)p_aout, "intf-change", val );
363
364             SetupVarMenu( (vlc_object_t *)p_aout, "audio-channels",
365                           MenuChannel, AoutVarClick );
366             SetupVarMenu( (vlc_object_t *)p_aout, "audio-channels",
367                           PopupChannel, AoutVarClick );
368
369             SetupVarMenu( (vlc_object_t *)p_aout, "audio-device",
370                           MenuADevice, AoutVarClick );
371             SetupVarMenu( (vlc_object_t *)p_aout, "audio-device",
372                           PopupADevice, AoutVarClick );
373
374             vlc_object_release( (vlc_object_t *)p_aout );
375         }
376
377         p_intf->p_sys->b_aout_update = 0;
378     }
379
380     if( p_intf->p_sys->b_vout_update )
381     {
382         vout_thread_t * p_vout;
383         p_vout = (vout_thread_t *)vlc_object_find( p_intf, VLC_OBJECT_VOUT,
384                                                    FIND_ANYWHERE );
385
386         if( p_vout != NULL )
387         {
388             vlc_value_t val;
389             val.b_bool = 0;
390
391             var_Set( (vlc_object_t *)p_vout, "intf-change", val );
392
393             SetupVarMenu( (vlc_object_t *)p_vout, "video-device",
394                           MenuVDevice, VoutVarClick );
395             SetupVarMenu( (vlc_object_t *)p_vout, "video-device",
396                           PopupVDevice, VoutVarClick );
397
398             vlc_object_release( (vlc_object_t *)p_vout );
399         }
400
401         p_intf->p_sys->b_vout_update = 0;
402     }
403
404     vlc_mutex_lock( &p_input->stream.stream_lock );
405 }
406
407
408 /*****************************************************************************
409  * Private functions
410  *****************************************************************************/
411 TMenuItem * TMenusGen::Index2Item( TMenuItem *Root, int i_index,
412                                    bool SingleColumn )
413 {
414     if( SingleColumn || ( i_index < 20 ) )
415         return Root->Items[i_index];
416     else
417         return Root->Items[i_index / 10]->Items[i_index % 10];
418 }
419
420 int TMenusGen::Item2Index( TMenuItem *Root, TMenuItem *Item )
421 {
422     if( Item->Parent == Root )
423         return Item->MenuIndex;
424     else
425         return( 10 * Item->Parent->MenuIndex + Item->MenuIndex );
426 }
427
428 int __fastcall TMenusGen::Data2Title( int data )
429 {
430     return (int) (data >> 16 );
431 }
432
433 int __fastcall TMenusGen::Data2Chapter( int data )
434 {
435     return (int) (data & 0xffff);
436 }
437
438 int __fastcall TMenusGen::Pos2Data( int title, int chapter )
439 {
440     return (int) (( title << 16 ) | ( chapter & 0xffff ));
441 }
442
443 /****************************************************************************
444  * VarChange: change a variable in a vlc_object_t
445  ****************************************************************************
446  * Change the variable and update the menuitems.
447  ****************************************************************************/
448 void __fastcall TMenusGen::VarChange( vlc_object_t *p_object,
449         const char *psz_variable, TMenuItem *RootMenu, TMenuItem *RootPopup,
450         TMenuItem *Item )
451 {
452     vlc_value_t val;
453     int i_index;
454
455     /* We must delete all the '&' characters in the caption string, because
456      * Borland automatically adds one when (and only when!) you click on
457      * the menuitem. Grrrrr... */
458     AnsiString Caption = Item->Caption;
459     while( Caption.LastDelimiter( "&" ) != 0 )
460     {\r
461         Caption.Delete( Caption.LastDelimiter( "&" ), 1 );\r
462     }\r
463     val.psz_string = Caption.c_str();
464
465     /* set the new value */
466     if( var_Set( p_object, psz_variable, val ) < 0 )
467     {
468         msg_Warn( p_object, "cannot set variable (%s)", val.psz_string );
469     }
470
471     i_index = Item->MenuIndex;
472     RootMenu->Items[i_index]->Checked = true;
473     RootPopup->Items[i_index]->Checked = true;
474 }
475
476 /****************************************************************************
477  * LangChange: change audio or subtitles languages
478  ****************************************************************************
479  * Toggle the language, and update the selected menuitems.
480  ****************************************************************************/
481 void __fastcall TMenusGen::LangChange( TMenuItem *RootCurrent, TMenuItem *Item,
482     TMenuItem *RootOther, int i_cat )
483 {
484     es_descriptor_t * p_es;
485     es_descriptor_t * p_es_old;
486     int i_index;
487     int i_es;
488
489     /* find the selected ES */
490     i_es = Item->Tag;
491
492     /* find selected menu item */
493     i_index = Item2Index( RootCurrent, Item ) - 1;
494     if( i_index < 0 )
495     {
496         /* 'None' was selected */
497         p_es = NULL;
498     }
499     else
500     {
501         vlc_mutex_lock( &p_intf->p_sys->p_input->stream.stream_lock );
502         p_es = p_intf->p_sys->p_input->stream.pp_es[i_es];
503         vlc_mutex_unlock( &p_intf->p_sys->p_input->stream.stream_lock );
504     }
505
506     /* find the current ES */
507     if( i_cat == AUDIO_ES )
508     {
509         p_es_old = this->p_audio_es_old;
510         this->p_audio_es_old = p_es;
511     }
512     else
513     {
514         p_es_old = this->p_spu_es_old;
515         this->p_spu_es_old = p_es;
516     }
517
518     /* exchange them */
519     input_ToggleES( p_intf->p_sys->p_input, p_es_old, false );
520     input_ToggleES( p_intf->p_sys->p_input, p_es, true );
521
522     Item->Checked = true;
523     Index2Item( RootOther, i_index + 1, true )->Checked = true;
524 }
525
526 /****************************************************************************
527  * ProgramChange: change the program
528  ****************************************************************************
529  * Toggle the program, and update the selected menuitems.
530  ****************************************************************************/
531 void __fastcall TMenusGen::ProgramChange( TMenuItem *Item,
532                                           TMenuItem *RootOther )
533 {
534     int i_program = Item->Tag;
535
536     /* toggle the program */
537     input_ChangeProgram( p_intf->p_sys->p_input, (uint16_t)i_program );
538
539     /* check selected menu items */
540     Item->Checked = true;
541     Index2Item( RootOther, i_program - 1, true )->Checked = true;
542
543     /* update audio/subtitles menus */
544     p_intf->p_sys->b_audio_update = 1;
545     p_intf->p_sys->b_spu_update = 1;
546     vlc_mutex_lock( &p_intf->p_sys->p_input->stream.stream_lock );
547     SetupMenus();
548     vlc_mutex_unlock( &p_intf->p_sys->p_input->stream.stream_lock );
549     p_intf->p_sys->b_audio_update = 0;
550     p_intf->p_sys->b_spu_update = 0;
551
552     input_SetStatus( p_intf->p_sys->p_input, INPUT_STATUS_PLAY );
553 }
554
555 /*****************************************************************************
556  * SetupVarMenu: build a menu allowing to change a variable
557  *****************************************************************************/
558 void __fastcall TMenusGen::SetupVarMenu( vlc_object_t *p_object,
559         const char *psz_variable, TMenuItem *Root, TNotifyEvent MenuItemClick )
560 {
561     TMenuItem * Item;
562     vlc_value_t val;
563     char * psz_value = NULL;
564     int i;
565
566     /* remove previous menu */
567     Root->Clear();
568
569     /* get the current value */
570     if( var_Get( p_object, psz_variable, &val ) < 0 )
571     {
572         return;
573     }
574     psz_value = val.psz_string;
575
576     if( var_Change( p_object, psz_variable, VLC_VAR_GETLIST, &val ) < 0 )
577     {
578         free( psz_value );
579         return;
580     }
581
582     /* append a menuitem for each option */
583     for( i = 0; i < val.p_list->i_count; i++ )
584     {
585         Item = new TMenuItem( Root );
586         Item->Caption = val.p_list->p_values[i].psz_string;
587         Item->Hint = val.p_list->p_values[i].psz_string;
588         Item->RadioItem = true;
589         Item->OnClick = MenuItemClick;
590         if( !strcmp( psz_value, val.p_list->p_values[i].psz_string ) )
591             Item->Checked = true;
592
593         /* Add the item to the submenu */
594         Root->Add( Item );
595     }
596
597     /* enable the menu if there is at least 1 item */
598     Root->Enabled = ( val.p_list->i_count > 0 );
599
600     /* clean up everything */
601     var_Change( p_object, psz_variable, VLC_VAR_FREELIST, &val );
602 //    free( psz_value );
603 }
604
605 /*****************************************************************************
606  * ProgramMenu: update the programs menu of the interface
607  *****************************************************************************
608  * Builds the program menu according to what have been found in the PAT
609  * by the input. Useful for multi-programs streams such as DVB ones.
610  *****************************************************************************/
611 void __fastcall TMenusGen::ProgramMenu( TMenuItem *Root,
612     pgrm_descriptor_t *p_pgrm, TNotifyEvent MenuItemClick )
613 {
614     TMenuItem * Item;
615
616     /* remove previous menu */
617     Root->Clear();
618     Root->Enabled = false;
619
620     /* create a set of program buttons and append them to the container */
621     for( unsigned int i = 0; i < p_intf->p_sys->p_input->stream.i_pgrm_number;
622          i++ )
623     {
624         AnsiString Name;
625         Name.sprintf( "id %d",
626             p_intf->p_sys->p_input->stream.pp_programs[i]->i_number );
627
628         Item = new TMenuItem( Root );
629         Item->Caption = Name;
630         Item->Hint = Name;
631         Item->RadioItem = true;
632         Item->OnClick = MenuItemClick;
633
634         /* FIXME: temporary hack to save the program id with the Item
635          * It will be used in the callback. */
636         Item->Tag = i + 1;
637
638         /* check the currently selected program */
639         if( p_pgrm == p_intf->p_sys->p_input->stream.pp_programs[i] )
640         {
641             Item->Checked = true;
642         }
643
644         /* add the item to the submenu */
645         Root->Add( Item );
646     }
647
648     /* be sure that menu is enabled if more than 1 program */
649     if( p_intf->p_sys->p_input->stream.i_pgrm_number > 1 )
650     {
651         Root->Enabled = true;
652     }
653 }
654
655 /*****************************************************************************
656  * RadioMenu: update interactive menus of the interface
657  *****************************************************************************
658  * Sets up menus with information from input
659  * Warning: since this function is designed to be called by management
660  * function, the interface lock has to be taken
661  *****************************************************************************/
662 void __fastcall TMenusGen::RadioMenu( TMenuItem *Root, AnsiString ItemName,
663      int i_nb, int i_selected, TNotifyEvent MenuItemClick )
664 {
665     TMenuItem  * ItemGroup;
666     TMenuItem  * Item;
667     AnsiString   Name;
668
669     /* remove previous menu */
670     Root->Enabled = false;
671     Root->Clear();
672
673     for( int i_item = 0; i_item < i_nb; i_item++ )
674     {
675         /* we group titles/chapters in packets of ten for small screens */
676         if( ( i_item % 10 == 0 ) && ( i_nb > 20 ) )
677         {
678             if( i_item != 0 )
679             {
680                 Root->Add( ItemGroup );
681             }
682
683             Name.sprintf( "%ss %d to %d", ItemName, i_item + 1, i_item + 10 );
684             ItemGroup = new TMenuItem( Root );
685             ItemGroup->Hint = Name;
686             ItemGroup->RadioItem = true;
687
688             /* set the accelerator character */
689             Name.Insert( "&", Name.Length() - 1 );
690             ItemGroup->Caption = Name;
691         }
692
693         Name.sprintf( "%s %d", ItemName, i_item + 1 );
694         Item = new TMenuItem( Root );
695         Item->RadioItem = true;
696         Item->Hint = Name;
697
698         /* set the accelerator character */
699         Name.Insert( "&", Name.Length() );
700         Item->Caption = Name;
701
702         /* FIXME: temporary hack to save i_item with the Item
703          * It will be used in the callback. */
704         Item->Tag = i_item + 1;
705
706         /* check the currently selected chapter */
707         if( i_selected == i_item + 1 )
708         {
709             Item->Checked = true;
710         }
711
712         /* setup signal handling */
713         Item->OnClick = MenuItemClick;
714
715         if( i_nb > 20 )
716             ItemGroup->Add( Item );
717         else
718             Root->Add( Item );
719     }
720
721 //  if( ( i_nb > 20 ) && ( i_item % 10 ) )  ?
722     if( i_nb > 20 )
723     {
724         Root->Add( ItemGroup );
725     }
726
727     /* be sure that menu is enabled, if there are several items */
728     if( i_nb > 1 )
729     {
730         Root->Enabled = true;
731     }
732 }
733
734 /*****************************************************************************
735  * LanguageMenus: update interactive menus of the interface
736  *****************************************************************************
737  * Sets up menus with information from input:
738  *  - languages
739  *  - sub-pictures
740  * Warning: since this function is designed to be called by management
741  * function, the interface lock has to be taken
742  *****************************************************************************/
743 void __fastcall TMenusGen::LanguageMenu( TMenuItem *Root, es_descriptor_t *p_es,
744     int i_cat, TNotifyEvent MenuItemClick )
745 {
746     TMenuItem     * Separator;
747     TMenuItem     * Item;
748     AnsiString      Name;
749
750     /* remove previous menu */
751     Root->Clear();
752     Root->Enabled = false;
753
754     /* special case for "off" item */
755     Name = "None";
756     Item = new TMenuItem( Root );
757     Item->RadioItem = true;
758     Item->Hint = Name;
759     Item->Caption = Name;
760     Item->OnClick = MenuItemClick;
761     Item->Tag = -1;
762     Root->Add( Item );
763
764     /* separator item */
765     Separator = new TMenuItem( Root );
766     Separator->Caption = "-";
767     Root->Add( Separator );
768
769     int i_item = 0;
770
771     vlc_mutex_lock( &p_intf->p_sys->p_input->stream.stream_lock );
772
773 #define ES p_intf->p_sys->p_input->stream.pp_es[i]
774     /* create a set of language buttons and append them to the Root */
775     for( unsigned int i = 0; i < p_intf->p_sys->p_input->stream.i_es_number;
776          i++ )
777     {
778         if( ( ES->i_cat == i_cat ) &&
779             ( !ES->p_pgrm ||
780               ES->p_pgrm ==
781                 p_intf->p_sys->p_input->stream.p_selected_program ) )
782         {
783             i_item++;
784             Name = p_intf->p_sys->p_input->stream.pp_es[i]->psz_desc;
785             if( Name.IsEmpty() )
786             {
787                 Name.sprintf( "Language %d", i_item );
788             }
789
790             Item = new TMenuItem( Root );
791             Item->RadioItem = true;
792             Item->Hint = Name;
793             Item->Caption = Name;
794             Item->Tag = i;
795
796             /* check the currently selected item */
797             if( p_es == p_intf->p_sys->p_input->stream.pp_es[i] )
798             {
799                 Item->Checked = true;
800             }
801
802             /* setup signal hanling */
803             Item->OnClick = MenuItemClick;
804             Root->Add( Item );
805         }
806     }
807 #undef ES
808
809     vlc_mutex_unlock( &p_intf->p_sys->p_input->stream.stream_lock );
810
811     /* be sure that menu is enabled if non empty */
812     if( i_item > 0 )
813     {
814         Root->Enabled = true;
815     }
816 }
817
818 /*****************************************************************************
819  * NavigationMenu: sets menus for titles and chapters selection
820  *****************************************************************************
821  * Generates two types of menus:
822  *  -simple list of titles
823  *  -cascaded lists of chapters for each title
824  *****************************************************************************/
825 void __fastcall TMenusGen::NavigationMenu( TMenuItem *Root,
826     TNotifyEvent MenuItemClick )
827 {
828     TMenuItem     * TitleGroup;
829     TMenuItem     * TitleItem;
830     TMenuItem     * ChapterGroup;
831     TMenuItem     * ChapterItem;
832     AnsiString      Name;
833     unsigned int    i_title_nb;
834     unsigned int    i_chapter_nb;
835
836
837     /* remove previous menu */
838     Root->Enabled = false;
839     Root->Clear();
840
841     i_title_nb = p_intf->p_sys->p_input->stream.i_area_nb;
842
843     /* loop on titles */
844     for( unsigned int i_title = 1; i_title < i_title_nb; i_title++ )
845     {
846         /* we group titles in packets of ten for small screens */
847         if( ( i_title % 10 == 1 ) && ( i_title_nb > 20 ) )
848         {
849             if( i_title != 1 )
850             {
851                 Root->Add( TitleGroup );
852             }
853
854             Name.sprintf( "%d - %d", i_title, i_title + 9 );
855             TitleGroup = new TMenuItem( Root );
856             TitleGroup->RadioItem = true;
857             TitleGroup->Hint = Name;
858             TitleGroup->Caption = Name;
859         }
860
861         Name.sprintf( "Title %d (%d)", i_title,
862             p_intf->p_sys->p_input->stream.pp_areas[i_title]->i_part_nb );
863         {
864             TitleItem = new TMenuItem( Root );
865             TitleItem->RadioItem = true;
866             TitleItem->Hint = Name;
867             TitleItem->Caption = Name;
868
869             i_chapter_nb =
870                 p_intf->p_sys->p_input->stream.pp_areas[i_title]->i_part_nb;
871
872             /* loop on chapters */
873             for( unsigned int i_chapter = 0; i_chapter < i_chapter_nb;
874                  i_chapter++ )
875             {
876                 /* we group chapters in packets of ten for small screens */
877                 if( ( i_chapter % 10 == 0 ) && ( i_chapter_nb > 20 ) )
878                 {
879                     if( i_chapter != 0 )
880                     {
881                         TitleItem->Add( ChapterGroup );
882                     }
883
884                     Name.sprintf( "%d - %d", i_chapter + 1, i_chapter + 10 );
885                     ChapterGroup = new TMenuItem( TitleItem );
886                     ChapterGroup->RadioItem = true;
887                     ChapterGroup->Hint = Name;
888                     ChapterGroup->Caption = Name;
889                 }
890
891                 Name.sprintf( "Chapter %d", i_chapter + 1 );
892
893                 ChapterItem = new TMenuItem( TitleItem );
894                 ChapterItem->RadioItem = true;
895                 ChapterItem->Hint = Name;
896                 ChapterItem->Caption = Name;
897
898                 /* FIXME: temporary hack to save i_title and i_chapter with
899                  * ChapterItem, since we will need them in the callback */
900                 ChapterItem->Tag = Pos2Data( i_title, i_chapter + 1 );
901
902 #define p_area p_intf->p_sys->p_input->stream.pp_areas[i_title]
903                 /* check the currently selected chapter */
904                 if( ( p_area ==
905                         p_intf->p_sys->p_input->stream.p_selected_area ) &&
906                     ( p_area->i_part == i_chapter + 1 ) )
907                 {
908                     ChapterItem->Checked = true;
909                 }
910 #undef p_area
911
912                 /* setup signal handling */
913                 ChapterItem->OnClick = MenuItemClick;
914
915                 if( i_chapter_nb > 20 )
916                     ChapterGroup->Add( ChapterItem );
917                 else
918                     TitleItem->Add( ChapterItem );
919             }
920
921             if( i_chapter_nb > 20 )
922             {
923                 TitleItem->Add( ChapterGroup );
924             }
925
926             if( p_intf->p_sys->p_input->stream.pp_areas[i_title]->i_part_nb
927                 > 1 )
928             {
929                 /* be sure that menu is sensitive */
930                 Root->Enabled = true;
931             }
932         }
933
934         if( i_title_nb > 20 )
935             TitleGroup->Add( TitleItem );
936         else
937             Root->Add( TitleItem );
938     }
939
940     if( i_title_nb > 20 )
941     {
942         Root->Add( TitleGroup );
943     }
944
945     /* be sure that menu is sensitive */
946     Root->Enabled = true;
947 }
948
949