open_panels.cpp 45.6 KB
Newer Older
1
/*****************************************************************************
Pere Orga's avatar
Pere Orga committed
2
 * open_panels.cpp : Panels for the open dialogs
Clément Stenac's avatar
Clément Stenac committed
3
 ****************************************************************************
4
 * Copyright (C) 2006-2009 the VideoLAN team
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
5 6
 * Copyright (C) 2007 Société des arts technologiques
 * Copyright (C) 2007 Savoir-faire Linux
7
 *
8
 * $Id$
Clément Stenac's avatar
Clément Stenac committed
9 10
 *
 * Authors: Clément Stenac <zorglub@videolan.org>
Clément Stenac's avatar
Clément Stenac committed
11
 *          Jean-Baptiste Kempf <jb@videolan.org>
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
12
 *          Pierre-Luc Beaudoin <pierre-luc.beaudoin@savoirfairelinux.com>
Clément Stenac's avatar
Clément Stenac committed
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
 *
 * 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.
 *****************************************************************************/

29 30 31
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
32

33
#include "qt.hpp"
34
#include "components/open_panels.hpp"
35
#include "dialogs/open.hpp"
36
#include "dialogs_provider.hpp" /* Open Subtitle file */
37
#include "util/qt_dirs.hpp"
François Cartegnie's avatar
François Cartegnie committed
38
#include "util/validators.hpp"
39
#include <vlc_intf_strings.h>
40
#include <vlc_modules.h>
41
#include <vlc_plugin.h>
42
#ifdef _WIN32
43 44
  #include <vlc_charset.h> /* FromWide for Win32 */
#endif
45

46 47 48
#include <QFileDialog>
#include <QDialogButtonBox>
#include <QLineEdit>
49
#include <QStackedLayout>
50
#include <QCompleter>
51
#include <QDirModel>
52
#include <QScrollArea>
53
#include <QUrl>
54
#include <QMimeData>
55
#include <QDropEvent>
56

57 58 59
#define I_DEVICE_TOOLTIP \
    I_DIR_OR_FOLDER( N_("Select a device or a VIDEO_TS directory"), \
                     N_("Select a device or a VIDEO_TS folder") )
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
60

61 62
#include "../../eject.c"

63 64 65 66
/* Populate a combobox with the devices matching a pattern.
   Combobox will automatically do autocompletion on the edit zone */
#define POPULATE_WITH_DEVS(ppsz_devlist, targetCombo) \
    QStringList targetCombo ## StringList = QStringList(); \
67
    for ( size_t i = 0; i< sizeof(ppsz_devlist) / sizeof(*ppsz_devlist); i++ ) \
68 69 70 71 72 73
        targetCombo ## StringList << QString( ppsz_devlist[ i ] ); \
    targetCombo->addItems( QDir( "/dev/" )\
        .entryList( targetCombo ## StringList, QDir::System )\
        .replaceInStrings( QRegExp("^"), "/dev/" ) \
    );

74
static const char psz_devModule[][8] = { "v4l2", "pvr", "dtv",
75 76
                                       "dshow", "screen", "jack" };

Clément Stenac's avatar
Clément Stenac committed
77
/**************************************************************************
78
 * Open Files and subtitles                                               *
Clément Stenac's avatar
Clément Stenac committed
79 80
 **************************************************************************/
FileOpenPanel::FileOpenPanel( QWidget *_parent, intf_thread_t *_p_intf ) :
81
                                OpenPanel( _parent, _p_intf ), dialogBox( NULL )
Clément Stenac's avatar
Clément Stenac committed
82
{
83
    /* Classic UI Setup */
Clément Stenac's avatar
Clément Stenac committed
84
    ui.setupUi( this );
85

86 87
    setAcceptDrops( true );

88
    /* Set Filters for file selection */
89
/*    QString fileTypes = "";
90 91 92 93 94
    ADD_FILTER_MEDIA( fileTypes );
    ADD_FILTER_VIDEO( fileTypes );
    ADD_FILTER_AUDIO( fileTypes );
    ADD_FILTER_PLAYLIST( fileTypes );
    ADD_FILTER_ALL( fileTypes );
95 96 97 98 99 100 101 102
    fileTypes.replace( QString(";*"), QString(" *")); */


/*    lineFileEdit = ui.fileEdit;
    //TODO later: fill the fileCompleteList with previous items played.
    QCompleter *fileCompleter = new QCompleter( fileCompleteList, this );
    fileCompleter->setModel( new QDirModel( fileCompleter ) );
    lineFileEdit->setCompleter( fileCompleter );*/
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
103
    if( var_InheritBool( p_intf, "qt-embedded-open" ) )
104 105 106 107
    {
        ui.tempWidget->hide();
        BuildOldPanel();
    }
108

109 110
    /* Subtitles */
    /* Deactivate the subtitles control by default. */
111
    ui.subGroupBox->setEnabled( false );
112 113 114

    /* Connects  */
    BUTTONACT( ui.fileBrowseButton, browseFile() );
115
    BUTTONACT( ui.removeFileButton, removeFile() );
116 117

    BUTTONACT( ui.subBrowseButton, browseFileSub() );
118
    CONNECT( ui.subGroupBox, toggled( bool ), this, updateMRL() );
119 120

    CONNECT( ui.fileListWidg, itemChanged( QListWidgetItem * ), this, updateMRL() );
121
    CONNECT( ui.subInput, textChanged( const QString& ), this, updateMRL() );
122
    updateButtons();
123 124 125 126 127 128 129 130 131 132
}

inline void FileOpenPanel::BuildOldPanel()
{
    /** BEGIN QFileDialog tweaking **/
    /* Use a QFileDialog and customize it because we don't want to
       rewrite it all. Be careful to your eyes cause there are a few hacks.
       Be very careful and test correctly when you modify this. */

    /* Make this QFileDialog a child of tempWidget from the ui. */
133
    dialogBox = new FileOpenBox( ui.tempWidget, NULL,
134
                                 p_intf->p_sys->filepath, "" );
135

136
    dialogBox->setFileMode( QFileDialog::ExistingFiles );
137
    dialogBox->setAcceptMode( QFileDialog::AcceptOpen );
138
    dialogBox->restoreState(
139
            getSettings()->value( "file-dialog-state" ).toByteArray() );
140

141 142
    /* We don't want to see a grip in the middle of the window, do we? */
    dialogBox->setSizeGripEnabled( false );
143 144

    /* Add a tooltip */
145
    dialogBox->setToolTip( qtr( "Select one or multiple files" ) );
146
    dialogBox->setMinimumHeight( 250 );
147 148

    // But hide the two OK/Cancel buttons. Enable them for debug.
149
    QDialogButtonBox *fileDialogAcceptBox =
150
                      dialogBox->findChildren<QDialogButtonBox*>()[0];
151
    fileDialogAcceptBox->hide();
152 153 154

    /* Ugly hacks to get the good Widget */
    //This lineEdit is the normal line in the fileDialog.
155
    QLineEdit *lineFileEdit = dialogBox->findChildren<QLineEdit*>()[0];
156
    /* Make a list of QLabel inside the QFileDialog to access the good ones */
157
    QList<QLabel *> listLabel = dialogBox->findChildren<QLabel*>();
158 159

    /* Hide the FileNames one. Enable it for debug */
160
    listLabel[1]->setText( qtr( "File names:" ) );
161
    /* Change the text that was uncool in the usual box */
162
    listLabel[2]->setText( qtr( "Filter:" ) );
Clément Stenac's avatar
Clément Stenac committed
163

164
    dialogBox->layout()->setMargin( 0 );
165
    dialogBox->layout()->setSizeConstraint( QLayout::SetNoConstraint );
166

167 168 169
    /** END of QFileDialog tweaking **/

    // Add the DialogBox to the layout
170
    ui.gridLayout->addWidget( dialogBox, 0, 0, 1, 3 );
171

172
    CONNECT( lineFileEdit, textChanged( const QString& ), this, updateMRL() );
173
    dialogBox->installEventFilter( this );
174 175
}

176 177
FileOpenPanel::~FileOpenPanel()
{
178 179
    if( dialogBox )
        getSettings()->setValue( "file-dialog-state", dialogBox->saveState() );
180 181
}

182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
void FileOpenPanel::dragEnterEvent( QDragEnterEvent *event )
{
    event->acceptProposedAction();
}

void FileOpenPanel::dragMoveEvent( QDragMoveEvent *event )
{
    event->acceptProposedAction();
}

void FileOpenPanel::dragLeaveEvent( QDragLeaveEvent *event )
{
    event->accept();
}

void FileOpenPanel::dropEvent( QDropEvent *event )
{
    if( event->possibleActions() & Qt::CopyAction )
       event->setDropAction( Qt::CopyAction );
    else
        return;

    const QMimeData *mimeData = event->mimeData();
    foreach( const QUrl &url, mimeData->urls() )
    {
        if( url.isValid() )
        {
            QListWidgetItem *item = new QListWidgetItem(
                                         toNativeSeparators( url.toLocalFile() ),
                                         ui.fileListWidg );
            item->setFlags( Qt::ItemIsEditable | Qt::ItemIsEnabled );
            ui.fileListWidg->addItem( item );
        }
    }
    updateMRL();
    updateButtons();
    event->accept();
}

221 222
void FileOpenPanel::browseFile()
{
223
    QStringList files = QFileDialog::getOpenFileNames( this, qtr( "Select one or multiple files" ), p_intf->p_sys->filepath );
224
    foreach( const QString &file, files )
225
    {
226 227
        QListWidgetItem *item =
            new QListWidgetItem( toNativeSeparators( file ), ui.fileListWidg );
228 229
        item->setFlags( Qt::ItemIsEditable | Qt::ItemIsEnabled );
        ui.fileListWidg->addItem( item );
230
        savedirpathFromFile( file );
231
    }
232
    updateButtons();
233
    updateMRL();
234
}
235

236
void FileOpenPanel::removeFile()
237 238 239 240 241 242 243 244 245
{
    int i = ui.fileListWidg->currentRow();
    if( i != -1 )
    {
        QListWidgetItem *temp = ui.fileListWidg->takeItem( i );
        delete temp;
    }

    updateMRL();
246
    updateButtons();
247 248
}

249
/* Show a fileBrowser to select a subtitle */
250 251
void FileOpenPanel::browseFileSub()
{
252
    // TODO Handle selection of more than one subtitles file
253
    QStringList urls = THEDP->showSimpleOpen( qtr("Open subtitle file"),
254
                           EXT_FILTER_SUBTITLE, p_intf->p_sys->filepath );
255

256 257
    if( urls.isEmpty() ) return;
    ui.subInput->setText( urls.join(" ") );
Clément Stenac's avatar
Clément Stenac committed
258
    updateMRL();
259 260
}

261
/* Update the current MRL */
262 263
void FileOpenPanel::updateMRL()
{
264 265
    QStringList fileList;
    QString mrl;
Clément Stenac's avatar
Clément Stenac committed
266

267 268 269 270 271
    /* File Listing */
    if( dialogBox == NULL )
        for( int i = 0; i < ui.fileListWidg->count(); i++ )
        {
            if( !ui.fileListWidg->item( i )->text().isEmpty() )
272
                fileList << toURI(ui.fileListWidg->item( i )->text());
273 274
        }
    else
275
    {
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
276
#if HAS_QT5
277 278 279 280
        QList<QUrl> urls = dialogBox->selectedUrls();
        foreach( const QUrl &url, urls )
            fileList.append( url.toEncoded() );
#else
281
        fileList = dialogBox->selectedFiles();
282
        for( int i = 0; i < fileList.count(); i++ )
283
            fileList[i] = toURI( fileList[i] );
284
#endif
285
    }
286 287

    /* Options */
288
    if( ui.subGroupBox->isChecked() &&  !ui.subInput->text().isEmpty() ) {
289
        mrl.append( " :sub-file=" + colon_escape( ui.subInput->text() ) );
Clément Stenac's avatar
Clément Stenac committed
290
    }
291

292
    emit methodChanged( "file-caching" );
293
    emit mrlUpdated( fileList, mrl );
294 295 296 297 298
}

/* Function called by Open Dialog when clicke on Play/Enqueue */
void FileOpenPanel::accept()
{
299 300
    if( dialogBox )
        p_intf->p_sys->filepath = dialogBox->directory().absolutePath();
301
    ui.fileListWidg->clear();
302
}
303

304
/* Function called by Open Dialog when clicked on cancel */
305 306
void FileOpenPanel::clear()
{
307
    ui.fileListWidg->clear();
308
    ui.subInput->clear();
309 310
}

311 312 313 314 315
/* Update buttons depending on current selection */
void FileOpenPanel::updateButtons()
{
    bool b_has_files = ( ui.fileListWidg->count() > 0 );
    ui.removeFileButton->setEnabled( b_has_files );
316
    ui.subGroupBox->setEnabled( b_has_files );
317 318
}

Clément Stenac's avatar
Clément Stenac committed
319
/**************************************************************************
320
 * Open Discs ( DVD, CD, VCD and similar devices )                        *
Clément Stenac's avatar
Clément Stenac committed
321
 **************************************************************************/
322
DiscOpenPanel::DiscOpenPanel( QWidget *_parent, intf_thread_t *_p_intf ) :
Clément Stenac's avatar
Clément Stenac committed
323 324 325
                                OpenPanel( _parent, _p_intf )
{
    ui.setupUi( this );
Clément Stenac's avatar
Clément Stenac committed
326

327
    /* Get the default configuration path for the devices */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
328 329 330
    psz_dvddiscpath = var_InheritString( p_intf, "dvd" );
    psz_vcddiscpath = var_InheritString( p_intf, "vcd" );
    psz_cddadiscpath = var_InheritString( p_intf, "cd-audio" );
331 332

    /* State to avoid overwritting the users changes with the configuration */
333
    m_discType = None;
334

335
    ui.browseDiscButton->setToolTip( qtr( I_DEVICE_TOOLTIP ));
Eric Lassauge's avatar
Eric Lassauge committed
336
    ui.deviceCombo->setToolTip( qtr(I_DEVICE_TOOLTIP) );
337
    ui.deviceCombo->setInsertPolicy( QComboBox::InsertAtTop );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
338

339
#if !defined( _WIN32 ) && !defined( __OS2__ )
340 341 342 343 344 345 346 347 348
    char const * const ppsz_discdevices[] = {
        "sr*",
        "sg*",
        "scd*",
        "dvd*",
        "cd*"
    };
    QComboBox *discCombo = ui.deviceCombo; /* avoid namespacing in macro */
    POPULATE_WITH_DEVS( ppsz_discdevices, discCombo );
Hannes Domani's avatar
Hannes Domani committed
349 350 351
    char *psz_config = config_GetPsz( p_intf, "dvd" );
    int temp = ui.deviceCombo->findData( psz_config, Qt::UserRole, Qt::MatchStartsWith );
    free( psz_config );
352 353
    if( temp != -1 )
        ui.deviceCombo->setCurrentIndex( temp );
354 355
#endif

356
    /* CONNECTs */
357 358 359
    BUTTONACT( ui.dvdRadioButton,     updateButtons() );
    BUTTONACT( ui.bdRadioButton,      updateButtons() );
    BUTTONACT( ui.vcdRadioButton,     updateButtons() );
360
    BUTTONACT( ui.audioCDRadioButton, updateButtons() );
361
    BUTTONACT( ui.dvdsimple,          updateButtons() );
362 363 364 365 366 367 368 369 370 371
    BUTTONACT( ui.browseDiscButton, browseDevice() );
    BUTTON_SET_ACT_I( ui.ejectButton, "", toolbar/eject, qtr( "Eject the disc" ),
            eject() );

    CONNECT( ui.deviceCombo, editTextChanged( QString ), this, updateMRL());
    CONNECT( ui.deviceCombo, currentIndexChanged( QString ), this, updateMRL());
    CONNECT( ui.titleSpin, valueChanged( int ), this, updateMRL());
    CONNECT( ui.chapterSpin, valueChanged( int ), this, updateMRL());
    CONNECT( ui.audioSpin, valueChanged( int ), this, updateMRL());
    CONNECT( ui.subtitlesSpin, valueChanged( int ), this, updateMRL());
372
    CONNECT( ui.dvdsimple, toggled( bool ), this, updateMRL());
373 374 375 376 377

    /* Run once the updateButtons function in order to fill correctly the comboBoxes */
    updateButtons();
}

378
#ifdef _WIN32 /* Disc drives probing for Windows */
379 380
void DiscOpenPanel::onFocus()
{
381
    ui.deviceCombo->clear();
382
    wchar_t szDrives[512];
383
    szDrives[0] = '\0';
384
    if( GetLogicalDriveStringsW( sizeof( szDrives ) / sizeof( *szDrives ) - 1, szDrives ) )
385
    {
386
        wchar_t *drive = szDrives;
387 388 389
        UINT oldMode = SetErrorMode( SEM_FAILCRITICALERRORS );
        while( *drive )
        {
390
            if( GetDriveTypeW(drive) == DRIVE_CDROM )
391
            {
392 393 394
                wchar_t psz_name[512] = L"";
                GetVolumeInformationW( drive, psz_name, 511, NULL, NULL, NULL, NULL, 0 );

Hannes Domani's avatar
Hannes Domani committed
395 396
                char *psz_drive = FromWide( drive );
                QString displayName = psz_drive;
397 398 399
                char *psz_title = FromWide( psz_name );
                if( !EMPTY_STR(psz_title)) {
                    displayName = displayName + " - "  + psz_title;
400 401
                }

Hannes Domani's avatar
Hannes Domani committed
402 403
                ui.deviceCombo->addItem( displayName, psz_drive );
                free( psz_drive );
404
                free( psz_title );
405
            }
406 407 408 409 410 411

            /* go to next drive */
            while( *(drive++) );
        }
        SetErrorMode(oldMode);
    }
412

Hannes Domani's avatar
Hannes Domani committed
413 414 415
    char *psz_config = config_GetPsz( p_intf, "dvd" );
    int temp = ui.deviceCombo->findData( psz_config, Qt::UserRole, Qt::MatchStartsWith );
    free( psz_config );
416 417
    if( temp != -1 )
        ui.deviceCombo->setCurrentIndex( temp );
Clément Stenac's avatar
Clément Stenac committed
418
}
419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459
#elif defined( __OS2__ ) /* Disc drives probing for OS/2 */
void DiscOpenPanel::onFocus()
{

    ui.deviceCombo->clear();

#define IOCTL_CDROMDISK2        0x82
#define CDROMDISK2_DRIVELETTERS 0x60

    HFILE hcd2;
    ULONG ulAction;
    ULONG ulParamLen;
    ULONG ulData;
    ULONG ulDataLen;

    if( DosOpen(( PSZ )"CD-ROM2$", ( PHFILE )&hcd2, &ulAction, 0, FILE_NORMAL,
                OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW,
                OPEN_ACCESS_READONLY | OPEN_SHARE_DENYNONE,
                NULL ))
        return;

    if( !DosDevIOCtl( hcd2, IOCTL_CDROMDISK2, CDROMDISK2_DRIVELETTERS,
                      NULL, 0, &ulParamLen, &ulData, sizeof( ulData ), &ulDataLen ))
    {
        char szDriveName[] = "X:\\";

        int count = LOUSHORT( ulData );
        int drive = HIUSHORT( ulData );

        for( ; count; --count, ++drive )
        {
            szDriveName[ 0 ] = 'A' + drive;

            QString name = qfu( szDriveName );

            ui.deviceCombo->addItem( name, name );
        }
    }

    DosClose( hcd2 );
}
460
#endif
Clément Stenac's avatar
Clément Stenac committed
461

462 463 464 465 466 467
void DiscOpenPanel::updateContext(int c)
{
    OpenPanel::updateContext(c);
    ui.dvdsimple->setChecked( context == OpenPanel::CONTEXT_BATCH );
}

468
DiscOpenPanel::~DiscOpenPanel()
469
{
470 471 472
    free( psz_dvddiscpath );
    free( psz_vcddiscpath );
    free( psz_cddadiscpath );
473
}
474

475
void DiscOpenPanel::clear()
Clément Stenac's avatar
Clément Stenac committed
476
{
477 478
    ui.titleSpin->setValue( 0 );
    ui.chapterSpin->setValue( 0 );
479 480
    ui.subtitlesSpin->setValue( -1 );
    ui.audioSpin->setValue( -1 );
481
    m_discType = None;
Clément Stenac's avatar
Clément Stenac committed
482
}
483

484
#if defined( _WIN32 ) || defined( __OS2__ )
485 486 487 488 489 490 491 492
    #define setDrive( psz_name ) {\
    int index = ui.deviceCombo->findText( qfu( psz_name ) ); \
    if( index != -1 ) ui.deviceCombo->setCurrentIndex( index );}
#else
    #define setDrive( psz_name ) {\
    ui.deviceCombo->setEditText( qfu( psz_name ) ); }
#endif

493
/* update the buttons according the type of device */
494 495 496 497
void DiscOpenPanel::updateButtons()
{
    if ( ui.dvdRadioButton->isChecked() )
    {
498
        if( m_discType != Dvd )
499 500
        {
            setDrive( psz_dvddiscpath );
501
            m_discType = Dvd;
502
        }
503 504 505 506
        ui.titleLabel->setText( qtr("Title") );
        ui.chapterLabel->show();
        ui.chapterSpin->show();
        ui.diskOptionBox_2->show();
507
        ui.dvdsimple->setEnabled( true );
508
    }
509
    else if ( ui.bdRadioButton->isChecked() )
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
510 511 512 513 514
    {
        if( m_discType != BRD )
        {
            setDrive( psz_dvddiscpath );
            m_discType = BRD;
515
            ui.dvdsimple->setChecked( !var_InheritBool( p_intf, "bluray-menu" ) );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
516 517 518 519 520 521 522
        }
        ui.titleLabel->setText( qtr("Title") );
        ui.chapterLabel->hide();
        ui.chapterSpin->hide();
        ui.diskOptionBox_2->hide();
        ui.dvdsimple->setEnabled( true );
    }
523 524
    else if ( ui.vcdRadioButton->isChecked() )
    {
525
        if( m_discType != Vcd )
526 527
        {
            setDrive( psz_vcddiscpath );
528
            m_discType = Vcd;
529
        }
530 531 532 533
        ui.titleLabel->setText( qtr("Entry") );
        ui.chapterLabel->hide();
        ui.chapterSpin->hide();
        ui.diskOptionBox_2->show();
534
        ui.dvdsimple->setEnabled( false );
535
    }
536
    else /* CDDA */
537
    {
538
        if( m_discType != Cdda )
539 540
        {
            setDrive( psz_cddadiscpath );
541
            m_discType = Cdda;
542
        }
543 544 545 546
        ui.titleLabel->setText( qtr("Track") );
        ui.chapterLabel->hide();
        ui.chapterSpin->hide();
        ui.diskOptionBox_2->hide();
547
        ui.dvdsimple->setEnabled( false );
548 549 550 551 552
    }

    updateMRL();
}

Rémi Duraffort's avatar
Rémi Duraffort committed
553 554
#undef setDrive

555
/* Update the current MRL */
556
void DiscOpenPanel::updateMRL()
Clément Stenac's avatar
Clément Stenac committed
557
{
558
    QString discPath;
559
    QStringList fileList;
560

561 562 563 564 565
    discPath = ui.deviceCombo->currentText();

    int tmp = ui.deviceCombo->findText( discPath );
    if( tmp != -1 &&  ui.deviceCombo->itemData( tmp ) != QVariant::Invalid )
        discPath = ui.deviceCombo->itemData( tmp ).toString();
566

567
    /* MRL scheme */
568
    const char *scheme;
Clément Stenac's avatar
Clément Stenac committed
569 570
    /* DVD */
    if( ui.dvdRadioButton->isChecked() ) {
571
        if( !ui.dvdsimple->isChecked() )
572
            scheme = "dvd";
573
        else
574
            scheme = "dvdsimple";
575
    } else if ( ui.bdRadioButton->isChecked() )
576
        scheme = "bluray";
577 578
    /* VCD */
    else if ( ui.vcdRadioButton->isChecked() )
579
        scheme = "vcd";
580 581
    /* CDDA */
    else
582
        scheme = "cdda";
583

584
    char *mrl = vlc_path2uri( qtu(discPath), scheme );
585 586
    if( unlikely(mrl == NULL) )
        return;
587 588

    /* Title/chapter encoded in MRL */
589
    QString anchor = "";
590 591
    if( ui.titleSpin->value() > 0 ) {
        if( ui.dvdRadioButton->isChecked() || ui.bdRadioButton->isChecked() ) {
592
            anchor = QString("#%1").arg( ui.titleSpin->value() );
593
            if ( ui.chapterSpin->value() > 0 )
594
                anchor += QString(":%1").arg( ui.chapterSpin->value() );
Clément Stenac's avatar
Clément Stenac committed
595
        }
596
        else if ( ui.vcdRadioButton->isChecked() )
597
            anchor = QString("#%1").arg( ui.titleSpin->value() );
598
    }
599

600
    emit methodChanged( "disc-caching" );
601

602 603 604 605
    fileList << (qfu(mrl) + anchor);
    free(mrl);

    QString opts = "";
606

607
    /* Input item options */
608 609
    if ( ui.dvdRadioButton->isChecked() || ui.vcdRadioButton->isChecked() )
    {
610 611
        if ( ui.audioSpin->value() >= 0 )
            opts += " :audio-track=" +
612
                QString("%1").arg( ui.audioSpin->value() );
613 614
        if ( ui.subtitlesSpin->value() >= 0 )
            opts += " :sub-track=" +
615
                QString("%1").arg( ui.subtitlesSpin->value() );
Clément Stenac's avatar
Clément Stenac committed
616
    }
617
    else if( ui.audioCDRadioButton->isChecked() )
618 619
    {
        if( ui.titleSpin->value() > 0 )
620
            opts += QString(" :cdda-track=%1").arg( ui.titleSpin->value() );
621
    }
622 623
    else if ( ui.bdRadioButton->isChecked() )
    {
624
        if ( ui.dvdsimple->isChecked() )
625
            opts += " :no-bluray-menu";
626
    }
627
    emit mrlUpdated( fileList, opts );
628 629
}

630 631
void DiscOpenPanel::browseDevice()
{
632
    QString dir = QFileDialog::getExistingDirectory( this,
633
            qtr( I_DEVICE_TOOLTIP ), p_intf->p_sys->filepath );
634 635 636 637 638
    if( !dir.isEmpty() )
    {
        ui.deviceCombo->addItem( toNativeSepNoSlash( dir ) );
        ui.deviceCombo->setCurrentIndex( ui.deviceCombo->findText( toNativeSepNoSlash( dir ) ) );
    }
639

640 641
    updateMRL();
}
642

643 644 645 646 647
void DiscOpenPanel::eject()
{
    intf_Eject( p_intf, qtu( ui.deviceCombo->currentText() ) );
}

648
void DiscOpenPanel::accept()
649
{}
650

651
/**************************************************************************
652
 * Open Network streams and URL pages                                     *
653 654 655 656 657
 **************************************************************************/
NetOpenPanel::NetOpenPanel( QWidget *_parent, intf_thread_t *_p_intf ) :
                                OpenPanel( _parent, _p_intf )
{
    ui.setupUi( this );
658
    CONNECT( ui.urlComboBox, editTextChanged( const QString& ), this, updateMRL());
Clément Stenac's avatar
Clément Stenac committed
659

660
    /* */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
661
    if( var_InheritBool( p_intf, "qt-recentplay" ) )
662
    {
663
        b_recentList = true;
664
        ui.urlComboBox->addItems( getSettings()->value( "OpenDialog/netMRL" ).toStringList() );
665
        ui.urlComboBox->setMaxCount( 10 );
666 667
    }
    else
668
        b_recentList = false;
669

670 671 672 673
    QFont smallFont = QApplication::font();
    smallFont.setPointSize( smallFont.pointSize() - 1 );
    ui.examples->setFont( smallFont );

674
    /* Use a simple validator for URLs */
675 676
    ui.urlComboBox->setValidator( new UrlValidator( this ) );
    ui.urlComboBox->setFocus();
Clément Stenac's avatar
Clément Stenac committed
677 678
}

679
NetOpenPanel::~NetOpenPanel()
680
{
681
    if( !b_recentList ) return;
682

683 684 685 686
    /* Create the list with the current items */
    QStringList mrlList;
    for( int i = 0; i < ui.urlComboBox->count(); i++ )
        mrlList << ui.urlComboBox->itemText( i );
687

688 689 690 691 692 693 694
    if( mrlList.count() > 0 )
    {
        /* Clean the list... */
        mrlList.removeDuplicates();
        /* ...and save the 8 last entries */
        getSettings()->setValue( "OpenDialog/netMRL", mrlList );
    }
695
}
696

Clément Stenac's avatar
Clément Stenac committed
697
void NetOpenPanel::clear()
698
{
699
    ui.urlComboBox->lineEdit()->clear();
700 701 702 703 704 705 706
}

void NetOpenPanel::onAccept()
{
    if( ui.urlComboBox->findText( ui.urlComboBox->currentText() ) == -1 )
        ui.urlComboBox->insertItem( 0, ui.urlComboBox->currentText());
}
707

708 709 710 711 712 713
void NetOpenPanel::onFocus()
{
    ui.urlComboBox->setFocus();
    ui.urlComboBox->lineEdit()->selectAll();
}

714 715
void NetOpenPanel::updateMRL()
{
716
    QString url = ui.urlComboBox->lineEdit()->text();
717

718
    emit methodChanged( qfu( "network-caching" ) );
719 720

    QStringList qsl;
721
    if( !url.isEmpty() ) qsl << url;
722
    emit mrlUpdated( qsl, "" );
Clément Stenac's avatar
Clément Stenac committed
723
}
724 725

/**************************************************************************
726
 * Open Capture device ( DVB, PVR, V4L, and similar )                     *
727 728 729 730
 **************************************************************************/
CaptureOpenPanel::CaptureOpenPanel( QWidget *_parent, intf_thread_t *_p_intf ) :
                                OpenPanel( _parent, _p_intf )
{
731
    isInitialized = false;
732 733 734 735
#ifdef _WIN32
    vdevDshowW = NULL;
    adevDshowW = NULL;
#endif
736 737 738 739 740 741 742 743
}

void CaptureOpenPanel::initialize()
{
    if( isInitialized ) return;

    isInitialized = true;

744
    ui.setupUi( this );
745

746
    BUTTONACT( ui.advancedButton, advancedDialog() );
747
    CONNECT( ui.deviceCombo, currentIndexChanged(int), this, enableAdvancedDialog(int) );
748

749
    /* Create two stacked layouts in the main comboBoxes */
750 751 752 753 754 755
    QStackedLayout *stackedDevLayout = new QStackedLayout;
    ui.cardBox->setLayout( stackedDevLayout );

    QStackedLayout *stackedPropLayout = new QStackedLayout;
    ui.optionsBox->setLayout( stackedPropLayout );

756
    /* Creation and connections of the WIdgets in the stacked layout */
757
#define addModuleAndLayouts( number, name, label, layout )            \
758 759
    QWidget * name ## DevPage = new QWidget( this );                  \
    QWidget * name ## PropPage = new QWidget( this );                 \
760 761
    stackedDevLayout->addWidget( name ## DevPage );        \
    stackedPropLayout->addWidget( name ## PropPage );      \
762 763
    layout * name ## DevLayout = new layout;                \
    layout * name ## PropLayout = new layout;               \
764 765
    name ## DevPage->setLayout( name ## DevLayout );                  \
    name ## PropPage->setLayout( name ## PropLayout );                \
766
    ui.deviceCombo->addItem( qtr( label ), QVariant( number ) );
767 768 769

#define CuMRL( widget, slot ) CONNECT( widget , slot , this, updateMRL() );

770
#ifdef _WIN32
771 772 773
    /*********************
     * DirectShow Stuffs *
     *********************/
774
    if( module_exists( "dshow" ) ){
775
    addModuleAndLayouts( DSHOW_DEVICE, dshow, "DirectShow", QGridLayout );
776

777 778 779 780 781
    /* dshow Main */
    int line = 0;
    module_config_t *p_config =
        config_FindConfig( VLC_OBJECT(p_intf), "dshow-vdev" );
    vdevDshowW = new StringListConfigControl(
782 783
        VLC_OBJECT(p_intf), p_config, this );
    vdevDshowW->insertIntoExistingGrid( dshowDevLayout, line );
784 785 786 787
    line++;

    p_config = config_FindConfig( VLC_OBJECT(p_intf), "dshow-adev" );
    adevDshowW = new StringListConfigControl(
788 789
        VLC_OBJECT(p_intf), p_config, this );
    adevDshowW->insertIntoExistingGrid( dshowDevLayout, line );
790 791
    line++;

792 793 794 795 796 797 798 799 800 801
    /* dshow Properties */
    QLabel *dshowVSizeLabel = new QLabel( qtr( "Video size" ) );
    dshowPropLayout->addWidget( dshowVSizeLabel, 0, 0 );

    dshowVSizeLine = new QLineEdit;
    dshowPropLayout->addWidget( dshowVSizeLine, 0, 1);
    dshowPropLayout->addItem( new QSpacerItem( 20, 20, QSizePolicy::Expanding ),
            1, 0, 3, 1 );

    /* dshow CONNECTs */
802 803
    CuMRL( vdevDshowW, changed() );
    CuMRL( adevDshowW, changed() );
804
    CuMRL( dshowVSizeLine, textChanged( const QString& ) );
805
    configList << "dshow-vdev" << "dshow-adev" << "dshow-size";
806
    }
807
#else /* _WIN32 */
808 809 810
    /*******
     * V4L2*
     *******/
811 812 813
    char const * const ppsz_v4lvdevices[] = {
        "video*"
    };
814 815
    if( module_exists( "v4l2" ) ){
    addModuleAndLayouts( V4L2_DEVICE, v4l2, "Video camera", QGridLayout );
816

817
    /* V4L2 main panel */
818 819 820
    QLabel *v4l2VideoDeviceLabel = new QLabel( qtr( "Video device name" ) );
    v4l2DevLayout->addWidget( v4l2VideoDeviceLabel, 0, 0 );

821 822 823
    v4l2VideoDevice = new QComboBox( this );
    v4l2VideoDevice->setEditable( true );
    POPULATE_WITH_DEVS( ppsz_v4lvdevices, v4l2VideoDevice );
824 825 826 827 828
    v4l2DevLayout->addWidget( v4l2VideoDevice, 0, 1 );

    QLabel *v4l2AudioDeviceLabel = new QLabel( qtr( "Audio device name" ) );
    v4l2DevLayout->addWidget( v4l2AudioDeviceLabel, 1, 0 );

829 830
    v4l2AudioDevice = new QComboBox( this );
    v4l2AudioDevice->setEditable( true );
831 832 833 834 835 836 837 838 839 840 841
    {
        QStringList patterns = QStringList();
        patterns << QString( "pcmC*D*c" );

        QStringList nodes = QDir( "/dev/snd" ).entryList( patterns,
                                                          QDir::System );
        QStringList names = nodes.replaceInStrings( QRegExp("^pcmC"), "hw:" )
                                 .replaceInStrings( QRegExp("c$"), "" )
                                 .replaceInStrings( QRegExp("D"), "," );
        v4l2AudioDevice->addItems( names );
    }
842
    v4l2AudioDevice->clearEditText();
843 844 845
    v4l2DevLayout->addWidget( v4l2AudioDevice, 1, 1 );

    /* v4l2 Props panel */
846
    QLabel *v4l2StdLabel = new QLabel( qtr( "Video standard" ) );
847 848 849
    v4l2PropLayout->addWidget( v4l2StdLabel, 0 , 0 );

    v4l2StdBox = new QComboBox;
850
    setfillVLCConfigCombo( "v4l2-standard", p_intf, v4l2StdBox );
851 852
    v4l2PropLayout->addWidget( v4l2StdBox, 0 , 1 );
    v4l2PropLayout->addItem( new QSpacerItem( 20, 20, QSizePolicy::Expanding ),
853
            1, 0, 3, 2 );
854 855

    /* v4l2 CONNECTs */
856 857 858 859
    CuMRL( v4l2VideoDevice->lineEdit(), textChanged( const QString& ) );
    CuMRL( v4l2VideoDevice,  currentIndexChanged ( int ) );
    CuMRL( v4l2AudioDevice->lineEdit(), textChanged( const QString& ) );
    CuMRL( v4l2AudioDevice,  currentIndexChanged ( int ) );
860
    CuMRL( v4l2StdBox,  currentIndexChanged ( int ) );
861
    configList << "v4l2-standard";
862 863
    }

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
864 865 866
    /*******
     * JACK *
     *******/
867
    if( module_exists( "jack" ) ){
868 869
    addModuleAndLayouts( JACK_DEVICE, jack, "JACK Audio Connection Kit",
                         QGridLayout);
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
870 871 872

    /* Jack Main panel */
    /* Channels */
873
    QLabel *jackChannelsLabel = new QLabel( qtr( "Channels:" ) );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
874 875 876 877 878 879 880 881 882 883
    jackDevLayout->addWidget( jackChannelsLabel, 1, 0 );

    jackChannels = new QSpinBox;
    setSpinBoxFreq( jackChannels );
    jackChannels->setMaximum(255);
    jackChannels->setValue(2);
    jackChannels->setAlignment( Qt::AlignRight );
    jackDevLayout->addWidget( jackChannels, 1, 1 );

    /* Selected ports */
884
    QLabel *jackPortsLabel = new QLabel( qtr( "Selected ports:" ) );
885
    jackDevLayout->addWidget( jackPortsLabel, 0 , 0 );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
886 887 888

    jackPortsSelected = new QLineEdit( qtr( ".*") );
    jackPortsSelected->setAlignment( Qt::AlignRight );
889 890 891
    jackDevLayout->addWidget( jackPortsSelected, 0, 1 );

    /* Jack Props panel */
892

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
893 894
    /* Pace */
    jackPace = new QCheckBox(qtr( "Use VLC pace" ));
895
    jackPropLayout->addWidget( jackPace, 1, 1 );
896

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
897
    /* Auto Connect */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
898
    jackConnect = new QCheckBox( qtr( "Auto connection" ));
899
    jackPropLayout->addWidget( jackConnect, 1, 2 );
900

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
901 902 903 904
    /* Jack CONNECTs */
    CuMRL( jackChannels, valueChanged( int ) );
    CuMRL( jackPace, stateChanged( int ) );
    CuMRL( jackConnect, stateChanged( int ) );
905
    CuMRL( jackPortsSelected, textChanged( const QString& ) );
906
    configList << "jack-input-use-vlc-pace" << "jack-input-auto-connect";
907
    }
908
#endif
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
909

910 911 912
    /*************
     * DVB Stuff *
     *************/
913
    if( module_exists( "dtv" ) ){
914
    addModuleAndLayouts( DTV_DEVICE, dvb, N_("TV - digital"), QGridLayout );
915 916

    /* DVB Main */
917 918
    QLabel *dvbDeviceLabel = new QLabel( qtr( "Tuner card" ) );
    QLabel *dvbTypeLabel = new QLabel( qtr( "Delivery system" ) );
919 920 921

    dvbCard = new QSpinBox;
    dvbCard->setAlignment( Qt::AlignRight );
922
#ifdef __unix__
923
    dvbCard->setPrefix( "/dev/dvb/adapter" );
924
#endif
925
    dvbDevLayout->addWidget( dvbDeviceLabel, 0, 0 );
926
    dvbDevLayout->addWidget( dvbCard, 0, 1, 1, 2 );
927 928

    dvbc = new QRadioButton( "DVB-C" );
929
    dvbs = new QRadioButton( "DVB-S" );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
930
    dvbs2 = new QRadioButton( "DVB-S2" );
931
    dvbt = new QRadioButton( "DVB-T" );
932
    dvbt2 = new QRadioButton( "DVB-T2" );
933 934
    atsc = new QRadioButton( "ATSC" );
    cqam = new QRadioButton( "Clear QAM" );
935
    dvbt->setChecked( true );
936

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
937
    dvbDevLayout->addWidget( dvbTypeLabel, 1, 0, 2, 1 );
938 939
    dvbDevLayout->addWidget( dvbc,  1, 1 );
    dvbDevLayout->addWidget( dvbs,  1, 2 );<