menus.cpp 32 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
        bool playlist,
168
        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, visual_selector_enabled, true ), qtr("&Tools") );
177
    BAR_DADD( AudioMenu( p_intf, NULL ), qtr("&Audio"), 2 );
178
    BAR_DADD( VideoMenu( p_intf, NULL ), qtr("&Video"), 1 );
179
    BAR_DADD( NavigMenu( p_intf, NULL ), qtr("&Navigation"), 3 );
180

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

/**
 * Media (File) Menu
 * Opening, streaming and quit
 **/
188 189 190
QMenu *QVLCMenu::FileMenu()
{
    QMenu *menu = new QMenu();
191
    
192 193
    DP_SADD( menu, qtr("Open &File..." ), "",
            ":/pixmaps/vlc_file-asym_16px.png", openFileDialog(), "Ctrl+O" );
194
    DP_SADD( menu, qtr( I_OPEN_FOLDER ), "",
195
            ":/pixmaps/vlc_folder-grey_16px.png", openDirDialog(), "Ctrl+F" );
196 197
    DP_SADD( menu, qtr("Open &Disc..." ), "", ":/pixmaps/vlc_disc_16px.png",
             openDiscDialog(), "Ctrl+D" );
198 199 200 201
    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(),
202
            "Ctrl+C" );
203
    menu->addSeparator();
204
    
205 206
    DP_SADD( menu, qtr("&Streaming..."), "", ":/pixmaps/vlc_stream_16px.png",
            openThenStreamingDialogs(), "Ctrl+S" );
207 208
    DP_SADD( menu, qtr("Conve&rt / Save..."), "", "",
            openThenTranscodingDialogs(), "Ctrl+R" );
209
    
210
    menu->addSeparator();
211
    DP_SADD( menu, qtr("&Quit") , "", ":/pixmaps/vlc_quit_16px.png", quit(),
212
            "Ctrl+Q");
213 214 215
    return menu;
}

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

225 226
    DP_SADD( menu, qtr( I_PL_LOAD ), "", "", openPlaylist(), "Ctrl+X" );
    DP_SADD( menu, qtr( I_PL_SAVE ), "", "", savePlaylist(), "Ctrl+Y" );
227 228
    menu->addSeparator();
    menu->addAction( qtr("Undock from interface"), mi,
229
            SLOT( undockPlaylist() ), qtr("Ctrl+U") );
Clément Stenac's avatar
Clément Stenac committed
230 231 232
    return menu;
}

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

263
    menu->addSeparator();
Clément Stenac's avatar
Clément Stenac committed
264 265
    if( mi )
    {
266 267
        /* Minimal View */
        QAction *action=menu->addAction( qtr( "Minimal View..." ), mi, SLOT( toggleMenus() ),
268
                qtr( "Ctrl+H") );
269 270 271 272 273 274 275
        action->setCheckable( true );
        if( mi->getControlsVisibilityStatus() & 0x2 ) action->setChecked( true );
        
        /* Advanced Controls */
        action = menu->addAction( qtr("Advanced controls" ), mi, SLOT( toggleAdvanced() ) );
        action->setCheckable( true );
        if( mi->getControlsVisibilityStatus() & 0x1 ) action->setChecked( true );
276
        menu->addSeparator();
277

278
#if 0 /* For Visualisations. Not yet working */
279
        adv = menu->addAction( qtr("Visualizations selector" ),
280
                mi, SLOT( visual() ) );
281 282
        adv->setCheckable( true );
        if( visual_selector_enabled ) adv->setChecked( true );
283
#endif
284
        menu->addAction ( QIcon(":/pixmaps/vlc_playlist_16px.png"),
285
                          qtr( "Playlist"), mi, SLOT( togglePlaylist() ),
286
                          qtr( "Ctrl+L") );
Clément Stenac's avatar
Clément Stenac committed
287
    }
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
 * Service Discovery SubMenu
403
 **/
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 );
437 438 439 440 441 442 443 444

        if( !strcmp( p_parser->psz_object_name, "podcast" ) )
        {
            QAction *b = new QAction( qfu( "Configure podcasts..." ), menu );
            //b->setEnabled( a->isChecked() );
            menu->addAction( b );
            CONNECT( b, triggered(), THEDP, podcastConfigureDialog() );
        }
Clément Stenac's avatar
Clément Stenac committed
445 446 447 448
    }
    vlc_list_release( p_list );
    return menu;
}
449 450 451
/**
 * Help/About Menu
**/
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
452 453 454
QMenu *QVLCMenu::HelpMenu()
{
    QMenu *menu = new QMenu();
455 456
    DP_SADD( menu, qtr("Help") , "", ":/pixmaps/vlc_help_16px.png",
             helpDialog(), "F1" );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
457
    menu->addSeparator();
458
    DP_SADD( menu, qtr( I_MENU_ABOUT ), "", "", aboutDialog(), "Ctrl+F1");
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
459 460 461 462
    return menu;
}


463
/*****************************************************************************
464
 * Popup menus - Right Click menus                                           *
465
 *****************************************************************************/
466 467
#define POPUP_BOILERPLATE \
    unsigned int i_last_separator = 0; \
468 469 470 471 472 473 474 475 476
    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() ); \
477
    p_intf->p_sys->p_popup_menu = NULL; \
478
    i_last_separator = 0;
479

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

501
#define POPUP_STATIC_ENTRIES( menu ) \
502
    QMenu *intfmenu = InterfacesMenu( p_intf, NULL ); \
503
    intfmenu->setTitle( qtr("Interfaces" ) ); \
504 505
    menu->addMenu( intfmenu ); \
    \
506
    QMenu *toolsmenu = ToolsMenu( p_intf, NULL, false, false ); \
507
    toolsmenu->setTitle( qtr("Tools" ) ); \
508
    menu->addMenu( toolsmenu ); \
509 510 511 512 513 514 515 516 517 518 519 520 521 522
    \
    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 ); \
    \
523
    DP_SADD( menu, qtr("Quit"), "", "", quit() , "Ctrl+Q" );
Clément Stenac's avatar
Qt4:  
Clément Stenac committed
524

525
/* Video Tracks and Subtitles tracks */
526
void QVLCMenu::VideoPopupMenu( intf_thread_t *p_intf )
527 528 529 530 531
{
    POPUP_BOILERPLATE;
    if( p_input )
    {
        vlc_object_yield( p_input );
532 533 534 535
        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 );
536
        vlc_object_t *p_vout = (vlc_object_t *)vlc_object_find( p_input,
537
                VLC_OBJECT_VOUT, FIND_CHILD );
538 539
        if( p_vout )
        {
540
            VideoAutoMenuBuilder( p_vout, objects, varnames );
541 542 543 544
            vlc_object_release( p_vout );
        }
        vlc_object_release( p_input );
    }
545
    CREATE_POPUP;
546 547
}

548
/* Audio Tracks */
549
void QVLCMenu::AudioPopupMenu( intf_thread_t *p_intf )
550 551 552 553 554
{
    POPUP_BOILERPLATE;
    if( p_input )
    {
        vlc_object_yield( p_input );
555 556
        varnames.push_back( "audio-es" );
        objects.push_back( p_input->i_object_id );
557
        vlc_object_t *p_aout = (vlc_object_t *)vlc_object_find( p_input,
558
                VLC_OBJECT_AOUT, FIND_ANYWHERE );
559 560
        if( p_aout )
        {
561
            AudioAutoMenuBuilder( p_aout, objects, varnames );
562 563 564 565
            vlc_object_release( p_aout );
        }
        vlc_object_release( p_input );
    }
566
    CREATE_POPUP;
567 568
}

569
/* Navigation stuff, and general menus (open) */
570
void QVLCMenu::MiscPopupMenu( intf_thread_t *p_intf )
571
{
572
    vlc_value_t val;
573
    POPUP_BOILERPLATE;
574

575 576 577
    if( p_input )
    {
        vlc_object_yield( p_input );
578 579
        varnames.push_back( "audio-es" );
        InputAutoMenuBuilder( VLC_OBJECT(p_input), objects, varnames );
580 581 582
        PUSH_SEPARATOR;
    }

583 584
    QMenu *menu = new QMenu();
    Populate( p_intf, menu, varnames, objects );
585

586
    menu->addSeparator();
587 588 589 590
    POPUP_PLAY_ENTRIES( menu );

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

592 593
    p_intf->p_sys->p_popup_menu = menu;
    menu->popup( QCursor::pos() );
594 595 596
    p_intf->p_sys->p_popup_menu = NULL;
}

597
/* Main Menu that sticks everything together  */
Damien Fouilleul's avatar
Damien Fouilleul committed
598
void QVLCMenu::PopupMenu( intf_thread_t *p_intf, bool show )
599
{
Damien Fouilleul's avatar
Damien Fouilleul committed
600
    if( show )
601
    {
602
        // create a  popup if there is none
Damien Fouilleul's avatar
Damien Fouilleul committed
603
        if( ! p_intf->p_sys->p_popup_menu )
604
        {
605 606 607 608 609
            POPUP_BOILERPLATE;
            if( p_input )
            {
                vlc_object_yield( p_input );
                InputAutoMenuBuilder( VLC_OBJECT(p_input), objects, varnames );
610 611 612 613 614 615 616 617 618 619 620 621 622
                
                /* 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 );
                }
                
623 624 625 626 627 628 629 630 631 632 633 634 635 636
                /* 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 );
                }
            }
637

638 639 640
            QMenu *menu = new QMenu();
            Populate( p_intf, menu, varnames, objects );
            menu->addSeparator();
641 642 643
            POPUP_PLAY_ENTRIES( menu );
            menu->addSeparator();
            POPUP_STATIC_ENTRIES( menu );
644

645 646 647
            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
648 649
    }
    else
650
    {
651 652 653
        // destroy popup if there is one
        delete p_intf->p_sys->p_popup_menu;
        p_intf->p_sys->p_popup_menu = NULL;
654 655 656
    }
}

657 658 659 660
/************************************************************************
 * Systray Menu                                                         *
 ************************************************************************/

661 662 663
void QVLCMenu::updateSystrayMenu( MainInterface *mi,
                                  intf_thread_t *p_intf,
                                  bool b_force_visible )
664 665
{
    POPUP_BOILERPLATE;
666 667

    /* Get the systray menu and clean it */
668 669
    QMenu *sysMenu = mi->getSysTrayMenu();
    sysMenu->clear();
670 671

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

685 686
    sysMenu->addSeparator();
    POPUP_PLAY_ENTRIES( sysMenu );
687

688
    sysMenu->addSeparator();
689 690
    DP_SADD( sysMenu, qtr("&Open Media" ), "",
            ":/pixmaps/vlc_file-wide_16px.png", openFileDialog(), "" );
691 692
    DP_SADD( sysMenu, qtr("&Quit") , "", ":/pixmaps/vlc_quit_16px.png",
             quit(), "" );
693

694
    /* Set the menu */
695 696 697
    mi->getSysTray()->setContextMenu( sysMenu );
}

698 699
#undef PUSH_VAR
#undef PUSH_SEPARATOR
700

701

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

    currentGroup = NULL;

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

721
#define APPEND_EMPTY { QAction *action = menu->addAction( qtr("Empty" ) ); \
722
    action->setEnabled( false ); }
723

724
    for( i = 0; i < (int)objects.size() ; i++ )
725
    {
726
        if( !varnames[i] || !*varnames[i] )
727 728 729 730 731 732 733 734
        {
            if( b_section_empty )
                APPEND_EMPTY;
            menu->addSeparator();
            b_section_empty = VLC_TRUE;
            continue;
        }

735
        if( objects[i] == 0  )
736 737
        {
            /// \bug What is this ?
738
            // Append( menu, varnames[i], NULL );
739 740 741 742 743
            b_section_empty = VLC_FALSE;
            continue;
        }

        p_object = (vlc_object_t *)vlc_object_get( p_intf,
744
                objects[i] );
745 746 747
        if( p_object == NULL ) continue;

        b_section_empty = VLC_FALSE;
748 749 750 751 752
        /* Ugly specific stuff */
        if( strstr(varnames[i], "intf-add" ) )
            CreateItem( menu, varnames[i], p_object, false );
        else
            CreateItem( menu, varnames[i], p_object, true );
753 754 755 756 757 758 759
        vlc_object_release( p_object );
    }

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

760
            return menu;
761 762 763 764 765 766 767
}

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

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

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

858
#define TEXT_OR_VAR qfu ( text.psz_string ? text.psz_string : psz_var )
859 860 861

    switch( i_type & VLC_VAR_TYPE )
    {
862 863 864 865 866 867 868 869 870 871 872 873
        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;
874
    }
875
    FREENULL( text.psz_string );
876 877 878
}


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

    switch( i_type & VLC_VAR_TYPE )
    {
893 894 895 896 897 898 899 900 901 902
        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;
903 904 905
    }

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

        switch( i_type & VLC_VAR_TYPE )
        {
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 952 953 954 955 956 957 958 959 960
            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;
961 962
        }
    }
963
    currentGroup = NULL;
964 965 966 967 968 969 970 971

    /* 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
972
    return VLC_SUCCESS;
973 974 975
}

void QVLCMenu::CreateAndConnect( QMenu *menu, const char *psz_var,
976 977 978 979
        QString text, QString help,
        int i_item_type, int i_object_id,
        vlc_value_t val, int i_val_type,
        bool checked )
980 981 982 983 984 985 986 987 988 989 990 991 992
{