1
/******************************************************************************
2
 * Setup dialog for the equalizer                                             *
3
 *                                                                            *
4
 * Copyright (C) 2004 Mark Kretschmann <kretschmann@kde.org>                  *
5
 *           (C) 2005 Seb Ruiz <ruiz@kde.org>                                 *
6
 *           (C) 2005 Markus Brueffer <markus@brueffer.de>                    *
7
 *                                                                            *
8
 * This program is free software; you can redistribute it and/or              *
9
 * modify it under the terms of the GNU General Public License as             *
10
 * published by the Free Software Foundation; either version 2 of             *
11
 * the License, or (at your option) any later version.                        *
12
 *                                                                            *
13
 * This program is distributed in the hope that it will be useful,            *
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
16
 * GNU General Public License for more details.                               *
17
 *                                                                            *
18
 * You should have received a copy of the GNU General Public License          *
19
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.      *
20
 ******************************************************************************/
21
22
#include "equalizersetup.h"
23
24
#include "Amarok.h"
25
#include "amarokconfig.h"
26
#include "EngineController.h"
27
#include "equalizergraph.h"
28
#include "equalizerpresetmanager.h"
29
#include "widgets/SliderWidget.h"
30
31
#include <KComboBox>
32
#include <KInputDialog>  //presets
33
#include <KLocale>
34
#include <KMessageBox>
35
#include <KStandardDirs> //locate()
36
#include <KVBox>
37
#include <KWindowSystem>
38
39
#include <QDomDocument>
40
#include <QDomElement>
41
#include <QDomNode>
42
#include <QFile>
43
#include <QGroupBox>
44
#include <QLabel>
45
#include <QPushButton>
46
#include <QTextStream>   //presets
47
48
49
EqualizerSetup* EqualizerSetup::s_instance = 0;
50
51
52
EqualizerSetup::EqualizerSetup()
53
        : KDialog( Amarok::mainWindow() )
54
{
55
    setModal( false );
56
    setButtons( Ok );
57
    showButtonSeparator( false );
58
59
    using Amarok::Slider;
60
61
    s_instance = this;
62
63
    kapp->setTopWidget( this );
64
    setCaption( KDialog::makeStandardCaption( i18n( "Equalizer" ) ) );
65
66
    // Gives the window a small title bar, and skips a taskbar entry
67
#ifdef Q_WS_X11
68
    KWindowSystem::setType( winId(), NET::Utility );
69
    KWindowSystem::setState( winId(), NET::SkipTaskbar );
70
#endif
71
72
    QWidget* vbox = new QWidget( this );
73
    setMainWidget( vbox );
74
    QVBoxLayout *vboxLayout = new QVBoxLayout;
75
    vbox->setLayout( vboxLayout );
76
    vboxLayout->setSpacing( KDialog::spacingHint() );
77
78
    // BEGIN Presets
79
    QWidget* presetBox = new QWidget( vbox );
80
    vboxLayout->addWidget( presetBox );
81
    QHBoxLayout *presetLayout = new QHBoxLayout;
82
    presetLayout->setSpacing( KDialog::spacingHint() );
83
    presetBox->setLayout( presetLayout );
84
85
    presetLayout->addWidget( new QLabel( i18n("Presets:") ) );
86
87
    m_presetCombo = new KComboBox;
88
    presetLayout->addWidget( m_presetCombo );
89
    m_presetCombo->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred ) );
90
91
    QPushButton* presetAdd = new QPushButton;
92
    presetLayout->addWidget( presetAdd );
93
    presetAdd->setIcon( KIcon( "list-add-amarok" ) );
94
    presetAdd->setToolTip( i18n("Add new preset") );
95
    connect( presetAdd, SIGNAL( clicked() ), SLOT( addPreset() ) );
96
97
    QPushButton* presetConf = new QPushButton;
98
    presetLayout->addWidget( presetConf );
99
    presetConf->setIcon( KIcon( "configure-amarok" ) );
100
    presetConf->setToolTip( i18n("Manage presets") );
101
    connect( presetConf, SIGNAL( clicked() ), SLOT( editPresets() ) );
102
103
    loadPresets();
104
    connect( m_presetCombo, SIGNAL( activated(int) ), SLOT( presetChanged(int) ) );
105
    // END Presets
106
107
    // BEGIN GroupBox
108
    m_groupBoxSliders = new QGroupBox( vbox );
109
    vboxLayout->addWidget( m_groupBoxSliders );
110
    m_groupBoxSliders->setTitle( i18n("Enable Equalizer") );
111
    m_groupBoxSliders->setCheckable( true );
112
    m_groupBoxSliders->setChecked( AmarokConfig::equalizerEnabled() );
113
    connect( m_groupBoxSliders, SIGNAL( toggled( bool ) ), SLOT( setEqualizerEnabled( bool ) ) );
114
    QVBoxLayout *v = new QVBoxLayout( m_groupBoxSliders );
115
116
    // Helper widget for layouting inside the groupbox
117
    QWidget* slidersLayoutWidget = new QWidget( m_groupBoxSliders );
118
    v->addWidget( slidersLayoutWidget );
119
    slidersLayoutWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
120
    QGridLayout* slidersGridLayout = new QGridLayout( slidersLayoutWidget );
121
    slidersGridLayout->setSpacing( KDialog::spacingHint() );
122
    // END GroupBox
123
124
    // BEGIN Preamp slider
125
    m_slider_preamp = new Slider( Qt::Vertical, slidersLayoutWidget, 100 );
126
    m_slider_preamp->setMinimum( -100 );
127
    m_slider_preamp->setTickPosition( QSlider::TicksRight );
128
    m_slider_preamp->setTickInterval( 100 );
129
    connect( m_slider_preamp, SIGNAL( valueChanged( int ) ), SLOT( setEqualizerParameters() ) );
130
    slidersGridLayout->addMultiCellWidget(m_slider_preamp, 0, 0, 0, 1, Qt::AlignHCenter );
131
132
    QLabel* preampLabel = new QLabel( i18n("Pre-amp"), slidersLayoutWidget );
133
    slidersGridLayout->addMultiCellWidget(preampLabel, 1, 1 , 0, 1, Qt::AlignHCenter );
134
    // END
135
136
    // BEGIN Band Sliders
137
    const char *bandLabels[] = { "30", "60", "125", "250", "500", "1k", "2k", "4k", "8k", "16k" };
138
139
    int minWidth = 0;
140
    QFontMetrics fm = fontMetrics(); //apparently it's an expensive call
141
    for ( int i = 0; i < 10; i++ ) {
142
         int w = fm.width( bandLabels[i] );
143
         if ( w > minWidth )
144
             minWidth = w;
145
    }
146
147
    for ( int i = 0; i < 10; i++ ) {
148
        Slider *slider = new Slider( Qt::Vertical, slidersLayoutWidget );
149
        QLabel *label  = new QLabel( slidersLayoutWidget );
150
        label->setText( bandLabels[i] );
151
152
        slider->setMinimum( -100 );
153
        slider->setMaximum( +100 );
154
        slider->setMinimumWidth( minWidth );
155
        slidersGridLayout->addMultiCellWidget(slider, 0, 0, 2 * i + 2, 2 * i + 3, Qt::AlignHCenter );
156
        slidersGridLayout->addMultiCellWidget(label, 1, 1, 2 * i + 2, 2 * i + 3, Qt::AlignHCenter );
157
        m_bandSliders.append( slider );
158
159
        connect( slider, SIGNAL( valueChanged( int ) ), SLOT( setEqualizerParameters() ) );
160
        connect( slider, SIGNAL( valueChanged( int ) ), SLOT( sliderChanged() ) );
161
    }
162
    // END
163
164
    // BEGIN Equalizer Graph Widget
165
    QGroupBox* graphGBox = new QGroupBox();
166
    vboxLayout->addWidget( graphGBox );
167
    QHBoxLayout* h = new QHBoxLayout( graphGBox );
168
169
    KVBox* graphVBox = new KVBox();
170
    h->addWidget( graphVBox );
171
    QLabel* graphLabel1 = new QLabel(graphVBox);
172
    graphLabel1->setText("+20 db");
173
    QLabel* graphLabel2 = new QLabel(graphVBox);
174
    graphLabel2->setText("0 db");
175
    QLabel* graphLabel3 = new QLabel(graphVBox);
176
    graphLabel3->setText("-20 db");
177
    graphLabel1->setAlignment( Qt::AlignRight | Qt::AlignTop );
178
    graphLabel2->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
179
    graphLabel3->setAlignment( Qt::AlignRight | Qt::AlignBottom );
180
181
    m_equalizerGraph = new EqualizerGraph( graphGBox );
182
    h->addWidget( m_equalizerGraph );
183
    m_equalizerGraph->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
184
    // END Graph Widget
185
186
    // Fill the combobox
187
    updatePresets( AmarokConfig::equalizerPreset() );
188
189
    // make sure to restore the current preamp value
190
    m_slider_preamp->setValue( AmarokConfig::equalizerPreamp() );
191
192
    // Init sliders
193
    presetChanged( AmarokConfig::equalizerPreset() );
194
}
195
196
197
EqualizerSetup::~EqualizerSetup()
198
{
199
    savePresets();
200
    s_instance = 0;
201
}
202
203
void
204
EqualizerSetup::setActive( bool active )
205
{
206
    m_groupBoxSliders->setChecked( active );
207
}
208
209
void
210
EqualizerSetup::setBands( int preamp, QList<int> gains )
211
{
212
    m_slider_preamp->setValue( preamp );
213
214
    // Note: As a side effect, this automatically switches the
215
    //       preset to 'Manual', which is by intention
216
    for ( int i = 0; i < m_bandSliders.count(); i++ )
217
        m_bandSliders.at(i)->setValue( gains.at(i) );
218
219
    setEqualizerParameters();
220
}
221
222
void
223
EqualizerSetup::setPreset( QString name )
224
{
225
    // Look for the preset id and verify name
226
    int i, count = m_presetCombo->count();
227
    bool found = false;
228
    for( i = 0; i < count; i++ ) {
229
        if ( m_presetCombo->itemText( i ) == name ) {
230
            found = true;
231
            break;
232
        }
233
    }
234
235
    if ( found ) {
236
        m_presetCombo->setCurrentIndex( i );
237
        presetChanged( name );
238
    }
239
}
240
241
/////////////////////////////////////////////////////////////////////////////////////
242
// EQUALIZER PRESETS
243
/////////////////////////////////////////////////////////////////////////////////////
244
245
QString
246
EqualizerSetup::presetsCache() const
247
{
248
    // returns the playlists stats cache file
249
    return Amarok::saveLocation() + "equalizerpresets_save.xml";
250
}
251
252
253
void
254
EqualizerSetup::loadPresets()
255
{
256
    // Create predefined presets 'Zero' and 'Manual'
257
    QList<int> zeroGains;
258
    zeroGains << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0 << 0;
259
    m_presets[ i18n("Manual") ] = zeroGains;
260
    m_presets[ i18n("Zero") ] = zeroGains;
261
262
    QFile file( presetsCache() );
263
    if ( !file.exists() )
264
        file.setFileName( KStandardDirs::locate( "data", "amarok/data/equalizer_presets.xml" ) );
265
266
    QTextStream stream( &file );
267
    stream.setCodec( "UTF8" );
268
269
    QDomDocument d;
270
271
    if( !file.open( QIODevice::ReadOnly ) || !d.setContent( stream.readAll() ) ) {
272
        // Everything went wrong, so at least provide the two predefined presets
273
        updatePresets( AmarokConfig::equalizerPreset() );
274
        return;
275
    }
276
277
    QDomNode n = d.namedItem( "equalizerpresets" ).namedItem("preset");
278
279
    for( ; !n.isNull();  n = n.nextSibling() )
280
    {
281
        QDomElement e = n.toElement();
282
        QString title = e.attribute( "name" );
283
284
        QList<int> gains;
285
        gains << e.namedItem( "b0" ).toElement().text().toInt();
286
        gains << e.namedItem( "b1" ).toElement().text().toInt();
287
        gains << e.namedItem( "b2" ).toElement().text().toInt();
288
        gains << e.namedItem( "b3" ).toElement().text().toInt();
289
        gains << e.namedItem( "b4" ).toElement().text().toInt();
290
        gains << e.namedItem( "b5" ).toElement().text().toInt();
291
        gains << e.namedItem( "b6" ).toElement().text().toInt();
292
        gains << e.namedItem( "b7" ).toElement().text().toInt();
293
        gains << e.namedItem( "b8" ).toElement().text().toInt();
294
        gains << e.namedItem( "b9" ).toElement().text().toInt();
295
296
        m_presets[ title ] = gains;
297
    }
298
299
    file.close();
300
}
301
302
303
void
304
EqualizerSetup::savePresets()
305
{
306
    QFile file( presetsCache() );
307
308
    if( !file.open( QIODevice::WriteOnly ) ) return;
309
310
    QDomDocument doc;
311
    QDomElement e = doc.createElement("equalizerpresets");
312
    e.setAttribute( "product", "Amarok" );
313
    e.setAttribute( "version", APP_VERSION );
314
    e.setAttribute( "formatversion", "1.1" );
315
316
    doc.appendChild( e );
317
318
    QStringList info;
319
    info << "b0" << "b1" << "b2" << "b3" << "b4"
320
         << "b5" << "b6" << "b7" << "b8" << "b9";
321
322
    for( int x = 0; x < m_presets.count(); x++ )
323
    {
324
        const QString title = m_presetCombo->itemText( x );
325
326
        // don't save the 'Zero' preset
327
        if ( title == i18n("Zero") )
328
            continue;
329
330
        QList<int> gains = m_presets[ title ];
331
332
        QDomElement i = doc.createElement("preset");
333
        i.setAttribute( "name", title );
334
335
        QDomElement attr;
336
        QDomText t;
337
        for( int y=0; y < info.count(); y++ )
338
        {
339
            attr = doc.createElement( info[y] );
340
            t    = doc.createTextNode( QString::number( gains.first() ) );
341
            attr.appendChild( t );
342
            i.appendChild( attr );
343
            gains.pop_front();
344
        }
345
        e.appendChild( i );
346
    }
347
348
    QTextStream stream( &file );
349
    stream.setCodec( "UTF8" );
350
    stream << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
351
    stream << doc.toString();
352
    file.close();
353
}
354
355
void
356
EqualizerSetup::editPresets()
357
{
358
    EqualizerPresetManager * editor = new EqualizerPresetManager(this);
359
    editor->setPresets(m_presets);
360
361
    if ( editor->exec() ) {
362
        QMap< QString, QList<int> > presets = editor->presets();
363
364
        QString currentTitle = m_presetCombo->currentText();
365
        QList<int> currentGains= m_presets[ currentTitle ];
366
367
        QString newTitle = currentTitle;
368
369
        // Check if the selected item was renamed
370
        if ( presets.find( currentTitle ) == presets.end() || currentGains != presets[ currentTitle ] ) {
371
372
            // Find the new name
373
            foreach( const QString &key, presets.keys() ) {
374
                if ( presets[key] == currentGains ) {
375
                    newTitle = key;
376
                    break;
377
                }
378
            }
379
        }
380
381
        m_presets = presets;
382
        updatePresets( newTitle );
383
    }
384
385
    delete editor;
386
}
387
388
void
389
EqualizerSetup::addPreset()
390
{
391
    bool ok;
392
    const QString title = KInputDialog::getText( i18n("Add Equalizer Preset"),
393
                                                 i18n("Enter preset name:"), i18n("Untitled"), &ok, this);
394
395
    if (ok) {
396
        // Check if the new preset title exists
397
        if ( m_presets.find( title ) != m_presets.end() ) {
398
            int button = KMessageBox::warningYesNo( this, i18n( "A preset with the name %1 already exists. Overwrite?", title ) );
399
400
            if ( button != KMessageBox::Yes )
401
                return;
402
        }
403
404
        // Add the new preset based on the current slider positions
405
        QList <int> gains;
406
        for ( int i = 0; i < m_bandSliders.count(); i++ )
407
            gains += m_bandSliders.at( i )->value();
408
        m_presets[ title ] = gains;
409
410
        // Rebuild the combobox
411
        updatePresets(title);
412
413
        // Save
414
        setEqualizerParameters();
415
    }
416
}
417
418
void
419
EqualizerSetup::updatePresets(QString selectTitle)
420
{
421
    // Save the selected item
422
    if ( selectTitle.isEmpty() )
423
        selectTitle = m_presetCombo->currentText();
424
425
    // Sort titles
426
    QStringList titles;
427
    QMap< QString, QList<int> >::ConstIterator end = m_presets.constEnd();
428
    for ( QMap< QString, QList<int> >::ConstIterator it = m_presets.constBegin(); it != end; ++it )
429
        titles << it.key();
430
431
    titles.sort();
432
433
    // rebuild preset combobox and look for the previously selected title
434
    int i = 0;
435
    int newIndex = -1;
436
    m_presetCombo->clear();
437
    QStringList::ConstIterator titlesEnd = titles.constEnd();
438
    for ( QStringList::ConstIterator it = titles.constBegin(); it != titlesEnd; ++it ) {
439
        m_presetCombo->addItem( *it );
440
        if ( *it == selectTitle )
441
            newIndex = i;
442
        if ( *it == i18n("Manual") )
443
            m_manualPos = i;
444
        i++;
445
    }
446
447
    if ( newIndex == -1 )
448
        newIndex = m_manualPos;
449
450
    m_presetCombo->setCurrentIndex( newIndex );
451
}
452
453
/////////////////////////////////////////////////////////////////////////////////////
454
// PRIVATE SLOTS
455
/////////////////////////////////////////////////////////////////////////////////////
456
457
void
458
EqualizerSetup::presetChanged( int id ) //SLOT
459
{
460
    presetChanged( m_presetCombo->itemText(id) );
461
}
462
463
void
464
EqualizerSetup::presetChanged( QString title ) //SLOT
465
{
466
    const QList<int> gains = m_presets[ title ];
467
468
    for ( int i = 0; i < m_bandSliders.count(); i++ ) {
469
        // Block signals to prevent unwanted setting to 'Manual'
470
        m_bandSliders.at(i)->blockSignals(true);
471
        m_bandSliders.at(i)->setValue( gains.at(i) );
472
        m_bandSliders.at(i)->blockSignals(false);
473
    }
474
475
    setEqualizerParameters();
476
}
477
478
void
479
EqualizerSetup::setEqualizerEnabled( bool active ) //SLOT
480
{
481
//TODO Phonon Equalizer    EngineController::engine()->setEqualizerEnabled( active );
482
    AmarokConfig::setEqualizerEnabled( active );
483
484
    if( active )
485
        //this way the developer of the eq doesn't have to cache the eq values
486
        setEqualizerParameters();
487
    else
488
        // zero the graph
489
        m_equalizerGraph->update();
490
}
491
492
493
void
494
EqualizerSetup::setEqualizerParameters() //SLOT
495
{
496
    AmarokConfig::setEqualizerPreamp( m_slider_preamp->value() );
497
    AmarokConfig::setEqualizerPreset( m_presetCombo->currentText() );
498
    AmarokConfig::setEqualizerGains ( m_presets[ m_presetCombo->currentText() ] );
499
500
    // Transfer values to the engine if the EQ is enabled
501
 //TODO PhononEqualizer    if ( AmarokConfig::equalizerEnabled() )
502
//       EngineController::engine()->setEqualizerParameters( m_slider_preamp->value(), m_presets[ m_presetCombo->currentText() ] );
503
504
    m_equalizerGraph->update();
505
}
506
507
508
void
509
EqualizerSetup::sliderChanged() //SLOT
510
{
511
    m_presetCombo->setCurrentIndex( m_manualPos );
512
513
    QList<int> gains;
514
    for ( int i = 0; i < m_bandSliders.count(); i++ )
515
        gains += m_bandSliders.at( i )->value();
516
517
    m_presets[ i18n("Manual") ] = gains;
518
}
519
520
#include "equalizersetup.moc"