extended_panels.cpp 55.7 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>

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

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

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

56
static QString ChangeFiltersString( struct intf_thread_t *p_intf, const char *psz_filter_type, const char *psz_name, bool b_add );
57
static void ChangeAFiltersString( struct intf_thread_t *p_intf, const char *psz_name, bool b_add );
58 59
static void ChangeVFiltersString( struct intf_thread_t *p_intf, const char *psz_name, bool b_add );

60
static const QString ModuleFromWidgetName( QObject *obj )
61
{
62
    return obj->objectName().replace( "Enable","" );
63 64
}

65
static QString OptionFromWidgetName( QObject *obj )
66 67
{
    /* Gruik ? ... nah */
68 69 70 71
    return obj->objectName()
        .remove( QRegExp( "Slider|Combo|Dial|Check|Spin|Text" ) )
        .replace( QRegExp( "([A-Z])" ), "-\\1" )
        .toLower();
72
}
Clément Stenac's avatar
Clément Stenac committed
73

74
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
75
{
76
    vlc_object_t *p_obj = ( vlc_object_t * )
77
        vlc_object_find_name( p_intf->obj.libvlc, psz_name );
78 79 80 81 82 83 84 85 86 87 88 89 90 91
    QCheckBox *checkbox = qobject_cast<QCheckBox*>( widget );
    QGroupBox *groupbox = qobject_cast<QGroupBox*>( widget );
    if( p_obj )
    {
        vlc_object_release( p_obj ); \
        if( checkbox ) checkbox->setChecked( true ); \
        else if (groupbox) groupbox->setChecked( true ); \
    }
    else
    {
        if( checkbox ) checkbox->setChecked( false );
        else if (groupbox) groupbox->setChecked( false );
    }
}
Clément Stenac's avatar
Clément Stenac committed
92

93
#define SETUP_VFILTER( widget ) \
94
    setup_vfilter( p_intf, #widget, ui.widget##Enable ); \
95
    CONNECT( ui.widget##Enable, clicked(), this, updateFilters() );
96

97
#define SETUP_VFILTER_OPTION( widget, signal ) \
98
    initComboBoxItems( ui.widget ); \
99 100
    setWidgetValue( ui.widget ); \
    CONNECT( ui.widget, signal, this, updateFilterOptions() );
Antoine Cellerier's avatar
Antoine Cellerier committed
101

102 103 104 105 106
ExtVideo::ExtVideo( intf_thread_t *_p_intf, QTabWidget *_parent ) :
            QObject( _parent ), p_intf( _p_intf )
{
    ui.setupUi( _parent );

107
    SETUP_VFILTER( adjust )
108 109 110 111 112 113
    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 ) )
114 115

    SETUP_VFILTER( extract )
116
    SETUP_VFILTER_OPTION( extractComponentText, textChanged( const QString& ) )
117

118 119
    SETUP_VFILTER( posterize )

120
    SETUP_VFILTER( colorthres )
121
    SETUP_VFILTER_OPTION( colorthresColorText, textChanged( const QString& ) )
122 123
    SETUP_VFILTER_OPTION( colorthresSaturationthresSlider, valueChanged( int ) )
    SETUP_VFILTER_OPTION( colorthresSimilaritythresSlider, valueChanged( int ) )
124

125 126 127
    SETUP_VFILTER( sepia )
    SETUP_VFILTER_OPTION( sepiaIntensitySpin, valueChanged( int ) )

128 129 130
    SETUP_VFILTER( invert )

    SETUP_VFILTER( gradient )
131 132 133
    SETUP_VFILTER_OPTION( gradientModeCombo, currentIndexChanged( QString ) )
    SETUP_VFILTER_OPTION( gradientTypeCheck, stateChanged( int ) )
    SETUP_VFILTER_OPTION( gradientCartoonCheck, stateChanged( int ) )
134

135
    SETUP_VFILTER( motionblur )
136
    SETUP_VFILTER_OPTION( blurFactorSlider, valueChanged( int ) )
137 138 139 140 141 142

    SETUP_VFILTER( motiondetect )

    SETUP_VFILTER( psychedelic )

    SETUP_VFILTER( sharpen )
143
    SETUP_VFILTER_OPTION( sharpenSigmaSlider, valueChanged( int ) )
144 145 146 147 148 149

    SETUP_VFILTER( ripple )

    SETUP_VFILTER( wave )

    SETUP_VFILTER( transform )
150
    SETUP_VFILTER_OPTION( transformTypeCombo, currentIndexChanged( QString ) )
151 152

    SETUP_VFILTER( rotate )
153
    SETUP_VFILTER_OPTION( rotateAngleDial, valueChanged( int ) )
154 155
    ui.rotateAngleDial->setWrapping( true );
    ui.rotateAngleDial->setNotchesVisible( true );
156 157

    SETUP_VFILTER( puzzle )
158 159
    SETUP_VFILTER_OPTION( puzzleRowsSpin, valueChanged( int ) )
    SETUP_VFILTER_OPTION( puzzleColsSpin, valueChanged( int ) )
160 161 162 163

    SETUP_VFILTER( magnify )

    SETUP_VFILTER( clone )
164
    SETUP_VFILTER_OPTION( cloneCountSpin, valueChanged( int ) )
165 166

    SETUP_VFILTER( wall )
167 168
    SETUP_VFILTER_OPTION( wallRowsSpin, valueChanged( int ) )
    SETUP_VFILTER_OPTION( wallColsSpin, valueChanged( int ) )
169

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

171 172
    SETUP_VFILTER( erase )
    SETUP_VFILTER_OPTION( eraseMaskText, editingFinished() )
173 174
    SETUP_VFILTER_OPTION( eraseYSpin, valueChanged( int ) )
    SETUP_VFILTER_OPTION( eraseXSpin, valueChanged( int ) )
175
    BUTTONACT( ui.eraseBrowseBtn, browseEraseFile() );
176 177

    SETUP_VFILTER( marq )
178
    SETUP_VFILTER_OPTION( marqMarqueeText, textChanged( const QString& ) )
179
    SETUP_VFILTER_OPTION( marqPositionCombo, currentIndexChanged( QString ) )
180 181 182

    SETUP_VFILTER( logo )
    SETUP_VFILTER_OPTION( logoFileText, editingFinished() )
183 184
    SETUP_VFILTER_OPTION( logoYSpin, valueChanged( int ) )
    SETUP_VFILTER_OPTION( logoXSpin, valueChanged( int ) )
185
    SETUP_VFILTER_OPTION( logoOpacitySlider, valueChanged( int ) )
186
    BUTTONACT( ui.logoBrowseBtn, browseLogo() );
187

188 189 190
    SETUP_VFILTER( gradfun )
    SETUP_VFILTER_OPTION( gradfunRadiusSlider, valueChanged( int ) )

Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
191 192 193
    SETUP_VFILTER( grain )
    SETUP_VFILTER_OPTION( grainVarianceSlider, valueChanged( int ) )

194 195 196
    SETUP_VFILTER( mirror )

    SETUP_VFILTER( gaussianblur )
197
    SETUP_VFILTER_OPTION( gaussianblurSigmaSlider, valueChanged( int ) )
198 199 200 201

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

202 203 204 205 206 207
    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 ) )

208

209 210
    SETUP_VFILTER( anaglyph )

211 212
#undef SETUP_VFILTER
#undef SETUP_VFILTER_OPTION
213 214 215 216 217

    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
218 219
    CONNECT( ui.leftRightCropSync, toggled ( bool ), this, cropChange() );
    CONNECT( ui.topBotCropSync, toggled ( bool ), this, cropChange() );
220 221 222 223
    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
224 225
}

226 227
void ExtVideo::cropChange()
{
Dylan Yudaken's avatar
Dylan Yudaken committed
228 229 230 231 232
    if( ui.topBotCropSync->isChecked() )
        ui.cropBotPx->setValue( ui.cropTopPx->value() );
    if( ui.leftRightCropSync->isChecked() )
        ui.cropRightPx->setValue( ui.cropLeftPx->value() );

233
    vout_thread_t *p_vout = THEMIM->getVout();
234
    if( p_vout )
235
    {
236 237 238 239
        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() );
240
        vlc_object_release( p_vout );
241 242 243
    }
}

244 245 246 247 248 249 250 251
void ExtVideo::clean()
{
    ui.cropTopPx->setValue( 0 );
    ui.cropBotPx->setValue( 0 );
    ui.cropLeftPx->setValue( 0 );
    ui.cropRightPx->setValue( 0 );
}

252
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
253
{
254
    char* psz_chain = config_GetPsz( p_intf, psz_filter_type );
255

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

259 260
    if( b_add ) list << psz_name;
    else        list.removeAll( psz_name );
Clément Stenac's avatar
Clément Stenac committed
261

262
    free( psz_chain );
Clément Stenac's avatar
Clément Stenac committed
263

KO Myung-Hun's avatar
KO Myung-Hun committed
264
    return list.join( ":" );
265 266
}

267 268 269 270 271 272 273 274 275
static void ChangeAFiltersString( struct intf_thread_t *p_intf, const char *psz_name, bool b_add )
{
    module_t *p_obj = module_find( psz_name );
    if( !p_obj )
    {
        msg_Err( p_intf, "Unable to find filter module \"%s\".", psz_name );
        return;
    }

276
    QString result = ChangeFiltersString( p_intf, "audio-filter", psz_name, b_add );
277
    config_PutPsz( p_intf, "audio-filter", qtu( result ) );
278 279
}

280
static const char* GetVFilterType( struct intf_thread_t *p_intf, const char *psz_name )
281 282 283 284 285
{
    module_t *p_obj = module_find( psz_name );
    if( !p_obj )
    {
        msg_Err( p_intf, "Unable to find filter module \"%s\".", psz_name );
286
        return NULL;
287 288 289
    }

    if( module_provides( p_obj, "video splitter" ) )
290
        return "video-splitter";
291
    else if( module_provides( p_obj, "video filter" ) )
292
        return "video-filter";
293
    else if( module_provides( p_obj, "sub source" ) )
294
        return "sub-source";
295
    else if( module_provides( p_obj, "sub filter" ) )
296
        return "sub-filter";
297 298 299
    else
    {
        msg_Err( p_intf, "Unknown video filter type." );
300
        return NULL;
301
    }
302 303 304 305 306
}

static void ChangeVFiltersString( struct intf_thread_t *p_intf, const char *psz_name, bool b_add )
{
    const char *psz_filter_type = GetVFilterType( p_intf, psz_name );
307

308 309 310
    if( psz_filter_type == NULL )
        return;

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

Clément Stenac's avatar
Clément Stenac committed
313
    /* Vout is not kept, so put that in the config */
314
    config_PutPsz( p_intf, psz_filter_type, qtu( result ) );
Clément Stenac's avatar
Clément Stenac committed
315 316

    /* Try to set on the fly */
317
    if( !strcmp( psz_filter_type, "video-splitter" ) )
Clément Stenac's avatar
Clément Stenac committed
318
    {
319
        playlist_t *p_playlist = THEPL;
320
        var_SetString( p_playlist, psz_filter_type, qtu( result ) );
321 322 323
    }
    else
    {
324
        vout_thread_t *p_vout = THEMIM->getVout();
325 326
        if( p_vout )
        {
327
            var_SetString( p_vout, psz_filter_type, qtu( result ) );
328 329
            vlc_object_release( p_vout );
        }
Clément Stenac's avatar
Clément Stenac committed
330 331 332 333 334
    }
}

void ExtVideo::updateFilters()
{
335
    QString module = ModuleFromWidgetName( sender() );
Clément Stenac's avatar
Clément Stenac committed
336

337 338
    QCheckBox *checkbox = qobject_cast<QCheckBox*>( sender() );
    QGroupBox *groupbox = qobject_cast<QGroupBox*>( sender() );
339

340
    ChangeVFiltersString( p_intf, qtu( module ),
341 342
                          checkbox ? checkbox->isChecked()
                                   : groupbox->isChecked() );
Clément Stenac's avatar
Clément Stenac committed
343 344
}

345 346 347 348 349 350
#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& ) ) );

351 352 353 354
void ExtVideo::browseLogo()
{
    QString file = QFileDialog::getOpenFileName( NULL, qtr( "Logo filenames" ),
                   p_intf->p_sys->filepath, "Images (*.png *.jpg);;All (*)" );
355 356

    UPDATE_AND_APPLY_TEXT( logoFileText, file );
357 358 359 360 361 362
}

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

    UPDATE_AND_APPLY_TEXT( eraseMaskText, file );
365 366
}

367 368
#undef UPDATE_AND_APPLY_TEXT

369 370
void ExtVideo::initComboBoxItems( QObject *widget )
{
371
    QComboBox *combobox = qobject_cast<QComboBox*>( widget );
372
    if( !combobox ) return;
373

374
    QString option = OptionFromWidgetName( widget );
375
    module_config_t *p_item = config_FindConfig( VLC_OBJECT( p_intf ),
376
                                                 qtu( option ) );
377
    if( p_item == NULL )
378
    {
379 380 381 382 383 384 385 386 387 388 389 390
        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++ )
391
        {
392
            combobox->addItem( qtr( texts[i] ), qlonglong(values[i]) );
393
            free( texts[i] );
394
        }
395 396
        free( texts );
        free( values );
397
    }
398
    else if( p_item->i_type == CONFIG_ITEM_STRING )
399
    {
400 401 402 403 404 405
        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++ )
        {
406
            combobox->addItem( qtr( texts[i] ), qfu(values[i]) );
407 408 409 410 411
            free( texts[i] );
            free( values[i] );
        }
        free( texts );
        free( values );
412 413 414
    }
}

415 416 417 418 419 420 421
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;

422
    vlc_object_t *p_obj = ( vlc_object_t * )
423
        vlc_object_find_name( p_intf->obj.libvlc, qtu( module ) );
424 425 426 427 428
    int i_type;
    vlc_value_t val;

    if( !p_obj )
    {
429
#if 0
430 431
        msg_Dbg( p_intf,
                 "Module instance %s not found, looking in config values.",
432
                 qtu( module ) );
433
#endif
434
        i_type = config_GetType( p_intf, qtu( option ) ) & VLC_VAR_CLASS;
435 436 437 438
        switch( i_type )
        {
            case VLC_VAR_INTEGER:
            case VLC_VAR_BOOL:
439
                val.i_int = config_GetInt( p_intf, qtu( option ) );
440 441
                break;
            case VLC_VAR_FLOAT:
442
                val.f_float = config_GetFloat( p_intf, qtu( option ) );
443 444
                break;
            case VLC_VAR_STRING:
445
                val.psz_string = config_GetPsz( p_intf, qtu( option ) );
446 447 448 449 450
                break;
        }
    }
    else
    {
451
        i_type = var_Type( p_obj, qtu( option ) ) & VLC_VAR_CLASS;
452
        var_Get( p_obj, qtu( option ), &val );
453 454 455 456 457
        vlc_object_release( p_obj );
    }

    /* Try to cast to all the widgets we're likely to encounter. Only
     * one of the casts is expected to work. */
458 459 460 461
    QSlider        *slider        = qobject_cast<QSlider*>       ( widget );
    QCheckBox      *checkbox      = qobject_cast<QCheckBox*>     ( widget );
    QSpinBox       *spinbox       = qobject_cast<QSpinBox*>      ( widget );
    QDoubleSpinBox *doublespinbox = qobject_cast<QDoubleSpinBox*>( widget );
462
    VLCQDial       *dial          = qobject_cast<VLCQDial*>      ( widget );
463 464
    QLineEdit      *lineedit      = qobject_cast<QLineEdit*>     ( widget );
    QComboBox      *combobox      = qobject_cast<QComboBox*>     ( widget );
465 466 467 468 469 470 471

    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 );
472
        else if( dial )     dial->setValue( (360 - val.i_int) % 360 );
473 474 475
        else if( lineedit )
        {
            char str[30];
Tristan Matthews's avatar
Tristan Matthews committed
476
            snprintf( str, sizeof(str), "%06" PRIX64, val.i_int );
477 478
            lineedit->setText( str );
        }
479
        else if( combobox ) combobox->setCurrentIndex(
480
                            combobox->findData( qlonglong(val.i_int) ) );
481
        else msg_Warn( p_intf, "Could not find the correct Integer widget" );
482 483 484
    }
    else if( i_type == VLC_VAR_FLOAT )
    {
485 486
        if( slider ) slider->setValue( ( int )( val.f_float*( double )slider->tickInterval() ) ); /* hack alert! */
        else if( doublespinbox ) doublespinbox->setValue( val.f_float );
487
        else if( dial ) dial->setValue( (360 - lroundf(val.f_float)) % 360 );
488
        else msg_Warn( p_intf, "Could not find the correct Float widget" );
489 490 491
    }
    else if( i_type == VLC_VAR_STRING )
    {
492
        if( lineedit ) lineedit->setText( qfu( val.psz_string ) );
493
        else if( combobox ) combobox->setCurrentIndex(
494
                            combobox->findData( qfu( val.psz_string ) ) );
495
        else msg_Warn( p_intf, "Could not find the correct String widget" );
496 497 498
        free( val.psz_string );
    }
    else
499 500 501 502 503 504
        if( p_obj )
            msg_Err( p_intf,
                     "Module %s's %s variable is of an unsupported type ( %d )",
                     qtu( module ),
                     qtu( option ),
                     i_type );
505 506
}

507 508
void ExtVideo::setFilterOption( struct intf_thread_t *p_intf, const char *psz_module, const char *psz_option,
        int i_int, double f_float, QString val )
509
{
510
    vlc_object_t *p_obj = ( vlc_object_t * )vlc_object_find_name( p_intf->obj.libvlc, psz_module );
511 512
    int i_type;
    bool b_is_command;
513

514 515
    if( !p_obj )
    {
516 517
        msg_Warn( p_intf, "Module %s not found. You'll need to restart the filter to take the change into account.", psz_module );
        i_type = config_GetType( p_intf, psz_option );
518 519 520 521
        b_is_command = false;
    }
    else
    {
522
        i_type = var_Type( p_obj, psz_option );
523
        if( i_type == 0 )
524
            i_type = config_GetType( p_intf, psz_option );
525
        b_is_command = ( i_type & VLC_VAR_ISCOMMAND );
526 527
    }

528
    i_type &= VLC_VAR_CLASS;
529 530
    if( i_type == VLC_VAR_INTEGER || i_type == VLC_VAR_BOOL )
    {
531 532 533
        if( i_int == -1 )
            msg_Warn( p_intf, "Could not find the correct Integer widget" );
        config_PutInt( p_intf, psz_option, i_int );
534 535 536
        if( b_is_command )
        {
            if( i_type == VLC_VAR_INTEGER )
537
                var_SetInteger( p_obj, psz_option, i_int );
538
            else
539
                var_SetBool( p_obj, psz_option, i_int );
540
        }
541 542 543
    }
    else if( i_type == VLC_VAR_FLOAT )
    {
544 545 546
        if( f_float == -1 )
            msg_Warn( p_intf, "Could not find the correct Float widget" );
        config_PutFloat( p_intf, psz_option, f_float );
547
        if( b_is_command )
548
            var_SetFloat( p_obj, psz_option, f_float );
549 550 551
    }
    else if( i_type == VLC_VAR_STRING )
    {
552
        if( val.isNull() )
553
            msg_Warn( p_intf, "Could not find the correct String widget" );
554
        config_PutPsz( p_intf, psz_option, qtu( val ) );
555
        if( b_is_command )
556
            var_SetString( p_obj, psz_option, qtu( val ) );
557 558 559
    }
    else
        msg_Err( p_intf,
560
                 "Module %s's %s variable is of an unsupported type ( %d )",
561 562
                 psz_module,
                 psz_option,
563 564
                 i_type );

565 566 567
    if( !b_is_command )
    {
        msg_Warn( p_intf, "Module %s's %s variable isn't a command. Brute-restarting the filter.",
568 569 570 571
                 psz_module,
                 psz_option );
        ChangeVFiltersString( p_intf, psz_module, false );
        ChangeVFiltersString( p_intf, psz_module, true );
572 573
    }

574
    if( p_obj ) vlc_object_release( p_obj );
575 576
}

577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605
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 ) {
606 607
        i_int = (360 - dial->value()) % 360;
        f_float = i_int;
608 609 610 611 612 613 614 615 616 617 618 619 620 621
    }
    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();
    }

    setFilterOption( p_intf, qtu( module ), qtu( option ), i_int, f_float, val);
}

622 623 624 625 626
/**********************************************************************
 * v4l2 controls
 **********************************************************************/

ExtV4l2::ExtV4l2( intf_thread_t *_p_intf, QWidget *_parent )
627
    : QWidget( _parent ), p_intf( _p_intf ), box( NULL )
628
{
629 630 631 632 633 634
    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 );
635
    help->setWordWrap( true );
636 637
    layout->addWidget( help );
    setLayout( layout );
638 639
}

640 641 642 643 644 645
void ExtV4l2::showEvent( QShowEvent *event )
{
    QWidget::showEvent( event );
    Refresh();
}

646 647
void ExtV4l2::Refresh( void )
{
648
    vlc_object_t *p_obj = (vlc_object_t*)vlc_object_find_name( THEPL, "v4l2" );
649
    help->hide();
650 651
    if( box )
    {
652
        layout()->removeWidget( box );
653 654 655
        delete box;
        box = NULL;
    }
656 657
    if( p_obj )
    {
658
        vlc_value_t val, text;
659 660 661 662
        int i_ret = var_Change( p_obj, "controls", VLC_VAR_GETCHOICES,
                                &val, &text );
        if( i_ret < 0 )
        {
663
            msg_Err( p_intf, "Oops, v4l2 object doesn't have a 'controls' variable." );
664
            help->show();
665 666 667
            vlc_object_release( p_obj );
            return;
        }
668 669

        box = new QGroupBox( this );
670
        layout()->addWidget( box );
671 672 673
        QVBoxLayout *layout = new QVBoxLayout( box );
        box->setLayout( layout );

674 675
        for( int i = 0; i < val.p_list->i_count; i++ )
        {
676
            vlc_value_t vartext;
677
            const char *psz_var = text.p_list->p_values[i].psz_string;
678 679 680 681 682 683

            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
684
            msg_Dbg( p_intf, "v4l2 control \"%" PRIx64 "\": %s (%s)",
685
                     val.p_list->p_values[i].i_int, psz_var, qtu( name ) );
686 687 688 689 690 691

            int i_type = var_Type( p_obj, psz_var );
            switch( i_type & VLC_VAR_TYPE )
            {
                case VLC_VAR_INTEGER:
                {
692
                    QLabel *label = new QLabel( name, box );
693
                    QHBoxLayout *hlayout = new QHBoxLayout();
694 695 696 697
                    hlayout->addWidget( label );
                    int i_val = var_GetInteger( p_obj, psz_var );
                    if( i_type & VLC_VAR_HASCHOICE )
                    {
698
                        QComboBox *combobox = new QComboBox( box );
699
                        combobox->setObjectName( qfu( psz_var ) );
700 701 702 703 704 705 706 707

                        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
708
                                       qlonglong( val2.p_list->p_values[j].i_int) );
709 710 711
                            if( i_val == val2.p_list->p_values[j].i_int )
                                combobox->setCurrentIndex( j );
                        }
712
                        var_FreeList( &val2, &text2 );
713 714 715 716 717 718 719

                        CONNECT( combobox, currentIndexChanged( int ), this,
                                 ValueChange( int ) );
                        hlayout->addWidget( combobox );
                    }
                    else
                    {
720
                        QSlider *slider = new QSlider( box );
721
                        slider->setObjectName( qfu( psz_var ) );
722 723 724 725
                        slider->setOrientation( Qt::Horizontal );
                        vlc_value_t val2;
                        var_Change( p_obj, psz_var, VLC_VAR_GETMIN,
                                    &val2, NULL );
726 727
                        if( val2.i_int < INT_MIN )
                            val2.i_int = INT_MIN; /* FIXME */
728 729 730
                        slider->setMinimum( val2.i_int );
                        var_Change( p_obj, psz_var, VLC_VAR_GETMAX,
                                    &val2, NULL );
731 732
                        if( val2.i_int > INT_MAX )
                            val2.i_int = INT_MAX; /* FIXME */
733
                        slider->setMaximum( val2.i_int );
734 735 736
                        if( !var_Change( p_obj, psz_var, VLC_VAR_GETSTEP,
                                         &val2, NULL ) )
                            slider->setSingleStep( val2.i_int );
737 738 739 740 741 742 743 744 745 746
                        slider->setValue( i_val );
                        CONNECT( slider, valueChanged( int ), this,
                                 ValueChange( int ) );
                        hlayout->addWidget( slider );
                    }
                    layout->addLayout( hlayout );
                    break;
                }
                case VLC_VAR_BOOL:
                {
747
                    QCheckBox *button = new QCheckBox( name, box );
748
                    button->setObjectName( qfu( psz_var ) );
749 750 751 752 753 754 755 756 757
                    button->setChecked( var_GetBool( p_obj, psz_var ) );

                    CONNECT( button, clicked( bool ), this,
                             ValueChange( bool ) );
                    layout->addWidget( button );
                    break;
                }
                case VLC_VAR_VOID:
                {
758 759
                    if( i_type & VLC_VAR_ISCOMMAND )
                    {
760
                        QPushButton *button = new QPushButton( name, box );
761
                        button->setObjectName( qfu( psz_var ) );
762

763 764 765 766 767 768
                        CONNECT( button, clicked( bool ), this,
                                 ValueChange( bool ) );
                        layout->addWidget( button );
                    }
                    else
                    {
769
                        QLabel *label = new QLabel( name, box );
770 771
                        layout->addWidget( label );
                    }
772 773 774 775 776 777 778
                    break;
                }
                default:
                    msg_Warn( p_intf, "Unhandled var type for %s", psz_var );
                    break;
            }
        }
779
        var_FreeList( &val, &text );
780 781 782 783 784
        vlc_object_release( p_obj );
    }
    else
    {
        msg_Dbg( p_intf, "Couldn't find v4l2 instance" );
785 786 787
        help->show();
        if ( isVisible() )
            QTimer::singleShot( 2000, this, SLOT(Refresh()) );
788 789 790 791 792 793 794 795 796 797 798
    }
}

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

void ExtV4l2::ValueChange( int value )
{
    QObject *s = sender();
799
    vlc_object_t *p_obj = (vlc_object_t*)vlc_object_find_name( THEPL, "v4l2" );
800 801
    if( p_obj )
    {
802 803
        QString var = s->objectName();
        int i_type = var_Type( p_obj, qtu( var ) );
804 805 806 807 808 809 810 811
        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();
                }
812
                var_SetInteger( p_obj, qtu( var ), value );
813 814
                break;
            case VLC_VAR_BOOL:
815
                var_SetBool( p_obj, qtu( var ), value );
816 817
                break;
            case VLC_VAR_VOID:
818
                var_TriggerCallback( p_obj, qtu( var ) );
819 820 821 822 823 824 825 826 827 828 829
                break;
        }
        vlc_object_release( p_obj );
    }
    else
    {
        msg_Warn( p_intf, "Oops, v4l2 object isn't available anymore" );
        Refresh();
    }
}

830 831 832 833
/**********************************************************************
 * Sliders
 **********************************************************************/

834 835
FilterSliderData::FilterSliderData( QObject *parent, QSlider *_slider ) :
    QObject( parent ), slider( _slider )
836 837 838
{
    b_save_to_config = false;
}
839

840 841 842 843 844
FilterSliderData::FilterSliderData( QObject *parent,
                                    intf_thread_t *_p_intf,
                                    QSlider *_slider,
                                    QLabel *_label, QLabel *_nameLabel,
                                    const slider_data_t *_p_data ):
845 846
    QObject( parent ), slider( _slider ), valueLabel( _label ),
    nameLabel( _nameLabel ), p_data( _p_data ), p_intf( _p_intf )
847
{
848
    b_save_to_config = false;
849 850
    slider->setMinimum( p_data->f_min / p_data->f_resolution );
    slider->setMaximum( p_data->f_max / p_data->f_resolution );
851
    nameLabel->setText( p_data->descs );
852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867
    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 )
{
868
    float f = ((float) i) * p_data->f_resolution * p_data->f_visual_multiplier;
869
    valueLabel->setText( QString( p_data->units )
870
                    .prepend( "%1 " )
871 872 873 874 875 876 877 878 879
                    .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 )
    {
880
        if ( var_Type( p_aout, qtu(p_data->name) ) == 0 )
881 882 883 884 885 886
        {
            vlc_object_release( p_aout );
            /* Not found, will try in config */
        }
        else
        {
887
            f = var_GetFloat( p_aout, qtu(p_data->name) );
888 889 890 891 892
            vlc_object_release( p_aout );
            return f;
        }
    }

893
    if ( ! config_FindConfig( VLC_OBJECT(p_intf), qtu(p_data->name) ) )
894 895
        return f;

896
    f = config_GetFloat( p_intf, qtu(p_data->name) );
897 898 899
    return f;
}

900
void FilterSliderData::onValueChanged( int i ) const
901 902 903 904 905
{
    float f = ((float) i) * p_data->f_resolution;
    vlc_object_t *p_aout = (vlc_object_t *) THEMIM->getAout();
    if ( p_aout )
    {
906
        var_SetFloat( p_aout, qtu(p_data->name), f );
907 908 909 910 911
        vlc_object_release( p_aout );
    }
    writeToConfig();
}