diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp b/src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp index d5bc190c9..0a8919ac9 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestControl.cpp @@ -26,6 +26,8 @@ #include #include #include "EchonestGenerator.h" +#include +#include Tomahawk::EchonestControl::EchonestControl( const QString& selectedType, const QStringList& typeSelectors, QObject* parent ) @@ -146,6 +148,8 @@ Tomahawk::EchonestControl::updateWidgets() input->setPlaceholderText( "Artist name" ); input->setSizePolicy( QSizePolicy::Ignored, QSizePolicy::Fixed ); + input->setCompleter( new QCompleter( QStringList(), input ) ); + input->completer()->setCaseSensitivity( Qt::CaseInsensitive ); connect( match, SIGNAL( currentIndexChanged(int) ), this, SLOT( updateData() ) ); connect( match, SIGNAL( currentIndexChanged(int) ), this, SIGNAL( changed() ) ); @@ -153,6 +157,7 @@ Tomahawk::EchonestControl::updateWidgets() connect( input, SIGNAL( editingFinished() ), this, SLOT( editingFinished() ) ); connect( input, SIGNAL( textEdited( QString ) ), &m_editingTimer, SLOT( stop() ) ); connect( input, SIGNAL( textEdited( QString ) ), &m_delayedEditTimer, SLOT( start() ) ); + connect( input, SIGNAL( textEdited( QString ) ), this, SLOT( artistTextEdited( QString ) ) ); match->hide(); input->hide(); @@ -552,6 +557,78 @@ Tomahawk::EchonestControl::editTimerFired() m_cacheData = m_data.second; } +void +Tomahawk::EchonestControl::artistTextEdited( const QString& text ) +{ + // if the user is editing an artist field, try to help him out and suggest from echonest + QLineEdit* l = qobject_cast( m_input.data() ); + Q_ASSERT( l ); +// l->setCompleter( new QCompleter( this ) ); // clear + + foreach( QNetworkReply* r, m_suggestWorkers ) { + r->abort(); + r->deleteLater(); + } + m_suggestWorkers.clear(); + + if( !text.isEmpty() ) { + if( m_suggestCache.contains( text ) ) { + addArtistSuggestions( m_suggestCache[ text ] ); + } else { // gotta look it up + QNetworkReply* r = Echonest::Artist::suggest( text ); + qDebug() << "Asking echonest for suggestions to help our completion..." << r->url().toString(); + r->setProperty( "curtext", text ); + + m_suggestWorkers.insert( r ); + connect( r, SIGNAL( finished() ), this, SLOT( suggestFinished() ) ); + } + } +} + +void +Tomahawk::EchonestControl::suggestFinished() +{ + qDebug() << Q_FUNC_INFO; + QNetworkReply* r = qobject_cast< QNetworkReply* >( sender() ); + Q_ASSERT( r ); + QLineEdit* l = qobject_cast( m_input.data() ); + Q_ASSERT( l ); + + m_suggestWorkers.remove( r ); + + if( r->error() != QNetworkReply::NoError ) + return; + + QString origText = r->property( "curtext" ).toString(); + if( origText != l->text() ) { // user might have kept on typing, then ignore + qDebug() << "Text changed meanwhile, stopping suggestion parsing"; + return; + } + + QStringList suggestions; + try { + Echonest::Artists artists = Echonest::Artist::parseSuggest( r ); + foreach( const Echonest::Artist& artist, artists ) + suggestions << artist.name(); + } catch( Echonest::ParseError& e ) { + qWarning() << "libechonest failed to parse this artist/suggest call..." << e.errorType() << e.what(); + return; + } + + m_suggestCache[ origText ] = suggestions; + addArtistSuggestions( suggestions ); +} + +void +Tomahawk::EchonestControl::addArtistSuggestions( const QStringList& suggestions ) +{ + // if the user is editing an artist field, try to help him out and suggest from echonest + QLineEdit* l = qobject_cast( m_input.data() ); + Q_ASSERT( l ); + + l->completer()->setModel( new QStringListModel( suggestions, l->completer() ) ); + l->completer()->complete(); +} void Tomahawk::EchonestControl::calculateSummary() diff --git a/src/libtomahawk/playlist/dynamic/echonest/EchonestControl.h b/src/libtomahawk/playlist/dynamic/echonest/EchonestControl.h index 897af0ad8..c8f448319 100644 --- a/src/libtomahawk/playlist/dynamic/echonest/EchonestControl.h +++ b/src/libtomahawk/playlist/dynamic/echonest/EchonestControl.h @@ -1,5 +1,5 @@ /* === This file is part of Tomahawk Player - === - * + * * Copyright 2010-2011, Christian Muehlhaeuser * * Tomahawk is free software: you can redistribute it and/or modify @@ -26,65 +26,73 @@ namespace Tomahawk { - + class EchonestControl : public DynamicControl { Q_OBJECT public: virtual QWidget* inputField(); virtual QWidget* matchSelector(); - + /// Converts this to an echonest suitable parameter Echonest::DynamicPlaylist::PlaylistParamData toENParam() const; - + virtual QString input() const; virtual QString match() const; virtual QString matchString() const; virtual QString summary() const; - + virtual void setInput(const QString& input); virtual void setMatch(const QString& match); - + /// DO NOT USE IF YOU ARE NOT A DBCMD EchonestControl( const QString& type, const QStringList& typeSelectors, QObject* parent = 0 ); - + public slots: virtual void setSelectedType ( const QString& type ); - private slots: void updateData(); void editingFinished(); void editTimerFired(); - + + void artistTextEdited( const QString& ); + void suggestFinished(); + + private: void updateWidgets(); void updateWidgetsFromData(); - + // utility void setupMinMaxWidgets( Echonest::DynamicPlaylist::PlaylistParam min, Echonest::DynamicPlaylist::PlaylistParam max, const QString& leftL, const QString& rightL, int maxRange ); void updateFromComboAndSlider( bool smooth = false ); void updateFromLabelAndCombo(); - + void updateToComboAndSlider( bool smooth = false ); void updateToLabelAndCombo(); - + + void addArtistSuggestions( const QStringList& suggestions ); + void calculateSummary(); - + Echonest::DynamicPlaylist::PlaylistParam m_currentType; int m_overrideType; - + QWeakPointer< QWidget > m_input; QWeakPointer< QWidget > m_match; QString m_matchData; QString m_matchString; QString m_summary; - + QTimer m_editingTimer; QTimer m_delayedEditTimer; - + Echonest::DynamicPlaylist::PlaylistParamData m_data; QVariant m_cacheData; - + + QSet< QNetworkReply* > m_suggestWorkers; + QHash< QString, QStringList > m_suggestCache; + friend class EchonestGenerator; };