]> git.sesse.net Git - vlc/blob - modules/gui/qt4/menus.cpp
b694ab1558b9564fb87b06d1c77f8398c0c9945d
[vlc] / modules / gui / qt4 / menus.cpp
1 /*****************************************************************************
2  * menus.cpp : Qt menus
3  *****************************************************************************
4  * Copyright ( C ) 2006-2007 the VideoLAN team
5  * $Id$
6  *
7  * Authors: ClĂ©ment Stenac <zorglub@videolan.org>
8  *          Jean-Baptiste Kempf <jb@videolan.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * ( at your option ) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #include <vlc_intf_strings.h>
26
27 #include "main_interface.hpp"
28 #include "menus.hpp"
29 #include "dialogs_provider.hpp"
30 #include "input_manager.hpp"
31
32 #include <QMenu>
33 #include <QMenuBar>
34 #include <QAction>
35 #include <QActionGroup>
36 #include <QSignalMapper>
37 #include <QSystemTrayIcon>
38
39 enum
40 {
41     ITEM_NORMAL,
42     ITEM_CHECK,
43     ITEM_RADIO
44 };
45
46 static QActionGroup *currentGroup;
47
48 // Add static entries to menus
49 void addDPStaticEntry( QMenu *menu,
50                        const QString text,
51                        char *help,
52                        char *icon,
53                        const char *member,
54                        char *shortcut )
55 {
56     if( !EMPTY_STR( icon ) > 0 )
57     {
58         if( !EMPTY_STR( shortcut ) > 0 )
59             menu->addAction( QIcon( icon ), text, THEDP, member, qtr( shortcut ) );
60         else
61             menu->addAction( QIcon( icon ), text, THEDP, member );
62     }
63     else
64     {
65         if( !EMPTY_STR( shortcut ) > 0 )
66             menu->addAction( text, THEDP, member, qtr( shortcut ) );
67         else
68             menu->addAction( text, THEDP, member );
69     }
70 }
71
72 void addMIMStaticEntry( intf_thread_t *p_intf,
73                         QMenu *menu,
74                         const QString text,
75                         char *help,
76                         char *icon,
77                         const char *member )
78 {
79     if( strlen( icon ) > 0 )
80     {
81         QAction *action = menu->addAction( text, THEMIM,  member );
82         action->setIcon( QIcon( icon ) );
83     }
84     else
85     {
86         menu->addAction( text, THEMIM, member );
87     }
88 }
89
90 /*****************************************************************************
91  * Definitions of variables for the dynamic menus
92  *****************************************************************************/
93 #define PUSH_VAR( var ) varnames.push_back( var ); \
94     objects.push_back( p_object->i_object_id )
95
96 #define PUSH_SEPARATOR if( objects.size() != i_last_separator ) { \
97     objects.push_back( 0 ); varnames.push_back( "" ); \
98     i_last_separator = objects.size(); }
99
100 static int InputAutoMenuBuilder( vlc_object_t *p_object,
101         vector<int> &objects,
102         vector<const char *> &varnames )
103 {
104     PUSH_VAR( "bookmark" );
105     PUSH_VAR( "title" );
106     PUSH_VAR( "chapter" );
107     PUSH_VAR( "program" );
108     PUSH_VAR( "navigation" );
109     PUSH_VAR( "dvd_menus" );
110     return VLC_SUCCESS;
111 }
112
113 static int VideoAutoMenuBuilder( vlc_object_t *p_object,
114         vector<int> &objects,
115         vector<const char *> &varnames )
116 {
117     PUSH_VAR( "fullscreen" );
118     PUSH_VAR( "zoom" );
119     PUSH_VAR( "deinterlace" );
120     PUSH_VAR( "aspect-ratio" );
121     PUSH_VAR( "crop" );
122     PUSH_VAR( "video-on-top" );
123     PUSH_VAR( "directx-wallpaper" );
124     PUSH_VAR( "video-snapshot" );
125
126     vlc_object_t *p_dec_obj = ( vlc_object_t * )vlc_object_find( p_object,
127             VLC_OBJECT_DECODER,
128             FIND_PARENT );
129     if( p_dec_obj != NULL )
130     {
131         vlc_object_t *p_object = p_dec_obj;
132         PUSH_VAR( "ffmpeg-pp-q" );
133         vlc_object_release( p_dec_obj );
134     }
135     return VLC_SUCCESS;
136 }
137
138 static int AudioAutoMenuBuilder( vlc_object_t *p_object,
139         vector<int> &objects,
140         vector<const char *> &varnames )
141 {
142     PUSH_VAR( "audio-device" );
143     PUSH_VAR( "audio-channels" );
144     PUSH_VAR( "visual" );
145     PUSH_VAR( "equalizer" );
146     return VLC_SUCCESS;
147 }
148
149 /*****************************************************************************
150  * All normal menus
151  * Simple Code
152  *****************************************************************************/
153
154 #define BAR_ADD( func, title ) { \
155     QMenu *menu = func; menu->setTitle( title ); bar->addMenu( menu ); }
156
157 #define BAR_DADD( func, title, id ) { \
158     QMenu *menu = func; menu->setTitle( title ); bar->addMenu( menu ); \
159     MenuFunc *f = new MenuFunc( menu, id ); \
160     CONNECT( menu, aboutToShow(), THEDP->menusUpdateMapper, map() ); \
161     THEDP->menusUpdateMapper->setMapping( menu, f ); }
162
163 /**
164  * Main Menu Bar Creation
165  **/
166 void QVLCMenu::createMenuBar( MainInterface *mi,
167                               intf_thread_t *p_intf,
168                               bool visual_selector_enabled )
169 {
170     QMenuBar *bar = mi->menuBar();
171     BAR_ADD( FileMenu(), qtr( "&Media" ) );
172     BAR_ADD( PlaylistMenu( p_intf, mi ), qtr( "&Playlist" ) );
173     BAR_ADD( ToolsMenu( p_intf, mi, visual_selector_enabled, true ), qtr( "&Tools" ) );
174     BAR_DADD( AudioMenu( p_intf, NULL ), qtr( "&Audio" ), 2 );
175     BAR_DADD( VideoMenu( p_intf, NULL ), qtr( "&Video" ), 1 );
176     BAR_DADD( NavigMenu( p_intf, NULL ), qtr( "&Playback" ), 3 );
177
178     BAR_ADD( HelpMenu(), qtr( "&Help" ) );
179 }
180 #undef BAR_ADD
181 #undef BAR_DADD
182
183 /**
184  * Media ( File ) Menu
185  * Opening, streaming and quit
186  **/
187 QMenu *QVLCMenu::FileMenu()
188 {
189     QMenu *menu = new QMenu();
190
191     addDPStaticEntry( menu, qtr( "&Open File..." ), "",
192         ":/pixmaps/file-asym_16px.png", SLOT( openFileDialog() ), "Ctrl+O" );
193     addDPStaticEntry( menu, qtr( I_OPEN_FOLDER ), "",
194         ":/pixmaps/folder-grey_16px.png", SLOT( PLAppendDir() ), "Ctrl+F" );
195     addDPStaticEntry( menu, qtr( "Open &Disc..." ), "",
196         ":/pixmaps/disc_16px.png", SLOT( openDiscDialog() ), "Ctrl+D" );
197     addDPStaticEntry( menu, qtr( "Open &Network..." ), "",
198         ":/pixmaps/network_16px.png", SLOT( openNetDialog() ), "Ctrl+N" );
199     addDPStaticEntry( menu, qtr( "Open &Capture Device..." ), "",
200         ":/pixmaps/capture-card_16px.png", SLOT( openCaptureDialog() ),
201         "Ctrl+C" );
202     menu->addSeparator();
203
204     addDPStaticEntry( menu, qtr( "&Streaming..." ), "",
205         ":/pixmaps/menus_stream_16px.png", SLOT( openThenStreamingDialogs() ),
206         "Ctrl+S" );
207     addDPStaticEntry( menu, qtr( "Conve&rt / Save..." ), "", "",
208         SLOT( openThenTranscodingDialogs() ), "Ctrl+R" );
209     menu->addSeparator();
210
211     addDPStaticEntry( menu, qtr( "&Quit" ) , "",
212         ":/pixmaps/menus_quit_16px.png", SLOT( quit() ), "Ctrl+Q" );
213     return menu;
214 }
215
216 /* Playlist/MediaLibrary Control */
217 QMenu *QVLCMenu::PlaylistMenu( intf_thread_t *p_intf, MainInterface *mi )
218 {
219     QMenu *menu = new QMenu();
220     menu->addMenu( SDMenu( p_intf ) );
221     menu->addAction( QIcon( ":/pixmaps/playlist_16px.png" ),
222                      qtr( "Show Playlist" ), mi, SLOT( togglePlaylist() ) );
223     menu->addSeparator();
224
225     addDPStaticEntry( menu, qtr( I_PL_LOAD ), "", "", SLOT( openAPlaylist() ),
226         "Ctrl+X" );
227     addDPStaticEntry( menu, qtr( I_PL_SAVE ), "", "", SLOT( saveAPlaylist() ),
228         "Ctrl+Y" );
229     menu->addSeparator();
230     menu->addAction( qtr( "Undock from interface" ), mi,
231                      SLOT( undockPlaylist() ), qtr( "Ctrl+U" ) );
232     return menu;
233 }
234
235 /**
236  * Tools/View Menu
237  * This is kept in the same menu for now, but could change if it gets much
238  * longer.
239  * This menu can be an interface menu but also a right click menu.
240  **/
241 QMenu *QVLCMenu::ToolsMenu( intf_thread_t *p_intf,
242                             MainInterface *mi,
243                             bool visual_selector_enabled,
244                             bool with_intf )
245 {
246     QMenu *menu = new QMenu;
247     if( mi )
248     {
249         menu->addAction( QIcon( ":/pixmaps/playlist_16px.png" ),
250                          qtr( "Playlist..." ), mi, SLOT( togglePlaylist() ),
251                          qtr( "Ctrl+L" ) );
252     }
253     addDPStaticEntry( menu, qtr( I_MENU_EXT ), "",
254         ":/pixmaps/menus_settings_16px.png", SLOT( extendedDialog() ),
255         "Ctrl+E" );
256
257     menu->addSeparator();
258
259     if( with_intf )
260     {
261         QMenu *intfmenu = InterfacesMenu( p_intf, NULL );
262         intfmenu->setTitle( qtr( "Add Interfaces" ) );
263         menu->addMenu( intfmenu );
264         menu->addSeparator();
265     }
266     if( mi )
267     {
268         /* Minimal View */
269         QAction *action=menu->addAction( qtr( "Minimal View..." ), mi,
270                 SLOT( toggleMinimalView() ), qtr( "Ctrl+H" ) );
271         action->setCheckable( true );
272         if( mi->getControlsVisibilityStatus() & CONTROLS_VISIBLE )
273             action->setChecked( true );
274
275         /* Advanced Controls */
276         action = menu->addAction( qtr( "Advanced controls" ), mi,
277                 SLOT( toggleAdvanced() ) );
278         action->setCheckable( true );
279         if( mi->getControlsVisibilityStatus() & CONTROLS_ADVANCED )
280             action->setChecked( true );
281 #if 0 /* For Visualisations. Not yet working */
282         adv = menu->addAction( qtr( "Visualizations selector" ),
283                 mi, SLOT( visual() ) );
284         adv->setCheckable( true );
285         if( visual_selector_enabled ) adv->setChecked( true );
286 #endif
287     }
288
289     menu->addSeparator();
290
291     addDPStaticEntry( menu, qtr( I_MENU_MSG ), "",
292         ":/pixmaps/menus_messages_16px.png", SLOT( messagesDialog() ),
293         "Ctrl+M" );
294     addDPStaticEntry( menu, qtr( I_MENU_INFO ) , "", "",
295         SLOT( mediaInfoDialog() ), "Ctrl+I" );
296     addDPStaticEntry( menu, qtr( I_MENU_CODECINFO ) , "",
297         ":/pixmaps/menus_info_16px.png", SLOT( mediaCodecDialog() ), "Ctrl+J" );
298
299 #if 0 /* Not Implemented yet */
300     addDPStaticEntry( menu, qtr( I_MENU_BOOKMARK ), "","", "bookmarksDialog()", "Ctrl+B" );
301 #endif
302     addDPStaticEntry( menu, qtr( I_MENU_VLM ), "", "", SLOT( vlmDialog() ),
303         "Ctrl+V" );
304
305     menu->addSeparator();
306     addDPStaticEntry( menu, qtr( "Preferences..." ), "",
307         ":/pixmaps/menus_preferences_16px.png", SLOT( prefsDialog() ), "Ctrl+P" );
308     return menu;
309 }
310
311 /**
312  * Interface Sub-Menu, to list extras interface and skins
313  **/
314 QMenu *QVLCMenu::InterfacesMenu( intf_thread_t *p_intf, QMenu *current )
315 {
316     vector<int> objects;
317     vector<const char *> varnames;
318     /** \todo add "switch to XXX" */
319     varnames.push_back( "intf-add" );
320     objects.push_back( p_intf->i_object_id );
321
322     QMenu *menu = Populate( p_intf, current, varnames, objects );
323
324     if( !p_intf->pf_show_dialog )
325     {
326         menu->addSeparator();
327         menu->addAction( qtr( "Switch to skins" ), THEDP, SLOT( switchToSkins() ),
328                 QString( "Ctrl+Z" ) );
329     }
330
331     CONNECT( menu, aboutToShow(), THEDP->menusUpdateMapper, map() );
332     THEDP->menusUpdateMapper->setMapping( menu, 4 );
333     return menu;
334 }
335
336 /**
337  * Main Audio Menu
338  */
339 QMenu *QVLCMenu::AudioMenu( intf_thread_t *p_intf, QMenu * current )
340 {
341     vector<int> objects;
342     vector<const char *> varnames;
343
344     vlc_object_t *p_object = ( vlc_object_t * )vlc_object_find( p_intf,
345             VLC_OBJECT_INPUT, FIND_ANYWHERE );
346     if( p_object != NULL )
347     {
348         PUSH_VAR( "audio-es" );
349         vlc_object_release( p_object );
350     }
351
352     p_object = ( vlc_object_t * )vlc_object_find( p_intf, VLC_OBJECT_AOUT,
353             FIND_ANYWHERE );
354     if( p_object )
355     {
356         AudioAutoMenuBuilder( p_object, objects, varnames );
357         vlc_object_release( p_object );
358     }
359     return Populate( p_intf, current, varnames, objects );
360 }
361
362 /**
363  * Main Video Menu
364  * Subtitles are part of Video.
365  **/
366 QMenu *QVLCMenu::VideoMenu( intf_thread_t *p_intf, QMenu *current )
367 {
368     vlc_object_t *p_object;
369     vector<int> objects;
370     vector<const char *> varnames;
371
372     p_object = ( vlc_object_t * )vlc_object_find( p_intf, VLC_OBJECT_INPUT,
373             FIND_ANYWHERE );
374     if( p_object != NULL )
375     {
376         PUSH_VAR( "video-es" );
377         PUSH_VAR( "spu-es" );
378         vlc_object_release( p_object );
379     }
380
381     p_object = ( vlc_object_t * )vlc_object_find( p_intf, VLC_OBJECT_VOUT,
382             FIND_ANYWHERE );
383     if( p_object != NULL )
384     {
385         VideoAutoMenuBuilder( p_object, objects, varnames );
386         vlc_object_release( p_object );
387     }
388     return Populate( p_intf, current, varnames, objects );
389 }
390
391 /**
392  * Navigation Menu
393  * For DVD, MP4, MOV and other chapter based format
394  **/
395 QMenu *QVLCMenu::NavigMenu( intf_thread_t *p_intf, QMenu *navMenu )
396 {
397     vlc_object_t *p_object;
398     vector<int> objects;
399     vector<const char *> varnames;
400
401     p_object = ( vlc_object_t * )vlc_object_find( p_intf, VLC_OBJECT_INPUT,
402             FIND_ANYWHERE );
403     if( p_object != NULL )
404     {
405         InputAutoMenuBuilder(  p_object, objects, varnames );
406         PUSH_VAR( "prev-title" ); PUSH_VAR ( "next-title" );
407         PUSH_VAR( "prev-chapter" ); PUSH_VAR( "next-chapter" );
408         vlc_object_release( p_object );
409     }
410     navMenu = new QMenu();
411     addDPStaticEntry( navMenu, qtr( I_MENU_GOTOTIME ), "","",
412         SLOT( gotoTimeDialog() ), "Ctrl+T" );
413     navMenu->addSeparator();
414     return Populate( p_intf, navMenu, varnames, objects, true );
415 }
416
417 /**
418  * Service Discovery SubMenu
419  **/
420 QMenu *QVLCMenu::SDMenu( intf_thread_t *p_intf )
421 {
422     QMenu *menu = new QMenu();
423     menu->setTitle( qtr( I_PL_SD ) );
424     char **ppsz_longnames;
425     char **ppsz_names = services_discovery_GetServicesNames( p_intf,
426                                                              &ppsz_longnames );
427     char **ppsz_name = ppsz_names, **ppsz_longname = ppsz_longnames;
428     for( ; *ppsz_name; ppsz_name++, ppsz_longname++ )
429     {
430         QAction *a = new QAction( qfu( *ppsz_longname ), menu );
431         a->setCheckable( true );
432         if( playlist_IsServicesDiscoveryLoaded( THEPL, *ppsz_name ) )
433             a->setChecked( true );
434         CONNECT( a , triggered(), THEDP->SDMapper, map() );
435         THEDP->SDMapper->setMapping( a, QString( *ppsz_name ) );
436         menu->addAction( a );
437
438         if( !strcmp( *ppsz_name, "podcast" ) )
439         {
440             QAction *b = new QAction( qfu( "Configure podcasts..." ), menu );
441             //b->setEnabled( a->isChecked() );
442             menu->addAction( b );
443             CONNECT( b, triggered(), THEDP, podcastConfigureDialog() );
444         }
445         free( *ppsz_name );
446         free( *ppsz_longname );
447     }
448     free( ppsz_names );
449     free( ppsz_longnames );
450     return menu;
451 }
452 /**
453  * Help/About Menu
454 **/
455 QMenu *QVLCMenu::HelpMenu()
456 {
457     QMenu *menu = new QMenu();
458     addDPStaticEntry( menu, qtr( "Help..." ) , "",
459         ":/pixmaps/menus_help_16px.png", SLOT( helpDialog() ), "F1" );
460     addDPStaticEntry( menu, qtr( "Update" ) , "", "", SLOT( updateDialog() ), "");
461     menu->addSeparator();
462     addDPStaticEntry( menu, qtr( I_MENU_ABOUT ), "", "", SLOT( aboutDialog() ),
463         "Ctrl+F1" );
464     return menu;
465 }
466
467
468 /*****************************************************************************
469  * Popup menus - Right Click menus                                           *
470  *****************************************************************************/
471 #define POPUP_BOILERPLATE \
472     unsigned int i_last_separator = 0; \
473     vector<int> objects; \
474     vector<const char *> varnames; \
475     input_thread_t *p_input = THEMIM->getInput();
476
477 #define CREATE_POPUP \
478     Populate( p_intf, menu, varnames, objects ); \
479     p_intf->p_sys->p_popup_menu = menu; \
480     menu->popup( QCursor::pos() ); \
481     p_intf->p_sys->p_popup_menu = NULL; \
482     i_last_separator = 0;
483
484 void QVLCMenu::PopupMenuControlEntries( QMenu *menu,
485                                         intf_thread_t *p_intf,
486                                         input_thread_t *p_input )
487 {
488     if( p_input )
489     {
490         vlc_value_t val;
491         var_Get( p_input, "state", &val );
492         if( val.i_int == PLAYING_S )
493             addMIMStaticEntry( p_intf, menu, qtr( "Pause" ), "",
494                     ":/pixmaps/pause_16px.png", SLOT( togglePlayPause() ) );
495         else
496             addMIMStaticEntry( p_intf, menu, qtr( "Play" ), "",
497                     ":/pixmaps/play_16px.png", SLOT( togglePlayPause() ) );
498     }
499     else if( THEPL->items.i_size && THEPL->i_enabled )
500         addMIMStaticEntry( p_intf, menu, qtr( "Play" ), "",
501                 ":/pixmaps/play_16px.png", SLOT( togglePlayPause() ) );
502
503     addMIMStaticEntry( p_intf, menu, qtr( "Stop" ), "",
504             ":/pixmaps/stop_16px.png", SLOT( stop() ) );
505     addMIMStaticEntry( p_intf, menu, qtr( "Previous" ), "",
506             ":/pixmaps/previous_16px.png", SLOT( prev() ) );
507     addMIMStaticEntry( p_intf, menu, qtr( "Next" ), "",
508             ":/pixmaps/next_16px.png", SLOT( next() ) );
509     }
510
511 void QVLCMenu::PopupMenuStaticEntries( intf_thread_t *p_intf, QMenu *menu )
512 {
513     QMenu *toolsmenu = ToolsMenu( p_intf, NULL, false, true );
514     toolsmenu->setTitle( qtr( "Tools" ) );
515     menu->addMenu( toolsmenu );
516
517     QMenu *openmenu = new QMenu( qtr( "Open" ) );
518     openmenu->addAction( qtr( "Open &File..." ), THEDP,
519                          SLOT( openFileDialog() ) );
520     openmenu->addAction( qtr( "Open &Disc..." ), THEDP,
521                          SLOT( openDiscDialog() ) );
522     openmenu->addAction( qtr( "Open &Network..." ), THEDP,
523                          SLOT( openNetDialog() ) );
524     openmenu->addAction( qtr( "Open &Capture Device..." ), THEDP,
525                          SLOT( openCaptureDialog() ) );
526     menu->addMenu( openmenu );
527
528     menu->addSeparator();
529     QMenu *helpmenu = HelpMenu();
530     helpmenu->setTitle( qtr( "Help" ) );
531     menu->addMenu( helpmenu );
532
533     addDPStaticEntry( menu, qtr( "Quit" ), "", "", SLOT( quit() ) , "Ctrl+Q" );
534 }
535
536 /* Video Tracks and Subtitles tracks */
537 void QVLCMenu::VideoPopupMenu( intf_thread_t *p_intf )
538 {
539     POPUP_BOILERPLATE;
540     if( p_input )
541     {
542         vlc_object_yield( p_input );
543         varnames.push_back( "video-es" );
544         objects.push_back( p_input->i_object_id );
545         varnames.push_back( "spu-es" );
546         objects.push_back( p_input->i_object_id );
547         vlc_object_t *p_vout = ( vlc_object_t * )vlc_object_find( p_input,
548                 VLC_OBJECT_VOUT, FIND_CHILD );
549         if( p_vout )
550         {
551             VideoAutoMenuBuilder( p_vout, objects, varnames );
552             vlc_object_release( p_vout );
553         }
554         vlc_object_release( p_input );
555     }
556     QMenu *menu = new QMenu();
557     CREATE_POPUP;
558 }
559
560 /* Audio Tracks */
561 void QVLCMenu::AudioPopupMenu( intf_thread_t *p_intf )
562 {
563     POPUP_BOILERPLATE;
564     if( p_input )
565     {
566         vlc_object_yield( p_input );
567         varnames.push_back( "audio-es" );
568         objects.push_back( p_input->i_object_id );
569         vlc_object_t *p_aout = ( vlc_object_t * )vlc_object_find( p_input,
570                 VLC_OBJECT_AOUT, FIND_ANYWHERE );
571         if( p_aout )
572         {
573             AudioAutoMenuBuilder( p_aout, objects, varnames );
574             vlc_object_release( p_aout );
575         }
576         vlc_object_release( p_input );
577     }
578     QMenu *menu = new QMenu();
579     CREATE_POPUP;
580 }
581
582 /* Navigation stuff, and general menus ( open ) */
583 void QVLCMenu::MiscPopupMenu( intf_thread_t *p_intf )
584 {
585     vlc_value_t val;
586     POPUP_BOILERPLATE;
587
588     if( p_input )
589     {
590         vlc_object_yield( p_input );
591         varnames.push_back( "audio-es" );
592         InputAutoMenuBuilder( VLC_OBJECT( p_input ), objects, varnames );
593         PUSH_SEPARATOR;
594     }
595
596     QMenu *menu = new QMenu();
597     Populate( p_intf, menu, varnames, objects );
598
599     menu->addSeparator();
600     PopupMenuControlEntries( menu, p_intf, p_input );
601
602     menu->addSeparator();
603     PopupMenuStaticEntries( p_intf, menu );
604
605     p_intf->p_sys->p_popup_menu = menu;
606     menu->popup( QCursor::pos() );
607     p_intf->p_sys->p_popup_menu = NULL;
608 }
609
610 /* Main Menu that sticks everything together  */
611 void QVLCMenu::PopupMenu( intf_thread_t *p_intf, bool show )
612 {
613     if( show )
614     {
615         // create a  popup if there is none
616         if( ! p_intf->p_sys->p_popup_menu )
617         {
618             POPUP_BOILERPLATE;
619             if( p_input )
620             {
621                 vlc_object_yield( p_input );
622                 InputAutoMenuBuilder( VLC_OBJECT( p_input ), objects, varnames );
623
624                 /* Audio menu */
625                 PUSH_SEPARATOR;
626                 varnames.push_back( "audio-es" );
627                 objects.push_back( p_input->i_object_id );
628                 vlc_object_t *p_aout = ( vlc_object_t * )
629                     vlc_object_find( p_input, VLC_OBJECT_AOUT, FIND_ANYWHERE );
630                 if( p_aout )
631                 {
632                     AudioAutoMenuBuilder( p_aout, objects, varnames );
633                     vlc_object_release( p_aout );
634                 }
635
636                 /* Video menu */
637                 PUSH_SEPARATOR;
638                 varnames.push_back( "video-es" );
639                 objects.push_back( p_input->i_object_id );
640                 varnames.push_back( "spu-es" );
641                 objects.push_back( p_input->i_object_id );
642                 vlc_object_t *p_vout = ( vlc_object_t * )
643                     vlc_object_find( p_input, VLC_OBJECT_VOUT, FIND_CHILD );
644                 if( p_vout )
645                 {
646                     VideoAutoMenuBuilder( p_vout, objects, varnames );
647                     vlc_object_release( p_vout );
648                 }
649             }
650
651             QMenu *menu = new QMenu();
652             Populate( p_intf, menu, varnames, objects );
653             menu->addSeparator();
654             PopupMenuControlEntries( menu, p_intf, p_input );
655             menu->addSeparator();
656             PopupMenuStaticEntries( p_intf, menu );
657
658             p_intf->p_sys->p_popup_menu = menu;
659         }
660         p_intf->p_sys->p_popup_menu->popup( QCursor::pos() );
661     }
662     else
663     {
664         // destroy popup if there is one
665         delete p_intf->p_sys->p_popup_menu;
666         p_intf->p_sys->p_popup_menu = NULL;
667     }
668 }
669
670 /************************************************************************
671  * Systray Menu                                                         *
672  ************************************************************************/
673
674 void QVLCMenu::updateSystrayMenu( MainInterface *mi,
675                                   intf_thread_t *p_intf,
676                                   bool b_force_visible )
677 {
678     POPUP_BOILERPLATE;
679
680     /* Get the systray menu and clean it */
681     QMenu *sysMenu = mi->getSysTrayMenu();
682     sysMenu->clear();
683
684     /* Hide / Show VLC and cone */
685     if( mi->isVisible() || b_force_visible )
686     {
687         sysMenu->addAction( QIcon( ":/vlc16.png" ),
688                 qtr( "Hide VLC media player in taskbar" ), mi,
689                 SLOT( toggleUpdateSystrayMenu() ) );
690     }
691     else
692     {
693         sysMenu->addAction( QIcon( ":/vlc16.png" ),
694                 qtr( "Show VLC media player" ), mi,
695                 SLOT( toggleUpdateSystrayMenu() ) );
696     }
697
698     sysMenu->addSeparator();
699     PopupMenuControlEntries( sysMenu, p_intf, p_input );
700
701     sysMenu->addSeparator();
702     addDPStaticEntry( sysMenu, qtr( "&Open Media" ), "",
703             ":/pixmaps/file-wide_16px.png", SLOT( openFileDialog() ), "" );
704     addDPStaticEntry( sysMenu, qtr( "&Quit" ) , "",
705         ":/pixmaps/menus_quit_16px.png", SLOT( quit() ), "" );
706
707     /* Set the menu */
708     mi->getSysTray()->setContextMenu( sysMenu );
709 }
710
711 #undef PUSH_VAR
712 #undef PUSH_SEPARATOR
713
714
715 /*************************************************************************
716  * Builders for automenus
717  *************************************************************************/
718 QMenu * QVLCMenu::Populate( intf_thread_t *p_intf,
719                             QMenu *current,
720                             vector< const char *> & varnames,
721                             vector<int> & objects,
722                             bool append )
723 {
724     QMenu *menu = current;
725     if( !menu )
726         menu = new QMenu();
727     else if( !append )
728         menu->clear();
729
730     currentGroup = NULL;
731
732     vlc_object_t *p_object;
733     vlc_bool_t b_section_empty = VLC_FALSE;
734     int i;
735
736 #define APPEND_EMPTY { QAction *action = menu->addAction( qtr( "Empty" ) ); \
737     action->setEnabled( false ); }
738
739     for( i = 0; i < ( int )objects.size() ; i++ )
740     {
741         if( !varnames[i] || !*varnames[i] )
742         {
743             if( b_section_empty )
744                 APPEND_EMPTY;
745             menu->addSeparator();
746             b_section_empty = VLC_TRUE;
747             continue;
748         }
749
750         if( objects[i] == 0 )
751         {
752             /// \bug What is this ?
753             // Append( menu, varnames[i], NULL );
754             b_section_empty = VLC_FALSE;
755             continue;
756         }
757
758         p_object = ( vlc_object_t * )vlc_object_get( p_intf, objects[i] );
759         if( p_object == NULL ) continue;
760
761         b_section_empty = VLC_FALSE;
762         /* Ugly specific stuff */
763         if( strstr( varnames[i], "intf-add" ) )
764             CreateItem( menu, varnames[i], p_object, false );
765         else
766             CreateItem( menu, varnames[i], p_object, true );
767         vlc_object_release( p_object );
768     }
769
770     /* Special case for empty menus */
771     if( menu->actions().size() == 0 || b_section_empty )
772         APPEND_EMPTY
773
774     return menu;
775 }
776 #undef APPEND_EMPTY
777
778 /*****************************************************************************
779  * Private methods.
780  *****************************************************************************/
781
782 static bool IsMenuEmpty( const char *psz_var,
783                          vlc_object_t *p_object,
784                          bool b_root = true )
785 {
786     vlc_value_t val, val_list;
787     int i_type, i_result, i;
788
789     /* Check the type of the object variable */
790     i_type = var_Type( p_object, psz_var );
791
792     /* Check if we want to display the variable */
793     if( !( i_type & VLC_VAR_HASCHOICE ) ) return false;
794
795     var_Change( p_object, psz_var, VLC_VAR_CHOICESCOUNT, &val, NULL );
796     if( val.i_int == 0 ) return true;
797
798     if( ( i_type & VLC_VAR_TYPE ) != VLC_VAR_VARIABLE )
799     {
800         /* Very evil hack ! intf-switch can have only one value */
801         if( !strcmp( psz_var, "intf-switch" ) ) return false;
802         if( val.i_int == 1 && b_root ) return true;
803         else return false;
804     }
805
806     /* Check children variables in case of VLC_VAR_VARIABLE */
807     if( var_Change( p_object, psz_var, VLC_VAR_GETLIST, &val_list, NULL ) < 0 )
808     {
809         return true;
810     }
811
812     for( i = 0, i_result = true; i < val_list.p_list->i_count; i++ )
813     {
814         if( !IsMenuEmpty( val_list.p_list->p_values[i].psz_string,
815                     p_object, false ) )
816         {
817             i_result = false;
818             break;
819         }
820     }
821
822     /* clean up everything */
823     var_Change( p_object, psz_var, VLC_VAR_FREELIST, &val_list, NULL );
824
825     return i_result;
826 }
827
828 void QVLCMenu::CreateItem( QMenu *menu, const char *psz_var,
829         vlc_object_t *p_object, bool b_submenu )
830 {
831     vlc_value_t val, text;
832     int i_type;
833
834     /* Check the type of the object variable */
835     i_type = var_Type( p_object, psz_var );
836
837     switch( i_type & VLC_VAR_TYPE )
838     {
839         case VLC_VAR_VOID:
840         case VLC_VAR_BOOL:
841         case VLC_VAR_VARIABLE:
842         case VLC_VAR_STRING:
843         case VLC_VAR_INTEGER:
844         case VLC_VAR_FLOAT:
845             break;
846         default:
847             /* Variable doesn't exist or isn't handled */
848             return;
849     }
850
851     /* Make sure we want to display the variable */
852     if( IsMenuEmpty( psz_var, p_object ) )  return;
853
854     /* Get the descriptive name of the variable */
855     var_Change( p_object, psz_var, VLC_VAR_GETTEXT, &text, NULL );
856
857     if( i_type & VLC_VAR_HASCHOICE )
858     {
859         /* Append choices menu */
860         if( b_submenu )
861         {
862             QMenu *submenu = new QMenu();
863             submenu->setTitle( qfu( text.psz_string ?
864                         text.psz_string : psz_var ) );
865             if( CreateChoicesMenu( submenu, psz_var, p_object, true ) == 0 )
866                 menu->addMenu( submenu );
867         }
868         else
869             CreateChoicesMenu( menu, psz_var, p_object, true );
870         FREENULL( text.psz_string );
871         return;
872     }
873
874 #define TEXT_OR_VAR qfu ( text.psz_string ? text.psz_string : psz_var )
875
876     switch( i_type & VLC_VAR_TYPE )
877     {
878         case VLC_VAR_VOID:
879             var_Get( p_object, psz_var, &val );
880             CreateAndConnect( menu, psz_var, TEXT_OR_VAR, "", ITEM_NORMAL,
881                     p_object->i_object_id, val, i_type );
882             break;
883
884         case VLC_VAR_BOOL:
885             var_Get( p_object, psz_var, &val );
886             val.b_bool = !val.b_bool;
887             CreateAndConnect( menu, psz_var, TEXT_OR_VAR, "", ITEM_CHECK,
888                     p_object->i_object_id, val, i_type, !val.b_bool );
889             break;
890     }
891     FREENULL( text.psz_string );
892 }
893
894
895 int QVLCMenu::CreateChoicesMenu( QMenu *submenu, const char *psz_var,
896         vlc_object_t *p_object, bool b_root )
897 {
898     vlc_value_t val, val_list, text_list;
899     int i_type, i;
900
901     /* Check the type of the object variable */
902     i_type = var_Type( p_object, psz_var );
903
904     /* Make sure we want to display the variable */
905     if( IsMenuEmpty( psz_var, p_object, b_root ) ) return VLC_EGENERIC;
906
907     switch( i_type & VLC_VAR_TYPE )
908     {
909         case VLC_VAR_VOID:
910         case VLC_VAR_BOOL:
911         case VLC_VAR_VARIABLE:
912         case VLC_VAR_STRING:
913         case VLC_VAR_INTEGER:
914         case VLC_VAR_FLOAT:
915             break;
916         default:
917             /* Variable doesn't exist or isn't handled */
918             return VLC_EGENERIC;
919     }
920
921     if( var_Change( p_object, psz_var, VLC_VAR_GETLIST,
922                 &val_list, &text_list ) < 0 )
923     {
924         return VLC_EGENERIC;
925     }
926
927 #define NORMAL_OR_RADIO i_type & VLC_VAR_ISCOMMAND ? ITEM_NORMAL: ITEM_RADIO
928 #define NOTCOMMAND !( i_type & VLC_VAR_ISCOMMAND )
929 #define CURVAL val_list.p_list->p_values[i]
930 #define CURTEXT text_list.p_list->p_values[i].psz_string
931
932     for( i = 0; i < val_list.p_list->i_count; i++ )
933     {
934         vlc_value_t another_val;
935         QString menutext;
936         QMenu *subsubmenu = new QMenu();
937
938         switch( i_type & VLC_VAR_TYPE )
939         {
940             case VLC_VAR_VARIABLE:
941                 CreateChoicesMenu( subsubmenu, CURVAL.psz_string, p_object, false );
942                 subsubmenu->setTitle( qfu( CURTEXT ? CURTEXT :CURVAL.psz_string ) );
943                 submenu->addMenu( subsubmenu );
944                 break;
945
946             case VLC_VAR_STRING:
947                 var_Get( p_object, psz_var, &val );
948                 another_val.psz_string = strdup( CURVAL.psz_string );
949                 menutext = qfu( CURTEXT ? CURTEXT : another_val.psz_string );
950                 CreateAndConnect( submenu, psz_var, menutext, "", NORMAL_OR_RADIO,
951                         p_object->i_object_id, another_val, i_type,
952                         NOTCOMMAND && val.psz_string &&
953                         !strcmp( val.psz_string, CURVAL.psz_string ) );
954
955                 if( val.psz_string ) free( val.psz_string );
956                 break;
957
958             case VLC_VAR_INTEGER:
959                 var_Get( p_object, psz_var, &val );
960                 if( CURTEXT ) menutext = qfu( CURTEXT );
961                 else menutext.sprintf( "%d", CURVAL.i_int );
962                 CreateAndConnect( submenu, psz_var, menutext, "", NORMAL_OR_RADIO,
963                         p_object->i_object_id, CURVAL, i_type,
964                         NOTCOMMAND && CURVAL.i_int == val.i_int );
965                 break;
966
967             case VLC_VAR_FLOAT:
968                 var_Get( p_object, psz_var, &val );
969                 if( CURTEXT ) menutext = qfu( CURTEXT );
970                 else menutext.sprintf( "%.2f", CURVAL.f_float );
971                 CreateAndConnect( submenu, psz_var, menutext, "", NORMAL_OR_RADIO,
972                         p_object->i_object_id, CURVAL, i_type,
973                         NOTCOMMAND && CURVAL.f_float == val.f_float );
974                 break;
975
976             default:
977                 break;
978         }
979     }
980     currentGroup = NULL;
981
982     /* clean up everything */
983     var_Change( p_object, psz_var, VLC_VAR_FREELIST, &val_list, &text_list );
984
985 #undef NORMAL_OR_RADIO
986 #undef NOTCOMMAND
987 #undef CURVAL
988 #undef CURTEXT
989     return VLC_SUCCESS;
990 }
991
992 void QVLCMenu::CreateAndConnect( QMenu *menu, const char *psz_var,
993         QString text, QString help,
994         int i_item_type, int i_object_id,
995         vlc_value_t val, int i_val_type,
996         bool checked )
997 {
998     QAction *action = new QAction( text, menu );
999     action->setText( text );
1000     action->setToolTip( help );
1001
1002     if( i_item_type == ITEM_CHECK )
1003     {
1004         action->setCheckable( true );
1005     }
1006     else if( i_item_type == ITEM_RADIO )
1007     {
1008         action->setCheckable( true );
1009         if( !currentGroup )
1010             currentGroup = new QActionGroup( menu );
1011         currentGroup->addAction( action );
1012     }
1013
1014     if( checked )
1015     {
1016         action->setChecked( true );
1017     }
1018     MenuItemData *itemData = new MenuItemData( i_object_id, i_val_type,
1019             val, psz_var );
1020     CONNECT( action, triggered(), THEDP->menusMapper, map() );
1021     THEDP->menusMapper->setMapping( action, itemData );
1022     menu->addAction( action );
1023 }
1024
1025 void QVLCMenu::DoAction( intf_thread_t *p_intf, QObject *data )
1026 {
1027     MenuItemData *itemData = qobject_cast<MenuItemData *>( data );
1028     vlc_object_t *p_object = ( vlc_object_t * )vlc_object_get( p_intf,
1029             itemData->i_object_id );
1030     if( p_object == NULL ) return;
1031
1032     var_Set( p_object, itemData->psz_var, itemData->val );
1033     vlc_object_release( p_object );
1034 }
1035