dialogs_provider.cpp 24.5 KB
Newer Older
Clément Stenac's avatar
Clément Stenac committed
1
/*****************************************************************************
2
 * dialogs_provider.cpp : Dialog Provider
3
 *****************************************************************************
4
 * Copyright (C) 2006-2009 the VideoLAN team
5
 * $Id$
Clément Stenac's avatar
Clément Stenac committed
6 7
 *
 * Authors: Clément Stenac <zorglub@videolan.org>
8
 *          Jean-Baptiste Kempf <jb@videolan.org>
Clément Stenac's avatar
Clément Stenac committed
9 10 11 12 13 14 15 16 17 18 19 20 21
 *
 * 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
22 23
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
24

25 26 27
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
Clément Stenac's avatar
Clément Stenac committed
28

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
29 30
#include <vlc_intf_strings.h>

Clément Stenac's avatar
Clément Stenac committed
31
#include "qt4.hpp"
32
#include "dialogs_provider.hpp"
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
33
#include "input_manager.hpp" /* Load Subtitles */
Clément Stenac's avatar
Clément Stenac committed
34
#include "menus.hpp"
Ludovic Fauvet's avatar
Ludovic Fauvet committed
35
#include "recents.hpp"
36
#include "util/qt_dirs.hpp"
37
#include "util/customwidgets.hpp" /* VLCKeyToString() */
38
#include "main_interface.hpp"
Clément Stenac's avatar
Clément Stenac committed
39 40

/* The dialogs */
Clément Stenac's avatar
Clément Stenac committed
41
#include "dialogs/playlist.hpp"
42
#include "dialogs/bookmarks.hpp"
43
#include "dialogs/preferences.hpp"
44
#include "dialogs/mediainfo.hpp"
45
#include "dialogs/messages.hpp"
46
#include "dialogs/extended.hpp"
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
47
#include "dialogs/vlm.hpp"
Clément Stenac's avatar
Clément Stenac committed
48
#include "dialogs/sout.hpp"
49
#include "dialogs/convert.hpp"
50
#include "dialogs/open.hpp"
51
#include "dialogs/openurl.hpp"
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
52
#include "dialogs/help.hpp"
53
#include "dialogs/gototime.hpp"
54
#include "dialogs/podcast_configuration.hpp"
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
55
#include "dialogs/toolbar.hpp"
56
#include "dialogs/plugins.hpp"
57
#include "dialogs/external.hpp"
58
#include "dialogs/epg.hpp"
59
#include "dialogs/errors.hpp"
Clément Stenac's avatar
Clément Stenac committed
60

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
61 62 63 64 65
#include <QEvent>
#include <QApplication>
#include <QSignalMapper>
#include <QFileDialog>

66 67
#define I_OP_DIR_WINTITLE I_DIR_OR_FOLDER( N_("Open Directory"), \
                                           N_("Open Folder") )
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
68

Clément Stenac's avatar
Clément Stenac committed
69 70
DialogsProvider* DialogsProvider::instance = NULL;

71
DialogsProvider::DialogsProvider( intf_thread_t *_p_intf ) :
72 73 74 75 76
                                  QObject( NULL ), p_intf( _p_intf ),
                                  popupMenu( NULL ),
                                  videoPopupMenu( NULL ),
                                  audioPopupMenu( NULL ),
                                  miscPopupMenu( NULL )
Clément Stenac's avatar
Clément Stenac committed
77
{
78
    b_isDying = false;
79

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
80
    /* Various signal mappers for the menus */
81
    menusMapper = new QSignalMapper();
Clément Stenac's avatar
Clément Stenac committed
82
    CONNECT( menusMapper, mapped(QObject *), this, menuAction( QObject *) );
83

84
    menusUpdateMapper = new QSignalMapper();
Clément Stenac's avatar
Clément Stenac committed
85 86
    CONNECT( menusUpdateMapper, mapped(QObject *),
             this, menuUpdateAction( QObject *) );
Clément Stenac's avatar
Clément Stenac committed
87 88 89

    SDMapper = new QSignalMapper();
    CONNECT( SDMapper, mapped (QString), this, SDMenuAction( QString ) );
90

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
91
    new DialogHandler (p_intf, this );
Clément Stenac's avatar
Clément Stenac committed
92
}
93

Clément Stenac's avatar
Clément Stenac committed
94 95 96
DialogsProvider::~DialogsProvider()
{
    PlaylistDialog::killInstance();
97
    MediaInfoDialog::killInstance();
98
    MessagesDialog::killInstance();
99
    BookmarksDialog::killInstance();
100
    HelpDialog::killInstance();
101
#ifdef UPDATE_CHECK
102
    UpdateDialog::killInstance();
103
#endif
104
    PluginDialog::killInstance();
105
    EpgDialog::killInstance();
106

107 108 109
    delete menusMapper;
    delete menusUpdateMapper;
    delete SDMapper;
110

111 112 113 114
    delete popupMenu;
    delete videoPopupMenu;
    delete audioPopupMenu;
    delete miscPopupMenu;
Clément Stenac's avatar
Clément Stenac committed
115 116
}

117 118
void DialogsProvider::quit()
{
119
    b_isDying = true;
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
120
    libvlc_Quit( p_intf->p_libvlc );
121 122
}

Clément Stenac's avatar
Clément Stenac committed
123 124
void DialogsProvider::customEvent( QEvent *event )
{
125
    if( event->type() == DialogEvent::DialogEvent_Type )
Clément Stenac's avatar
Clément Stenac committed
126
    {
Clément Stenac's avatar
Clément Stenac committed
127
        DialogEvent *de = static_cast<DialogEvent*>(event);
Clément Stenac's avatar
Clément Stenac committed
128 129
        switch( de->i_dialog )
        {
130 131 132
        case INTF_DIALOG_FILE_SIMPLE:
        case INTF_DIALOG_FILE:
            openDialog(); break;
133 134
        case INTF_DIALOG_FILE_GENERIC:
            openFileGenericDialog( de->p_arg ); break;
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
        case INTF_DIALOG_DISC:
            openDiscDialog(); break;
        case INTF_DIALOG_NET:
            openNetDialog(); break;
        case INTF_DIALOG_SAT:
        case INTF_DIALOG_CAPTURE:
            openCaptureDialog(); break;
        case INTF_DIALOG_DIRECTORY:
            PLAppendDir(); break;
        case INTF_DIALOG_PLAYLIST:
            playlistDialog(); break;
        case INTF_DIALOG_MESSAGES:
            messagesDialog(); break;
        case INTF_DIALOG_FILEINFO:
           mediaInfoDialog(); break;
        case INTF_DIALOG_PREFS:
           prefsDialog(); break;
        case INTF_DIALOG_BOOKMARKS:
           bookmarksDialog(); break;
        case INTF_DIALOG_EXTENDED:
           extendedDialog(); break;
156 157
        case INTF_DIALOG_SENDKEY:
           sendKey( de->i_arg ); break;
158
#ifdef ENABLE_VLM
159 160
        case INTF_DIALOG_VLM:
           vlmDialog(); break;
161
#endif
162
        case INTF_DIALOG_POPUPMENU:
163 164 165 166 167 168 169
        {
           delete popupMenu; popupMenu = NULL;
           bool show = (de->i_arg != 0);
           if( show )
               popupMenu = VLCMenuBar::PopupMenu( p_intf, show );
           break;
        }
170
        case INTF_DIALOG_AUDIOPOPUPMENU:
171 172 173 174 175 176 177
        {
           delete audioPopupMenu; audioPopupMenu = NULL;
           bool show = (de->i_arg != 0);
           if( show )
               audioPopupMenu = VLCMenuBar::AudioPopupMenu( p_intf, show );
           break;
        }
178
        case INTF_DIALOG_VIDEOPOPUPMENU:
179 180 181 182 183 184 185
        {
           delete videoPopupMenu; videoPopupMenu = NULL;
           bool show = (de->i_arg != 0);
           if( show )
               videoPopupMenu = VLCMenuBar::VideoPopupMenu( p_intf, show );
           break;
        }
186
        case INTF_DIALOG_MISCPOPUPMENU:
187 188 189 190 191 192 193
        {
           delete miscPopupMenu; miscPopupMenu = NULL;
           bool show = (de->i_arg != 0);
           if( show )
               miscPopupMenu = VLCMenuBar::MiscPopupMenu( p_intf, show );
           break;
        }
194 195
        case INTF_DIALOG_WIZARD:
        case INTF_DIALOG_STREAMWIZARD:
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
196
            openAndStreamingDialogs(); break;
Rafaël Carré's avatar
Rafaël Carré committed
197
#ifdef UPDATE_CHECK
198
        case INTF_DIALOG_UPDATEVLC:
199
            updateDialog(); break;
Rafaël Carré's avatar
Rafaël Carré committed
200
#endif
201
        case INTF_DIALOG_EXIT:
202
            quit(); break;
203 204
        default:
           msg_Warn( p_intf, "unimplemented dialog" );
Clément Stenac's avatar
Clément Stenac committed
205 206 207 208
        }
    }
}

209 210 211
/****************************************************************************
 * Individual simple dialogs
 ****************************************************************************/
212 213 214
const QEvent::Type DialogEvent::DialogEvent_Type =
        (QEvent::Type)QEvent::registerEventType();

Clément Stenac's avatar
Clément Stenac committed
215 216 217 218 219
void DialogsProvider::playlistDialog()
{
    PlaylistDialog::getInstance( p_intf )->toggleVisible();
}

220 221
void DialogsProvider::prefsDialog()
{
222 223
    PrefsDialog *p = new PrefsDialog( (QWidget *)p_intf->p_sys->p_mi, p_intf );
    p->toggleVisible();
224
}
225

226 227
void DialogsProvider::extendedDialog()
{
228 229 230 231 232
    ExtendedDialog *extDialog = ExtendedDialog::getInstance(p_intf );

    if( !extDialog->isVisible() || /* Hidden */
        extDialog->currentTab() != 0 )  /* wrong tab */
        extDialog->showTab( 0 );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
233
    else
234
        extDialog->hide();
235 236
}

237 238
void DialogsProvider::synchroDialog()
{
239 240 241 242 243
    ExtendedDialog *extDialog = ExtendedDialog::getInstance(p_intf );

    if( !extDialog->isVisible() || /* Hidden */
        extDialog->currentTab() != 2 )  /* wrong tab */
        extDialog->showTab( 2 );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
244
    else
245
        extDialog->hide();
246 247
}

248 249 250 251 252
void DialogsProvider::messagesDialog()
{
    MessagesDialog::getInstance( p_intf )->toggleVisible();
}

253 254 255 256 257
void DialogsProvider::gotoTimeDialog()
{
    GotoTimeDialog::getInstance( p_intf )->toggleVisible();
}

258
#ifdef ENABLE_VLM
259 260
void DialogsProvider::vlmDialog()
{
261
    VLMDialog::getInstance( p_intf )->toggleVisible();
262
}
263
#endif
264

265 266 267 268 269
void DialogsProvider::helpDialog()
{
    HelpDialog::getInstance( p_intf )->toggleVisible();
}

Rafaël Carré's avatar
Rafaël Carré committed
270
#ifdef UPDATE_CHECK
271 272 273
void DialogsProvider::updateDialog()
{
    UpdateDialog::getInstance( p_intf )->toggleVisible();
274
}
Rafaël Carré's avatar
Rafaël Carré committed
275
#endif
276

277 278 279 280 281 282 283
void DialogsProvider::aboutDialog()
{
    AboutDialog::getInstance( p_intf )->toggleVisible();
}

void DialogsProvider::mediaInfoDialog()
{
284
    MediaInfoDialog::getInstance( p_intf )->showTab( MediaInfoDialog::META_PANEL );
285 286
}

287 288
void DialogsProvider::mediaCodecDialog()
{
289
    MediaInfoDialog::getInstance( p_intf )->showTab( MediaInfoDialog::INFO_PANEL );
290 291
}

292 293
void DialogsProvider::bookmarksDialog()
{
294
    BookmarksDialog::getInstance( p_intf )->toggleVisible();
295 296
}

297 298 299 300 301
void DialogsProvider::podcastConfigureDialog()
{
    PodcastConfigDialog::getInstance( p_intf )->toggleVisible();
}

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
302 303
void DialogsProvider::toolbarDialog()
{
304
    ToolbarEditDialog *toolbarEditor = new ToolbarEditDialog( (QWidget *)p_intf->p_sys->p_mi, p_intf );
305 306
    if( toolbarEditor->exec() == QDialog::Accepted )
        emit toolBarConfUpdated();
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
307 308
}

309 310
void DialogsProvider::pluginDialog()
{
311
    PluginDialog::getInstance( p_intf )->toggleVisible();
312 313
}

314 315 316 317 318
void DialogsProvider::epgDialog()
{
    EpgDialog::getInstance( p_intf )->toggleVisible();
}

319 320 321 322 323 324 325 326 327 328 329 330
void DialogsProvider::setPopupMenu()
{
    delete popupMenu;
    popupMenu = VLCMenuBar::PopupMenu( p_intf, true );
}

void DialogsProvider::destroyPopupMenu()
{
    delete popupMenu;
    popupMenu = NULL;
}

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
331
/* Generic open file */
332 333 334 335
void DialogsProvider::openFileGenericDialog( intf_dialog_args_t *p_arg )
{
    if( p_arg == NULL )
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
336
        msg_Warn( p_intf, "openFileGenericDialog() called with NULL arg" );
337 338
        return;
    }
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
339 340

    /* Replace the extensions to a Qt format */
341 342 343 344 345 346 347 348 349
    int i = 0;
    QString extensions = qfu( p_arg->psz_extensions );
    while ( ( i = extensions.indexOf( "|", i ) ) != -1 )
    {
        if( ( extensions.count( "|" ) % 2 ) == 0 )
            extensions.replace( i, 1, ");;" );
        else
            extensions.replace( i, 1, "(" );
    }
Rémi Duraffort's avatar
Rémi Duraffort committed
350
    extensions.replace( ";*", " *" );
351
    extensions.append( ")" );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
352 353

    /* Save */
354 355
    if( p_arg->b_save )
    {
356 357
        QString file = QFileDialog::getSaveFileName( NULL,
                                        qfu( p_arg->psz_title ),
358
                                        p_intf->p_sys->filepath, extensions );
359 360 361
        if( !file.isEmpty() )
        {
            p_arg->i_results = 1;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
362
            p_arg->psz_results = (char **)malloc( p_arg->i_results * sizeof( char * ) );
363
            p_arg->psz_results[0] = strdup( qtu( toNativeSepNoSlash( file ) ) );
364 365 366 367
        }
        else
            p_arg->i_results = 0;
    }
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
368
    else /* non-save mode */
369
    {
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
370
        QStringList files = QFileDialog::getOpenFileNames( NULL,
371
                qfu( p_arg->psz_title ), p_intf->p_sys->filepath,
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
372
                extensions );
373
        p_arg->i_results = files.count();
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
374
        p_arg->psz_results = (char **)malloc( p_arg->i_results * sizeof( char * ) );
375
        i = 0;
376
        foreach( const QString &file, files )
377
            p_arg->psz_results[i++] = strdup( qtu( toNativeSepNoSlash( file ) ) );
378
        if(i == 0)
379
            p_intf->p_sys->filepath = QString::fromLatin1("");
380
        else
381
            p_intf->p_sys->filepath = qfu( p_arg->psz_results[i-1] );
382
    }
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
383 384

    /* Callback */
385 386
    if( p_arg->pf_callback )
        p_arg->pf_callback( p_arg );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
387 388

    /* Clean afterwards */
389 390 391 392 393 394 395 396 397 398
    if( p_arg->psz_results )
    {
        for( i = 0; i < p_arg->i_results; i++ )
            free( p_arg->psz_results[i] );
        free( p_arg->psz_results );
    }
    free( p_arg->psz_title );
    free( p_arg->psz_extensions );
    free( p_arg );
}
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
399 400 401 402
/****************************************************************************
 * All the open/add stuff
 * Open Dialog first - Simple Open then
 ****************************************************************************/
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
403

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
404 405 406 407 408 409 410 411
void DialogsProvider::openDialog( int i_tab )
{
    OpenDialog::getInstance( p_intf->p_sys->p_mi , p_intf )->showTab( i_tab );
}
void DialogsProvider::openDialog()
{
    openDialog( OPEN_FILE_TAB );
}
412 413
void DialogsProvider::openFileDialog()
{
414
    openDialog( OPEN_FILE_TAB );
415
}
416
void DialogsProvider::openDiscDialog()
Clément Stenac's avatar
Clément Stenac committed
417
{
418
    openDialog( OPEN_DISC_TAB );
Clément Stenac's avatar
Clément Stenac committed
419
}
420
void DialogsProvider::openNetDialog()
Clément Stenac's avatar
Clément Stenac committed
421
{
422
    openDialog( OPEN_NETWORK_TAB );
Clément Stenac's avatar
Clément Stenac committed
423
}
424 425
void DialogsProvider::openCaptureDialog()
{
426
    openDialog( OPEN_CAPTURE_TAB );
427
}
Clément Stenac's avatar
Clément Stenac committed
428

429
/* Same as the open one, but force the enqueue */
430
void DialogsProvider::PLAppendDialog( int tab )
431
{
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
432
    OpenDialog::getInstance( p_intf->p_sys->p_mi, p_intf, false,
433
                             OPEN_AND_ENQUEUE )->showTab( tab );
434
}
435

436
void DialogsProvider::MLAppendDialog( int tab )
437 438 439
{
    OpenDialog::getInstance( p_intf->p_sys->p_mi, p_intf, false,
                            OPEN_AND_ENQUEUE, false, false )
440
                                    ->showTab( tab );
441
}
442

443 444 445
/**
 * Simple open
 ***/
Rémi Duraffort's avatar
Rémi Duraffort committed
446
QStringList DialogsProvider::showSimpleOpen( const QString& help,
447
                                             int filters,
Rémi Duraffort's avatar
Rémi Duraffort committed
448
                                             const QString& path )
449
{
450
    QString fileTypes = "";
451
    if( filters & EXT_FILTER_MEDIA ) {
452
        ADD_EXT_FILTER( fileTypes, EXTENSIONS_MEDIA );
453
    }
454
    if( filters & EXT_FILTER_VIDEO ) {
455
        ADD_EXT_FILTER( fileTypes, EXTENSIONS_VIDEO );
456
    }
457
    if( filters & EXT_FILTER_AUDIO ) {
458
        ADD_EXT_FILTER( fileTypes, EXTENSIONS_AUDIO );
459
    }
460
    if( filters & EXT_FILTER_PLAYLIST ) {
461
        ADD_EXT_FILTER( fileTypes, EXTENSIONS_PLAYLIST );
462
    }
463
    if( filters & EXT_FILTER_SUBTITLE ) {
464
        ADD_EXT_FILTER( fileTypes, EXTENSIONS_SUBTITLE );
465
    }
466
    ADD_EXT_FILTER( fileTypes, EXTENSIONS_ALL );
Rémi Duraffort's avatar
Rémi Duraffort committed
467
    fileTypes.replace( ";*", " *");
468

469
    QStringList files = QFileDialog::getOpenFileNames( NULL,
470
        help.isEmpty() ? qtr(I_OP_SEL_FILES ) : help,
471
        path.isEmpty() ? p_intf->p_sys->filepath : path,
472
        fileTypes );
473 474 475 476

    if( !files.isEmpty() ) savedirpathFromFile( files.last() );

    return files;
477 478
}

479 480 481 482 483
/**
 * Open a file,
 * pl helps you to choose from playlist or media library,
 * go to start or enqueue
 **/
484 485 486
void DialogsProvider::addFromSimple( bool pl, bool go)
{
    QStringList files = DialogsProvider::showSimpleOpen();
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
487

488
    bool first = go;
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
489
    files.sort();
490
    foreach( const QString &file, files )
491
    {
492
        QString url = toURI( toNativeSeparators( file ) );
493 494
        Open::openMRL( p_intf, url, first, pl);
        first = false;
495 496 497
    }
}

498 499 500 501 502
void DialogsProvider::simpleOpenDialog()
{
    addFromSimple( true, true ); /* Playlist and Go */
}

503 504 505 506 507 508 509
/* Url & Clipboard */
/**
 * Open a MRL.
 * If the clipboard contains URLs, the first is automatically 'preselected'.
 **/
void DialogsProvider::openUrlDialog()
{
510 511 512 513 514 515 516 517 518
    OpenUrlDialog oud( p_intf );
    if( oud.exec() != QDialog::Accepted )
        return;

    QString url = oud.url();
    if( url.isEmpty() )
        return;

    if( !url.contains( qfu( "://" ) ) )
519
    {
520 521 522 523 524
        char *uri = vlc_path2uri( qtu( url ), NULL );
        if( uri == NULL )
            return;
        url = qfu(uri);
        free( uri );
525
    }
526 527

    Open::openMRL( p_intf, qtu(url), !oud.shouldEnqueue() );
528 529
}

530 531 532 533 534 535
/* Directory */
/**
 * Open a directory,
 * pl helps you to choose from playlist or media library,
 * go to start or enqueue
 **/
536
static void openDirectory( intf_thread_t *p_intf, bool pl, bool go )
537
{
538 539 540
    QString uri = DialogsProvider::getDirectoryDialog( p_intf );
    if( !uri.isEmpty() )
        Open::openMRL( p_intf, uri, go, pl );
541 542
}

543
QString DialogsProvider::getDirectoryDialog( intf_thread_t *p_intf )
544
{
545 546
    QString dir = QFileDialog::getExistingDirectory( NULL,
            qtr( I_OP_DIR_WINTITLE ), p_intf->p_sys->filepath );
547 548 549 550 551 552

    if( dir.isEmpty() ) return QString();

    p_intf->p_sys->filepath = dir;

    const char *scheme = "directory";
553
    if( dir.endsWith( DIR_SEP "VIDEO_TS", Qt::CaseInsensitive ) )
554
        scheme = "dvd";
555
    else if( dir.endsWith( DIR_SEP "BDMV", Qt::CaseInsensitive ) )
556 557 558 559 560 561
    {
        scheme = "bluray";
        dir.remove( "BDMV" );
    }

    char *uri = vlc_path2uri( qtu( toNativeSeparators( dir ) ), scheme );
562 563 564
    if( unlikely(uri == NULL) )
        return QString();

565 566 567 568 569 570 571 572
    dir = qfu( uri );
    free( uri );

    RecentsMRL::getInstance( p_intf )->addRecent( dir );

    return dir;
}

573 574 575 576 577
void DialogsProvider::PLOpenDir()
{
    openDirectory( p_intf, true, true );
}

578 579
void DialogsProvider::PLAppendDir()
{
580
    openDirectory( p_intf, true, false );
581 582
}

583 584 585
/****************
 * Playlist     *
 ****************/
586
void DialogsProvider::openAPlaylist()
587
{
588
    QStringList files = showSimpleOpen( qtr( "Open playlist..." ),
589
                                        EXT_FILTER_PLAYLIST );
590
    foreach( const QString &file, files )
591
    {
592
        playlist_Import( THEPL, qtu( toNativeSeparators( file ) ) );
593 594 595
    }
}

596
void DialogsProvider::saveAPlaylist(playlist_t *p_playlist, playlist_item_t *p_node)
597
{
598 599
    static const struct
    {
600
        char filter_name[14];
601
        char filter_patterns[5];
602 603
        char module[12];
    } types[] = {
604 605 606 607
        { N_("XSPF playlist"), "xspf", "export-xspf", },
        { N_("M3U playlist"),  "m3u",  "export-m3u", },
        { N_("M3U8 playlist"), "m3u8", "export-m3u8", },
        { N_("HTML playlist"), "html", "export-html", },
608
    };
609 610 611

    QStringList filters;
    QString ext = getSettings()->value( "last-playlist-ext" ).toString();
612 613 614

    for( size_t i = 0; i < sizeof (types) / sizeof (types[0]); i++ )
    {
615
        QString tmp = qfu( vlc_gettext( types[i].filter_name ) ) + " (*." + types[i].filter_patterns + ")";
616 617 618 619
        if( ext == qfu( types[i].filter_patterns ) )
            filters.insert( 0, tmp );
        else
            filters.append( tmp );
620 621
    }

622
    QString selected;
623 624 625 626
    QString file = QFileDialog::getSaveFileName( NULL,
                                                 qtr( "Save playlist as..." ),
                                                 p_intf->p_sys->filepath, filters.join( ";;" ),
                                                 &selected );
627 628 629
    const char *psz_selected_module = NULL;
    const char *psz_last_playlist_ext = NULL;

630 631
    if( file.isEmpty() )
        return;
632

633
    /* First test if the file extension is set, and different to selected filter */
634
    for( size_t i = 0; i < sizeof (types) / sizeof (types[0]); i++)
635 636
    {
        if ( file.endsWith( QString( "." ) + qfu( types[i].filter_patterns ) ) )
637
        {
638 639
            psz_selected_module = types[i].module;
            psz_last_playlist_ext = types[i].filter_patterns;
640
            break;
641
        }
642 643 644 645 646 647 648
    }

    /* otherwise apply the selected extension */
    if ( !psz_last_playlist_ext )
    {
        for( size_t i = 0; i < sizeof (types) / sizeof (types[0]); i++)
        {
649
            if ( selected.startsWith( qfu( vlc_gettext( types[i].filter_name ) ) ) )
650 651 652 653 654 655 656 657 658 659 660 661
            {
                psz_selected_module = types[i].module;
                psz_last_playlist_ext = types[i].filter_patterns;
                /* Fix file extension */
                file = file.append( QString( "." ) + qfu( psz_last_playlist_ext ) );
                break;
            }
        }
    }

    if ( psz_selected_module )
    {
662 663
        playlist_Export( p_playlist, qtu( toNativeSeparators( file ) ),
                         p_node, psz_selected_module );
664 665
        getSettings()->setValue( "last-playlist-ext", psz_last_playlist_ext );
    }
666 667
}

668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686
void DialogsProvider::savePlayingToPlaylist()
{
    saveAPlaylist(THEPL, THEPL->p_playing);
}

void DialogsProvider::saveRecentsToPlaylist()
{
    playlist_item_t *p_node_recents = RecentsMRL::getInstance(p_intf)->toPlaylist(0);

    if (p_node_recents == NULL)
    {
        msg_Warn(p_intf, "cannot create playlist from recents");
        return;
    }

    saveAPlaylist(THEPL, p_node_recents);
    playlist_NodeDelete(THEPL, p_node_recents, true, false);
}

687 688 689 690
/****************************************************************************
 * Sout emulation
 ****************************************************************************/

691
void DialogsProvider::streamingDialog( QWidget *parent,
692
                                       const QStringList& mrls,
693 694
                                       bool b_transcode_only,
                                       QStringList options )
695
{
696
    QStringList outputMRLs;
697 698

    /* Stream */
699 700
    // Does streaming multiple files make sense?  I suppose so, just stream one
    // after the other, but not at the moment.
701 702
    if( !b_transcode_only )
    {
703
        SoutDialog *s = new SoutDialog( parent, p_intf, mrls[0] );
704
        s->setAttribute( Qt::WA_QuitOnClose, false ); // See #4883
705
        if( s->exec() == QDialog::Accepted )
706
        {
707
            outputMRLs.append(s->getMrl());
708 709 710 711
            delete s;
        }
        else
        {
712
            delete s; return;
713 714
        }
    } else {
715
    /* Convert */
716
        ConvertDialog *s = new ConvertDialog( parent, p_intf, mrls );
717
        s->setAttribute( Qt::WA_QuitOnClose, false ); // See #4883
718
        if( s->exec() == QDialog::Accepted )
719
        {
720 721 722 723 724
            /* Clear the playlist.  This is because we're going to be populating
               it */
            playlist_Clear( THEPL, pl_Unlocked );

            outputMRLs = s->getMrls();
725 726 727 728
            delete s;
        }
        else
        {
729
            delete s; return;
730
        }
731 732
    }

733 734
    /* Get SoutMRL(s) */
    if( !outputMRLs.isEmpty() )
735
    {
736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751
        /* For all of our MRLs */
        for(int i = 0; i < outputMRLs.length(); i++)
        {

            /* Duplicate the options list.  This is because we need to have a
             copy for every file we add to the playlist.*/
            QStringList optionsCopy;
            for(int j = 0; j < options.length(); j++)
            {
                optionsCopy.append(options[j]);
            }

            optionsCopy+= outputMRLs[i].split( " :");
            QString title = "Converting " + mrls[i];

            /* Add each file to convert to our playlist, making sure to not attempt to start playing it.*/
752
            Open::openMRLwithOptions( p_intf, mrls[i], &optionsCopy, false, true, qtu( title ) );
753
        }
754

755 756
        /* Start the playlist from the beginning */
        playlist_Control(THEPL,PLAYLIST_PLAY,pl_Unlocked);
757 758 759
    }
}

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
760
void DialogsProvider::openAndStreamingDialogs()
761
{
762 763
    OpenDialog::getInstance( p_intf->p_sys->p_mi, p_intf, false, OPEN_AND_STREAM )
                                ->showTab( OPEN_FILE_TAB );
764 765
}

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
766
void DialogsProvider::openAndTranscodingDialogs()
767
{
768 769
    OpenDialog::getInstance( p_intf->p_sys->p_mi , p_intf, false, OPEN_AND_SAVE )
                                ->showTab( OPEN_FILE_TAB );
770 771
}

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
772 773 774 775 776 777 778 779
void DialogsProvider::loadSubtitlesFile()
{
    input_thread_t *p_input = THEMIM->getInput();
    if( !p_input ) return;

    input_item_t *p_item = input_GetItem( p_input );
    if( !p_item ) return;

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
780 781 782 783
    char *path = input_item_GetURI( p_item );
    char *path2 = NULL;
    if( path )
    {
784
        path2 = vlc_uri2path( path );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
785 786 787 788 789 790 791
        free( path );
        if( path2 )
        {
            char *sep = strrchr( path2, DIR_SEP_CHAR );
            if( sep ) *sep = '\0';
        }
    }
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
792 793 794

    QStringList qsl = showSimpleOpen( qtr( "Open subtitles..." ),
                                      EXT_FILTER_SUBTITLE,
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
795 796
                                      qfu( path2 ) );
    free( path2 );
797
    foreach( const QString &qsFile, qsl )
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
798
    {
799 800
        if( input_AddSubtitleOSD( p_input, qtu( toNativeSeparators( qsFile ) ),
                    true, true ) )
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
801 802 803 804 805 806
            msg_Warn( p_intf, "unable to load subtitles from '%s'",
                      qtu( qsFile ) );
    }
}


807
/****************************************************************************
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
808
 * Menus
809
 ****************************************************************************/
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
810

811 812
void DialogsProvider::menuAction( QObject *data )
{
813
    VLCMenuBar::DoAction( data );
814 815
}

816 817
void DialogsProvider::menuUpdateAction( QObject *data )
{
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
818 819 820
    MenuFunc *func = qobject_cast<MenuFunc *>(data);
    assert( func );
    func->doFunc( p_intf );
821 822
}

Rémi Duraffort's avatar
Rémi Duraffort committed
823
void DialogsProvider::SDMenuAction( const QString& data )
Clément Stenac's avatar
Clément Stenac committed
824
{
825 826
    if( !playlist_IsServicesDiscoveryLoaded( THEPL, qtu( data ) ) )
        playlist_ServicesDiscoveryAdd( THEPL, qtu( data ) );
Clément Stenac's avatar
Clément Stenac committed
827
    else
828
        playlist_ServicesDiscoveryRemove( THEPL, qtu( data ) );
Clément Stenac's avatar
Clément Stenac committed
829 830
}

831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859
void DialogsProvider::sendKey( int key )
{
     // translate from a vlc keycode into a Qt sequence
     QKeySequence kseq0( VLCKeyToString( key, true ) );

     if( popupMenu == NULL )
     {
         // make sure at least a non visible popupmenu is available
         popupMenu = VLCMenuBar::PopupMenu( p_intf, false );
         if( unlikely( popupMenu == NULL ) )
             return;
     }

     // test against key accelerators from the popupmenu
     QList<QAction*> actions = popupMenu->findChildren<QAction*>();
     for( int i = 0; i < actions.size(); i++ )
     {
         QAction* action = actions.at(i);
         QKeySequence kseq = action->shortcut();
         if( kseq == kseq0 )
         {
             action->trigger();
             return;
         }
     }

     // forward key to vlc core when not a key accelerator
     var_SetInteger( p_intf->p_libvlc, "key-pressed", key );
}