menus.cpp 32.1 KB
Newer Older
1 2 3
/*****************************************************************************
 * menus.cpp : Qt menus
 *****************************************************************************
4
 * Copyright (C) 2006-2007 the VideoLAN team
5
 * $Id$
6
 *
7
 * Authors: Clment 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 26 27 28
#ifndef WIN32
#   include <signal.h>
#endif

29
#include <vlc_intf_strings.h>
30

31
#include "main_interface.hpp"
Clément Stenac's avatar
Clément Stenac committed
32 33 34 35
#include "menus.hpp"
#include "dialogs_provider.hpp"
#include "input_manager.hpp"

36 37 38 39 40 41 42
#include <QMenu>
#include <QMenuBar>
#include <QAction>
#include <QActionGroup>
#include <QSignalMapper>
#include <QSystemTrayIcon>

43 44 45 46 47 48 49 50 51
enum
{
    ITEM_NORMAL,
    ITEM_CHECK,
    ITEM_RADIO
};

static QActionGroup *currentGroup;

52
// Add static entries to menus
53
#define DP_SADD( menu, text, help, icon, slot, shortcut ) \
54
{ \
55
    if( strlen( icon ) > 0 ) \
56
    { \
57
        if( strlen( shortcut ) > 0 ) \
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
58
        { \
59 60
            menu->addAction( QIcon( icon ), text, THEDP, SLOT( slot ), \
                    qtr( shortcut ) );\
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
61 62 63
        } \
        else \
        { \
64
            menu->addAction( QIcon( icon ), text, THEDP, SLOT( slot ) );\
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
65
        } \
66 67 68
    } \
    else \
    { \
69
        if( strlen( shortcut ) > 0 ) \
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
70 71
        { \
            menu->addAction( text, THEDP, SLOT( slot ), \
72
                    qtr( shortcut ) ); \
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
73 74 75 76 77
        } \
        else \
        { \
            menu->addAction( text, THEDP, SLOT( slot ) ); \
        } \
78
    } \
79
}
80
#define MIM_SADD( menu, text, help, icon, slot ) \
81
{ \
82
    if( strlen( icon ) > 0 ) \
83 84
    { \
        QAction *action = menu->addAction( text, THEMIM, SLOT( slot ) ); \
85
        action->setIcon( QIcon( icon ) ); \
86 87 88 89 90
    } \
    else \
    { \
        menu->addAction( text, THEMIM, SLOT( slot ) ); \
    } \
91
}
Clément Stenac's avatar
Qt4:  
Clément Stenac committed
92
#define PL_SADD
93 94

/*****************************************************************************
95
 * Definitions of variables for the dynamic menus
96
 *****************************************************************************/
97
#define PUSH_VAR( var ) varnames.push_back( var ); \
98
    objects.push_back( p_object->i_object_id )
99 100

#define PUSH_SEPARATOR if( objects.size() != i_last_separator ) { \
101 102
    objects.push_back( 0 ); varnames.push_back( "" ); \
    i_last_separator = objects.size(); }
103 104

static int InputAutoMenuBuilder( vlc_object_t *p_object,
105 106
        vector<int> &objects,
        vector<const char *> &varnames )
107 108 109 110 111 112 113 114 115 116
{
    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
117
static int VideoAutoMenuBuilder( vlc_object_t *p_object,
118 119
        vector<int> &objects,
        vector<const char *> &varnames )
120 121 122 123 124 125 126 127 128 129 130
{
    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,
131 132
            VLC_OBJECT_DECODER,
            FIND_PARENT );
133 134
    if( p_dec_obj != NULL )
    {
135
        vlc_object_t *p_object = p_dec_obj;
136 137 138 139 140 141 142
        PUSH_VAR( "ffmpeg-pp-q" );
        vlc_object_release( p_dec_obj );
    }
    return VLC_SUCCESS;
}

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

153 154
/*****************************************************************************
 * All normal menus
155
 * Simple Code
156 157 158 159 160 161 162 163
 *****************************************************************************/

#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
164
    CONNECT( menu, aboutToShow(), THEDP->menusUpdateMapper, map() ); \
165 166
    THEDP->menusUpdateMapper->setMapping( menu, f ); }

167 168 169
/**
 * Main Menu Bar Creation
 **/
170
void QVLCMenu::createMenuBar( MainInterface *mi, intf_thread_t *p_intf,
171 172
        bool playlist, bool adv_controls_enabled,
        bool visual_selector_enabled )
Clément Stenac's avatar
Qt4:  
Clément Stenac committed
173
{
174
#ifndef WIN32
175
    /* Ugly klugde
176 177 178 179 180 181
     * Remove SIGCHLD from the ignored signal the time to initialise
     * Qt because it call gconf to get the icon theme */
    sigset_t set;

    sigemptyset( &set );
    sigaddset( &set, SIGCHLD );
182
    pthread_sigmask( SIG_UNBLOCK, &set, NULL );
183
#endif /* WIN32 */
184
    QMenuBar *bar = mi->menuBar();
185
#ifndef WIN32
186
    pthread_sigmask( SIG_BLOCK, &set, NULL );
187
#endif /* WIN32 */
188
    BAR_ADD( FileMenu(), qtr("&Media") );
Clément Stenac's avatar
Clément Stenac committed
189 190
    if( playlist )
    {
191
        BAR_ADD( PlaylistMenu( mi,p_intf ), qtr("&Playlist" ) );
Clément Stenac's avatar
Clément Stenac committed
192
    }
193
    BAR_ADD( ToolsMenu( p_intf, mi, adv_controls_enabled,
194
                visual_selector_enabled ), qtr("&Tools") );
195 196 197
    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 );
198

199
    BAR_ADD( HelpMenu(), qtr("&Help" ) );
200
}
201 202 203 204 205

/**
 * Media (File) Menu
 * Opening, streaming and quit
 **/
206 207 208
QMenu *QVLCMenu::FileMenu()
{
    QMenu *menu = new QMenu();
209 210
    DP_SADD( menu, qtr("Open &File..." ), "",
            ":/pixmaps/vlc_file-asym_16px.png", openFileDialog(), "Ctrl+O" );
211 212 213 214 215 216 217 218 219 220

    /* 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 */

221 222
    DP_SADD( menu, qtr("Open &Disc..." ), "", ":/pixmaps/vlc_disc_16px.png",
             openDiscDialog(), "Ctrl+D" );
223 224 225 226
    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(),
227
            "Ctrl+C" );
228
    menu->addSeparator();
229 230
    DP_SADD( menu, qtr("&Streaming..."), "", ":/pixmaps/vlc_stream_16px.png",
            openThenStreamingDialogs(), "Ctrl+S" );
231 232
    DP_SADD( menu, qtr("Conve&rt / Save..."), "", "",
            openThenTranscodingDialogs(), "Ctrl+R" );
233
    menu->addSeparator();
234
    DP_SADD( menu, qtr("&Quit") , "", ":/pixmaps/vlc_quit_16px.png", quit(),
235
            "Ctrl+Q");
236 237 238
    return menu;
}

239
/* Playlist Menu, undocked when playlist is undocked */
240
QMenu *QVLCMenu::PlaylistMenu( MainInterface *mi, intf_thread_t *p_intf )
Clément Stenac's avatar
Clément Stenac committed
241 242 243
{
    QMenu *menu = new QMenu();
    menu->addMenu( SDMenu( p_intf ) );
244
    menu->addAction ( QIcon(":/pixmaps/vlc_playlist_16px.png"),
245
                      qtr( "Show Playlist"), mi, SLOT( playlist() ) );
Clément Stenac's avatar
Clément Stenac committed
246 247
    menu->addSeparator();

248 249
    DP_SADD( menu, qtr( I_PL_LOAD ), "", "", openPlaylist(), "Ctrl+X" );
    DP_SADD( menu, qtr( I_PL_SAVE ), "", "", savePlaylist(), "Ctrl+Y" );
250 251
    menu->addSeparator();
    menu->addAction( qtr("Undock from interface"), mi,
252
            SLOT( undockPlaylist() ), qtr("Ctrl+U") );
Clément Stenac's avatar
Clément Stenac committed
253 254 255
    return menu;
}

256 257 258 259 260
/**
 * Tools/View Menu
 * This is kept in the same menu for now, but could change if it gets much 
 * longer.
 **/
Clément Stenac's avatar
Clément Stenac committed
261
QMenu *QVLCMenu::ToolsMenu( intf_thread_t *p_intf, MainInterface *mi,
262 263
        bool adv_controls_enabled,
        bool visual_selector_enabled, bool with_intf )
264 265 266 267 268
{
    QMenu *menu = new QMenu();
    if( with_intf )
    {
        QMenu *intfmenu = InterfacesMenu( p_intf, NULL );
269
        intfmenu->setTitle( qtr("Interfaces" ) );
270 271 272
        menu->addMenu( intfmenu );
        menu->addSeparator();
    }
273
    DP_SADD( menu, qtr( I_MENU_MSG ), "", ":/pixmaps/vlc_messages_16px.png",
274
             messagesDialog(), "Ctrl+M" );
275 276
    DP_SADD( menu, qtr( I_MENU_INFO ) , "", "", mediaInfoDialog(), "Ctrl+J" );
    DP_SADD( menu, qtr( I_MENU_CODECINFO ) , "", ":/pixmaps/vlc_info_16px.png",
277
             mediaCodecDialog(), "Ctrl+I" );
278
    DP_SADD( menu, qtr( I_MENU_GOTOTIME ), "","", gotoTimeDialog(), "Ctrl+T" );
279
#if 0 /* Not Implemented yet */
280 281
    DP_SADD( menu, qtr( I_MENU_BOOKMARK ), "","", bookmarksDialog(), "Ctrl+B" );
    DP_SADD( menu, qtr( I_MENU_VLM ), "","", vlmDialog(), "Ctrl+V" );
282
#endif
283

284
    menu->addSeparator();
Clément Stenac's avatar
Clément Stenac committed
285 286 287
    if( mi )
    {
        QAction *adv = menu->addAction( qtr("Advanced controls" ),
288
                mi, SLOT( advanced() ) );
Clément Stenac's avatar
Clément Stenac committed
289 290
        adv->setCheckable( true );
        if( adv_controls_enabled ) adv->setChecked( true );
291

292 293
        menu->addAction( qtr( "Hide Menus..." ), mi, SLOT( toggleMenus() ),
                qtr( "Ctrl+H") );
294
        menu->addSeparator();
295

296
#if 0 /* For Visualisations. Not yet working */
297
        adv = menu->addAction( qtr("Visualizations selector" ),
298
                mi, SLOT( visual() ) );
299 300
        adv->setCheckable( true );
        if( visual_selector_enabled ) adv->setChecked( true );
301
#endif
302
        menu->addAction ( QIcon(":/pixmaps/vlc_playlist_16px.png"),
303 304
                          qtr( "Playlist"), mi, SLOT( playlist() ),
                          qtr( "Ctrl+L") );
Clément Stenac's avatar
Clément Stenac committed
305
    }
306 307
    DP_SADD( menu, qtr( I_MENU_EXT ), "", ":/pixmaps/vlc_settings_16px.png",
                 extendedDialog() ,  "Ctrl+E"  );
308

309
    menu->addSeparator();
310 311
    DP_SADD( menu, qtr("Preferences"), "", ":/pixmaps/vlc_preferences_16px.png",
             prefsDialog(), "Ctrl+P" );
312 313 314
    return menu;
}

315 316 317
/**
 * Interface Sub-Menu, to list extras interface and skins
 **/
318 319 320 321 322 323 324
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
325

326
    QMenu *menu = Populate( p_intf, current, varnames, objects );
Clément Stenac's avatar
Clément Stenac committed
327 328 329 330

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

Clément Stenac's avatar
Clément Stenac committed
335
    CONNECT( menu, aboutToShow(), THEDP->menusUpdateMapper, map() );
336 337 338 339
    THEDP->menusUpdateMapper->setMapping( menu, 4 );
    return menu;
}

340 341 342
/**
 * Main Audio Menu
 */
343 344 345 346 347 348
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,
349
            VLC_OBJECT_INPUT, FIND_ANYWHERE );
350 351 352 353 354 355 356
    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,
357
            FIND_ANYWHERE );
358 359 360 361 362 363 364 365
    if( p_object )
    {
        AudioAutoMenuBuilder( p_object, objects, varnames );
        vlc_object_release( p_object );
    }
    return Populate( p_intf, current, varnames, objects );
}

366 367 368 369
/**
 * Main Video Menu
 * Subtitles are part of Video.
 **/
370
QMenu *QVLCMenu::VideoMenu( intf_thread_t *p_intf, QMenu *current )
371 372
{
    vlc_object_t *p_object;
373 374 375 376
    vector<int> objects;
    vector<const char *> varnames;

    p_object = (vlc_object_t *)vlc_object_find( p_intf, VLC_OBJECT_INPUT,
377
            FIND_ANYWHERE );
378 379
    if( p_object != NULL )
    {
380 381
        PUSH_VAR( "video-es" );
        PUSH_VAR( "spu-es" );
382 383
        vlc_object_release( p_object );
    }
384 385

    p_object = (vlc_object_t *)vlc_object_find( p_intf, VLC_OBJECT_VOUT,
386
            FIND_ANYWHERE );
387 388 389 390 391 392
    if( p_object != NULL )
    {
        VideoAutoMenuBuilder( p_object, objects, varnames );
        vlc_object_release( p_object );
    }
    return Populate( p_intf, current, varnames, objects );
393 394
}

395 396 397 398
/**
 * Navigation Menu
 * For DVD, MP4, MOV and other chapter based format
 **/
399 400 401 402 403
QMenu *QVLCMenu::NavigMenu( intf_thread_t *p_intf, QMenu *current )
{
    vlc_object_t *p_object;
    vector<int> objects;
    vector<const char *> varnames;
404

405 406
    /* FIXME */
    p_object = (vlc_object_t *)vlc_object_find( p_intf, VLC_OBJECT_INPUT,
407
            FIND_ANYWHERE );
408 409 410 411 412 413 414 415 416
    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 );
}
417

418 419 420
/**
 * Service Discovery Menu
 **/
Clément Stenac's avatar
Clément Stenac committed
421 422 423
QMenu *QVLCMenu::SDMenu( intf_thread_t *p_intf )
{
    QMenu *menu = new QMenu();
424
    menu->setTitle( qtr( I_PL_SD ) );
Clément Stenac's avatar
Clément Stenac committed
425
    vlc_list_t *p_list = vlc_list_find( p_intf, VLC_OBJECT_MODULE,
426
            FIND_ANYWHERE );
Clément Stenac's avatar
Clément Stenac committed
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445
    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 ;
        if( !strcmp( p_parser->psz_capability, "services_discovery" ) )
            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;
        if( !strcmp( p_parser->psz_capability, "services_discovery" ) )
        {
            QAction *a = new QAction( qfu( p_parser->psz_longname ), menu );
            a->setCheckable( true );
            /* hack to handle submodules properly */
            int i = -1;
            while( p_parser->pp_shortcuts[++i] != NULL );
            i--;
            if( playlist_IsServicesDiscoveryLoaded( THEPL,
446 447
                        i>=0?p_parser->pp_shortcuts[i]
                        : p_parser->psz_object_name ) )
Clément Stenac's avatar
Clément Stenac committed
448 449 450 451 452
            {
                a->setChecked( true );
            }
            CONNECT( a , triggered(), THEDP->SDMapper, map() );
            THEDP->SDMapper->setMapping( a, i>=0? p_parser->pp_shortcuts[i] :
453
                    p_parser->psz_object_name );
Clément Stenac's avatar
Clément Stenac committed
454 455 456 457 458 459
            menu->addAction( a );
        }
    }
    vlc_list_release( p_list );
    return menu;
}
460 461 462
/**
 * Help/About Menu
**/
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
463 464 465
QMenu *QVLCMenu::HelpMenu()
{
    QMenu *menu = new QMenu();
466 467
    DP_SADD( menu, qtr("Help") , "", ":/pixmaps/vlc_help_16px.png",
             helpDialog(), "F1" );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
468
    menu->addSeparator();
469
    DP_SADD( menu, qtr( I_MENU_ABOUT ), "", "", aboutDialog(), "Ctrl+F1");
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
470 471 472 473
    return menu;
}


474
/*****************************************************************************
475
 * Popup menus - Right Click menus                                           *
476
 *****************************************************************************/
477 478
#define POPUP_BOILERPLATE \
    unsigned int i_last_separator = 0; \
479 480 481 482 483 484 485 486 487
    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() ); \
488
    p_intf->p_sys->p_popup_menu = NULL; \
489
    i_last_separator = 0;
490

491
#define POPUP_PLAY_ENTRIES( menu )\
492 493
    vlc_value_t val; \
    if( p_input ) \
494 495
    { \
        var_Get( p_input, "state", &val ); \
496
        if( val.i_int == PLAYING_S ) \
497 498
            MIM_SADD( menu, qtr("Pause"), "", ":/pixmaps/vlc_pause_16px.png", \
                    togglePlayPause() ) \
499
        else \
500 501
            MIM_SADD( menu, qtr("Play"), "", ":/pixmaps/vlc_play_16px.png", \
                    togglePlayPause() ) \
502
    } \
Clément Stenac's avatar
Clément Stenac committed
503
    else if( THEPL->items.i_size && THEPL->i_enabled ) \
504 505
        MIM_SADD( menu, qtr("Play"), "", ":/pixmaps/vlc_play_16px.png", \
                togglePlayPause() ); \
506
    \
507
    MIM_SADD( menu, qtr("Stop"), "", ":/pixmaps/vlc_stop_16px.png", stop() ); \
508 509 510
    MIM_SADD( menu, qtr("Previous"), "", ":/pixmaps/vlc_previous_16px.png", \
            prev() ); \
    MIM_SADD( menu, qtr("Next"), "", ":/pixmaps/vlc_next_16px.png", next() );
511 512 513

#define POPUP_STATIC_ENTRIES \
    POPUP_PLAY_ENTRIES( menu ); \
514
    \
515
    menu->addSeparator(); \
516
    QMenu *intfmenu = InterfacesMenu( p_intf, NULL ); \
517
    intfmenu->setTitle( qtr("Interfaces" ) ); \
518 519
    menu->addMenu( intfmenu ); \
    \
520
    QMenu *toolsmenu = ToolsMenu( p_intf, NULL, false, false, false ); \
521
    toolsmenu->setTitle( qtr("Tools" ) ); \
522
    menu->addMenu( toolsmenu ); \
523 524 525 526 527 528 529 530 531 532 533 534 535 536
    \
    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 ); \
    \
537
    DP_SADD( menu, qtr("Quit"), "", "", quit() , "Ctrl+Q" );
Clément Stenac's avatar
Qt4:  
Clément Stenac committed
538

539
/* Video Tracks and Subtitles tracks */
540
void QVLCMenu::VideoPopupMenu( intf_thread_t *p_intf )
541 542 543 544 545
{
    POPUP_BOILERPLATE;
    if( p_input )
    {
        vlc_object_yield( p_input );
546 547 548 549
        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 );
550
        vlc_object_t *p_vout = (vlc_object_t *)vlc_object_find( p_input,
551
                VLC_OBJECT_VOUT, FIND_CHILD );
552 553
        if( p_vout )
        {
554
            VideoAutoMenuBuilder( p_vout, objects, varnames );
555 556 557 558
            vlc_object_release( p_vout );
        }
        vlc_object_release( p_input );
    }
559
    CREATE_POPUP;
560 561
}

562
/* Audio Tracks */
563
void QVLCMenu::AudioPopupMenu( intf_thread_t *p_intf )
564 565 566 567 568
{
    POPUP_BOILERPLATE;
    if( p_input )
    {
        vlc_object_yield( p_input );
569 570
        varnames.push_back( "audio-es" );
        objects.push_back( p_input->i_object_id );
571
        vlc_object_t *p_aout = (vlc_object_t *)vlc_object_find( p_input,
572
                VLC_OBJECT_AOUT, FIND_ANYWHERE );
573 574
        if( p_aout )
        {
575
            AudioAutoMenuBuilder( p_aout, objects, varnames );
576 577 578 579
            vlc_object_release( p_aout );
        }
        vlc_object_release( p_input );
    }
580
    CREATE_POPUP;
581 582
}

583
/* Navigation stuff, and general menus (open) */
584
void QVLCMenu::MiscPopupMenu( intf_thread_t *p_intf )
585 586 587 588 589
{
    POPUP_BOILERPLATE;
    if( p_input )
    {
        vlc_object_yield( p_input );
590 591
        varnames.push_back( "audio-es" );
        InputAutoMenuBuilder( VLC_OBJECT(p_input), objects, varnames );
592 593 594
        PUSH_SEPARATOR;
    }

595 596 597
    QMenu *menu = new QMenu();
    Populate( p_intf, menu, varnames, objects );
    menu->addSeparator();
598 599
    POPUP_STATIC_ENTRIES;

600 601
    p_intf->p_sys->p_popup_menu = menu;
    menu->popup( QCursor::pos() );
602 603 604
    p_intf->p_sys->p_popup_menu = NULL;
}

605
/* Main Menu that sticks everything together  */
Damien Fouilleul's avatar
Damien Fouilleul committed
606
void QVLCMenu::PopupMenu( intf_thread_t *p_intf, bool show )
607
{
Damien Fouilleul's avatar
Damien Fouilleul committed
608
    if( show )
609
    {
610
        // create a  popup if there is none
Damien Fouilleul's avatar
Damien Fouilleul committed
611
        if( ! p_intf->p_sys->p_popup_menu )
612
        {
613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643
            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 );
                }
            }
644

645 646 647 648
            QMenu *menu = new QMenu();
            Populate( p_intf, menu, varnames, objects );
            menu->addSeparator();
            POPUP_STATIC_ENTRIES;
649

650 651 652
            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
653 654
    }
    else
655
    {
656 657 658
        // destroy popup if there is one
        delete p_intf->p_sys->p_popup_menu;
        p_intf->p_sys->p_popup_menu = NULL;
659 660 661
    }
}

662 663 664 665
/************************************************************************
 * Systray Menu                                                         *
 ************************************************************************/

666 667 668
void QVLCMenu::updateSystrayMenu( MainInterface *mi,
                                  intf_thread_t *p_intf,
                                  bool b_force_visible )
669 670
{
    POPUP_BOILERPLATE;
671 672

    /* Get the systray menu and clean it */
673 674
    QMenu *sysMenu = mi->getSysTrayMenu();
    sysMenu->clear();
675 676

    /* Hide / Show VLC and cone */
677
    if( mi->isVisible() || b_force_visible )
678
    {
679 680
        sysMenu->addAction( QIcon( ":/vlc16.png" ),
                qtr("Hide VLC media player"), mi,
681
                SLOT( toggleUpdateSystrayMenu() ) );
682 683 684
    }
    else
    {
685 686
        sysMenu->addAction( QIcon( ":/vlc16.png" ),
                qtr("Show VLC media player"), mi,
687
                SLOT( toggleUpdateSystrayMenu() ) );
688
    }
689

690 691
    sysMenu->addSeparator();
    POPUP_PLAY_ENTRIES( sysMenu );
692

693
    sysMenu->addSeparator();
694 695
    DP_SADD( sysMenu, qtr("&Open Media" ), "",
            ":/pixmaps/vlc_file-wide_16px.png", openFileDialog(), "" );
696 697
    DP_SADD( sysMenu, qtr("&Quit") , "", ":/pixmaps/vlc_quit_16px.png",
             quit(), "" );
698

699
    /* Set the menu */
700 701 702
    mi->getSysTray()->setContextMenu( sysMenu );
}

703 704
#undef PUSH_VAR
#undef PUSH_SEPARATOR
705

706

707 708 709
/*************************************************************************
 * Builders for automenus
 *************************************************************************/
710
QMenu * QVLCMenu::Populate( intf_thread_t *p_intf, QMenu *current,
711 712
        vector< const char *> & varnames,
        vector<int> & objects, bool append )
713 714 715 716
{
    QMenu *menu = current;
    if( !menu )
        menu = new QMenu();
717
    else if( !append )
718 719 720 721 722 723 724 725
        menu->clear();

    currentGroup = NULL;

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

726
#define APPEND_EMPTY { QAction *action = menu->addAction( qtr("Empty" ) ); \
727
    action->setEnabled( false ); }
728

729
    for( i = 0; i < (int)objects.size() ; i++ )
730
    {
731
        if( !varnames[i] || !*varnames[i] )
732 733 734 735 736 737 738 739
        {
            if( b_section_empty )
                APPEND_EMPTY;
            menu->addSeparator();
            b_section_empty = VLC_TRUE;
            continue;
        }

740
        if( objects[i] == 0  )
741 742
        {
            /// \bug What is this ?
743
            // Append( menu, varnames[i], NULL );
744 745 746 747 748
            b_section_empty = VLC_FALSE;
            continue;
        }

        p_object = (vlc_object_t *)vlc_object_get( p_intf,
749
                objects[i] );
750 751 752
        if( p_object == NULL ) continue;

        b_section_empty = VLC_FALSE;
753 754 755 756 757
        /* Ugly specific stuff */
        if( strstr(varnames[i], "intf-add" ) )
            CreateItem( menu, varnames[i], p_object, false );
        else
            CreateItem( menu, varnames[i], p_object, true );
758 759 760 761 762 763 764
        vlc_object_release( p_object );
    }

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

765
            return menu;
766 767 768 769 770 771 772
}

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

static bool IsMenuEmpty( const char *psz_var, vlc_object_t *p_object,
773
        bool b_root = TRUE )
774 775 776 777 778 779 780 781 782 783 784 785 786 787 788
{
    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 )
    {
789
        /* Very evil hack ! intf-switch can have only one value */
790 791 792 793 794 795 796 797 798 799 800 801 802 803
        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,
804
                    p_object, FALSE ) )
805 806 807 808 809 810 811 812 813 814 815 816 817
        {
            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,
818
        vlc_object_t *p_object, bool b_submenu )
819 820 821 822 823 824 825 826 827
{
    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 )
    {
828 829 830 831 832 833 834 835 836 837
        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;
838 839 840 841 842 843 844 845 846 847 848
    }

    /* 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 */
849 850 851
        if( b_submenu )
        {
            QMenu *submenu = new QMenu();
Clément Stenac's avatar
Qt4:  
Clément Stenac committed
852
            submenu->setTitle( qfu( text.psz_string ?
853
                        text.psz_string : psz_var ) );
854 855 856 857 858
            if( CreateChoicesMenu( submenu, psz_var, p_object, true ) == 0)
                menu->addMenu( submenu );
        }
        else
            CreateChoicesMenu( menu, psz_var, p_object, true );
859
        FREENULL( text.psz_string );
860 861 862
        return;
    }

863
#define TEXT_OR_VAR qfu ( text.psz_string ? text.psz_string : psz_var )
864 865 866

    switch( i_type & VLC_VAR_TYPE )
    {
867 868 869 870 871 872 873 874 875 876 877 878
        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;
879
    }
880
    FREENULL( text.psz_string );
881 882 883
}


884
int QVLCMenu::CreateChoicesMenu( QMenu *submenu, const char *psz_var,
885
        vlc_object_t *p_object, bool b_root )
886 887 888 889 890 891 892 893
{
    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 */
894
    if( IsMenuEmpty( psz_var, p_object, b_root ) ) return VLC_EGENERIC;
895 896 897

    switch( i_type & VLC_VAR_TYPE )
    {
898 899 900 901 902 903 904 905 906 907
        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;
908 909 910
    }

    if( var_Change( p_object, psz_var, VLC_VAR_GETLIST,
911
                &val_list, &text_list ) < 0 )
912
    {
913
        return VLC_EGENERIC;
914 915
    }
#define NORMAL_OR_RADIO i_type & VLC_VAR_ISCOMMAND ? ITEM_NORMAL: ITEM_RADIO
Clément Stenac's avatar
Qt4:  
Clément Stenac committed
916
#define NOTCOMMAND !(i_type & VLC_VAR_ISCOMMAND)
917 918 919 920 921 922 923
#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;
924
        QMenu *subsubmenu = new QMenu();
925 926 927

        switch( i_type & VLC_VAR_TYPE )
        {
928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965
            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;
966 967
        }
    }
968
    currentGroup = NULL;
969 970 971 972 973 974 975 976

    /* 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
977
    return VLC_SUCCESS;
978 979 980
}

void QVLCMenu::CreateAndConnect( QMenu *menu, const char *psz_var,
981 982 983 984
        QString text, QString help,
        int i_item_type, int i_object_id,
        vlc_value_t val, int i_val_type,
        bool checked )
985 986