PageRenderTime 1472ms CodeModel.GetById 125ms app.highlight 911ms RepoModel.GetById 375ms app.codeStats 1ms

/src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp

http://github.com/tomahawk-player/tomahawk
C++ | 1174 lines | 960 code | 182 blank | 32 comment | 365 complexity | 42cd38aba52a595323780b2488833d14 MD5 | raw file
   1/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
   2 *
   3 *   Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
   4 *
   5 *   Tomahawk is free software: you can redistribute it and/or modify
   6 *   it under the terms of the GNU General Public License as published by
   7 *   the Free Software Foundation, either version 3 of the License, or
   8 *   (at your option) any later version.
   9 *
  10 *   Tomahawk is distributed in the hope that it will be useful,
  11 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13 *   GNU General Public License for more details.
  14 *
  15 *   You should have received a copy of the GNU General Public License
  16 *   along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
  17 */
  18
  19#include "playlist/dynamic/echonest/EchonestControl.h"
  20
  21#include "playlist/dynamic/widgets/MiscControlWidgets.h"
  22
  23#include "EchonestGenerator.h"
  24
  25#include "utils/Logger.h"
  26#include "SourceList.h"
  27
  28#include <QComboBox>
  29#include <QLineEdit>
  30#include <QLabel>
  31#include <QCompleter>
  32#include <QStringListModel>
  33
  34
  35QHash< QString, QStringList > Tomahawk::EchonestControl::s_suggestCache = QHash< QString, QStringList >();
  36bool Tomahawk::EchonestControl::s_fetchingMoodsStylesAndGenres = false;
  37int Tomahawk::EchonestControl::s_stylePollCount = 0;
  38
  39
  40Tomahawk::EchonestControl::EchonestControl( const QString& selectedType, const QStringList& typeSelectors, QObject* parent )
  41    : DynamicControl ( selectedType.isEmpty() ? "Artist" : selectedType, typeSelectors, parent )
  42{
  43    setType( "echonest" );
  44    m_editingTimer.setInterval( 500 ); //timeout to edits
  45    m_editingTimer.setSingleShot( true );
  46    connect( &m_editingTimer, SIGNAL( timeout() ), this, SLOT( editTimerFired() ) );
  47
  48    m_delayedEditTimer.setInterval( 250 ); // additional timer for "just typing" without enter or focus change
  49    m_delayedEditTimer.setSingleShot( true );
  50    connect( &m_delayedEditTimer, SIGNAL( timeout() ), &m_editingTimer, SLOT( start() ) );
  51
  52    updateWidgets();
  53}
  54
  55
  56QWidget*
  57Tomahawk::EchonestControl::inputField()
  58{
  59    return m_input.data();
  60}
  61
  62
  63QWidget*
  64Tomahawk::EchonestControl::matchSelector()
  65{
  66    return m_match.data();
  67}
  68
  69
  70void
  71Tomahawk::EchonestControl::setSelectedType ( const QString& type )
  72{
  73    if( type != selectedType() )
  74    {
  75        if( !m_input.isNull() )
  76            delete m_input.data();
  77        if( !m_match.isNull() )
  78            delete m_match.data();
  79
  80        Tomahawk::DynamicControl::setSelectedType ( type );
  81        updateWidgets();
  82        updateData();
  83        //qDebug() << "Setting new type, set data to:" << m_data.first << m_data.second;
  84    }
  85}
  86
  87
  88Echonest::DynamicPlaylist::PlaylistParamData
  89Tomahawk::EchonestControl::toENParam() const
  90{
  91    if( m_overrideType != -1 )
  92    {
  93        Echonest::DynamicPlaylist::PlaylistParamData newData = m_data;
  94        newData.first = static_cast<Echonest::DynamicPlaylist::PlaylistParam>( m_overrideType );
  95        return newData;
  96    }
  97    return m_data;
  98}
  99
 100
 101QString
 102Tomahawk::EchonestControl::input() const
 103{
 104    return m_data.second.toString();
 105}
 106
 107
 108QString
 109Tomahawk::EchonestControl::match() const
 110{
 111    return m_matchData;
 112}
 113
 114
 115QString
 116Tomahawk::EchonestControl::matchString() const
 117{
 118    return m_matchString;
 119}
 120
 121
 122QString
 123Tomahawk::EchonestControl::summary() const
 124{
 125    if( m_summary.isEmpty() )
 126        const_cast< EchonestControl* >( this )->calculateSummary();
 127
 128    return m_summary;
 129}
 130
 131
 132void
 133Tomahawk::EchonestControl::setInput( const QString& input )
 134{
 135    m_data.second = input;
 136    updateWidgetsFromData();
 137}
 138
 139
 140void
 141Tomahawk::EchonestControl::setMatch( const QString& match )
 142{
 143    m_matchData = match;
 144    updateWidgetsFromData();
 145}
 146
 147
 148void
 149Tomahawk::EchonestControl::updateWidgets()
 150{
 151    if( !m_input.isNull() )
 152        delete m_input.data();
 153    if( !m_match.isNull() )
 154        delete m_match.data();
 155    m_overrideType = -1;
 156
 157    // make sure the widgets are the proper kind for the selected type, and hook up to their slots
 158    if( selectedType() == "Artist" )
 159    {
 160        m_currentType = Echonest::DynamicPlaylist::Artist;
 161
 162        QComboBox* match = new QComboBox();
 163        QLineEdit* input =  new QLineEdit();
 164
 165        match->addItem( tr( "Similar To" ), Echonest::DynamicPlaylist::ArtistRadioType );
 166        match->addItem( tr( "Limit To" ), Echonest::DynamicPlaylist::ArtistType );
 167        m_matchString = match->currentText();
 168        m_matchData = match->itemData( match->currentIndex() ).toString();
 169
 170        input->setPlaceholderText( tr( "Artist name" ) );
 171        input->setSizePolicy( QSizePolicy::Ignored, QSizePolicy::Fixed );
 172        input->setCompleter( new QCompleter( QStringList(), input ) );
 173        input->completer()->setCaseSensitivity( Qt::CaseInsensitive );
 174
 175        connect( match, SIGNAL( currentIndexChanged(int) ), this, SLOT( updateData() ) );
 176        connect( match, SIGNAL( currentIndexChanged(int) ), this, SIGNAL( changed() ) );
 177        connect( input, SIGNAL( textChanged(QString) ), this, SLOT( updateData() ) );
 178        connect( input, SIGNAL( editingFinished() ), this, SLOT( editingFinished() ) );
 179        connect( input, SIGNAL( textEdited( QString ) ), &m_editingTimer, SLOT( stop() ) );
 180        connect( input, SIGNAL( textEdited( QString ) ), &m_delayedEditTimer, SLOT( start() ) );
 181        connect( input, SIGNAL( textEdited( QString ) ), this, SLOT( artistTextEdited( QString ) ) );
 182
 183        match->hide();
 184        input->hide();
 185        m_match = QPointer< QWidget >( match );
 186        m_input = QPointer< QWidget >( input );
 187        m_data.first = m_currentType;
 188    }
 189    else if( selectedType() == "Artist Description" )
 190    {
 191        m_currentType = Echonest::DynamicPlaylist::Description;
 192
 193        QLabel* match = new QLabel( tr( "is" ) );
 194        QLineEdit* input =  new QLineEdit();
 195
 196        m_matchString = QString();
 197        m_matchData = QString::number( (int)Echonest::DynamicPlaylist::ArtistDescriptionType );
 198
 199        connect( input, SIGNAL( textChanged(QString) ), this, SLOT( updateData() ) );
 200        connect( input, SIGNAL( editingFinished() ), this, SLOT( editingFinished() ) );
 201        connect( input, SIGNAL( textEdited( QString ) ), &m_editingTimer, SLOT( stop() ) );
 202        connect( input, SIGNAL( textEdited( QString ) ), &m_delayedEditTimer, SLOT( start() ) );
 203
 204        match->hide();
 205        input->hide();
 206        m_match = QPointer< QWidget >( match );
 207        m_input = QPointer< QWidget >( input );
 208        m_data.first = m_currentType;
 209    }
 210    else if( selectedType() == "User Radio" )
 211    {
 212        m_currentType = Echonest::DynamicPlaylist::SourceCatalog;
 213
 214        QLabel* match = new QLabel( tr( "from user" ) );
 215        QComboBox* combo =  new QComboBox();
 216
 217        foreach( const QString& str, EchonestGenerator::userCatalogs() )
 218        {
 219            combo->addItem( str, EchonestGenerator::catalogId( str ) );
 220        }
 221
 222        if ( EchonestGenerator::userCatalogs().isEmpty() )
 223            combo->addItem( tr( "No users with Echo Nest Catalogs enabled. Try enabling option in Collection settings" ) );
 224
 225        if ( combo->findData( m_data.second ) < 0 )
 226            combo->setCurrentIndex( 0 );
 227
 228        m_matchString = match->text();
 229        m_matchData = match->text();
 230
 231
 232        connect( combo, SIGNAL( activated( int ) ), this, SLOT( updateData() ) );
 233        connect( combo, SIGNAL( activated( int ) ), this, SLOT( editingFinished() ) );
 234
 235        match->hide();
 236        combo->hide();
 237        m_match = QPointer< QWidget >( match );
 238        m_input = QPointer< QWidget >( combo );
 239    }
 240    else if( selectedType() == "Song" )
 241    {
 242        m_currentType = Echonest::DynamicPlaylist::SongId;
 243
 244        QLabel* match = new QLabel( tr( "similar to" ) );
 245        QLineEdit* input =  new QLineEdit();
 246        input->setPlaceholderText( tr( "Enter any combination of song name and artist here..." ) );
 247
 248        m_matchString = QString();
 249        m_matchData = QString::number( (int)Echonest::DynamicPlaylist::SongRadioType );
 250
 251        connect( input, SIGNAL( textChanged(QString) ), this, SLOT( updateData() ) );
 252        connect( input, SIGNAL( editingFinished() ), this, SLOT( editingFinished() ) );
 253        connect( input, SIGNAL( textEdited( QString ) ), &m_editingTimer, SLOT( stop() ) );
 254        connect( input, SIGNAL( textEdited( QString ) ), &m_delayedEditTimer, SLOT( start() ) );
 255
 256        match->hide();
 257        input->hide();
 258        m_match = QPointer< QWidget >( match );
 259        m_input = QPointer< QWidget >( input );
 260        m_data.first = m_currentType;
 261    }
 262    else if( selectedType() == "Variety" )
 263    {
 264        m_currentType = Echonest::DynamicPlaylist::Variety;
 265
 266        QLabel* match = new QLabel( tr( "is" ) );
 267        LabeledSlider* input = new LabeledSlider( tr( "Less" ), tr( "More" ) );
 268        input->slider()->setRange( 0, 10000 );
 269        input->slider()->setTickInterval( 1 );
 270        input->slider()->setTracking( false );
 271
 272        m_matchString = match->text();
 273        m_matchData = match->text();
 274
 275
 276        connect( input->slider(), SIGNAL( valueChanged( int ) ), this, SLOT( updateData() ) );
 277        connect( input->slider(), SIGNAL( valueChanged( int ) ), this, SLOT( editingFinished() ) );
 278
 279        match->hide();
 280        input->hide();
 281        m_match = QPointer< QWidget >( match );
 282        m_input = QPointer< QWidget >( input );
 283        m_data.first = m_currentType;
 284    }
 285    else if( selectedType() == "Adventurousness" )
 286    {
 287        m_currentType = Echonest::DynamicPlaylist::Adventurousness;
 288
 289        QLabel* match = new QLabel( tr( "is" ) );
 290        LabeledSlider* input = new LabeledSlider( tr( "Less" ), tr( "More" ) );
 291        input->slider()->setRange( 0, 10000 );
 292        input->slider()->setTickInterval( 1 );
 293        input->slider()->setTracking( false );
 294        input->slider()->setValue( 10000 * .2 );
 295
 296        m_matchString = match->text();
 297        m_matchData = match->text();
 298
 299
 300        connect( input->slider(), SIGNAL( valueChanged( int ) ), this, SLOT( updateData() ) );
 301        connect( input->slider(), SIGNAL( valueChanged( int ) ), this, SLOT( editingFinished() ) );
 302
 303        match->hide();
 304        input->hide();
 305        m_match = QPointer< QWidget >( match );
 306        m_input = QPointer< QWidget >( input );
 307        m_data.first = m_currentType;
 308    }
 309    else if( selectedType() == "Tempo" )
 310    {
 311        m_currentType = Echonest::DynamicPlaylist::MinTempo;
 312
 313        setupMinMaxWidgets( Echonest::DynamicPlaylist::MinTempo, Echonest::DynamicPlaylist::MaxTempo, tr( "0 BPM" ), tr( "500 BPM" ), 500 );
 314    }
 315    else if( selectedType() == "Duration" )
 316    {
 317        m_currentType = Echonest::DynamicPlaylist::MinDuration;
 318
 319        setupMinMaxWidgets( Echonest::DynamicPlaylist::MinDuration, Echonest::DynamicPlaylist::MaxDuration, tr( "0 secs" ), tr( "3600 secs" ), 3600 );
 320    }
 321    else if( selectedType() == "Loudness" )
 322    {
 323        m_currentType = Echonest::DynamicPlaylist::MinLoudness;
 324
 325        setupMinMaxWidgets( Echonest::DynamicPlaylist::MinLoudness, Echonest::DynamicPlaylist::MaxLoudness, tr( "-100 dB" ), tr( "100 dB" ), 100 );
 326        qobject_cast< LabeledSlider* >( m_input.data() )->slider()->setMinimum( -100 );
 327    }
 328    else if( selectedType() == "Danceability" )
 329    {
 330        m_currentType = Echonest::DynamicPlaylist::MinDanceability;
 331
 332        setupMinMaxWidgets( Echonest::DynamicPlaylist::MinDanceability, Echonest::DynamicPlaylist::MaxDanceability, tr( "Less" ), tr( "More" ), 10000 );
 333    }
 334    else if( selectedType() == "Energy" )
 335    {
 336        m_currentType = Echonest::DynamicPlaylist::MinEnergy;
 337
 338        setupMinMaxWidgets( Echonest::DynamicPlaylist::MinEnergy, Echonest::DynamicPlaylist::MaxEnergy, tr( "Less" ), tr( "More" ), 10000 );
 339    }
 340    else if( selectedType() == "Artist Familiarity" )
 341    {
 342        m_currentType = Echonest::DynamicPlaylist::ArtistMinFamiliarity;
 343
 344        setupMinMaxWidgets( Echonest::DynamicPlaylist::ArtistMinFamiliarity, Echonest::DynamicPlaylist::ArtistMaxFamiliarity, tr( "Less" ), tr( "More" ), 10000 );
 345    }
 346    else if( selectedType() == "Artist Hotttnesss" )
 347    {
 348        m_currentType = Echonest::DynamicPlaylist::ArtistMinHotttnesss;
 349
 350        setupMinMaxWidgets( Echonest::DynamicPlaylist::ArtistMinHotttnesss, Echonest::DynamicPlaylist::ArtistMaxHotttnesss, tr( "Less" ), tr( "More" ), 10000 );
 351    }
 352    else if( selectedType() == "Song Hotttnesss" )
 353    {
 354        m_currentType = Echonest::DynamicPlaylist::SongMinHotttnesss;
 355
 356        setupMinMaxWidgets( Echonest::DynamicPlaylist::SongMinHotttnesss, Echonest::DynamicPlaylist::SongMaxHotttnesss, tr( "Less" ), tr( "More" ), 10000 );
 357    }
 358    else if( selectedType() == "Latitude" )
 359    {
 360        m_currentType = Echonest::DynamicPlaylist::ArtistMinLatitude;
 361        QString deg = QString( QChar( 0x00B0 ) );
 362        setupMinMaxWidgets( Echonest::DynamicPlaylist::ArtistMinLatitude, Echonest::DynamicPlaylist::ArtistMaxLatitude, QString( tr( "-180%1" ) ).arg( deg ), QString( tr( "180%1" ) ).arg( deg ), 180 );
 363        qobject_cast< LabeledSlider* >( m_input.data() )->slider()->setMinimum( -180 );
 364    }
 365    else if( selectedType() == "Longitude" )
 366    {
 367        m_currentType = Echonest::DynamicPlaylist::ArtistMinLongitude;
 368        QString deg = QString( QChar( 0x00B0 ) );
 369        setupMinMaxWidgets( Echonest::DynamicPlaylist::ArtistMinLongitude, Echonest::DynamicPlaylist::ArtistMaxLongitude, QString( tr( "-180%1" ) ).arg( deg ), QString( tr( "180%1" ) ).arg( deg ), 180 );
 370        qobject_cast< LabeledSlider* >( m_input.data() )->slider()->setMinimum( -180 );
 371    }
 372    else if( selectedType() == "Mode" )
 373    {
 374        m_currentType = Echonest::DynamicPlaylist::Mode;
 375
 376        QLabel* match = new QLabel( tr( "is" ) );
 377        QComboBox* combo = new QComboBox;
 378        combo->addItem( tr( "Major" ), QString::number( 1 ) );
 379        combo->addItem( tr( "Minor" ), QString::number( 0 ) );
 380
 381        m_matchString = match->text();
 382        m_matchData = match->text();
 383
 384
 385        connect( combo, SIGNAL( activated( int ) ), this, SLOT( updateData() ) );
 386        connect( combo, SIGNAL( activated( int ) ), this, SLOT( editingFinished() ) );
 387
 388        match->hide();
 389        combo->hide();
 390        m_match = QPointer< QWidget >( match );
 391        m_input = QPointer< QWidget >( combo );
 392    }
 393    else if( selectedType() == "Key" )
 394    {
 395        m_currentType = Echonest::DynamicPlaylist::Key;
 396
 397        QLabel* match = new QLabel( tr( "is" ) );
 398        QComboBox* combo = new QComboBox;
 399        combo->addItem( tr( "C" ), QString::number( 0 ) );
 400        combo->addItem( tr( "C Sharp" ), QString::number( 1 ) );
 401        combo->addItem( tr( "D" ), QString::number( 2 ) );
 402        combo->addItem( tr( "E Flat" ), QString::number( 3 ) );
 403        combo->addItem( tr( "E" ), QString::number( 4 ) );
 404        combo->addItem( tr( "F" ), QString::number( 5 ) );
 405        combo->addItem( tr( "F Sharp" ), QString::number( 6 ) );
 406        combo->addItem( tr( "G" ), QString::number( 7 ) );
 407        combo->addItem( tr( "A Flat" ), QString::number( 8 ) );
 408        combo->addItem( tr( "A" ), QString::number( 9 ) );
 409        combo->addItem( tr( "B Flat" ), QString::number( 10 ) );
 410        combo->addItem( tr( "B" ), QString::number( 11 ) );
 411
 412        m_matchString = match->text();
 413        m_matchData = match->text();
 414
 415
 416        connect( combo, SIGNAL( activated( int ) ), this, SLOT( updateData() ) );
 417        connect( combo, SIGNAL( activated( int ) ), this, SLOT( editingFinished() ) );
 418
 419        match->hide();
 420        combo->hide();
 421        m_match = QPointer< QWidget >( match );
 422        m_input = QPointer< QWidget >( combo );
 423    }
 424    else if( selectedType() == "Sorting" )
 425    {
 426        m_currentType = Echonest::DynamicPlaylist::Sort;
 427
 428        QComboBox* match = new QComboBox();
 429        match->addItem( tr( "Ascending" ), 0 );
 430        match->addItem( tr( "Descending" ), 1 );
 431
 432        QComboBox* combo = new QComboBox;
 433        combo->addItem( tr( "Tempo" ), QString::number( Echonest::DynamicPlaylist::SortTempoAscending ) );
 434        combo->addItem( tr( "Duration" ), QString::number( Echonest::DynamicPlaylist::SortDurationAscending ) );
 435        combo->addItem( tr( "Loudness" ), QString::number( Echonest::DynamicPlaylist::SortLoudnessAscending ) );
 436        combo->addItem( tr( "Artist Familiarity" ), QString::number( Echonest::DynamicPlaylist::SortArtistFamiliarityAscending ) );
 437        combo->addItem( tr( "Artist Hotttnesss" ), QString::number( Echonest::DynamicPlaylist::SortArtistHotttnessAscending ) );
 438        combo->addItem( tr( "Song Hotttnesss" ), QString::number( Echonest::DynamicPlaylist::SortSongHotttnesssAscending ) );
 439        combo->addItem( tr( "Latitude" ), QString::number( Echonest::DynamicPlaylist::SortLatitudeAscending ) );
 440        combo->addItem( tr( "Longitude" ), QString::number( Echonest::DynamicPlaylist::SortLongitudeAscending ) );
 441        combo->addItem( tr( "Mode" ), QString::number( Echonest::DynamicPlaylist::SortModeAscending ) );
 442        combo->addItem( tr( "Key" ), QString::number( Echonest::DynamicPlaylist::SortKeyAscending ) );
 443        combo->addItem( tr( "Energy" ), QString::number( Echonest::DynamicPlaylist::SortEnergyAscending ) );
 444        combo->addItem( tr( "Danceability" ), QString::number( Echonest::DynamicPlaylist::SortDanceabilityAscending ) );
 445
 446        m_matchString = "Ascending"; // default
 447        m_matchData = Echonest::DynamicPlaylist::SortTempoAscending;
 448
 449        connect( match, SIGNAL( activated( int ) ), this, SLOT( updateData() ) );
 450        connect( match, SIGNAL( activated( int ) ), this, SLOT( editingFinished() ) );
 451        connect( combo, SIGNAL( activated( int ) ), this, SLOT( updateData() ) );
 452        connect( combo, SIGNAL( activated( int ) ), this, SLOT( editingFinished() ) );
 453
 454        match->hide();
 455        combo->hide();
 456        m_match = QPointer< QWidget >( match );
 457        m_input = QPointer< QWidget >( combo );
 458    }
 459    else if( selectedType() == "Mood" || selectedType() == "Style" || selectedType() == "Genre" )
 460    {
 461        if( selectedType() == "Mood" )
 462            m_currentType = Echonest::DynamicPlaylist::Mood;
 463        else if ( selectedType() == "Style" )
 464            m_currentType = Echonest::DynamicPlaylist::Style;
 465        else
 466            m_currentType = Echonest::DynamicPlaylist::Genre;
 467
 468        QLabel* match = new QLabel( tr( "is" ) );
 469
 470        QComboBox* combo = new QComboBox;
 471
 472        m_matchString = match->text();
 473        m_matchData = match->text();
 474
 475
 476        connect( combo, SIGNAL( activated( int ) ), this, SLOT( updateData() ) );
 477        connect( combo, SIGNAL( activated( int ) ), this, SLOT( editingFinished() ) );
 478
 479        match->hide();
 480        combo->hide();
 481        m_match = QPointer< QWidget >( match );
 482        m_input = QPointer< QWidget >( combo );
 483
 484        insertMoodsStylesAndGenres();
 485    }
 486    else if( selectedType() == "Song Type" )
 487    {
 488        m_currentType = Echonest::DynamicPlaylist::SongType;
 489
 490        QComboBox* match = new QComboBox();
 491        match->addItem( tr( "is" ), 1 );
 492        match->addItem( tr( "is not" ), 0 );
 493
 494        QComboBox* combo = new QComboBox();
 495        combo->addItem( tr( "Studio", "Song type: The song was recorded in a studio." ), "studio" );
 496        combo->addItem( tr( "Live", "Song type: The song was a life performance." ), "live" );
 497        combo->addItem( tr( "Acoustic", "Song type" ), "acoustic" );
 498        combo->addItem( tr( "Electric", "Song type" ), "electric" );
 499        combo->addItem( tr( "Christmas", "Song type: A christmas song" ), "christmas" );
 500
 501        connect( match, SIGNAL( activated( int ) ), this, SLOT( updateData() ) );
 502        connect( match, SIGNAL( activated( int ) ), this, SLOT( editingFinished() ) );
 503        connect( combo, SIGNAL( activated( int ) ), this, SLOT( updateData() ) );
 504        connect( combo, SIGNAL( activated( int ) ), this, SLOT( editingFinished() ) );
 505
 506        m_matchString = "is";
 507        m_matchData = 1;
 508
 509        m_match = QPointer< QWidget >( match );
 510        m_input = QPointer< QWidget >( combo );
 511    }
 512    else if( selectedType() == "Distribution" )
 513    {
 514        m_currentType = Echonest::DynamicPlaylist::Distribution;
 515
 516        QLabel* match = new QLabel( tr( "is" ) );
 517
 518        QComboBox* combo = new QComboBox();
 519        combo->addItem( tr( "Focused", "Distribution: Songs that are tightly clustered around the seeds" ), "focused" );
 520        combo->addItem( tr( "Wandering", "Distribution: Songs from a broader range of artists"), "wandering" );
 521
 522        m_matchString = match->text();
 523        m_matchData = match->text();
 524
 525        connect( combo, SIGNAL( activated( int ) ), this, SLOT( updateData() ) );
 526        connect( combo, SIGNAL( activated( int ) ), this, SLOT( editingFinished() ) );
 527
 528        m_match = QPointer<QWidget>( match );
 529        m_input = QPointer<QWidget>( combo );
 530    }
 531    else if( selectedType() == "Genre Preset" )
 532    {
 533        m_currentType = Echonest::DynamicPlaylist::GenrePreset;
 534
 535        QComboBox* match = new QComboBox();
 536        match->addItem( tr( "Classics", "Genre preset: songs intended to introduce the genre to a novice listener" ), "core" );
 537        match->addItem( tr( "Popular", "Genre preset: most popular songs being played in the genre today" ), "in_rotation" );
 538        match->addItem( tr( "Emerging", "Genre preset: songs that are more popular than expected, but which are unfamiliar to most listeners" ), "emerging" );
 539
 540        QComboBox* combo = new QComboBox();
 541        combo->addItem( tr( "Best", "Genre preset: optimal collection of songs" ), "best" );
 542        combo->addItem( tr( "Mix", "Genre preset: a varying collection of songs" ), "shuffled" );
 543
 544        m_matchString = match->currentText();
 545        m_matchData = match->itemData( match->currentIndex() ).toString();
 546
 547        connect( match, SIGNAL( activated( int ) ), this, SLOT( updateData() ) );
 548        connect( match, SIGNAL( activated( int ) ), this, SLOT( editingFinished() ) );
 549        connect( combo, SIGNAL( activated( int ) ), this, SLOT( updateData() ) );
 550        connect( combo, SIGNAL( activated( int ) ), this, SLOT( editingFinished() ) );
 551
 552        m_match = QPointer<QWidget>( match );
 553        m_input = QPointer<QWidget>( combo );
 554    }
 555    else
 556    {
 557        m_match = QPointer<QWidget>( new QWidget );
 558        m_input = QPointer<QWidget>( new QWidget );
 559    }
 560    updateData();
 561    calculateSummary();
 562}
 563
 564
 565void
 566Tomahawk::EchonestControl::setupMinMaxWidgets( Echonest::DynamicPlaylist::PlaylistParam min, Echonest::DynamicPlaylist::PlaylistParam max, const QString& leftL, const QString& rightL, int maxRange )
 567{
 568    QComboBox* match = new QComboBox;
 569    match->addItem( tr( "At Least" ), min );
 570    match->addItem( tr( "At Most" ), max );
 571
 572    LabeledSlider* input = new LabeledSlider( leftL, rightL );
 573    input->slider()->setRange( 0, maxRange );
 574    input->slider()->setTickInterval( 1 );
 575    input->slider()->setTracking( false );
 576
 577    m_matchString = match->currentText();
 578    m_matchData = match->itemData( match->currentIndex() ).toString();
 579
 580    connect( match, SIGNAL( activated( int ) ), this, SLOT( updateData() ) );
 581    connect( match, SIGNAL( activated( int ) ), this, SLOT( editingFinished() ) );
 582    connect( input->slider(), SIGNAL( valueChanged( int ) ), this, SLOT( updateData() ) );
 583    connect( input->slider(), SIGNAL( valueChanged( int ) ), this, SLOT( editingFinished() ) );
 584
 585    match->hide();
 586    input->hide();
 587    m_match = QPointer< QWidget >( match );
 588    m_input = QPointer< QWidget >( input );
 589}
 590
 591
 592void
 593Tomahawk::EchonestControl::updateData()
 594{
 595    if( selectedType() == "Artist" )
 596    {
 597        QComboBox* combo = qobject_cast<QComboBox*>( m_match.data() );
 598        if( combo )
 599        {
 600            m_matchString = combo->currentText();
 601            m_matchData = combo->itemData( combo->currentIndex() ).toString();
 602        }
 603        QLineEdit* edit = qobject_cast<QLineEdit*>( m_input.data() );
 604        if( edit && !edit->text().isEmpty() )
 605        {
 606            m_data.first = m_currentType;
 607            m_data.second = edit->text();
 608        }
 609    }
 610    else if( selectedType() == "Artist Description" || selectedType() == "Song" )
 611    {
 612        QLineEdit* edit = qobject_cast<QLineEdit*>( m_input.data() );
 613        if( edit && !edit->text().isEmpty() )
 614        {
 615            m_data.first = m_currentType;
 616            m_data.second = edit->text();
 617        }
 618    }
 619    else if( selectedType() == "Variety" || selectedType() == "Adventurousness" )
 620    {
 621        LabeledSlider* s = qobject_cast<LabeledSlider*>( m_input.data() );
 622        if( s )
 623        {
 624            m_data.first = m_currentType;
 625            m_data.second = (qreal)s->slider()->value() / 10000.0;
 626        }
 627    }
 628    else if( selectedType() == "Tempo" || selectedType() == "Duration" || selectedType() == "Loudness" || selectedType() == "Latitude" || selectedType() == "Longitude" )
 629    {
 630        updateFromComboAndSlider();
 631    }
 632    else if( selectedType() == "Danceability" || selectedType() == "Energy" || selectedType() == "Artist Familiarity" || selectedType() == "Artist Hotttnesss" || selectedType() == "Song Hotttnesss" )
 633    {
 634        updateFromComboAndSlider( true );
 635    }
 636    else if( selectedType() == "Mode" || selectedType() == "Key" || selectedType() == "Mood" || selectedType() == "Style" || selectedType() == "Genre" || selectedType() == "User Radio" )
 637    {
 638        updateFromLabelAndCombo();
 639    }
 640    else if( selectedType() == "Sorting" )
 641    {
 642        QComboBox* match = qobject_cast<QComboBox*>( m_match.data() );
 643        QComboBox* input = qobject_cast< QComboBox* >( m_input.data() );
 644        if( match && input ) {
 645            m_matchString = match->currentText();
 646            m_matchData = match->itemData( match->currentIndex() ).toString();
 647
 648            // what a HACK
 649            int enumVal = input->itemData( input->currentIndex() ).toInt() + m_matchData.toInt();
 650            m_data.first = Echonest::DynamicPlaylist::Sort;
 651            m_data.second = enumVal;
 652//             qDebug() << "SAVING" << input->currentIndex() << "AS" << enumVal << "(" << input->itemData( input->currentIndex() ).toInt() << "+" << m_matchData.toInt() << ")";
 653        }
 654    }
 655    else if( selectedType() == "Song Type" )
 656    {
 657        QComboBox* match = qobject_cast<QComboBox*>( m_match.data() );
 658        QComboBox* combo = qobject_cast< QComboBox* >( m_input.data() );
 659        if ( match && combo )
 660        {
 661            m_matchString = match->currentText();
 662            m_matchData = match->itemData( match->currentIndex() ).toString();
 663
 664            QString songType = combo->itemData( combo->currentIndex() ).toString();
 665            if ( match->currentIndex() == 1 )
 666               songType.append( ":false" );
 667
 668            m_data.first = Echonest::DynamicPlaylist::SongType;
 669            m_data.second = songType;
 670        }
 671
 672    }
 673    else if( selectedType() == "Distribution" )
 674    {
 675        QComboBox* combo = qobject_cast< QComboBox* >( m_input.data() );
 676        if ( combo )
 677        {
 678            m_data.first = Echonest::DynamicPlaylist::Distribution;
 679            m_data.second = combo->itemData( combo->currentIndex() ).toString();
 680        }
 681    }
 682    else if( selectedType() == "Genre Preset" )
 683    {
 684        QComboBox* match = qobject_cast<QComboBox*>( m_match.data() );
 685        QComboBox* combo = qobject_cast< QComboBox* >( m_input.data() );
 686        if ( match && combo )
 687        {
 688            m_matchString = match->currentText();
 689            m_matchData = match->itemData( match->currentIndex() ).toString();
 690
 691            QString presetType = m_matchData.append( "_%1" );
 692            presetType = presetType.arg( combo->itemData( combo->currentIndex() ).toString() );
 693
 694            m_data.first = Echonest::DynamicPlaylist::GenrePreset;
 695            m_data.second = presetType;
 696        }
 697    }
 698    calculateSummary();
 699}
 700
 701
 702void
 703Tomahawk::EchonestControl::updateFromComboAndSlider( bool smooth )
 704{
 705    QComboBox* combo = qobject_cast<QComboBox*>( m_match.data() );
 706    if( combo )
 707    {
 708        m_matchString = combo->currentText();
 709        m_matchData = combo->itemData( combo->currentIndex() ).toString();
 710
 711        LabeledSlider* ls = qobject_cast<LabeledSlider*>( m_input.data() );
 712        if( ls && ls->slider() )
 713        {
 714            m_data.first = static_cast< Echonest::DynamicPlaylist::PlaylistParam >( combo->itemData( combo->currentIndex() ).toInt() );
 715            m_data.second = ls->slider()->value() / ( smooth ? 10000. : 1.0 );
 716        }
 717    }
 718}
 719
 720
 721void
 722Tomahawk::EchonestControl::updateFromLabelAndCombo()
 723{
 724    QComboBox* s = qobject_cast<QComboBox*>( m_input.data() );
 725    if( s )
 726    {
 727        m_data.first = m_currentType;
 728        m_data.second = s->itemData( s->currentIndex() );
 729    }
 730}
 731
 732
 733// fills in the current widget with the data from json or dbcmd (m_data.second and m_matchData)
 734void
 735Tomahawk::EchonestControl::updateWidgetsFromData()
 736{
 737    if( selectedType() == "Artist" )
 738    {
 739        QComboBox* combo = qobject_cast<QComboBox*>( m_match.data() );
 740        if( combo )
 741            combo->setCurrentIndex( combo->findData( m_matchData ) );
 742        QLineEdit* edit = qobject_cast<QLineEdit*>( m_input.data() );
 743        if( edit )
 744            edit->setText( m_data.second.toString() );
 745    }
 746    else if( selectedType() == "Artist Description" || selectedType() == "Song" )
 747    {
 748        QLineEdit* edit = qobject_cast<QLineEdit*>( m_input.data() );
 749        if( edit )
 750            edit->setText( m_data.second.toString() );
 751    }
 752    else if ( selectedType() == "User Radio"  )
 753    {
 754        QComboBox* combo = qobject_cast< QComboBox* >( m_input.data() );
 755        if ( combo )
 756        {
 757            combo->clear();
 758
 759            foreach( const QString& str, EchonestGenerator::userCatalogs() )
 760            {
 761                combo->addItem( str, EchonestGenerator::catalogId( str ) );
 762            }
 763
 764            if ( EchonestGenerator::userCatalogs().isEmpty() )
 765                combo->addItem( tr( "No users with Echo Nest Catalogs enabled. Try enabling option in Collection settings" ) );
 766
 767            if ( combo->findData( m_data.second ) < 0 )
 768                combo->setCurrentIndex( 0 );
 769
 770            combo->setCurrentIndex( combo->findData( m_data.second ) );
 771        }
 772    }
 773    else if( selectedType() == "Variety" || selectedType() == "Adventurousness" )
 774    {
 775        LabeledSlider* s = qobject_cast<LabeledSlider*>( m_input.data() );
 776        if( s )
 777            s->slider()->setValue( m_data.second.toDouble() * 10000 );
 778    }
 779    else if( selectedType() == "Tempo" || selectedType() == "Duration" || selectedType() == "Loudness"  || selectedType() == "Latitude" || selectedType() == "Longitude" )
 780    {
 781        updateToComboAndSlider();
 782    }
 783    else if( selectedType() == "Danceability" || selectedType() == "Energy" || selectedType() == "Artist Familiarity" || selectedType() == "Artist Hotttnesss" || selectedType() == "Song Hotttnesss" )
 784    {
 785        updateToComboAndSlider( true );
 786    }
 787    else if( selectedType() == "Mode" || selectedType() == "Key" || selectedType() == "Mood" || selectedType() == "Style" || selectedType() == "Genre" )
 788    {
 789        updateToLabelAndCombo();
 790    }
 791    else if( selectedType() == "Sorting" )
 792    {
 793        QComboBox* match = qobject_cast<QComboBox*>( m_match.data() );
 794        QComboBox* input = qobject_cast< QComboBox* >( m_input.data() );
 795        if( match && input ) {
 796            match->setCurrentIndex( match->findData( m_matchData ));
 797
 798            // HACK alert. if it's odd, subtract 1
 799            int val = ( m_data.second.toInt() - ( m_data.second.toInt() % 2 ) ) / 2;
 800            input->setCurrentIndex( val );
 801            //qDebug() << "LOADING" << m_data.second.toInt() << "AS" << val;
 802        }
 803    }
 804    else if( selectedType() == "Song Type" )
 805    {
 806        QComboBox* match = qobject_cast<QComboBox*>( m_match.data() );
 807        QComboBox* combo = qobject_cast< QComboBox* >( m_input.data() );
 808        if ( match && combo ) {
 809            match->setCurrentIndex( match->findData( m_matchData ));
 810
 811            QString songType = m_data.second.toString().split( ":" ).at( 0 );
 812            combo->setCurrentIndex( combo->findData( songType ) );
 813        }
 814    }
 815    else if( selectedType() == "Distribution" )
 816    {
 817        QComboBox* combo = qobject_cast< QComboBox* >( m_input.data() );
 818        if ( combo ) {
 819            combo->setCurrentIndex( combo->findData( m_data.second.toString() ));
 820        }
 821    }
 822    calculateSummary();
 823}
 824
 825
 826void
 827Tomahawk::EchonestControl::updateToComboAndSlider( bool smooth )
 828{
 829    QComboBox* combo = qobject_cast<QComboBox*>( m_match.data() );
 830    if( combo )
 831        combo->setCurrentIndex( combo->findData( m_matchData ) );
 832    LabeledSlider* ls = qobject_cast<LabeledSlider*>( m_input.data() );
 833    if( ls )
 834        ls->slider()->setValue( m_data.second.toDouble() * ( smooth ? 10000. : 1 ) );
 835}
 836
 837
 838void
 839Tomahawk::EchonestControl::updateToLabelAndCombo()
 840{
 841    QComboBox* s = qobject_cast< QComboBox* >( m_input.data() );
 842    if( s )
 843        s->setCurrentIndex( s->findData( m_data.second ) );
 844}
 845
 846
 847void
 848Tomahawk::EchonestControl::editingFinished()
 849{
 850//    qDebug() << Q_FUNC_INFO;
 851    m_editingTimer.start();
 852}
 853
 854
 855void
 856Tomahawk::EchonestControl::editTimerFired()
 857{
 858    // make sure it's really changed
 859    if( m_cacheData != m_data.second ) { // new, so emit changed
 860        emit changed();
 861    }
 862
 863    m_cacheData = m_data.second;
 864}
 865
 866
 867void
 868Tomahawk::EchonestControl::artistTextEdited( const QString& text )
 869{
 870    // if the user is editing an artist field, try to help him out and suggest from echonest
 871    QLineEdit* l = qobject_cast<QLineEdit*>( m_input.data() );
 872    Q_ASSERT( l );
 873    Q_UNUSED( l );
 874//     l->setCompleter( new QCompleter( this ) ); // clear
 875
 876    foreach( QNetworkReply* r, m_suggestWorkers ) {
 877        r->abort();
 878        r->deleteLater();
 879    }
 880    m_suggestWorkers.clear();
 881
 882    if( !text.isEmpty() ) {
 883        if( s_suggestCache.contains( text ) ) {
 884            addArtistSuggestions( s_suggestCache[ text ] );
 885        } else { // gotta look it up
 886            QNetworkReply* r = Echonest::Artist::suggest( text );
 887            qDebug() << "Asking echonest for suggestions to help our completion..." << r->url().toString();
 888            r->setProperty( "curtext", text );
 889
 890            m_suggestWorkers.insert( r );
 891            connect( r, SIGNAL( finished() ), this, SLOT( suggestFinished() ) );
 892        }
 893    }
 894}
 895
 896
 897void
 898Tomahawk::EchonestControl::suggestFinished()
 899{
 900    qDebug() << Q_FUNC_INFO;
 901    QNetworkReply* r = qobject_cast< QNetworkReply* >( sender() );
 902    Q_ASSERT( r );
 903    QLineEdit* l = qobject_cast<QLineEdit*>( m_input.data() );
 904    Q_ASSERT( l );
 905
 906    m_suggestWorkers.remove( r );
 907
 908    if( r->error() != QNetworkReply::NoError )
 909        return;
 910
 911    QString origText = r->property( "curtext" ).toString();
 912    if( origText != l->text() ) { // user might have kept on typing, then ignore
 913        qDebug() << "Text changed meanwhile, stopping suggestion parsing";
 914        return;
 915    }
 916
 917    QStringList suggestions;
 918    try {
 919        Echonest::Artists artists = Echonest::Artist::parseSuggest( r );
 920        foreach( const Echonest::Artist& artist, artists )
 921            suggestions << artist.name();
 922    } catch( Echonest::ParseError& e ) {
 923        qWarning() << "libechonest failed to parse this artist/suggest call..." << e.errorType() << e.what();
 924        return;
 925    }
 926
 927    s_suggestCache[ origText ] = suggestions;
 928    addArtistSuggestions( suggestions );
 929}
 930
 931
 932void
 933Tomahawk::EchonestControl::addArtistSuggestions( const QStringList& suggestions )
 934{
 935    // if the user is editing an artist field, try to help him out and suggest from echonest
 936    QLineEdit* l = qobject_cast<QLineEdit*>( m_input.data() );
 937    Q_ASSERT( l );
 938
 939    l->completer()->setModel( new QStringListModel( suggestions, l->completer() ) );
 940    l->completer()->complete();
 941}
 942
 943
 944void
 945Tomahawk::EchonestControl::calculateSummary()
 946{
 947    // turns the current control into an english phrase suitable for embedding into a sentence summary
 948    QString summary;
 949    if ( selectedType() == "Artist" )
 950    {
 951        // magic char is used by EchonestGenerator to split the prefix from the artist name
 952        if ( static_cast< Echonest::DynamicPlaylist::ArtistTypeEnum >( m_matchData.toInt() ) == Echonest::DynamicPlaylist::ArtistType )
 953            summary = tr( "only by ~%1" ).arg( m_data.second.toString() );
 954        else if ( static_cast< Echonest::DynamicPlaylist::ArtistTypeEnum >( m_matchData.toInt() ) == Echonest::DynamicPlaylist::ArtistRadioType )
 955            summary = tr( "similar to ~%1" ).arg( m_data.second.toString() );
 956    }
 957    else if( selectedType() == "Artist Description" )
 958    {
 959        summary = tr( "with genre ~%1" ).arg( m_data.second.toString() );
 960    }
 961    else if( selectedType() == "User Radio" )
 962    {
 963        QComboBox* b = qobject_cast< QComboBox* >( m_input.data() );
 964        if ( b )
 965        {
 966            if ( b->currentText().isEmpty() || b->itemData( b->currentIndex() ).isNull() )
 967                summary = tr( "from no one" );
 968            else
 969            {
 970                QString subSum;
 971                if ( b->currentText() == tr( "You" ) )
 972                    summary = tr( "from my radio" );
 973                else
 974                    summary = tr( "from %1 radio" ).arg( b->currentText() );
 975            }
 976        }
 977        else
 978            summary = tr( "from no one" );
 979    }
 980    else if ( selectedType() == "Artist Description" || selectedType() == "Song" )
 981    {
 982        summary = tr( "similar to ~%1" ).arg( m_data.second.toString() );
 983    }
 984    else if ( selectedType() == QT_TR_NOOP( "Variety" ) || selectedType() == QT_TR_NOOP( "Danceability" ) ||
 985              selectedType() == QT_TR_NOOP( "Artist Hotttnesss" ) || selectedType() == QT_TR_NOOP( "Energy" ) ||
 986              selectedType() == QT_TR_NOOP( "Artist Familiarity" ) || selectedType() == QT_TR_NOOP( "Song Hotttnesss" ) ||
 987              selectedType() == QT_TR_NOOP( "Adventurousness" ) )
 988    {
 989        QString modifier;
 990        qreal sliderVal = m_data.second.toReal();
 991        // divide into avpproximate chunks
 992        if( 0.0 <= sliderVal && sliderVal < 0.2 )
 993            modifier = tr( "very low" );
 994        else if( 0.2 <= sliderVal && sliderVal < 0.4 )
 995            modifier = tr( "low" );
 996        else if( 0.4 <= sliderVal && sliderVal < 0.6 )
 997            modifier = tr( "moderate" );
 998        else if( 0.6 <= sliderVal && sliderVal < 0.8 )
 999            modifier = tr( "high" );
1000        else if( 0.8 <= sliderVal && sliderVal <= 1 )
1001            modifier = tr( "very high" );
1002        summary = tr( "with %1 %2" ).arg( modifier ).arg( tr( selectedType().toStdString().c_str() ) );
1003    }
1004    else if ( selectedType() == "Tempo" )
1005    {
1006        summary = tr( "about %1 BPM" ).arg( m_data.second.toString() );
1007    }
1008    else if ( selectedType() == "Duration" )
1009    {
1010        summary = tr( "about %n minute(s) long", "", m_data.second.toInt() / 60 );
1011    }
1012    else if ( selectedType() == "Loudness" )
1013    {
1014        summary = tr( "about %1 dB" ).arg( m_data.second.toString() );
1015    }
1016    else if ( selectedType() == "Latitude" || selectedType() == "Longitude" )
1017    {
1018        summary = tr( "at around %1%2 %3" ).arg( m_data.second.toString() ).arg( QString( QChar( 0x00B0 ) ) ).arg( tr( selectedType().toStdString().c_str() ) );
1019    }
1020    else if ( selectedType() == "Key" )
1021    {
1022        Q_ASSERT( !m_input.isNull() );
1023        Q_ASSERT( qobject_cast< QComboBox* >( m_input.data() ) );
1024        QString keyName = qobject_cast< QComboBox* >( m_input.data() )->currentText().toLower();
1025        summary = tr( "in %1" ).arg( keyName );
1026    }
1027    else if ( selectedType() == "Mode" )
1028    {
1029        Q_ASSERT( !m_input.isNull() );
1030        Q_ASSERT( qobject_cast< QComboBox* >( m_input.data() ) );
1031        QString modeName = qobject_cast< QComboBox* >( m_input.data() )->currentText().toLower();
1032        summary = tr( "in a %1 key" ).arg( modeName );
1033    }
1034    else if ( selectedType() == "Sorting" )
1035    {
1036        Q_ASSERT( !m_input.isNull() );
1037        Q_ASSERT( qobject_cast< QComboBox* >( m_input.data() ) );
1038        QString sortType = qobject_cast< QComboBox* >( m_input.data() )->currentText().toLower();
1039
1040        Q_ASSERT( !m_match.isNull() );
1041        Q_ASSERT( qobject_cast< QComboBox* >( m_match.data() ) );
1042        QString ascdesc = qobject_cast< QComboBox* >( m_match.data() )->currentText().toLower();
1043
1044        summary = tr( "sorted in %1 %2 order" ).arg( ascdesc ).arg( sortType );
1045    }
1046    else if ( selectedType() == "Mood" )
1047    {
1048        Q_ASSERT( !m_input.isNull() );
1049        Q_ASSERT( qobject_cast< QComboBox* >( m_input.data() ) );
1050        QString text = qobject_cast< QComboBox* >( m_input.data() )->currentText().toLower();
1051        summary = tr( "with a %1 mood" ).arg( text );
1052    }
1053    else if ( selectedType() == "Style" )
1054    {
1055        Q_ASSERT( !m_input.isNull() );
1056        Q_ASSERT( qobject_cast< QComboBox* >( m_input.data() ) );
1057        QString text = qobject_cast< QComboBox* >( m_input.data() )->currentText().toLower();
1058        summary = tr( "in a %1 style" ).arg( text );
1059    }
1060    else if ( selectedType() == "Genre" )
1061    {
1062        Q_ASSERT( !m_input.isNull() );
1063        Q_ASSERT( qobject_cast< QComboBox* >( m_input.data() ) );
1064        QString text = qobject_cast< QComboBox* >( m_input.data() )->currentText().toLower();
1065        summary = tr( "where genre is %1" ).arg( text );
1066    }
1067    else if ( selectedType() == "Song Type" )
1068    {
1069        Q_ASSERT( !m_input.isNull() );
1070        Q_ASSERT( qobject_cast< QComboBox* >( m_input.data() ) );
1071        QString text = qobject_cast< QComboBox* >( m_input.data() )->currentText();
1072
1073
1074        Q_ASSERT( !m_match.isNull() );
1075        Q_ASSERT( qobject_cast< QComboBox* >( m_match.data() ) );
1076        QComboBox* combo = qobject_cast< QComboBox* >( m_match.data() );
1077        if ( combo->currentIndex() == 0 )
1078            summary = tr( "where song type is %1" ).arg( text );
1079        else
1080            summary = tr( "where song type is not %1" ).arg( text );
1081    }
1082    else if ( selectedType() == "Distribution" )
1083    {
1084        Q_ASSERT( !m_input.isNull() );
1085        Q_ASSERT( qobject_cast< QComboBox* >( m_input.data() ) );
1086        QString text = qobject_cast< QComboBox* >( m_input.data() )->currentText().toLower();
1087
1088        summary = tr( "with a %1 distribution" ).arg( text );
1089    }
1090    else if( selectedType() == "Genre Preset" )
1091    {
1092        Q_ASSERT( !m_input.isNull() );
1093        Q_ASSERT( qobject_cast< QComboBox* >( m_input.data() ) );
1094        QComboBox* input = qobject_cast< QComboBox* >( m_input.data() );
1095
1096        Q_ASSERT( !m_match.isNull() );
1097        Q_ASSERT( qobject_cast< QComboBox* >( m_match.data() ) );
1098        QComboBox* match = qobject_cast< QComboBox* >( m_match.data() );
1099
1100        summary = tr( "preset to %1 collection of %2 genre songs" );
1101        if ( input->itemData( input->currentIndex() ) == "best" )
1102            summary = summary.arg( tr( "an optimal" ) );
1103        else
1104            summary = summary.arg( tr( "a mixed" ) );
1105
1106        if ( match->itemData( match->currentIndex() ) == "core" )
1107        {
1108            summary = summary.arg( tr( "classic" ) );
1109        }
1110        else
1111        {
1112            if ( match->itemData( match->currentIndex() ) == "in_rotation" )
1113                summary = summary.arg( tr( "popular" ) );
1114            else
1115                summary = summary.arg( tr( "emerging" ) );
1116        }
1117    }
1118
1119    m_summary = summary;
1120}
1121
1122
1123void
1124Tomahawk::EchonestControl::checkForMoodsStylesOrGenresFetched()
1125{
1126    s_fetchingMoodsStylesAndGenres = false;
1127    if( selectedType() == "Mood" || selectedType() == "Style" || selectedType() == "Genre" ) {
1128        QComboBox* cb = qobject_cast< QComboBox* >( m_input.data() );
1129        if( cb && cb->count() == 0 ) { // got nothing, so lets populate
1130            if( insertMoodsStylesAndGenres() )
1131                updateWidgetsFromData();
1132        }
1133    }
1134}
1135
1136
1137bool
1138Tomahawk::EchonestControl::insertMoodsStylesAndGenres()
1139{
1140    QStringList src;
1141    if ( selectedType() == "Mood" )
1142    {
1143        src = EchonestGenerator::moods();
1144    }
1145    else if ( selectedType() == "Style" )
1146    {
1147        src = EchonestGenerator::styles();
1148    }
1149    else
1150    {
1151        src = EchonestGenerator::genres();
1152    }
1153
1154    QComboBox* combo = qobject_cast< QComboBox* >( m_input.data() );
1155    if( !combo )
1156        return false;
1157
1158    foreach( const QString& item, src ) {
1159        combo->addItem( item, item );
1160    }
1161
1162    if( src.isEmpty() && !combo->count() )
1163    {
1164        if( s_stylePollCount <= 20 && !s_fetchingMoodsStylesAndGenres )
1165        { // try for 20s to get the styles...
1166            s_fetchingMoodsStylesAndGenres = true;
1167            QTimer::singleShot( 1000, this, SLOT( checkForMoodsStylesOrGenresFetched() ) );
1168        }
1169        s_stylePollCount++;
1170        return false;
1171    }
1172
1173    return true;
1174}