menus.cpp 31.7 KB
Newer Older
1 2 3
/*****************************************************************************
 * menus.cpp : Qt menus
 *****************************************************************************
4
 * Copyright (C) 2006-2007 the VideoLAN team
5
 * $Id$
6
 *
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
7
 * Authors: Clément Stenac <zorglub@videolan.org>
8
 *          Jean-Baptiste Kempf <jb@videolan.org>
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

25
#include <vlc_intf_strings.h>
26

27
#include "main_interface.hpp"
Clément Stenac's avatar
Clément Stenac committed
28 29 30 31
#include "menus.hpp"
#include "dialogs_provider.hpp"
#include "input_manager.hpp"

32 33 34 35 36 37 38
#include <QMenu>
#include <QMenuBar>
#include <QAction>
#include <QActionGroup>
#include <QSignalMapper>
#include <QSystemTrayIcon>

39 40 41 42 43 44 45 46 47
enum
{
    ITEM_NORMAL,
    ITEM_CHECK,
    ITEM_RADIO
};

static QActionGroup *currentGroup;

48
// Add static entries to menus
49
#define DP_SADD( menu, text, help, icon, slot, shortcut ) \
50
{ \
51
    if( strlen( icon ) > 0 ) \
52
    { \
53
        if( strlen( shortcut ) > 0 ) \
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
54
        { \
55 56
            menu->addAction( QIcon( icon ), text, THEDP, SLOT( slot ), \
                    qtr( shortcut ) );\
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
57 58 59
        } \
        else \
        { \
60
            menu->addAction( QIcon( icon ), text, THEDP, SLOT( slot ) );\
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
61
        } \
62 63 64
    } \
    else \
    { \
65
        if( strlen( shortcut ) > 0 ) \
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
66 67
        { \
            menu->addAction( text, THEDP, SLOT( slot ), \
68
                    qtr( shortcut ) ); \
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
69 70 71 72 73
        } \
        else \
        { \
            menu->addAction( text, THEDP, SLOT( slot ) ); \
        } \
74
    } \
75
}
76
#define MIM_SADD( menu, text, help, icon, slot ) \
77
{ \
78
    if( strlen( icon ) > 0 ) \
79 80
    { \
        QAction *action = menu->addAction( text, THEMIM, SLOT( slot ) ); \
81
        action->setIcon( QIcon( icon ) ); \
82 83 84 85 86
    } \
    else \
    { \
        menu->addAction( text, THEMIM, SLOT( slot ) ); \
    } \
87
}
Clément Stenac's avatar
Qt4:  
Clément Stenac committed
88
#define PL_SADD
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 98
    objects.push_back( 0 ); varnames.push_back( "" ); \
    i_last_separator = objects.size(); }
99 100

static int InputAutoMenuBuilder( vlc_object_t *p_object,
101 102
        vector<int> &objects,
        vector<const char *> &varnames )
103 104 105 106 107 108 109 110 111 112
{
    PUSH_VAR( "bookmark");
    PUSH_VAR( "title" );
    PUSH_VAR ("chapter" );
    PUSH_VAR( "program" );
    PUSH_VAR( "navigation" );
    PUSH_VAR( "dvd_menus" );
    return VLC_SUCCESS;
}

Clément Stenac's avatar
Qt4:  
Clément Stenac committed
113
static int VideoAutoMenuBuilder( vlc_object_t *p_object,
114 115
        vector<int> &objects,
        vector<const char *> &varnames )
116 117 118 119 120 121 122 123 124 125 126
{
    PUSH_VAR( "fullscreen" );
    PUSH_VAR( "zoom" );
    PUSH_VAR( "deinterlace" );
    PUSH_VAR( "aspect-ratio" );
    PUSH_VAR( "crop" );
    PUSH_VAR( "video-on-top" );
    PUSH_VAR( "directx-wallpaper" );
    PUSH_VAR( "video-snapshot" );

    vlc_object_t *p_dec_obj = (vlc_object_t *)vlc_object_find( p_object,
127 128
            VLC_OBJECT_DECODER,
            FIND_PARENT );
129 130
    if( p_dec_obj != NULL )
    {
131
        vlc_object_t *p_object = p_dec_obj;
132 133 134 135 136 137 138
        PUSH_VAR( "ffmpeg-pp-q" );
        vlc_object_release( p_dec_obj );
    }
    return VLC_SUCCESS;
}

static int AudioAutoMenuBuilder( vlc_object_t *p_object,
139 140
        vector<int> &objects,
        vector<const char *> &varnames )
141 142 143 144 145 146 147 148
{
    PUSH_VAR( "audio-device" );
    PUSH_VAR( "audio-channels" );
    PUSH_VAR( "visual" );
    PUSH_VAR( "equalizer" );
    return VLC_SUCCESS;
}

149 150
/*****************************************************************************
 * All normal menus
151
 * Simple Code
152 153 154 155 156 157 158 159
 *****************************************************************************/

#define BAR_ADD( func, title ) { \
    QMenu *menu = func; menu->setTitle( title  ); bar->addMenu( menu ); }

#define BAR_DADD( func, title, id ) { \
    QMenu *menu = func; menu->setTitle( title  ); bar->addMenu( menu ); \
    MenuFunc *f = new MenuFunc( menu, id ); \
Clément Stenac's avatar
Clément Stenac committed
160
    CONNECT( menu, aboutToShow(), THEDP->menusUpdateMapper, map() ); \
161 162
    THEDP->menusUpdateMapper->setMapping( menu, f ); }

163 164 165
/**
 * Main Menu Bar Creation
 **/
166
void QVLCMenu::createMenuBar( MainInterface *mi, intf_thread_t *p_intf,
167 168
        bool playlist, bool adv_controls_enabled,
        bool visual_selector_enabled )
Clément Stenac's avatar
Qt4:  
Clément Stenac committed
169
{
170
    QMenuBar *bar = mi->menuBar();
171
    BAR_ADD( FileMenu(), qtr("&Media") );
Clément Stenac's avatar
Clément Stenac committed
172 173
    if( playlist )
    {
174
        BAR_ADD( PlaylistMenu( mi,p_intf ), qtr("&Playlist" ) );
Clément Stenac's avatar
Clément Stenac committed
175
    }
176
    BAR_ADD( ToolsMenu( p_intf, mi, adv_controls_enabled,
177
                visual_selector_enabled ), qtr("&Tools") );
178 179 180
    BAR_DADD( VideoMenu( p_intf, NULL ), qtr("&Video"), 1 );
    BAR_DADD( AudioMenu( p_intf, NULL ), qtr("&Audio"), 2 );
    BAR_DADD( NavigMenu( p_intf, NULL ), qtr("&Navigation"), 3 );
181

182
    BAR_ADD( HelpMenu(), qtr("&Help" ) );
183
}
184 185 186 187 188

/**
 * Media (File) Menu
 * Opening, streaming and quit
 **/
189 190 191
QMenu *QVLCMenu::FileMenu()
{
    QMenu *menu = new QMenu();
192 193
    DP_SADD( menu, qtr("Open &File..." ), "",
            ":/pixmaps/vlc_file-asym_16px.png", openFileDialog(), "Ctrl+O" );
194 195 196 197 198 199 200 201 202 203

    /* Folder vs. Directory */
#ifdef WIN32
    DP_SADD( menu, qtr( "Open Folder..." ), "",
            ":/pixmaps/vlc_folder-grey_16px.png", openDirDialog(), "Ctrl+F" );
#else
    DP_SADD( menu, qtr( "Open Directory..." ), "",
            ":/pixmaps/vlc_folder-grey_16px.png", openDirDialog(), "Ctrl+F" );
#endif /* WIN32 */

204 205
    DP_SADD( menu, qtr("Open &Disc..." ), "", ":/pixmaps/vlc_disc_16px.png",
             openDiscDialog(), "Ctrl+D" );
206 207 208 209
    DP_SADD( menu, qtr("Open &Network..." ), "",
                ":/pixmaps/vlc_network_16px.png", openNetDialog(), "Ctrl+N" );
    DP_SADD( menu, qtr("Open &Capture Device..." ), "",
            ":/pixmaps/vlc_capture-card_16px.png", openCaptureDialog(),
210
            "Ctrl+C" );
211
    menu->addSeparator();
212 213
    DP_SADD( menu, qtr("&Streaming..."), "", ":/pixmaps/vlc_stream_16px.png",
            openThenStreamingDialogs(), "Ctrl+S" );
214 215
    DP_SADD( menu, qtr("Conve&rt / Save..."), "", "",
            openThenTranscodingDialogs(), "Ctrl+R" );
216
    menu->addSeparator();
217
    DP_SADD( menu, qtr("&Quit") , "", ":/pixmaps/vlc_quit_16px.png", quit(),
218
            "Ctrl+Q");
219 220 221
    return menu;
}

222
/* Playlist Menu, undocked when playlist is undocked */
223
QMenu *QVLCMenu::PlaylistMenu( MainInterface *mi, intf_thread_t *p_intf )
Clément Stenac's avatar
Clément Stenac committed
224 225 226
{
    QMenu *menu = new QMenu();
    menu->addMenu( SDMenu( p_intf ) );
227
    menu->addAction ( QIcon(":/pixmaps/vlc_playlist_16px.png"),
228
                      qtr( "Show Playlist"), mi, SLOT( togglePlaylist() ) );
Clément Stenac's avatar
Clément Stenac committed
229 230
    menu->addSeparator();

231 232
    DP_SADD( menu, qtr( I_PL_LOAD ), "", "", openPlaylist(), "Ctrl+X" );
    DP_SADD( menu, qtr( I_PL_SAVE ), "", "", savePlaylist(), "Ctrl+Y" );
233 234
    menu->addSeparator();
    menu->addAction( qtr("Undock from interface"), mi,
235
            SLOT( undockPlaylist() ), qtr("Ctrl+U") );
Clément Stenac's avatar
Clément Stenac committed
236 237 238
    return menu;
}

239 240
/**
 * Tools/View Menu
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
241
 * This is kept in the same menu for now, but could change if it gets much
242 243
 * longer.
 **/
Clément Stenac's avatar
Clément Stenac committed
244
QMenu *QVLCMenu::ToolsMenu( intf_thread_t *p_intf, MainInterface *mi,
245 246
        bool adv_controls_enabled,
        bool visual_selector_enabled, bool with_intf )
247 248 249 250 251
{
    QMenu *menu = new QMenu();
    if( with_intf )
    {
        QMenu *intfmenu = InterfacesMenu( p_intf, NULL );
252
        intfmenu->setTitle( qtr("Interfaces" ) );
253 254 255
        menu->addMenu( intfmenu );
        menu->addSeparator();
    }
256
    DP_SADD( menu, qtr( I_MENU_MSG ), "", ":/pixmaps/vlc_messages_16px.png",
257
             messagesDialog(), "Ctrl+M" );
258 259
    DP_SADD( menu, qtr( I_MENU_INFO ) , "", "", mediaInfoDialog(), "Ctrl+J" );
    DP_SADD( menu, qtr( I_MENU_CODECINFO ) , "", ":/pixmaps/vlc_info_16px.png",
260
             mediaCodecDialog(), "Ctrl+I" );
261
    DP_SADD( menu, qtr( I_MENU_GOTOTIME ), "","", gotoTimeDialog(), "Ctrl+T" );
262
#if 0 /* Not Implemented yet */
263 264
    DP_SADD( menu, qtr( I_MENU_BOOKMARK ), "","", bookmarksDialog(), "Ctrl+B" );
    DP_SADD( menu, qtr( I_MENU_VLM ), "","", vlmDialog(), "Ctrl+V" );
265
#endif
266

267
    menu->addSeparator();
Clément Stenac's avatar
Clément Stenac committed
268 269 270
    if( mi )
    {
        QAction *adv = menu->addAction( qtr("Advanced controls" ),
271
                mi, SLOT( toggleAdvanced() ) );
Clément Stenac's avatar
Clément Stenac committed
272 273
        adv->setCheckable( true );
        if( adv_controls_enabled ) adv->setChecked( true );
274

275 276
        menu->addAction( qtr( "Hide Menus..." ), mi, SLOT( toggleMenus() ),
                qtr( "Ctrl+H") );
277
        menu->addSeparator();
278

279
#if 0 /* For Visualisations. Not yet working */
280
        adv = menu->addAction( qtr("Visualizations selector" ),
281
                mi, SLOT( visual() ) );
282 283
        adv->setCheckable( true );
        if( visual_selector_enabled ) adv->setChecked( true );
284
#endif
285
        menu->addAction ( QIcon(":/pixmaps/vlc_playlist_16px.png"),
286
                          qtr( "Playlist"), mi, SLOT( togglePlaylist() ),
287
                          qtr( "Ctrl+L") );
Clément Stenac's avatar
Clément Stenac committed
288
    }
289 290
    DP_SADD( menu, qtr( I_MENU_EXT ), "", ":/pixmaps/vlc_settings_16px.png",
                 extendedDialog() ,  "Ctrl+E"  );
291

292
    menu->addSeparator();
293 294
    DP_SADD( menu, qtr("Preferences"), "", ":/pixmaps/vlc_preferences_16px.png",
             prefsDialog(), "Ctrl+P" );
295 296 297
    return menu;
}

298 299 300
/**
 * Interface Sub-Menu, to list extras interface and skins
 **/
301 302 303 304 305 306 307
QMenu *QVLCMenu::InterfacesMenu( intf_thread_t *p_intf, QMenu *current )
{
    vector<int> objects;
    vector<const char *> varnames;
    /** \todo add "switch to XXX" */
    varnames.push_back( "intf-add" );
    objects.push_back( p_intf->i_object_id );
Clément Stenac's avatar
Qt4:  
Clément Stenac committed
308

309
    QMenu *menu = Populate( p_intf, current, varnames, objects );
Clément Stenac's avatar
Clément Stenac committed
310 311 312 313

    if( !p_intf->pf_show_dialog )
    {
        menu->addSeparator();
314
        menu->addAction( qtr("Switch to skins"), THEDP, SLOT( switchToSkins() ),
Christophe Mutricy's avatar
Christophe Mutricy committed
315
                QString("Ctrl+Z") );
Clément Stenac's avatar
Clément Stenac committed
316 317
    }

Clément Stenac's avatar
Clément Stenac committed
318
    CONNECT( menu, aboutToShow(), THEDP->menusUpdateMapper, map() );
319 320 321 322
    THEDP->menusUpdateMapper->setMapping( menu, 4 );
    return menu;
}

323 324 325
/**
 * Main Audio Menu
 */
326 327 328 329 330 331
QMenu *QVLCMenu::AudioMenu( intf_thread_t *p_intf, QMenu * current )
{
    vector<int> objects;
    vector<const char *> varnames;

    vlc_object_t *p_object = (vlc_object_t *)vlc_object_find( p_intf,
332
            VLC_OBJECT_INPUT, FIND_ANYWHERE );
333 334 335 336 337 338 339
    if( p_object != NULL )
    {
        PUSH_VAR( "audio-es" );
        vlc_object_release( p_object );
    }

    p_object = (vlc_object_t *)vlc_object_find( p_intf, VLC_OBJECT_AOUT,
340
            FIND_ANYWHERE );
341 342 343 344 345 346 347 348
    if( p_object )
    {
        AudioAutoMenuBuilder( p_object, objects, varnames );
        vlc_object_release( p_object );
    }
    return Populate( p_intf, current, varnames, objects );
}

349 350 351 352
/**
 * Main Video Menu
 * Subtitles are part of Video.
 **/
353
QMenu *QVLCMenu::VideoMenu( intf_thread_t *p_intf, QMenu *current )
354 355
{
    vlc_object_t *p_object;
356 357 358 359
    vector<int> objects;
    vector<const char *> varnames;

    p_object = (vlc_object_t *)vlc_object_find( p_intf, VLC_OBJECT_INPUT,
360
            FIND_ANYWHERE );
361 362
    if( p_object != NULL )
    {
363 364
        PUSH_VAR( "video-es" );
        PUSH_VAR( "spu-es" );
365 366
        vlc_object_release( p_object );
    }
367 368

    p_object = (vlc_object_t *)vlc_object_find( p_intf, VLC_OBJECT_VOUT,
369
            FIND_ANYWHERE );
370 371 372 373 374 375
    if( p_object != NULL )
    {
        VideoAutoMenuBuilder( p_object, objects, varnames );
        vlc_object_release( p_object );
    }
    return Populate( p_intf, current, varnames, objects );
376 377
}

378 379 380 381
/**
 * Navigation Menu
 * For DVD, MP4, MOV and other chapter based format
 **/
382 383 384 385 386
QMenu *QVLCMenu::NavigMenu( intf_thread_t *p_intf, QMenu *current )
{
    vlc_object_t *p_object;
    vector<int> objects;
    vector<const char *> varnames;
387

388 389
    /* FIXME */
    p_object = (vlc_object_t *)vlc_object_find( p_intf, VLC_OBJECT_INPUT,
390
            FIND_ANYWHERE );
391 392 393 394 395 396 397 398 399
    if( p_object != NULL )
    {
        InputAutoMenuBuilder( p_object, objects, varnames );
        PUSH_VAR( "prev-title"); PUSH_VAR ( "next-title" );
        PUSH_VAR( "prev-chapter"); PUSH_VAR( "next-chapter" );
        vlc_object_release( p_object );
    }
    return Populate( p_intf, current, varnames, objects );
}
400

401 402 403
/**
 * Service Discovery Menu
 **/
Clément Stenac's avatar
Clément Stenac committed
404 405 406
QMenu *QVLCMenu::SDMenu( intf_thread_t *p_intf )
{
    QMenu *menu = new QMenu();
407
    menu->setTitle( qtr( I_PL_SD ) );
Clément Stenac's avatar
Clément Stenac committed
408
    vlc_list_t *p_list = vlc_list_find( p_intf, VLC_OBJECT_MODULE,
409
            FIND_ANYWHERE );
Clément Stenac's avatar
Clément Stenac committed
410 411 412 413
    int i_num = 0;
    for( int i_index = 0 ; i_index < p_list->i_count; i_index++ )
    {
        module_t * p_parser = (module_t *)p_list->p_values[i_index].p_object ;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
414
        if( module_IsCapable( p_parser, "services_discovery" ) )
Clément Stenac's avatar
Clément Stenac committed
415 416 417 418 419
            i_num++;
    }
    for( int i_index = 0 ; i_index < p_list->i_count; i_index++ )
    {
        module_t * p_parser = (module_t *)p_list->p_values[i_index].p_object;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
420 421 422
        if( !module_IsCapable( p_parser, "services_discovery" ) )
            continue;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
423
        QAction *a = new QAction( qfu( module_GetLongName( p_parser ) ), menu );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
424 425 426 427 428 429
        a->setCheckable( true );
        /* hack to handle submodules properly */
        int i = -1;
        while( p_parser->pp_shortcuts[++i] != NULL );
        i--;
        if( playlist_IsServicesDiscoveryLoaded( THEPL,
430 431
                    i>=0?p_parser->pp_shortcuts[i]
                    : module_GetObjName( p_parser ) ) )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
432 433 434
            a->setChecked( true );
        CONNECT( a , triggered(), THEDP->SDMapper, map() );
        THEDP->SDMapper->setMapping( a, i>=0? p_parser->pp_shortcuts[i] :
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
435
                                              module_GetObjName( p_parser ) );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
436
        menu->addAction( a );
Clément Stenac's avatar
Clément Stenac committed
437 438 439 440
    }
    vlc_list_release( p_list );
    return menu;
}
441 442 443
/**
 * Help/About Menu
**/
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
444 445 446
QMenu *QVLCMenu::HelpMenu()
{
    QMenu *menu = new QMenu();
447 448
    DP_SADD( menu, qtr("Help") , "", ":/pixmaps/vlc_help_16px.png",
             helpDialog(), "F1" );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
449
    menu->addSeparator();
450
    DP_SADD( menu, qtr( I_MENU_ABOUT ), "", "", aboutDialog(), "Ctrl+F1");
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
451 452 453 454
    return menu;
}


455
/*****************************************************************************
456
 * Popup menus - Right Click menus                                           *
457
 *****************************************************************************/
458 459
#define POPUP_BOILERPLATE \
    unsigned int i_last_separator = 0; \
460 461 462 463 464 465 466 467 468
    vector<int> objects; \
    vector<const char *> varnames; \
    input_thread_t *p_input = THEMIM->getInput();

#define CREATE_POPUP \
    QMenu *menu = new QMenu(); \
    Populate( p_intf, menu, varnames, objects ); \
    p_intf->p_sys->p_popup_menu = menu; \
    menu->popup( QCursor::pos() ); \
469
    p_intf->p_sys->p_popup_menu = NULL; \
470
    i_last_separator = 0;
471

472
#define POPUP_PLAY_ENTRIES( menu )\
473
    if( p_input ) \
474
    { \
475
        vlc_value_t val; \
476
        var_Get( p_input, "state", &val ); \
477
        if( val.i_int == PLAYING_S ) \
478 479
            MIM_SADD( menu, qtr("Pause"), "", ":/pixmaps/vlc_pause_16px.png", \
                    togglePlayPause() ) \
480
        else \
481 482
            MIM_SADD( menu, qtr("Play"), "", ":/pixmaps/vlc_play_16px.png", \
                    togglePlayPause() ) \
483
    } \
Clément Stenac's avatar
Clément Stenac committed
484
    else if( THEPL->items.i_size && THEPL->i_enabled ) \
485 486
        MIM_SADD( menu, qtr("Play"), "", ":/pixmaps/vlc_play_16px.png", \
                togglePlayPause() ); \
487
    \
488
    MIM_SADD( menu, qtr("Stop"), "", ":/pixmaps/vlc_stop_16px.png", stop() ); \
489 490 491
    MIM_SADD( menu, qtr("Previous"), "", ":/pixmaps/vlc_previous_16px.png", \
            prev() ); \
    MIM_SADD( menu, qtr("Next"), "", ":/pixmaps/vlc_next_16px.png", next() );
492

493
#define POPUP_STATIC_ENTRIES( menu ) \
494
    QMenu *intfmenu = InterfacesMenu( p_intf, NULL ); \
495
    intfmenu->setTitle( qtr("Interfaces" ) ); \
496 497
    menu->addMenu( intfmenu ); \
    \
498
    QMenu *toolsmenu = ToolsMenu( p_intf, NULL, false, false, false ); \
499
    toolsmenu->setTitle( qtr("Tools" ) ); \
500
    menu->addMenu( toolsmenu ); \
501 502 503 504 505 506 507 508 509 510 511 512 513 514
    \
    QMenu *openmenu = new QMenu( qtr("Open") ); \
    openmenu->addAction( qtr("Open &File..." ), THEDP, SLOT( openFileDialog() ) ); \
    openmenu->addAction( qtr("Open &Disc..." ), THEDP, SLOT( openDiscDialog() ) ); \
    openmenu->addAction( qtr("Open &Network..." ), THEDP, SLOT( openNetDialog() ) ); \
    openmenu->addAction( qtr("Open &Capture Device..." ), THEDP, \
            SLOT( openCaptureDialog() ) ); \
    menu->addMenu( openmenu ); \
    \
    menu->addSeparator(); \
    QMenu *helpmenu = HelpMenu(); \
    helpmenu->setTitle( qtr("Help") ); \
    menu->addMenu( helpmenu ); \
    \
515
    DP_SADD( menu, qtr("Quit"), "", "", quit() , "Ctrl+Q" );
Clément Stenac's avatar
Qt4:  
Clément Stenac committed
516

517
/* Video Tracks and Subtitles tracks */
518
void QVLCMenu::VideoPopupMenu( intf_thread_t *p_intf )
519 520 521 522 523
{
    POPUP_BOILERPLATE;
    if( p_input )
    {
        vlc_object_yield( p_input );
524 525 526 527
        varnames.push_back( "video-es" );
        objects.push_back( p_input->i_object_id );
        varnames.push_back( "spu-es" );
        objects.push_back( p_input->i_object_id );
528
        vlc_object_t *p_vout = (vlc_object_t *)vlc_object_find( p_input,
529
                VLC_OBJECT_VOUT, FIND_CHILD );
530 531
        if( p_vout )
        {
532
            VideoAutoMenuBuilder( p_vout, objects, varnames );
533 534 535 536
            vlc_object_release( p_vout );
        }
        vlc_object_release( p_input );
    }
537
    CREATE_POPUP;
538 539
}

540
/* Audio Tracks */
541
void QVLCMenu::AudioPopupMenu( intf_thread_t *p_intf )
542 543 544 545 546
{
    POPUP_BOILERPLATE;
    if( p_input )
    {
        vlc_object_yield( p_input );
547 548
        varnames.push_back( "audio-es" );
        objects.push_back( p_input->i_object_id );
549
        vlc_object_t *p_aout = (vlc_object_t *)vlc_object_find( p_input,
550
                VLC_OBJECT_AOUT, FIND_ANYWHERE );
551 552
        if( p_aout )
        {
553
            AudioAutoMenuBuilder( p_aout, objects, varnames );
554 555 556 557
            vlc_object_release( p_aout );
        }
        vlc_object_release( p_input );
    }
558
    CREATE_POPUP;
559 560
}

561
/* Navigation stuff, and general menus (open) */
562
void QVLCMenu::MiscPopupMenu( intf_thread_t *p_intf )
563
{
564
    vlc_value_t val;
565
    POPUP_BOILERPLATE;
566

567 568 569
    if( p_input )
    {
        vlc_object_yield( p_input );
570 571
        varnames.push_back( "audio-es" );
        InputAutoMenuBuilder( VLC_OBJECT(p_input), objects, varnames );
572 573 574
        PUSH_SEPARATOR;
    }

575 576
    QMenu *menu = new QMenu();
    Populate( p_intf, menu, varnames, objects );
577

578
    menu->addSeparator();
579 580 581 582
    POPUP_PLAY_ENTRIES( menu );

    menu->addSeparator();
    POPUP_STATIC_ENTRIES( menu );
583

584 585
    p_intf->p_sys->p_popup_menu = menu;
    menu->popup( QCursor::pos() );
586 587 588
    p_intf->p_sys->p_popup_menu = NULL;
}

589
/* Main Menu that sticks everything together  */
Damien Fouilleul's avatar
Damien Fouilleul committed
590
void QVLCMenu::PopupMenu( intf_thread_t *p_intf, bool show )
591
{
Damien Fouilleul's avatar
Damien Fouilleul committed
592
    if( show )
593
    {
594
        // create a  popup if there is none
Damien Fouilleul's avatar
Damien Fouilleul committed
595
        if( ! p_intf->p_sys->p_popup_menu )
596
        {
597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627
            POPUP_BOILERPLATE;
            if( p_input )
            {
                vlc_object_yield( p_input );
                InputAutoMenuBuilder( VLC_OBJECT(p_input), objects, varnames );

                /* Video menu */
                PUSH_SEPARATOR;
                varnames.push_back( "video-es" );
                objects.push_back( p_input->i_object_id );
                varnames.push_back( "spu-es" );
                objects.push_back( p_input->i_object_id );
                vlc_object_t *p_vout = (vlc_object_t *)vlc_object_find( p_input,
                        VLC_OBJECT_VOUT, FIND_CHILD );
                if( p_vout )
                {
                    VideoAutoMenuBuilder( p_vout, objects, varnames );
                    vlc_object_release( p_vout );
                }
                /* Audio menu */
                PUSH_SEPARATOR
                    varnames.push_back( "audio-es" );
                objects.push_back( p_input->i_object_id );
                vlc_object_t *p_aout = (vlc_object_t *)vlc_object_find( p_input,
                        VLC_OBJECT_AOUT, FIND_ANYWHERE );
                if( p_aout )
                {
                    AudioAutoMenuBuilder( p_aout, objects, varnames );
                    vlc_object_release( p_aout );
                }
            }
628

629 630 631
            QMenu *menu = new QMenu();
            Populate( p_intf, menu, varnames, objects );
            menu->addSeparator();
632 633 634
            POPUP_PLAY_ENTRIES( menu );
            menu->addSeparator();
            POPUP_STATIC_ENTRIES( menu );
635

636 637 638
            p_intf->p_sys->p_popup_menu = menu;
        }
        p_intf->p_sys->p_popup_menu->popup( QCursor::pos() );
Damien Fouilleul's avatar
Damien Fouilleul committed
639 640
    }
    else
641
    {
642 643 644
        // destroy popup if there is one
        delete p_intf->p_sys->p_popup_menu;
        p_intf->p_sys->p_popup_menu = NULL;
645 646 647
    }
}

648 649 650 651
/************************************************************************
 * Systray Menu                                                         *
 ************************************************************************/

652 653 654
void QVLCMenu::updateSystrayMenu( MainInterface *mi,
                                  intf_thread_t *p_intf,
                                  bool b_force_visible )
655 656
{
    POPUP_BOILERPLATE;
657 658

    /* Get the systray menu and clean it */
659 660
    QMenu *sysMenu = mi->getSysTrayMenu();
    sysMenu->clear();
661 662

    /* Hide / Show VLC and cone */
663
    if( mi->isVisible() || b_force_visible )
664
    {
665 666
        sysMenu->addAction( QIcon( ":/vlc16.png" ),
                qtr("Hide VLC media player"), mi,
667
                SLOT( toggleUpdateSystrayMenu() ) );
668 669 670
    }
    else
    {
671 672
        sysMenu->addAction( QIcon( ":/vlc16.png" ),
                qtr("Show VLC media player"), mi,
673
                SLOT( toggleUpdateSystrayMenu() ) );
674
    }
675

676 677
    sysMenu->addSeparator();
    POPUP_PLAY_ENTRIES( sysMenu );
678

679
    sysMenu->addSeparator();
680 681
    DP_SADD( sysMenu, qtr("&Open Media" ), "",
            ":/pixmaps/vlc_file-wide_16px.png", openFileDialog(), "" );
682 683
    DP_SADD( sysMenu, qtr("&Quit") , "", ":/pixmaps/vlc_quit_16px.png",
             quit(), "" );
684

685
    /* Set the menu */
686 687 688
    mi->getSysTray()->setContextMenu( sysMenu );
}

689 690
#undef PUSH_VAR
#undef PUSH_SEPARATOR
691

692

693 694 695
/*************************************************************************
 * Builders for automenus
 *************************************************************************/
696
QMenu * QVLCMenu::Populate( intf_thread_t *p_intf, QMenu *current,
697 698
        vector< const char *> & varnames,
        vector<int> & objects, bool append )
699 700 701 702
{
    QMenu *menu = current;
    if( !menu )
        menu = new QMenu();
703
    else if( !append )
704 705 706 707 708 709 710 711
        menu->clear();

    currentGroup = NULL;

    vlc_object_t *p_object;
    vlc_bool_t b_section_empty = VLC_FALSE;
    int i;

712
#define APPEND_EMPTY { QAction *action = menu->addAction( qtr("Empty" ) ); \
713
    action->setEnabled( false ); }
714

715
    for( i = 0; i < (int)objects.size() ; i++ )
716
    {
717
        if( !varnames[i] || !*varnames[i] )
718 719 720 721 722 723 724 725
        {
            if( b_section_empty )
                APPEND_EMPTY;
            menu->addSeparator();
            b_section_empty = VLC_TRUE;
            continue;
        }

726
        if( objects[i] == 0  )
727 728
        {
            /// \bug What is this ?
729
            // Append( menu, varnames[i], NULL );
730 731 732 733 734
            b_section_empty = VLC_FALSE;
            continue;
        }

        p_object = (vlc_object_t *)vlc_object_get( p_intf,
735
                objects[i] );
736 737 738
        if( p_object == NULL ) continue;

        b_section_empty = VLC_FALSE;
739 740 741 742 743
        /* Ugly specific stuff */
        if( strstr(varnames[i], "intf-add" ) )
            CreateItem( menu, varnames[i], p_object, false );
        else
            CreateItem( menu, varnames[i], p_object, true );
744 745 746 747 748 749 750
        vlc_object_release( p_object );
    }

    /* Special case for empty menus */
    if( menu->actions().size() == 0 || b_section_empty )
        APPEND_EMPTY

751
            return menu;
752 753 754 755 756 757 758
}

/*****************************************************************************
 * Private methods.
 *****************************************************************************/

static bool IsMenuEmpty( const char *psz_var, vlc_object_t *p_object,
759
        bool b_root = TRUE )
760 761 762 763 764 765 766 767 768 769 770 771 772 773 774
{
    vlc_value_t val, val_list;
    int i_type, i_result, i;

    /* Check the type of the object variable */
    i_type = var_Type( p_object, psz_var );

    /* Check if we want to display the variable */
    if( !(i_type & VLC_VAR_HASCHOICE) ) return FALSE;

    var_Change( p_object, psz_var, VLC_VAR_CHOICESCOUNT, &val, NULL );
    if( val.i_int == 0 ) return TRUE;

    if( (i_type & VLC_VAR_TYPE) != VLC_VAR_VARIABLE )
    {
775
        /* Very evil hack ! intf-switch can have only one value */
776 777 778 779 780 781 782 783 784 785 786 787 788 789
        if( !strcmp( psz_var, "intf-switch" ) ) return FALSE;
        if( val.i_int == 1 && b_root ) return TRUE;
        else return FALSE;
    }

    /* Check children variables in case of VLC_VAR_VARIABLE */
    if( var_Change( p_object, psz_var, VLC_VAR_GETLIST, &val_list, NULL ) < 0 )
    {
        return TRUE;
    }

    for( i = 0, i_result = TRUE; i < val_list.p_list->i_count; i++ )
    {
        if( !IsMenuEmpty( val_list.p_list->p_values[i].psz_string,
790
                    p_object, FALSE ) )
791 792 793 794 795 796 797 798 799 800 801 802 803
        {
            i_result = FALSE;
            break;
        }
    }

    /* clean up everything */
    var_Change( p_object, psz_var, VLC_VAR_FREELIST, &val_list, NULL );

    return i_result;
}

void QVLCMenu::CreateItem( QMenu *menu, const char *psz_var,
804
        vlc_object_t *p_object, bool b_submenu )
805 806 807 808 809 810 811 812 813
{
    vlc_value_t val, text;
    int i_type;

    /* Check the type of the object variable */
    i_type = var_Type( p_object, psz_var );

    switch( i_type & VLC_VAR_TYPE )
    {
814 815 816 817 818 819 820 821 822 823
        case VLC_VAR_VOID:
        case VLC_VAR_BOOL:
        case VLC_VAR_VARIABLE:
        case VLC_VAR_STRING:
        case VLC_VAR_INTEGER:
        case VLC_VAR_FLOAT:
            break;
        default:
            /* Variable doesn't exist or isn't handled */
            return;
824 825 826 827 828 829 830 831 832 833 834
    }

    /* Make sure we want to display the variable */
    if( IsMenuEmpty( psz_var, p_object ) )  return;

    /* Get the descriptive name of the variable */
    var_Change( p_object, psz_var, VLC_VAR_GETTEXT, &text, NULL );

    if( i_type & VLC_VAR_HASCHOICE )
    {
        /* Append choices menu */
835 836 837
        if( b_submenu )
        {
            QMenu *submenu = new QMenu();
Clément Stenac's avatar
Qt4:  
Clément Stenac committed
838
            submenu->setTitle( qfu( text.psz_string ?
839
                        text.psz_string : psz_var ) );
840 841 842 843 844
            if( CreateChoicesMenu( submenu, psz_var, p_object, true ) == 0)
                menu->addMenu( submenu );
        }
        else
            CreateChoicesMenu( menu, psz_var, p_object, true );
845
        FREENULL( text.psz_string );
846 847 848
        return;
    }

849
#define TEXT_OR_VAR qfu ( text.psz_string ? text.psz_string : psz_var )
850 851 852

    switch( i_type & VLC_VAR_TYPE )
    {
853 854 855 856 857 858 859 860 861 862 863 864
        case VLC_VAR_VOID:
            var_Get( p_object, psz_var, &val );
            CreateAndConnect( menu, psz_var, TEXT_OR_VAR, "", ITEM_NORMAL,
                    p_object->i_object_id, val, i_type );
            break;

        case VLC_VAR_BOOL:
            var_Get( p_object, psz_var, &val );
            val.b_bool = !val.b_bool;
            CreateAndConnect( menu, psz_var, TEXT_OR_VAR, "", ITEM_CHECK,
                    p_object->i_object_id, val, i_type, !val.b_bool );
            break;
865
    }
866
    FREENULL( text.psz_string );
867 868 869
}


870
int QVLCMenu::CreateChoicesMenu( QMenu *submenu, const char *psz_var,
871
        vlc_object_t *p_object, bool b_root )
872 873 874 875 876 877 878 879
{
    vlc_value_t val, val_list, text_list;
    int i_type, i;

    /* Check the type of the object variable */
    i_type = var_Type( p_object, psz_var );

    /* Make sure we want to display the variable */
880
    if( IsMenuEmpty( psz_var, p_object, b_root ) ) return VLC_EGENERIC;
881 882 883

    switch( i_type & VLC_VAR_TYPE )
    {
884 885 886 887 888 889 890 891 892 893
        case VLC_VAR_VOID:
        case VLC_VAR_BOOL:
        case VLC_VAR_VARIABLE:
        case VLC_VAR_STRING:
        case VLC_VAR_INTEGER:
        case VLC_VAR_FLOAT:
            break;
        default:
            /* Variable doesn't exist or isn't handled */
            return VLC_EGENERIC;
894 895 896
    }

    if( var_Change( p_object, psz_var, VLC_VAR_GETLIST,
897
                &val_list, &text_list ) < 0 )
898
    {
899
        return VLC_EGENERIC;
900 901
    }
#define NORMAL_OR_RADIO i_type & VLC_VAR_ISCOMMAND ? ITEM_NORMAL: ITEM_RADIO
Clément Stenac's avatar
Qt4:  
Clément Stenac committed
902
#define NOTCOMMAND !(i_type & VLC_VAR_ISCOMMAND)
903 904 905 906 907 908 909
#define CURVAL val_list.p_list->p_values[i]
#define CURTEXT text_list.p_list->p_values[i].psz_string

    for( i = 0; i < val_list.p_list->i_count; i++ )
    {
        vlc_value_t another_val;
        QString menutext;
910
        QMenu *subsubmenu = new QMenu();
911 912 913

        switch( i_type & VLC_VAR_TYPE )
        {
914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951
            case VLC_VAR_VARIABLE:
                CreateChoicesMenu( subsubmenu, CURVAL.psz_string, p_object, false );
                subsubmenu->setTitle( qfu( CURTEXT ? CURTEXT :CURVAL.psz_string ) );
                submenu->addMenu( subsubmenu );
                break;

            case VLC_VAR_STRING:
                var_Get( p_object, psz_var, &val );
                another_val.psz_string = strdup( CURVAL.psz_string );
                menutext = qfu( CURTEXT ? CURTEXT : another_val.psz_string );
                CreateAndConnect( submenu, psz_var, menutext, "", NORMAL_OR_RADIO,
                        p_object->i_object_id, another_val, i_type,
                        NOTCOMMAND && val.psz_string &&
                        !strcmp( val.psz_string, CURVAL.psz_string ) );

                if( val.psz_string ) free( val.psz_string );
                break;

            case VLC_VAR_INTEGER:
                var_Get( p_object, psz_var, &val );
                if( CURTEXT ) menutext = qfu( CURTEXT );
                else menutext.sprintf( "%d", CURVAL.i_int);
                CreateAndConnect( submenu, psz_var, menutext, "", NORMAL_OR_RADIO,
                        p_object->i_object_id, CURVAL, i_type,
                        NOTCOMMAND && CURVAL.i_int == val.i_int );
                break;

            case VLC_VAR_FLOAT:
                var_Get( p_object, psz_var, &val );
                if( CURTEXT ) menutext = qfu( CURTEXT );
                else menutext.sprintf( "%.2f", CURVAL.f_float );
                CreateAndConnect( submenu, psz_var, menutext, "", NORMAL_OR_RADIO,
                        p_object->i_object_id, CURVAL, i_type,
                        NOTCOMMAND && CURVAL.f_float == val.f_float );
                break;

            default:
                break;
952 953
        }
    }
954
    currentGroup = NULL;
955 956 957 958 959 960 961 962

    /* clean up everything */
    var_Change( p_object, psz_var, VLC_VAR_FREELIST, &val_list, &text_list );

#undef NORMAL_OR_RADIO
#undef NOTCOMMAND
#undef CURVAL
#undef CURTEXT
963
    return VLC_SUCCESS;