extended_panels.cpp 55 KB
Newer Older
Clément Stenac's avatar
Clément Stenac committed
1
/*****************************************************************************
Clément Stenac's avatar
Clément Stenac committed
2
 * extended_panels.cpp : Extended controls panels
Clément Stenac's avatar
Clément Stenac committed
3
 ****************************************************************************
Ludovic Fauvet's avatar
Ludovic Fauvet committed
4
 * Copyright (C) 2006-2013 the VideoLAN team
5
 * $Id$
Clément Stenac's avatar
Clément Stenac committed
6 7
 *
 * Authors: Clément Stenac <zorglub@videolan.org>
8
 *          Antoine Cellerier <dionoea .t videolan d@t org>
9
 *          Jean-Baptiste Kempf <jb@videolan.org>
Clément Stenac's avatar
Clément Stenac committed
10 11 12 13
 *
 * 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
14
 * ( at your option ) any later version.
Clément Stenac's avatar
Clément Stenac committed
15 16 17 18 19 20 21 22 23 24
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/
25

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

30 31
#include <math.h>

KO Myung-Hun's avatar
KO Myung-Hun committed
32 33
#include <algorithm>

Clément Stenac's avatar
Clément Stenac committed
34 35 36 37 38
#include <QLabel>
#include <QVariant>
#include <QString>
#include <QFont>
#include <QGridLayout>
39
#include <QComboBox>
40
#include <QTimer>
41
#include <QFileDialog>
42 43
#include <QGraphicsScene>
#include <QPainter>
44
#include <QRegExp>
Clément Stenac's avatar
Clément Stenac committed
45

Clément Stenac's avatar
Clément Stenac committed
46
#include "components/extended_panels.hpp"
47
#include "dialogs/preferences.hpp"
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
48
#include "qt.hpp"
49
#include "input_manager.hpp"
50
#include "util/qt_dirs.hpp"
51
#include "util/customwidgets.hpp"
Clément Stenac's avatar
Clément Stenac committed
52

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
53
#include "../../audio_filter/equalizer_presets.h"
Clément Stenac's avatar
Clément Stenac committed
54
#include <vlc_vout.h>
55
#include <vlc_modules.h>
56
#include <vlc_plugin.h>
Clément Stenac's avatar
Clément Stenac committed
57

58 59 60 61 62 63 64 65 66 67 68
static bool filterIsPresent( const QString &filters, const QString &filter )
{
    QStringList list = filters.split( ':', QString::SplitBehavior::SkipEmptyParts );
    foreach( const QString &filterCmp, list )
    {
        if( filterCmp.compare( filter ) == 0 )
            return true;
    }
    return false;
}

69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
static const char* GetVFilterType( struct intf_thread_t *p_intf, const char *psz_name )
{
    module_t *p_obj = module_find( psz_name );
    if( !p_obj )
    {
        msg_Err( p_intf, "Unable to find filter module \"%s\".", psz_name );
        return NULL;
    }

    if( module_provides( p_obj, "video splitter" ) )
        return "video-splitter";
    else if( module_provides( p_obj, "video filter" ) )
        return "video-filter";
    else if( module_provides( p_obj, "sub source" ) )
        return "sub-source";
    else if( module_provides( p_obj, "sub filter" ) )
        return "sub-filter";
    else
    {
        msg_Err( p_intf, "Unknown video filter type." );
        return NULL;
    }
}
92

93
static const QString ModuleFromWidgetName( QObject *obj )
94
{
95
    return obj->objectName().replace( "Enable","" );
96 97
}

98
static QString OptionFromWidgetName( QObject *obj )
99 100
{
    /* Gruik ? ... nah */
101 102 103 104
    return obj->objectName()
        .remove( QRegExp( "Slider|Combo|Dial|Check|Spin|Text" ) )
        .replace( QRegExp( "([A-Z])" ), "-\\1" )
        .toLower();
105
}
Clément Stenac's avatar
Clément Stenac committed
106

107
static inline void setup_vfilter( intf_thread_t *p_intf, const char* psz_name, QWidget *widget )
Clément Stenac's avatar
Clément Stenac committed
108
{
109 110 111 112 113 114 115 116
    const char *psz_filter_type = GetVFilterType( p_intf, psz_name );
    if( psz_filter_type == NULL )
        return;

    char *psz_filters = var_InheritString( THEPL, psz_filter_type );
    if( psz_filters == NULL )
        return;

117 118
    QCheckBox *checkbox = qobject_cast<QCheckBox*>( widget );
    QGroupBox *groupbox = qobject_cast<QGroupBox*>( widget );
119
    if( filterIsPresent( qfu(psz_filters), qfu(psz_name) ) )
120 121 122 123 124 125 126 127 128
    {
        if( checkbox ) checkbox->setChecked( true ); \
        else if (groupbox) groupbox->setChecked( true ); \
    }
    else
    {
        if( checkbox ) checkbox->setChecked( false );
        else if (groupbox) groupbox->setChecked( false );
    }
129
    free( psz_filters );
130
}
Clément Stenac's avatar
Clément Stenac committed
131

132
#define SETUP_VFILTER( widget ) \
133
    setup_vfilter( p_intf, #widget, ui.widget##Enable ); \
134
    CONNECT( ui.widget##Enable, clicked(), this, updateFilters() );
135

136
#define SETUP_VFILTER_OPTION( widget, signal ) \
137
    initComboBoxItems( ui.widget ); \
138 139
    setWidgetValue( ui.widget ); \
    CONNECT( ui.widget, signal, this, updateFilterOptions() );
Antoine Cellerier's avatar
Antoine Cellerier committed
140

141 142 143 144 145
ExtVideo::ExtVideo( intf_thread_t *_p_intf, QTabWidget *_parent ) :
            QObject( _parent ), p_intf( _p_intf )
{
    ui.setupUi( _parent );

146
    SETUP_VFILTER( adjust )
147 148 149 150 151 152
    SETUP_VFILTER_OPTION( hueSlider, valueChanged( int ) )
    SETUP_VFILTER_OPTION( contrastSlider, valueChanged( int ) )
    SETUP_VFILTER_OPTION( brightnessSlider, valueChanged( int ) )
    SETUP_VFILTER_OPTION( saturationSlider, valueChanged( int ) )
    SETUP_VFILTER_OPTION( gammaSlider, valueChanged( int ) )
    SETUP_VFILTER_OPTION( brightnessThresholdCheck, stateChanged( int ) )
153 154

    SETUP_VFILTER( extract )
155
    SETUP_VFILTER_OPTION( extractComponentText, textChanged( const QString& ) )
156

157 158
    SETUP_VFILTER( posterize )

159
    SETUP_VFILTER( colorthres )
160
    SETUP_VFILTER_OPTION( colorthresColorText, textChanged( const QString& ) )
161 162
    SETUP_VFILTER_OPTION( colorthresSaturationthresSlider, valueChanged( int ) )
    SETUP_VFILTER_OPTION( colorthresSimilaritythresSlider, valueChanged( int ) )
163

164 165 166
    SETUP_VFILTER( sepia )
    SETUP_VFILTER_OPTION( sepiaIntensitySpin, valueChanged( int ) )

167 168 169
    SETUP_VFILTER( invert )

    SETUP_VFILTER( gradient )
170 171 172
    SETUP_VFILTER_OPTION( gradientModeCombo, currentIndexChanged( QString ) )
    SETUP_VFILTER_OPTION( gradientTypeCheck, stateChanged( int ) )
    SETUP_VFILTER_OPTION( gradientCartoonCheck, stateChanged( int ) )
173

174
    SETUP_VFILTER( motionblur )
175
    SETUP_VFILTER_OPTION( blurFactorSlider, valueChanged( int ) )
176 177 178 179 180 181

    SETUP_VFILTER( motiondetect )

    SETUP_VFILTER( psychedelic )

    SETUP_VFILTER( sharpen )
182
    SETUP_VFILTER_OPTION( sharpenSigmaSlider, valueChanged( int ) )
183 184 185 186 187 188

    SETUP_VFILTER( ripple )

    SETUP_VFILTER( wave )

    SETUP_VFILTER( transform )
189
    SETUP_VFILTER_OPTION( transformTypeCombo, currentIndexChanged( QString ) )
190 191

    SETUP_VFILTER( rotate )
192
    SETUP_VFILTER_OPTION( rotateAngleDial, valueChanged( int ) )
193 194
    ui.rotateAngleDial->setWrapping( true );
    ui.rotateAngleDial->setNotchesVisible( true );
195 196

    SETUP_VFILTER( puzzle )
197 198
    SETUP_VFILTER_OPTION( puzzleRowsSpin, valueChanged( int ) )
    SETUP_VFILTER_OPTION( puzzleColsSpin, valueChanged( int ) )
199 200 201 202

    SETUP_VFILTER( magnify )

    SETUP_VFILTER( clone )
203
    SETUP_VFILTER_OPTION( cloneCountSpin, valueChanged( int ) )
204 205

    SETUP_VFILTER( wall )
206 207
    SETUP_VFILTER_OPTION( wallRowsSpin, valueChanged( int ) )
    SETUP_VFILTER_OPTION( wallColsSpin, valueChanged( int ) )
208

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
209

210 211
    SETUP_VFILTER( erase )
    SETUP_VFILTER_OPTION( eraseMaskText, editingFinished() )
212 213
    SETUP_VFILTER_OPTION( eraseYSpin, valueChanged( int ) )
    SETUP_VFILTER_OPTION( eraseXSpin, valueChanged( int ) )
214
    BUTTONACT( ui.eraseBrowseBtn, browseEraseFile() );
215 216

    SETUP_VFILTER( marq )
217
    SETUP_VFILTER_OPTION( marqMarqueeText, textChanged( const QString& ) )
218
    SETUP_VFILTER_OPTION( marqPositionCombo, currentIndexChanged( QString ) )
219 220 221

    SETUP_VFILTER( logo )
    SETUP_VFILTER_OPTION( logoFileText, editingFinished() )
222 223
    SETUP_VFILTER_OPTION( logoYSpin, valueChanged( int ) )
    SETUP_VFILTER_OPTION( logoXSpin, valueChanged( int ) )
224
    SETUP_VFILTER_OPTION( logoOpacitySlider, valueChanged( int ) )
225
    BUTTONACT( ui.logoBrowseBtn, browseLogo() );
226

227 228 229
    SETUP_VFILTER( gradfun )
    SETUP_VFILTER_OPTION( gradfunRadiusSlider, valueChanged( int ) )

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
230 231 232
    SETUP_VFILTER( grain )
    SETUP_VFILTER_OPTION( grainVarianceSlider, valueChanged( int ) )

233 234 235
    SETUP_VFILTER( mirror )

    SETUP_VFILTER( gaussianblur )
236
    SETUP_VFILTER_OPTION( gaussianblurSigmaSlider, valueChanged( int ) )
237 238 239 240

    SETUP_VFILTER( antiflicker )
    SETUP_VFILTER_OPTION( antiflickerSofteningSizeSlider, valueChanged( int ) )

241 242 243 244 245 246
    SETUP_VFILTER( hqdn3d )
    SETUP_VFILTER_OPTION( hqdn3dLumaSpatSlider, valueChanged( int ) )
    SETUP_VFILTER_OPTION( hqdn3dLumaTempSlider, valueChanged( int ) )
    SETUP_VFILTER_OPTION( hqdn3dChromaSpatSlider, valueChanged( int ) )
    SETUP_VFILTER_OPTION( hqdn3dChromaTempSlider, valueChanged( int ) )

247

248 249
    SETUP_VFILTER( anaglyph )

250 251
#undef SETUP_VFILTER
#undef SETUP_VFILTER_OPTION
252 253 254 255 256

    CONNECT( ui.cropTopPx, valueChanged( int ), this, cropChange() );
    CONNECT( ui.cropBotPx, valueChanged( int ), this, cropChange() );
    CONNECT( ui.cropLeftPx, valueChanged( int ), this, cropChange() );
    CONNECT( ui.cropRightPx, valueChanged( int ), this, cropChange() );
Dylan Yudaken's avatar
Dylan Yudaken committed
257 258
    CONNECT( ui.leftRightCropSync, toggled ( bool ), this, cropChange() );
    CONNECT( ui.topBotCropSync, toggled ( bool ), this, cropChange() );
259 260 261 262
    CONNECT( ui.topBotCropSync, toggled( bool ),
             ui.cropBotPx, setDisabled( bool ) );
    CONNECT( ui.leftRightCropSync, toggled( bool ),
             ui.cropRightPx, setDisabled( bool ) );
Clément Stenac's avatar
Clément Stenac committed
263 264
}

265 266
void ExtVideo::cropChange()
{
Dylan Yudaken's avatar
Dylan Yudaken committed
267 268 269 270 271
    if( ui.topBotCropSync->isChecked() )
        ui.cropBotPx->setValue( ui.cropTopPx->value() );
    if( ui.leftRightCropSync->isChecked() )
        ui.cropRightPx->setValue( ui.cropLeftPx->value() );

272 273
    QVector<vout_thread_t*> p_vouts = THEMIM->getVouts();
    foreach( vout_thread_t *p_vout, p_vouts )
274
    {
275 276 277 278
        var_SetInteger( p_vout, "crop-top", ui.cropTopPx->value() );
        var_SetInteger( p_vout, "crop-bottom", ui.cropBotPx->value() );
        var_SetInteger( p_vout, "crop-left", ui.cropLeftPx->value() );
        var_SetInteger( p_vout, "crop-right", ui.cropRightPx->value() );
279
        vlc_object_release( p_vout );
280 281 282
    }
}

283 284 285 286 287 288 289 290
void ExtVideo::clean()
{
    ui.cropTopPx->setValue( 0 );
    ui.cropBotPx->setValue( 0 );
    ui.cropLeftPx->setValue( 0 );
    ui.cropRightPx->setValue( 0 );
}

291
static QString ChangeFiltersString( struct intf_thread_t *p_intf, const char *psz_filter_type, const char *psz_name, bool b_add )
Clément Stenac's avatar
Clément Stenac committed
292
{
293
    char* psz_chain = var_GetString( THEPL, psz_filter_type );
294

295 296
    QString const chain = QString( psz_chain ? psz_chain : "" );
    QStringList list = chain.split( ':', QString::SplitBehavior::SkipEmptyParts );
Clément Stenac's avatar
Clément Stenac committed
297

298 299 300 301
    if( b_add && std::find(list.begin(), list.end(), psz_name) == list.end() )
        list << psz_name;
    else if (!b_add)
        list.removeAll( psz_name );
Clément Stenac's avatar
Clément Stenac committed
302

303
    free( psz_chain );
Clément Stenac's avatar
Clément Stenac committed
304

KO Myung-Hun's avatar
KO Myung-Hun committed
305
    return list.join( ":" );
306 307
}

308 309
static void UpdateVFiltersString( struct intf_thread_t *p_intf,
                                  const char *psz_filter_type, const char *value )
310
{
311
    var_SetString( THEPL, psz_filter_type, value );
312 313 314

    /* Try to set non splitter filters on the fly */
    if( strcmp( psz_filter_type, "video-splitter" ) )
315
    {
316 317
        QVector<vout_thread_t*> p_vouts = THEMIM->getVouts();
        foreach( vout_thread_t *p_vout, p_vouts )
318
        {
319
            var_SetString( p_vout, psz_filter_type, value );
320 321
            vlc_object_release( p_vout );
        }
Clément Stenac's avatar
Clément Stenac committed
322 323 324
    }
}

325 326 327 328 329 330 331 332
void ExtVideo::changeVFiltersString( const char *psz_name, bool b_add )
{
    const char *psz_filter_type = GetVFilterType( p_intf, psz_name );
    if( psz_filter_type == NULL )
        return;

    QString result = ChangeFiltersString( p_intf, psz_filter_type, psz_name, b_add );

333
    emit configChanged( qfu( psz_filter_type ), result );
334 335 336 337

    UpdateVFiltersString( p_intf, psz_filter_type, qtu( result ) );
}

Clément Stenac's avatar
Clément Stenac committed
338 339
void ExtVideo::updateFilters()
{
340
    QString module = ModuleFromWidgetName( sender() );
Clément Stenac's avatar
Clément Stenac committed
341

342 343
    QCheckBox *checkbox = qobject_cast<QCheckBox*>( sender() );
    QGroupBox *groupbox = qobject_cast<QGroupBox*>( sender() );
344

345
    changeVFiltersString( qtu( module ),
346 347
                          checkbox ? checkbox->isChecked()
                                   : groupbox->isChecked() );
Clément Stenac's avatar
Clément Stenac committed
348 349
}

350 351 352 353 354 355
#define UPDATE_AND_APPLY_TEXT( widget, file ) \
    CONNECT( ui.widget, textChanged( const QString& ), \
             this, updateFilterOptions() ); \
    ui.widget->setText( toNativeSeparators( file ) ); \
    ui.widget->disconnect( SIGNAL( textChanged( const QString& ) ) );

356 357 358 359
void ExtVideo::browseLogo()
{
    QString file = QFileDialog::getOpenFileName( NULL, qtr( "Logo filenames" ),
                   p_intf->p_sys->filepath, "Images (*.png *.jpg);;All (*)" );
360 361

    UPDATE_AND_APPLY_TEXT( logoFileText, file );
362 363 364 365 366 367
}

void ExtVideo::browseEraseFile()
{
    QString file = QFileDialog::getOpenFileName( NULL, qtr( "Image mask" ),
                   p_intf->p_sys->filepath, "Images (*.png *.jpg);;All (*)" );
368 369

    UPDATE_AND_APPLY_TEXT( eraseMaskText, file );
370 371
}

372 373
#undef UPDATE_AND_APPLY_TEXT

374 375
void ExtVideo::initComboBoxItems( QObject *widget )
{
376
    QComboBox *combobox = qobject_cast<QComboBox*>( widget );
377
    if( !combobox ) return;
378

379
    QString option = OptionFromWidgetName( widget );
380
    module_config_t *p_item = config_FindConfig( qtu( option ) );
381
    if( p_item == NULL )
382
    {
383 384 385 386 387 388 389 390 391 392 393 394
        msg_Err( p_intf, "Couldn't find option \"%s\".", qtu( option ) );
        return;
    }

    if( p_item->i_type == CONFIG_ITEM_INTEGER
     || p_item->i_type == CONFIG_ITEM_BOOL )
    {
        int64_t *values;
        char **texts;
        ssize_t count = config_GetIntChoices( VLC_OBJECT( p_intf ),
                                              qtu( option ), &values, &texts );
        for( ssize_t i = 0; i < count; i++ )
395
        {
396
            combobox->addItem( qtr( texts[i] ), qlonglong(values[i]) );
397
            free( texts[i] );
398
        }
399 400
        free( texts );
        free( values );
401
    }
402
    else if( p_item->i_type == CONFIG_ITEM_STRING )
403
    {
404 405 406 407 408 409
        char **values;
        char **texts;
        ssize_t count = config_GetPszChoices( VLC_OBJECT( p_intf ),
                                              qtu( option ), &values, &texts );
        for( ssize_t i = 0; i < count; i++ )
        {
410
            combobox->addItem( qtr( texts[i] ), qfu(values[i]) );
411 412 413 414 415
            free( texts[i] );
            free( values[i] );
        }
        free( texts );
        free( values );
416 417 418
    }
}

419 420 421 422 423 424 425 426
void ExtVideo::setWidgetValue( QObject *widget )
{
    QString module = ModuleFromWidgetName( widget->parent() );
    //std::cout << "Module name: " << module.toStdString() << std::endl;
    QString option = OptionFromWidgetName( widget );
    //std::cout << "Option name: " << option.toStdString() << std::endl;

    vlc_value_t val;
427
    int i_type = config_GetType( qtu( option ) ) & VLC_VAR_CLASS;
428
    switch( i_type )
429
    {
430 431 432 433 434 435 436 437 438 439
        case VLC_VAR_INTEGER:
        case VLC_VAR_BOOL:
        case VLC_VAR_FLOAT:
        case VLC_VAR_STRING:
            break;
        default:
            msg_Err( p_intf,
                     "Module %s's %s variable is of an unsupported type ( %d )",
                     qtu( module ), qtu( option ), i_type );
            return;
440
    }
441 442 443 444
    if( var_Create( THEPL, qtu( option ), i_type | VLC_VAR_DOINHERIT ) )
        return;
    if( var_GetChecked( THEPL, qtu( option ), i_type, &val ) )
        return;
445 446 447

    /* Try to cast to all the widgets we're likely to encounter. Only
     * one of the casts is expected to work. */
448 449 450 451
    QSlider        *slider        = qobject_cast<QSlider*>       ( widget );
    QCheckBox      *checkbox      = qobject_cast<QCheckBox*>     ( widget );
    QSpinBox       *spinbox       = qobject_cast<QSpinBox*>      ( widget );
    QDoubleSpinBox *doublespinbox = qobject_cast<QDoubleSpinBox*>( widget );
452
    VLCQDial       *dial          = qobject_cast<VLCQDial*>      ( widget );
453 454
    QLineEdit      *lineedit      = qobject_cast<QLineEdit*>     ( widget );
    QComboBox      *combobox      = qobject_cast<QComboBox*>     ( widget );
455 456 457 458 459 460 461

    if( i_type == VLC_VAR_INTEGER || i_type == VLC_VAR_BOOL )
    {
        if( slider )        slider->setValue( val.i_int );
        else if( checkbox ) checkbox->setCheckState( val.i_int? Qt::Checked
                                                              : Qt::Unchecked );
        else if( spinbox )  spinbox->setValue( val.i_int );
462
        else if( dial )     dial->setValue( (360 - val.i_int) % 360 );
463 464 465
        else if( lineedit )
        {
            char str[30];
Tristan Matthews's avatar
Tristan Matthews committed
466
            snprintf( str, sizeof(str), "%06" PRIX64, val.i_int );
467 468
            lineedit->setText( str );
        }
469
        else if( combobox ) combobox->setCurrentIndex(
470
                            combobox->findData( qlonglong(val.i_int) ) );
471
        else msg_Warn( p_intf, "Could not find the correct Integer widget" );
472 473 474
    }
    else if( i_type == VLC_VAR_FLOAT )
    {
475 476
        if( slider ) slider->setValue( ( int )( val.f_float*( double )slider->tickInterval() ) ); /* hack alert! */
        else if( doublespinbox ) doublespinbox->setValue( val.f_float );
477
        else if( dial ) dial->setValue( (360 - lroundf(val.f_float)) % 360 );
478
        else msg_Warn( p_intf, "Could not find the correct Float widget" );
479 480 481
    }
    else if( i_type == VLC_VAR_STRING )
    {
482
        if( lineedit ) lineedit->setText( qfu( val.psz_string ) );
483
        else if( combobox ) combobox->setCurrentIndex(
484
                            combobox->findData( qfu( val.psz_string ) ) );
485
        else msg_Warn( p_intf, "Could not find the correct String widget" );
486 487 488 489
        free( val.psz_string );
    }
}

490
void ExtVideo::setFilterOption( const char *psz_module, const char *psz_option,
491
        int i_int, double f_float, const char *psz_string )
492
{
493 494 495
    QVector<vout_thread_t*> p_vouts = THEMIM->getVouts();
    int i_type = 0;
    bool b_is_command = false;
496

497
    if( !p_vouts.isEmpty() )
498
    {
499
        i_type = var_Type( p_vouts.at(0), psz_option );
500
        b_is_command = ( i_type & VLC_VAR_ISCOMMAND );
501
    }
502
    if( i_type == 0 )
503
        i_type = config_GetType( psz_option );
504

505
    vlc_value_t val;
506
    i_type &= VLC_VAR_CLASS;
507 508
    if( i_type == VLC_VAR_INTEGER || i_type == VLC_VAR_BOOL )
    {
509
        emit configChanged( qfu( psz_option ), QVariant( i_int ) );
510
        if( i_type == VLC_VAR_INTEGER )
511
        {
512
            val.i_int = i_int;
513 514
            var_SetInteger( THEPL, psz_option, i_int );
        }
515
        else
516 517
        {
            var_SetBool( THEPL, psz_option, i_int );
518
            val.b_bool = i_int;
519
        }
520 521 522
    }
    else if( i_type == VLC_VAR_FLOAT )
    {
523
        emit configChanged( qfu( psz_option ), QVariant( f_float ) );
524
        var_SetFloat( THEPL, psz_option, f_float );
525
        val.f_float = f_float;
526 527 528
    }
    else if( i_type == VLC_VAR_STRING )
    {
529 530
        if( psz_string == NULL )
            psz_string = "";
531
        emit configChanged( qfu( psz_option ), QVariant( psz_string ) );
532
        var_SetString( THEPL, psz_option, psz_string );
533
        val.psz_string = (char *) psz_string;
534 535
    }
    else
536
    {
537
        msg_Err( p_intf,
538
                 "Module %s's %s variable is of an unsupported type ( %d )",
539 540
                 psz_module,
                 psz_option,
541
                 i_type );
542 543
        b_is_command = false;
    }
544

545
    if( b_is_command )
546 547 548 549 550 551 552 553 554 555 556
    {
        foreach( vout_thread_t *p_vout, p_vouts )
        {
            var_SetChecked( p_vout, psz_option, i_type, val );
#ifndef NDEBUG
            int i_cur_type = var_Type( p_vout, psz_option );
            assert( ( i_cur_type & VLC_VAR_CLASS ) == i_type );
            assert( !!( i_cur_type & VLC_VAR_ISCOMMAND ) == b_is_command );
#endif
        }
    }
557

558 559
    foreach( vout_thread_t *p_vout, p_vouts )
        vlc_object_release( p_vout );
560 561
}

562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590
void ExtVideo::updateFilterOptions()
{
    QString module = ModuleFromWidgetName( sender()->parent() );
    //msg_Dbg( p_intf, "Module name: %s", qtu( module ) );
    QString option = OptionFromWidgetName( sender() );
    //msg_Dbg( p_intf, "Option name: %s", qtu( option ) );

    /* Try to cast to all the widgets we're likely to encounter. Only
     * one of the casts is expected to work. */
    QSlider        *slider        = qobject_cast<QSlider*>       ( sender() );
    QCheckBox      *checkbox      = qobject_cast<QCheckBox*>     ( sender() );
    QSpinBox       *spinbox       = qobject_cast<QSpinBox*>      ( sender() );
    QDoubleSpinBox *doublespinbox = qobject_cast<QDoubleSpinBox*>( sender() );
    VLCQDial       *dial          = qobject_cast<VLCQDial*>      ( sender() );
    QLineEdit      *lineedit      = qobject_cast<QLineEdit*>     ( sender() );
    QComboBox      *combobox      = qobject_cast<QComboBox*>     ( sender() );

    int i_int = -1;
    double f_float = -1.;
    QString val;

    if( slider ) {
        i_int = slider->value();
        f_float = ( double )slider->value() / ( double )slider->tickInterval(); /* hack alert! */
    }
    else if( checkbox ) i_int = checkbox->checkState() == Qt::Checked;
    else if( spinbox ) i_int = spinbox->value();
    else if( doublespinbox ) f_float = doublespinbox->value();
    else if( dial ) {
591 592
        i_int = (360 - dial->value()) % 360;
        f_float = i_int;
593 594 595 596 597 598 599 600 601 602 603
    }
    else if( lineedit ) {
        i_int = lineedit->text().toInt( NULL,16 );
        f_float = lineedit->text().toDouble();
        val = lineedit->text();
    }
    else if( combobox ) {
        i_int = combobox->itemData( combobox->currentIndex() ).toInt();
        val = combobox->itemData( combobox->currentIndex() ).toString();
    }

604
    setFilterOption( qtu( module ), qtu( option ), i_int, f_float, qtu( val ) );
605 606
}

607 608 609 610 611
/**********************************************************************
 * v4l2 controls
 **********************************************************************/

ExtV4l2::ExtV4l2( intf_thread_t *_p_intf, QWidget *_parent )
612
    : QWidget( _parent ), p_intf( _p_intf ), box( NULL )
613
{
614 615 616 617 618 619
    QVBoxLayout *layout = new QVBoxLayout( this );
    help = new QLabel( qtr("No v4l2 instance found.\n"
      "Please check that the device has been opened with VLC and is playing.\n\n"
      "Controls will automatically appear here.")
      , this );
    help->setAlignment( Qt::AlignHCenter | Qt::AlignVCenter );
620
    help->setWordWrap( true );
621 622
    layout->addWidget( help );
    setLayout( layout );
623 624
}

625 626 627 628 629 630
void ExtV4l2::showEvent( QShowEvent *event )
{
    QWidget::showEvent( event );
    Refresh();
}

631 632
void ExtV4l2::Refresh( void )
{
633
    vlc_object_t *p_obj = (vlc_object_t*)vlc_object_find_name( THEPL, "v4l2" );
634
    help->hide();
635 636
    if( box )
    {
637
        layout()->removeWidget( box );
638 639 640
        delete box;
        box = NULL;
    }
641 642
    if( p_obj )
    {
643
        vlc_value_t val, text;
644 645 646 647
        int i_ret = var_Change( p_obj, "controls", VLC_VAR_GETCHOICES,
                                &val, &text );
        if( i_ret < 0 )
        {
648
            msg_Err( p_intf, "Oops, v4l2 object doesn't have a 'controls' variable." );
649
            help->show();
650 651 652
            vlc_object_release( p_obj );
            return;
        }
653 654

        box = new QGroupBox( this );
655
        layout()->addWidget( box );
656 657 658
        QVBoxLayout *layout = new QVBoxLayout( box );
        box->setLayout( layout );

659 660
        for( int i = 0; i < val.p_list->i_count; i++ )
        {
661
            vlc_value_t vartext;
662
            const char *psz_var = text.p_list->p_values[i].psz_string;
663 664 665 666 667 668

            if( var_Change( p_obj, psz_var, VLC_VAR_GETTEXT, &vartext, NULL ) )
                continue;

            QString name = qtr( vartext.psz_string );
            free( vartext.psz_string );
Tristan Matthews's avatar
Tristan Matthews committed
669
            msg_Dbg( p_intf, "v4l2 control \"%" PRIx64 "\": %s (%s)",
670
                     val.p_list->p_values[i].i_int, psz_var, qtu( name ) );
671 672 673 674 675 676

            int i_type = var_Type( p_obj, psz_var );
            switch( i_type & VLC_VAR_TYPE )
            {
                case VLC_VAR_INTEGER:
                {
677
                    QLabel *label = new QLabel( name, box );
678
                    QHBoxLayout *hlayout = new QHBoxLayout();
679 680 681 682
                    hlayout->addWidget( label );
                    int i_val = var_GetInteger( p_obj, psz_var );
                    if( i_type & VLC_VAR_HASCHOICE )
                    {
683
                        QComboBox *combobox = new QComboBox( box );
684
                        combobox->setObjectName( qfu( psz_var ) );
685 686 687 688 689 690 691 692

                        vlc_value_t val2, text2;
                        var_Change( p_obj, psz_var, VLC_VAR_GETCHOICES,
                                    &val2, &text2 );
                        for( int j = 0; j < val2.p_list->i_count; j++ )
                        {
                            combobox->addItem(
                                       text2.p_list->p_values[j].psz_string,
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
693
                                       qlonglong( val2.p_list->p_values[j].i_int) );
694 695 696
                            if( i_val == val2.p_list->p_values[j].i_int )
                                combobox->setCurrentIndex( j );
                        }
697
                        var_FreeList( &val2, &text2 );
698 699 700 701 702 703 704

                        CONNECT( combobox, currentIndexChanged( int ), this,
                                 ValueChange( int ) );
                        hlayout->addWidget( combobox );
                    }
                    else
                    {
705
                        QSlider *slider = new QSlider( box );
706
                        slider->setObjectName( qfu( psz_var ) );
707 708 709 710
                        slider->setOrientation( Qt::Horizontal );
                        vlc_value_t val2;
                        var_Change( p_obj, psz_var, VLC_VAR_GETMIN,
                                    &val2, NULL );
711 712
                        if( val2.i_int < INT_MIN )
                            val2.i_int = INT_MIN; /* FIXME */
713 714 715
                        slider->setMinimum( val2.i_int );
                        var_Change( p_obj, psz_var, VLC_VAR_GETMAX,
                                    &val2, NULL );
716 717
                        if( val2.i_int > INT_MAX )
                            val2.i_int = INT_MAX; /* FIXME */
718
                        slider->setMaximum( val2.i_int );
719 720 721
                        if( !var_Change( p_obj, psz_var, VLC_VAR_GETSTEP,
                                         &val2, NULL ) )
                            slider->setSingleStep( val2.i_int );
722 723 724 725 726 727 728 729 730 731
                        slider->setValue( i_val );
                        CONNECT( slider, valueChanged( int ), this,
                                 ValueChange( int ) );
                        hlayout->addWidget( slider );
                    }
                    layout->addLayout( hlayout );
                    break;
                }
                case VLC_VAR_BOOL:
                {
732
                    QCheckBox *button = new QCheckBox( name, box );
733
                    button->setObjectName( qfu( psz_var ) );
734 735 736 737 738 739 740 741 742
                    button->setChecked( var_GetBool( p_obj, psz_var ) );

                    CONNECT( button, clicked( bool ), this,
                             ValueChange( bool ) );
                    layout->addWidget( button );
                    break;
                }
                case VLC_VAR_VOID:
                {
743 744
                    if( i_type & VLC_VAR_ISCOMMAND )
                    {
745
                        QPushButton *button = new QPushButton( name, box );
746
                        button->setObjectName( qfu( psz_var ) );
747

748 749 750 751 752 753
                        CONNECT( button, clicked( bool ), this,
                                 ValueChange( bool ) );
                        layout->addWidget( button );
                    }
                    else
                    {
754
                        QLabel *label = new QLabel( name, box );
755 756
                        layout->addWidget( label );
                    }
757 758 759 760 761 762 763
                    break;
                }
                default:
                    msg_Warn( p_intf, "Unhandled var type for %s", psz_var );
                    break;
            }
        }
764
        var_FreeList( &val, &text );
765 766 767 768 769
        vlc_object_release( p_obj );
    }
    else
    {
        msg_Dbg( p_intf, "Couldn't find v4l2 instance" );
770 771 772
        help->show();
        if ( isVisible() )
            QTimer::singleShot( 2000, this, SLOT(Refresh()) );
773 774 775 776 777 778 779 780 781 782 783
    }
}

void ExtV4l2::ValueChange( bool value )
{
    ValueChange( (int)value );
}

void ExtV4l2::ValueChange( int value )
{
    QObject *s = sender();
784
    vlc_object_t *p_obj = (vlc_object_t*)vlc_object_find_name( THEPL, "v4l2" );
785 786
    if( p_obj )
    {
787 788
        QString var = s->objectName();
        int i_type = var_Type( p_obj, qtu( var ) );
789 790 791 792 793 794 795 796
        switch( i_type & VLC_VAR_TYPE )
        {
            case VLC_VAR_INTEGER:
                if( i_type & VLC_VAR_HASCHOICE )
                {
                    QComboBox *combobox = qobject_cast<QComboBox*>( s );
                    value = combobox->itemData( value ).toInt();
                }
797
                var_SetInteger( p_obj, qtu( var ), value );
798 799
                break;
            case VLC_VAR_BOOL:
800
                var_SetBool( p_obj, qtu( var ), value );
801 802
                break;
            case VLC_VAR_VOID:
803
                var_TriggerCallback( p_obj, qtu( var ) );
804 805 806 807 808 809 810 811 812 813 814
                break;
        }
        vlc_object_release( p_obj );
    }
    else
    {
        msg_Warn( p_intf, "Oops, v4l2 object isn't available anymore" );
        Refresh();
    }
}

815 816 817 818
/**********************************************************************
 * Sliders
 **********************************************************************/

819 820
FilterSliderData::FilterSliderData( QObject *parent, QSlider *_slider ) :
    QObject( parent ), slider( _slider )
821 822
{
}
823

824 825 826 827 828
FilterSliderData::FilterSliderData( QObject *parent,
                                    intf_thread_t *_p_intf,
                                    QSlider *_slider,
                                    QLabel *_label, QLabel *_nameLabel,
                                    const slider_data_t *_p_data ):
829 830
    QObject( parent ), slider( _slider ), valueLabel( _label ),
    nameLabel( _nameLabel ), p_data( _p_data ), p_intf( _p_intf )
831 832 833
{
    slider->setMinimum( p_data->f_min / p_data->f_resolution );
    slider->setMaximum( p_data->f_max / p_data->f_resolution );
834
    nameLabel->setText( p_data->descs );
835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850
    CONNECT( slider, valueChanged( int ), this, updateText( int ) );
    setValue( initialValue() );
    /* In case current == min|max text would not be first updated */
    if ( slider->value() == slider->maximum() ||
         slider->value() == slider->minimum() )
        updateText( slider->value() );
    CONNECT( slider, valueChanged( int ), this, onValueChanged( int ) );
}

void FilterSliderData::setValue( float f )
{
    slider->setValue( f / p_data->f_resolution );
}

void FilterSliderData::updateText( int i )
{
851
    float f = ((float) i) * p_data->f_resolution * p_data->f_visual_multiplier;
852
    valueLabel->setText( QString( p_data->units )
853
                    .prepend( "%1 " )
854 855 856 857 858 859 860 861 862
                    .arg( QString::number( f, 'f', 1 ) ) );
}

float FilterSliderData::initialValue()
{
    vlc_object_t *p_aout = (vlc_object_t *) THEMIM->getAout();
    float f = p_data->f_value;
    if( p_aout )
    {
863
        if ( var_Type( p_aout, qtu(p_data->name) ) == 0 )
864 865 866 867 868 869
        {
            vlc_object_release( p_aout );
            /* Not found, will try in config */
        }
        else
        {
870
            f = var_GetFloat( p_aout, qtu(p_data->name) );
871 872 873 874 875
            vlc_object_release( p_aout );
            return f;
        }
    }

876
    if ( ! config_FindConfig( qtu(p_data->name) ) )
877 878
        return f;

879
    f = config_GetFloat( p_intf, qtu(p_data->name) );
880 881 882
    return f;
}

883
void FilterSliderData::onValueChanged( int i )
884 885 886 887 888
{
    float f = ((float) i) * p_data->f_resolution;
    vlc_object_t *p_aout = (vlc_object_t *) THEMIM->getAout();
    if ( p_aout )
    {
889
        var_SetFloat( p_aout, qtu(p_data->name), f );
890 891 892 893 894
        vlc_object_release( p_aout );
    }
    writeToConfig();
}

895
void FilterSliderData::writeToConfig()
896 897
{
    float f = ((float) slider->value()) * p_data->f_resolution;
898
    emit configChanged( p_data->name, QVariant( f ) );
899 900
}

901 902
AudioFilterControlWidget::AudioFilterControlWidget
( intf_thread_t *_p_intf, QWidget *parent, const char *_name ) :
903
    QWidget( parent ), p_intf( _p_intf ), name( _name ), i_smallfont(0)
904 905
{}

906 907 908 909 910 911
void AudioFilterControlWidget::connectConfigChanged( FilterSliderData *slider )
{
    connect( slider, SIGNAL( configChanged(QString, QVariant) ),
             this, SIGNAL( configChanged(QString, QVariant) ) );
}

912 913 914
void AudioFilterControlWidget::build()
{
    QFont smallFont = QApplication::font();
915
    smallFont.setPointSize( smallFont.pointSize() + i_smallfont );
916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940

    QVBoxLayout *layout = new QVBoxLayout( this );
    slidersBox = new QGroupBox( qtr( "Enable" ) );
    slidersBox->setCheckable( true );
    layout->addWidget( slidersBox );

    QGridLayout *ctrlLayout = new QGridLayout( slidersBox );

    int i = 0;
    foreach( const FilterSliderData::slider_data_t &data, controls )
    {
        QSlider *slider = new QSlider( Qt::Vertical );
        QLabel *valueLabel = new QLabel();
        valueLabel->setFont( smallFont );
        valueLabel->setAlignment( Qt::AlignHCenter );
        QLabel *nameLabel = new QLabel();
        nameLabel->setFont( smallFont );
        nameLabel->setAlignment( Qt::AlignHCenter );
        FilterSliderData *filter =
            new FilterSliderData( this, p_intf,
                                  slider, valueLabel, nameLabel, & data );
        ctrlLayout->addWidget( slider, 0, i, Qt::AlignHCenter );
        ctrlLayout->addWidget( valueLabel, 1, i, Qt::AlignHCenter );
        ctrlLayout->addWidget( nameLabel, 2, i, Qt::AlignHCenter );
        i++;
941
        sliderDatas << filter;
942
        connectConfigChanged( filter );
943 944
    }

945
    char *psz_af = var_InheritString( THEPL, "audio-filter" );
946