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