open_panels.cpp 45.4 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 "qt4.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 files = THEDP->showSimpleOpen( qtr("Open subtitle file"),
254
                           EXT_FILTER_SUBTITLE, p_intf->p_sys->filepath );
255

256
    if( files.isEmpty() ) return;
257
    ui.subInput->setText( toNativeSeparators( files.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
    {
276
        fileList = dialogBox->selectedFiles();
277
        for( int i = 0; i < fileList.count(); i++ )
278
            fileList[i] = toURI( fileList[i] );
279
    }
280 281

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

286
    emit methodChanged( "file-caching" );
287
    emit mrlUpdated( fileList, mrl );
288 289 290 291 292
}

/* Function called by Open Dialog when clicke on Play/Enqueue */
void FileOpenPanel::accept()
{
293 294
    if( dialogBox )
        p_intf->p_sys->filepath = dialogBox->directory().absolutePath();
295
    ui.fileListWidg->clear();
296
}
297

298
/* Function called by Open Dialog when clicked on cancel */
299 300
void FileOpenPanel::clear()
{
301
    ui.fileListWidg->clear();
302
    ui.subInput->clear();
303 304
}

305 306 307 308 309
/* Update buttons depending on current selection */
void FileOpenPanel::updateButtons()
{
    bool b_has_files = ( ui.fileListWidg->count() > 0 );
    ui.removeFileButton->setEnabled( b_has_files );
310
    ui.subGroupBox->setEnabled( b_has_files );
311 312
}

Clément Stenac's avatar
Clément Stenac committed
313
/**************************************************************************
314
 * Open Discs ( DVD, CD, VCD and similar devices )                        *
Clément Stenac's avatar
Clément Stenac committed
315
 **************************************************************************/
316
DiscOpenPanel::DiscOpenPanel( QWidget *_parent, intf_thread_t *_p_intf ) :
Clément Stenac's avatar
Clément Stenac committed
317 318 319
                                OpenPanel( _parent, _p_intf )
{
    ui.setupUi( this );
Clément Stenac's avatar
Clément Stenac committed
320

321
    /* Get the default configuration path for the devices */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
322 323 324
    psz_dvddiscpath = var_InheritString( p_intf, "dvd" );
    psz_vcddiscpath = var_InheritString( p_intf, "vcd" );
    psz_cddadiscpath = var_InheritString( p_intf, "cd-audio" );
325 326

    /* State to avoid overwritting the users changes with the configuration */
327
    m_discType = None;
328

329
    ui.browseDiscButton->setToolTip( qtr( I_DEVICE_TOOLTIP ));
Eric Lassauge's avatar
Eric Lassauge committed
330
    ui.deviceCombo->setToolTip( qtr(I_DEVICE_TOOLTIP) );
331
    ui.deviceCombo->setInsertPolicy( QComboBox::InsertAtTop );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
332

333
#if !defined( _WIN32 ) && !defined( __OS2__ )
334 335 336 337 338 339 340 341 342
    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
343 344 345
    char *psz_config = config_GetPsz( p_intf, "dvd" );
    int temp = ui.deviceCombo->findData( psz_config, Qt::UserRole, Qt::MatchStartsWith );
    free( psz_config );
346 347
    if( temp != -1 )
        ui.deviceCombo->setCurrentIndex( temp );
348 349
#endif

350
    /* CONNECTs */
351 352 353
    BUTTONACT( ui.dvdRadioButton,     updateButtons() );
    BUTTONACT( ui.bdRadioButton,      updateButtons() );
    BUTTONACT( ui.vcdRadioButton,     updateButtons() );
354
    BUTTONACT( ui.audioCDRadioButton, updateButtons() );
355
    BUTTONACT( ui.dvdsimple,          updateButtons() );
356 357 358 359 360 361 362 363 364 365
    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());
366
    CONNECT( ui.dvdsimple, toggled( bool ), this, updateMRL());
367 368 369 370 371

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

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

Hannes Domani's avatar
Hannes Domani committed
389 390
                char *psz_drive = FromWide( drive );
                QString displayName = psz_drive;
391 392 393
                char *psz_title = FromWide( psz_name );
                if( !EMPTY_STR(psz_title)) {
                    displayName = displayName + " - "  + psz_title;
394 395
                }

Hannes Domani's avatar
Hannes Domani committed
396 397
                ui.deviceCombo->addItem( displayName, psz_drive );
                free( psz_drive );
398
                free( psz_title );
399
            }
400 401 402 403 404 405

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

Hannes Domani's avatar
Hannes Domani committed
407 408 409
    char *psz_config = config_GetPsz( p_intf, "dvd" );
    int temp = ui.deviceCombo->findData( psz_config, Qt::UserRole, Qt::MatchStartsWith );
    free( psz_config );
410 411
    if( temp != -1 )
        ui.deviceCombo->setCurrentIndex( temp );
Clément Stenac's avatar
Clément Stenac committed
412
}
413 414 415 416 417 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
#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 );
}
454
#endif
Clément Stenac's avatar
Clément Stenac committed
455

456 457 458 459 460 461
void DiscOpenPanel::updateContext(int c)
{
    OpenPanel::updateContext(c);
    ui.dvdsimple->setChecked( context == OpenPanel::CONTEXT_BATCH );
}

462
DiscOpenPanel::~DiscOpenPanel()
463
{
464 465 466
    free( psz_dvddiscpath );
    free( psz_vcddiscpath );
    free( psz_cddadiscpath );
467
}
468

469
void DiscOpenPanel::clear()
Clément Stenac's avatar
Clément Stenac committed
470
{
471 472
    ui.titleSpin->setValue( 0 );
    ui.chapterSpin->setValue( 0 );
473 474
    ui.subtitlesSpin->setValue( -1 );
    ui.audioSpin->setValue( -1 );
475
    m_discType = None;
Clément Stenac's avatar
Clément Stenac committed
476
}
477

478
#if defined( _WIN32 ) || defined( __OS2__ )
479 480 481 482 483 484 485 486
    #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

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

    updateMRL();
}

Rémi Duraffort's avatar
Rémi Duraffort committed
547 548
#undef setDrive

549
/* Update the current MRL */
550
void DiscOpenPanel::updateMRL()
Clément Stenac's avatar
Clément Stenac committed
551
{
552
    QString discPath;
553
    QStringList fileList;
554

555 556 557 558 559
    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();
560

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

578
    char *mrl = vlc_path2uri( qtu(discPath), scheme );
579 580
    if( unlikely(mrl == NULL) )
        return;
581 582

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

594
    emit methodChanged( "disc-caching" );
595

596 597 598 599
    fileList << (qfu(mrl) + anchor);
    free(mrl);

    QString opts = "";
600

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

624 625
void DiscOpenPanel::browseDevice()
{
626
    QString dir = QFileDialog::getExistingDirectory( this,
627
            qtr( I_DEVICE_TOOLTIP ), p_intf->p_sys->filepath );
628 629 630 631 632
    if( !dir.isEmpty() )
    {
        ui.deviceCombo->addItem( toNativeSepNoSlash( dir ) );
        ui.deviceCombo->setCurrentIndex( ui.deviceCombo->findText( toNativeSepNoSlash( dir ) ) );
    }
633

634 635
    updateMRL();
}
636

637 638 639 640 641
void DiscOpenPanel::eject()
{
    intf_Eject( p_intf, qtu( ui.deviceCombo->currentText() ) );
}

642
void DiscOpenPanel::accept()
643
{}
644

645
/**************************************************************************
646
 * Open Network streams and URL pages                                     *
647 648 649 650 651
 **************************************************************************/
NetOpenPanel::NetOpenPanel( QWidget *_parent, intf_thread_t *_p_intf ) :
                                OpenPanel( _parent, _p_intf )
{
    ui.setupUi( this );
652
    CONNECT( ui.urlComboBox, editTextChanged( const QString& ), this, updateMRL());
Clément Stenac's avatar
Clément Stenac committed
653

654
    /* */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
655
    if( var_InheritBool( p_intf, "qt-recentplay" ) )
656
    {
657
        b_recentList = true;
658
        ui.urlComboBox->addItems( getSettings()->value( "OpenDialog/netMRL" ).toStringList() );
659
        ui.urlComboBox->setMaxCount( 10 );
660 661
    }
    else
662
        b_recentList = false;
663

664 665 666 667
    QFont smallFont = QApplication::font();
    smallFont.setPointSize( smallFont.pointSize() - 1 );
    ui.examples->setFont( smallFont );

668
    /* Use a simple validator for URLs */
669 670
    ui.urlComboBox->setValidator( new UrlValidator( this ) );
    ui.urlComboBox->setFocus();
Clément Stenac's avatar
Clément Stenac committed
671 672
}

673
NetOpenPanel::~NetOpenPanel()
674
{
675
    if( !b_recentList ) return;
676

677 678 679 680
    /* Create the list with the current items */
    QStringList mrlList;
    for( int i = 0; i < ui.urlComboBox->count(); i++ )
        mrlList << ui.urlComboBox->itemText( i );
681

682 683 684 685 686 687 688
    if( mrlList.count() > 0 )
    {
        /* Clean the list... */
        mrlList.removeDuplicates();
        /* ...and save the 8 last entries */
        getSettings()->setValue( "OpenDialog/netMRL", mrlList );
    }
689
}
690

Clément Stenac's avatar
Clément Stenac committed
691
void NetOpenPanel::clear()
692
{
693
    ui.urlComboBox->lineEdit()->clear();
694 695 696 697 698 699 700
}

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

702 703 704 705 706 707
void NetOpenPanel::onFocus()
{
    ui.urlComboBox->setFocus();
    ui.urlComboBox->lineEdit()->selectAll();
}

708 709
void NetOpenPanel::updateMRL()
{
710
    QString url = ui.urlComboBox->lineEdit()->text();
711

712
    emit methodChanged( qfu( "network-caching" ) );
713 714

    QStringList qsl;
715
    if( !url.isEmpty() ) qsl << url;
716
    emit mrlUpdated( qsl, "" );
Clément Stenac's avatar
Clément Stenac committed
717
}
718 719

/**************************************************************************
720
 * Open Capture device ( DVB, PVR, V4L, and similar )                     *
721 722 723 724
 **************************************************************************/
CaptureOpenPanel::CaptureOpenPanel( QWidget *_parent, intf_thread_t *_p_intf ) :
                                OpenPanel( _parent, _p_intf )
{
725
    isInitialized = false;
726 727 728 729
#ifdef _WIN32
    vdevDshowW = NULL;
    adevDshowW = NULL;
#endif
730 731 732 733 734 735 736 737
}

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

    isInitialized = true;

738
    ui.setupUi( this );
739

740
    BUTTONACT( ui.advancedButton, advancedDialog() );
741
    CONNECT( ui.deviceCombo, currentIndexChanged(int), this, enableAdvancedDialog(int) );
742

743
    /* Create two stacked layouts in the main comboBoxes */
744 745 746 747 748 749
    QStackedLayout *stackedDevLayout = new QStackedLayout;
    ui.cardBox->setLayout( stackedDevLayout );

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

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

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

764
#ifdef _WIN32
765 766 767
    /*********************
     * DirectShow Stuffs *
     *********************/
768
    if( module_exists( "dshow" ) ){
769
    addModuleAndLayouts( DSHOW_DEVICE, dshow, "DirectShow", QGridLayout );
770

771 772 773 774 775
    /* dshow Main */
    int line = 0;
    module_config_t *p_config =
        config_FindConfig( VLC_OBJECT(p_intf), "dshow-vdev" );
    vdevDshowW = new StringListConfigControl(
776 777
        VLC_OBJECT(p_intf), p_config, this );
    vdevDshowW->insertIntoExistingGrid( dshowDevLayout, line );
778 779 780 781
    line++;

    p_config = config_FindConfig( VLC_OBJECT(p_intf), "dshow-adev" );
    adevDshowW = new StringListConfigControl(
782 783
        VLC_OBJECT(p_intf), p_config, this );
    adevDshowW->insertIntoExistingGrid( dshowDevLayout, line );
784 785
    line++;

786 787 788 789 790 791 792 793 794 795
    /* 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 */
796 797
    CuMRL( vdevDshowW, changed() );
    CuMRL( adevDshowW, changed() );
798
    CuMRL( dshowVSizeLine, textChanged( const QString& ) );
799
    configList << "dshow-vdev" << "dshow-adev" << "dshow-size";
800
    }
801
#else /* _WIN32 */
802 803 804
    /*******
     * V4L2*
     *******/
805 806 807
    char const * const ppsz_v4lvdevices[] = {
        "video*"
    };
808 809
    if( module_exists( "v4l2" ) ){
    addModuleAndLayouts( V4L2_DEVICE, v4l2, "Video camera", QGridLayout );
810

811
    /* V4L2 main panel */
812 813 814
    QLabel *v4l2VideoDeviceLabel = new QLabel( qtr( "Video device name" ) );
    v4l2DevLayout->addWidget( v4l2VideoDeviceLabel, 0, 0 );

815 816 817
    v4l2VideoDevice = new QComboBox( this );
    v4l2VideoDevice->setEditable( true );
    POPULATE_WITH_DEVS( ppsz_v4lvdevices, v4l2VideoDevice );
818 819 820 821 822
    v4l2DevLayout->addWidget( v4l2VideoDevice, 0, 1 );

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

823 824
    v4l2AudioDevice = new QComboBox( this );
    v4l2AudioDevice->setEditable( true );
825 826 827 828 829 830 831 832 833 834 835
    {
        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 );
    }
836
    v4l2AudioDevice->clearEditText();
837 838 839
    v4l2DevLayout->addWidget( v4l2AudioDevice, 1, 1 );

    /* v4l2 Props panel */
840
    QLabel *v4l2StdLabel = new QLabel( qtr( "Video standard" ) );
841 842 843
    v4l2PropLayout->addWidget( v4l2StdLabel, 0 , 0 );

    v4l2StdBox = new QComboBox;
844
    setfillVLCConfigCombo( "v4l2-standard", p_intf, v4l2StdBox );
845 846
    v4l2PropLayout->addWidget( v4l2StdBox, 0 , 1 );
    v4l2PropLayout->addItem( new QSpacerItem( 20, 20, QSizePolicy::Expanding ),
847
            1, 0, 3, 2 );
848 849

    /* v4l2 CONNECTs */
850 851 852 853
    CuMRL( v4l2VideoDevice->lineEdit(), textChanged( const QString& ) );
    CuMRL( v4l2VideoDevice,  currentIndexChanged ( int ) );
    CuMRL( v4l2AudioDevice->lineEdit(), textChanged( const QString& ) );
    CuMRL( v4l2AudioDevice,  currentIndexChanged ( int ) );
854
    CuMRL( v4l2StdBox,  currentIndexChanged ( int ) );
855
    configList << "v4l2-standard";
856 857
    }

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
858 859 860
    /*******
     * JACK *
     *******/
861
    if( module_exists( "jack" ) ){
862 863
    addModuleAndLayouts( JACK_DEVICE, jack, "JACK Audio Connection Kit",
                         QGridLayout);
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
864 865 866

    /* Jack Main panel */
    /* Channels */
867
    QLabel *jackChannelsLabel = new QLabel( qtr( "Channels:" ) );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
868 869 870 871 872 873 874 875 876 877
    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 */
878
    QLabel *jackPortsLabel = new QLabel( qtr( "Selected ports:" ) );
879
    jackDevLayout->addWidget( jackPortsLabel, 0 , 0 );
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
880 881 882

    jackPortsSelected = new QLineEdit( qtr( ".*") );
    jackPortsSelected->setAlignment( Qt::AlignRight );
883 884 885
    jackDevLayout->addWidget( jackPortsSelected, 0, 1 );

    /* Jack Props panel */
886

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
887 888
    /* Pace */
    jackPace = new QCheckBox(qtr( "Use VLC pace" ));
889
    jackPropLayout->addWidget( jackPace, 1, 1 );
890

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

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
895 896 897 898
    /* Jack CONNECTs */
    CuMRL( jackChannels, valueChanged( int ) );
    CuMRL( jackPace, stateChanged( int ) );
    CuMRL( jackConnect, stateChanged( int ) );
899
    CuMRL( jackPortsSelected, textChanged( const QString& ) );
900
    configList << "jack-input-use-vlc-pace" << "jack-input-auto-connect";
901
    }
902
#endif
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
903

904 905 906
    /*************
     * DVB Stuff *
     *************/
907
    if( module_exists( "dtv" ) ){
908
    addModuleAndLayouts( DTV_DEVICE, dvb, N_("TV - digital"), QGridLayout );
909 910

    /* DVB Main */
911 912
    QLabel *dvbDeviceLabel = new QLabel( qtr( "Tuner card" ) );
    QLabel *dvbTypeLabel = new QLabel( qtr( "Delivery system" ) );
913 914 915

    dvbCard = new QSpinBox;
    dvbCard->setAlignment( Qt::AlignRight );
916
#ifdef __unix__
917
    dvbCard->setPrefix( "/dev/dvb/adapter" );
918
#endif
919
    dvbDevLayout->addWidget( dvbDeviceLabel, 0, 0 );
920
    dvbDevLayout->addWidget( dvbCard, 0, 1, 1, 2 );
921 922

    dvbc = new QRadioButton( "DVB-C" );
923
    dvbs = new QRadioButton( "DVB-S" );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
924
    dvbs2 = new QRadioButton( "DVB-S2" );
925
    dvbt = new QRadioButton( "DVB-T" );
926
    dvbt2 = new QRadioButton( "DVB-T2" );
927 928
    atsc = new QRadioButton( "ATSC" );
    cqam = new QRadioButton( "Clear QAM" );
929
    dvbt->setChecked( true );
930

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
931
    dvbDevLayout->addWidget( dvbTypeLabel, 1, 0, 2, 1 );
932 933
    dvbDevLayout->addWidget( dvbc,  1, 1 );
    dvbDevLayout->addWidget( dvbs,  1, 2 );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
934
    dvbDevLayout->addWidget( dvbs2, 2, 2 );
935 936 937 938
    dvbDevLayout->addWidget( dvbt,  1, 3 );
    dvbDevLayout->addWidget( dvbt2, 2, 3 );
    dvbDevLayout->addWidget( atsc,  1, 4 );
    dvbDevLayout->addWidget( cqam,  2, 4 );
939 940 941 942 943 944 945 946