mirror of
https://github.com/tomahawk-player/tomahawk.git
synced 2025-01-17 22:38:33 +01:00
Add a summary generator to the dynamic playlists. Now when you collapse the controls a sentence representing the controls is shown.
This will need some work to make the english flow better, but this is a good start.
This commit is contained in:
parent
7bf2eb3c9e
commit
1fd4464891
@ -61,6 +61,7 @@
|
||||
<file>./data/images/list-add.png</file>
|
||||
<file>./data/images/list-remove.png</file>
|
||||
<file>./data/images/arrow-up-double.png</file>
|
||||
<file>./data/images/arrow-down-double.png</file>
|
||||
<file>./data/images/volume-icon-full.png</file>
|
||||
<file>./data/images/volume-icon-muted.png</file>
|
||||
<file>./data/images/volume-slider-bkg.png</file>
|
||||
|
@ -44,6 +44,7 @@ class DynamicControl : public QObject
|
||||
Q_PROPERTY( QString selectedType READ selectedType WRITE setSelectedType )
|
||||
Q_PROPERTY( QString match READ match WRITE setMatch )
|
||||
Q_PROPERTY( QString input READ input WRITE setInput )
|
||||
Q_PROPERTY( QString summary READ summary ) // a summary of the control in phrase form
|
||||
|
||||
public:
|
||||
DynamicControl( const QStringList& typeSelectors = QStringList() );
|
||||
@ -66,17 +67,18 @@ public:
|
||||
virtual QWidget* inputField() { Q_ASSERT( false ); return 0; }
|
||||
|
||||
/// The user-readable match value, for showing in read-only playlists
|
||||
virtual QString matchString() { Q_ASSERT( false ); return QString(); }
|
||||
virtual QString matchString() const { Q_ASSERT( false ); return QString(); }
|
||||
|
||||
/// the serializable value of the match
|
||||
virtual QString match() const { Q_ASSERT( false ); return QString(); }
|
||||
/// the serializable value of the input
|
||||
virtual QString input() const { Q_ASSERT( false ); return QString(); }
|
||||
/// the user-readable summary phrase
|
||||
virtual QString summary() const { Q_ASSERT( false ); return QString(); }
|
||||
|
||||
// used by JSON serialization
|
||||
virtual void setMatch( const QString& match ) { Q_ASSERT( false ); }
|
||||
virtual void setInput( const QString& input ) { Q_ASSERT( false ); }
|
||||
|
||||
/// All the potential type selectors for this control
|
||||
QStringList typeSelectors() const { return m_typeSelectors; }
|
||||
|
||||
@ -104,9 +106,6 @@ protected:
|
||||
// Private constructor, you can't make one. Get it from your Generator.
|
||||
explicit DynamicControl( const QString& selectedType, const QStringList& typeSelectors, QObject* parent = 0 );
|
||||
|
||||
QString m_match;
|
||||
QString m_input;
|
||||
|
||||
private:
|
||||
QString m_type;
|
||||
QString m_selectedType;
|
||||
|
@ -80,6 +80,11 @@ public:
|
||||
*/
|
||||
virtual void fetchNext( int rating = -1 ) {}
|
||||
|
||||
/**
|
||||
* Return a sentence that describes this generator's controls. TODO english only ATM
|
||||
*/
|
||||
virtual QString sentenceSummary() {}
|
||||
|
||||
/// The type of this generator
|
||||
QString type() const { return m_type; }
|
||||
|
||||
|
@ -76,30 +76,40 @@ Tomahawk::EchonestControl::toENParam() const
|
||||
return m_data;
|
||||
}
|
||||
|
||||
QString Tomahawk::EchonestControl::input() const
|
||||
QString
|
||||
Tomahawk::EchonestControl::input() const
|
||||
{
|
||||
return m_data.second.toString();
|
||||
}
|
||||
|
||||
QString Tomahawk::EchonestControl::match() const
|
||||
QString
|
||||
Tomahawk::EchonestControl::match() const
|
||||
{
|
||||
return m_matchData;
|
||||
}
|
||||
|
||||
QString Tomahawk::EchonestControl::matchString()
|
||||
QString
|
||||
Tomahawk::EchonestControl::matchString() const
|
||||
{
|
||||
return m_matchString;
|
||||
}
|
||||
|
||||
QString
|
||||
Tomahawk::EchonestControl::summary() const
|
||||
{
|
||||
return m_summary;
|
||||
}
|
||||
|
||||
void Tomahawk::EchonestControl::setInput(const QString& input)
|
||||
void
|
||||
Tomahawk::EchonestControl::setInput(const QString& input)
|
||||
{
|
||||
// TODO generate widgets
|
||||
m_data.second = input;
|
||||
updateWidgetsFromData();
|
||||
}
|
||||
|
||||
void Tomahawk::EchonestControl::setMatch(const QString& match)
|
||||
void
|
||||
Tomahawk::EchonestControl::setMatch(const QString& match)
|
||||
{
|
||||
// TODO generate widgets
|
||||
m_matchData = match;
|
||||
@ -295,6 +305,8 @@ Tomahawk::EchonestControl::updateWidgets()
|
||||
m_match = QWeakPointer<QWidget>( new QWidget );
|
||||
m_input = QWeakPointer<QWidget>( new QWidget );
|
||||
}
|
||||
|
||||
calculateSummary();
|
||||
}
|
||||
|
||||
void
|
||||
@ -368,6 +380,8 @@ Tomahawk::EchonestControl::updateData()
|
||||
m_data.second = enumVal;
|
||||
}
|
||||
}
|
||||
|
||||
calculateSummary();
|
||||
}
|
||||
|
||||
void
|
||||
@ -428,6 +442,7 @@ Tomahawk::EchonestControl::updateWidgetsFromData()
|
||||
input->setCurrentIndex( val );
|
||||
}
|
||||
}
|
||||
calculateSummary();
|
||||
}
|
||||
|
||||
void
|
||||
@ -449,10 +464,69 @@ void Tomahawk::EchonestControl::updateToLabelAndCombo()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Tomahawk::EchonestControl::editingFinished()
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
m_editingTimer.start();
|
||||
}
|
||||
|
||||
void
|
||||
Tomahawk::EchonestControl::calculateSummary()
|
||||
{
|
||||
// turns the current control into an english phrase suitable for embedding into a sentence summary
|
||||
QString summary;
|
||||
if( selectedType() == "Artist" ) {
|
||||
// magic char is used by EchonestGenerator to split the prefix from the artist name
|
||||
if( static_cast< Echonest::DynamicPlaylist::ArtistTypeEnum >( m_matchData.toInt() ) == Echonest::DynamicPlaylist::ArtistType )
|
||||
summary = QString( "only by ~%1" ).arg( m_data.second.toString() );
|
||||
else if( static_cast< Echonest::DynamicPlaylist::ArtistTypeEnum >( m_matchData.toInt() ) == Echonest::DynamicPlaylist::ArtistRadioType )
|
||||
summary = QString( "similar to ~%1" ).arg( m_data.second.toString() );
|
||||
else if( static_cast< Echonest::DynamicPlaylist::ArtistTypeEnum >( m_matchData.toInt() ) == Echonest::DynamicPlaylist::ArtistDescriptionType )
|
||||
summary = QString( "like ~%1" ).arg( m_data.second.toString() );
|
||||
} else if( selectedType() == "Variety" || selectedType() == "Danceability" || selectedType() == "Artist Hotttnesss" || selectedType() == "Energy" || selectedType() == "Artist Familiarity" || selectedType() == "Song Hotttnesss" ) {
|
||||
QString modifier;
|
||||
qreal sliderVal = m_data.second.toReal();
|
||||
// divide into avpproximate chunks
|
||||
if( 0.0 <= sliderVal && sliderVal < 0.2 )
|
||||
modifier = "very low";
|
||||
else if( 0.2 <= sliderVal && sliderVal < 0.4 )
|
||||
modifier = "low";
|
||||
else if( 0.4 <= sliderVal && sliderVal < 0.6 )
|
||||
modifier = "moderate";
|
||||
else if( 0.6 <= sliderVal && sliderVal < 0.8 )
|
||||
modifier = "high";
|
||||
else if( 0.8 <= sliderVal && sliderVal <= 1 )
|
||||
modifier = "very high";
|
||||
summary = QString( "with %1 %2" ).arg( modifier ).arg( selectedType().toLower() );
|
||||
} else if( selectedType() == "Tempo" ) {
|
||||
summary = QString( "about %1 BPM" ).arg( m_data.second.toString() );
|
||||
} else if( selectedType() == "Duration" ) {
|
||||
summary = QString( "about %1 minutes long" ).arg( m_data.second.toString() );
|
||||
} else if( selectedType() == "Loudness" ) {
|
||||
summary = QString( "about %1 dB" ).arg( m_data.second.toString() );
|
||||
} else if( selectedType() == "Latitude" || selectedType() == "Longitude" ) {
|
||||
summary = QString( "at around %1%2 %3" ).arg( m_data.second.toString() ).arg( QString( QChar( 0x00B0 ) ) ).arg( selectedType().toLower() );
|
||||
} else if( selectedType() == "Key" ) {
|
||||
Q_ASSERT( !m_input.isNull() );
|
||||
Q_ASSERT( qobject_cast< QComboBox* >( m_input.data() ) );
|
||||
QString keyName = qobject_cast< QComboBox* >( m_input.data() )->currentText().toLower();
|
||||
summary = QString( "in %1" ).arg( keyName );
|
||||
} else if( selectedType() == "Mode" ) {
|
||||
Q_ASSERT( !m_input.isNull() );
|
||||
Q_ASSERT( qobject_cast< QComboBox* >( m_input.data() ) );
|
||||
QString modeName = qobject_cast< QComboBox* >( m_input.data() )->currentText().toLower();
|
||||
summary = QString( "in a %1 key" ).arg( modeName );
|
||||
} else if( selectedType() == "Sorting" ) {
|
||||
Q_ASSERT( !m_input.isNull() );
|
||||
Q_ASSERT( qobject_cast< QComboBox* >( m_input.data() ) );
|
||||
QString sortType = qobject_cast< QComboBox* >( m_input.data() )->currentText().toLower();
|
||||
|
||||
Q_ASSERT( !m_match.isNull() );
|
||||
Q_ASSERT( qobject_cast< QComboBox* >( m_match.data() ) );
|
||||
QString ascdesc = qobject_cast< QComboBox* >( m_match.data() )->currentText().toLower();
|
||||
|
||||
summary = QString( "sorted in %1 %2 order" ).arg( ascdesc ).arg( sortType );
|
||||
}
|
||||
m_summary = summary;
|
||||
}
|
||||
|
@ -37,11 +37,12 @@ public:
|
||||
|
||||
virtual QString input() const;
|
||||
virtual QString match() const;
|
||||
virtual QString matchString();
|
||||
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
|
||||
explicit EchonestControl( const QString& type, const QStringList& typeSelectors, QObject* parent = 0 );
|
||||
|
||||
@ -64,6 +65,8 @@ private:
|
||||
void updateToComboAndSlider( bool smooth = false );
|
||||
void updateToLabelAndCombo();
|
||||
|
||||
void calculateSummary();
|
||||
|
||||
Echonest::DynamicPlaylist::PlaylistParam m_currentType;
|
||||
int m_overrideType;
|
||||
|
||||
@ -71,6 +74,7 @@ private:
|
||||
QWeakPointer< QWidget > m_match;
|
||||
QString m_matchData;
|
||||
QString m_matchString;
|
||||
QString m_summary;
|
||||
|
||||
QTimer m_editingTimer;
|
||||
|
||||
|
@ -224,14 +224,16 @@ void
|
||||
EchonestGenerator::appendRadioType( Echonest::DynamicPlaylist::PlaylistParams& params ) const throw( std::runtime_error )
|
||||
{
|
||||
/**
|
||||
* so we try to match the best type of echonest playlist, based on the controls
|
||||
* the types are artist, artist-radio, artist-description, catalog, catalog-radio, song-radio. we don't care about the catalog ones.
|
||||
* So we try to match the best type of echonest playlist, based on the controls
|
||||
* the types are artist, artist-radio, artist-description, catalog, catalog-radio, song-radio. we don't care about the catalog ones, and
|
||||
* we can't use the song ones since for the moment EN only accepts Song IDs, not names, and we don't want to insert an extra song.search
|
||||
* call first.
|
||||
*
|
||||
*/
|
||||
|
||||
/// 1. artist: If all the artist controls are Limit-To. If some were but not all, error out.
|
||||
/// 2. artist-description: If all the artist entries are Description. If some were but not all, error out.
|
||||
/// 3. Artist-Radio: If all the artist entries are Similar To. If some were but not all, error out.
|
||||
/// 3. artist-radio: If all the artist entries are Similar To. If some were but not all, error out.
|
||||
if( onlyThisArtistType( Echonest::DynamicPlaylist::ArtistType ) )
|
||||
params.append( Echonest::DynamicPlaylist::PlaylistParamData( Echonest::DynamicPlaylist::Type, Echonest::DynamicPlaylist::ArtistType ) );
|
||||
else if( onlyThisArtistType( Echonest::DynamicPlaylist::ArtistDescriptionType ) )
|
||||
@ -249,3 +251,80 @@ EchonestGenerator::queryFromSong(const Echonest::Song& song)
|
||||
track[ "track" ] = song.title();
|
||||
return query_ptr( new Query( track ) );
|
||||
}
|
||||
|
||||
QString
|
||||
EchonestGenerator::sentenceSummary()
|
||||
{
|
||||
/**
|
||||
* The idea is we generate an english sentence from the individual phrases of the controls. We have to follow a few rules, but othewise it's quite straightforward.
|
||||
*
|
||||
* Rules:
|
||||
* - Sentence starts with "Songs "
|
||||
* - Artists always go first
|
||||
* - Separate phrases by comma, and before last phrase
|
||||
* - sorting always at end
|
||||
* - collapse artists. "Like X, like Y, like Z, ..." -> "Like X, Y, and Z"
|
||||
*
|
||||
* NOTE / TODO: In order for the sentence to be grammatically correct, we must follow the EN API rules. That means we can't have multiple of some types of filters,
|
||||
* and all Artist types must be the same. The filters aren't checked at the moment until Generate / Play is pressed. Consider doing a check on hide as well.
|
||||
*/
|
||||
QList< dyncontrol_ptr > allcontrols = m_controls;
|
||||
QString sentence = "Songs ";
|
||||
|
||||
/// 1. Collect all artist filters
|
||||
/// 2. Get the sorted by filter if it exists.
|
||||
QList< dyncontrol_ptr > artists;
|
||||
dyncontrol_ptr sorting;
|
||||
foreach( const dyncontrol_ptr& control, allcontrols ) {
|
||||
if( control->selectedType() == "Artist" )
|
||||
artists << control;
|
||||
else if( control->selectedType() == "Sorting" )
|
||||
sorting = control;
|
||||
}
|
||||
if( !sorting.isNull() )
|
||||
allcontrols.removeAll( sorting );
|
||||
|
||||
/// Do the assembling. Start with the artists if there are any, then do all the rest.
|
||||
for( int i = 0; i < artists.size(); i++ ) {
|
||||
dyncontrol_ptr artist = artists.value( i );
|
||||
allcontrols.removeAll( artist ); // remove from pool while we're here
|
||||
|
||||
/// Collapse artist lists
|
||||
QString center, suffix;
|
||||
QString summary = artist.dynamicCast< EchonestControl >()->summary();
|
||||
if( i == 0 ) { // if it's the first and only one
|
||||
center = summary.remove( "~" );
|
||||
if( artists.size() == 2 ) // special case for 2, no comma. ( X and Y )
|
||||
suffix = " and ";
|
||||
else
|
||||
suffix = ", ";
|
||||
} else {
|
||||
center = summary.mid( summary.indexOf( "~" ) + 1 );
|
||||
suffix = ", ";
|
||||
if( i != artists.size() - 1 ) // if there are more, add an " and "
|
||||
suffix += "and ";
|
||||
}
|
||||
sentence += center + suffix;
|
||||
}
|
||||
qDebug() << "Got artists:" << sentence;
|
||||
for( int i = 0; i < allcontrols.size(); i++ ) {
|
||||
// end case: if this is the last AND there is not a sorting filter (so this is the real last one)
|
||||
const bool last = ( i == allcontrols.size() - 1 && sorting.isNull() );
|
||||
QString prefix, suffix;
|
||||
if( last ) {
|
||||
prefix = "and ";
|
||||
suffix = ".";
|
||||
} else
|
||||
suffix = ", ";
|
||||
sentence += prefix + allcontrols.value( i ).dynamicCast< EchonestControl >()->summary() + suffix;
|
||||
}
|
||||
qDebug() << "Got artists and contents:" << sentence;
|
||||
|
||||
if( !sorting.isNull() ) {
|
||||
sentence += "and " + sorting.dynamicCast< EchonestControl >()->summary() + ".";
|
||||
}
|
||||
qDebug() << "Got full summary:" << sentence;
|
||||
|
||||
return sentence;
|
||||
}
|
||||
|
||||
|
@ -49,6 +49,7 @@ public:
|
||||
virtual void generate ( int number = -1 );
|
||||
virtual void startOnDemand();
|
||||
virtual void fetchNext( int rating = -1 );
|
||||
virtual QString sentenceSummary();
|
||||
|
||||
private slots:
|
||||
void staticFinished();
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
#include "CollapsibleControls.h"
|
||||
|
||||
#include "tomahawk/tomahawkapp.h"
|
||||
#include "DynamicControlList.h"
|
||||
#include "DynamicControlWrapper.h"
|
||||
#include "dynamic/GeneratorInterface.h"
|
||||
@ -23,6 +24,8 @@
|
||||
|
||||
#include <QLabel>
|
||||
#include <QStackedLayout>
|
||||
#include <QToolButton>
|
||||
#include <QAction>
|
||||
|
||||
using namespace Tomahawk;
|
||||
|
||||
@ -32,11 +35,12 @@ CollapsibleControls::CollapsibleControls( QWidget* parent )
|
||||
init();
|
||||
}
|
||||
|
||||
CollapsibleControls::CollapsibleControls( const geninterface_ptr& generator, const QList< dyncontrol_ptr >& controls, bool isLocal, QWidget* parent )
|
||||
CollapsibleControls::CollapsibleControls( const dynplaylist_ptr& playlist, bool isLocal, QWidget* parent )
|
||||
: QWidget( parent )
|
||||
, m_dynplaylist( playlist )
|
||||
{
|
||||
init();
|
||||
setControls( generator, controls, isLocal );
|
||||
setControls( m_dynplaylist, isLocal );
|
||||
}
|
||||
|
||||
Tomahawk::CollapsibleControls::~CollapsibleControls()
|
||||
@ -54,16 +58,28 @@ CollapsibleControls::init()
|
||||
|
||||
m_controls = new Tomahawk::DynamicControlList( this );
|
||||
m_layout->addWidget( m_controls );
|
||||
connect( m_controls, SIGNAL( toggleCollapse() ), this, SLOT( toggleCollapse() ) );
|
||||
|
||||
m_summaryWidget = new QWidget( this );
|
||||
// TODO replace
|
||||
// m_summaryWidget->setMinimumHeight( 24 );
|
||||
// m_summaryWidget->setMaximumHeight( 24 );
|
||||
m_summaryWidget->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
|
||||
m_summaryWidget->setLayout( new QVBoxLayout );
|
||||
QHBoxLayout* summaryLayout = new QHBoxLayout;
|
||||
m_summaryWidget->setLayout( summaryLayout );
|
||||
m_summaryWidget->layout()->setMargin( 0 );
|
||||
m_summaryWidget->layout()->addWidget( new QLabel( "replace me plz", m_summaryWidget ) );
|
||||
|
||||
m_summary = new QLabel( m_summaryWidget );
|
||||
summaryLayout->addWidget( m_summary, 1 );
|
||||
m_summaryExpand = new QToolButton( m_summary );
|
||||
m_summaryExpand->setIconSize( QSize( 16, 16 ) );
|
||||
m_summaryExpand->setIcon( QIcon( RESPATH "images/arrow-down-double.png" ) );
|
||||
m_summaryExpand->setToolButtonStyle( Qt::ToolButtonIconOnly );
|
||||
m_summaryExpand->setAutoRaise( true );
|
||||
m_summaryExpand->setContentsMargins( 0, 0, 0, 0 );
|
||||
summaryLayout->addWidget( m_summaryExpand );
|
||||
m_layout->addWidget( m_summaryWidget );
|
||||
connect( m_summaryExpand, SIGNAL( clicked( bool ) ), this, SLOT( toggleCollapse() ) );
|
||||
|
||||
m_layout->setCurrentIndex( 0 );
|
||||
connect( m_controls, SIGNAL( controlChanged( Tomahawk::dyncontrol_ptr ) ), SIGNAL( controlChanged( Tomahawk::dyncontrol_ptr ) ) );
|
||||
@ -82,13 +98,19 @@ Tomahawk::CollapsibleControls::controls() const
|
||||
}
|
||||
|
||||
void
|
||||
CollapsibleControls::setControls( const geninterface_ptr& generator, const QList< dyncontrol_ptr >& controls, bool isLocal )
|
||||
CollapsibleControls::setControls( const dynplaylist_ptr& playlist, bool isLocal )
|
||||
{
|
||||
m_controls->setControls( generator, controls, isLocal );
|
||||
m_dynplaylist = playlist;
|
||||
m_controls->setControls( m_dynplaylist->generator(), m_dynplaylist->generator()->controls(), isLocal );
|
||||
}
|
||||
|
||||
void
|
||||
CollapsibleControls::toggleCollapse()
|
||||
{
|
||||
|
||||
if( m_layout->currentWidget() == m_controls ) {
|
||||
m_summary->setText( m_dynplaylist->generator()->sentenceSummary() );
|
||||
m_layout->setCurrentWidget( m_summaryWidget );
|
||||
} else {
|
||||
m_layout->setCurrentWidget( m_controls );
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,8 @@
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class QToolButton;
|
||||
class QLabel;
|
||||
class QStackedLayout;
|
||||
namespace Tomahawk
|
||||
{
|
||||
@ -33,10 +35,10 @@ class CollapsibleControls : public QWidget
|
||||
Q_OBJECT
|
||||
public:
|
||||
CollapsibleControls( QWidget* parent );
|
||||
CollapsibleControls( const geninterface_ptr& generator, const QList< dyncontrol_ptr >& controls, bool isLocal, QWidget* parent = 0 );
|
||||
CollapsibleControls( const dynplaylist_ptr& playlist, bool isLocal, QWidget* parent = 0 );
|
||||
virtual ~CollapsibleControls();
|
||||
|
||||
void setControls( const geninterface_ptr& generator, const QList< dyncontrol_ptr >& controls, bool isLocal );
|
||||
void setControls( const dynplaylist_ptr& playlist, bool isLocal );
|
||||
QList< DynamicControlWrapper* > controls() const;
|
||||
|
||||
signals:
|
||||
@ -49,10 +51,13 @@ private slots:
|
||||
private:
|
||||
void init();
|
||||
|
||||
dynplaylist_ptr m_dynplaylist;
|
||||
QStackedLayout* m_layout;
|
||||
DynamicControlList* m_controls;
|
||||
QWidget* m_summaryWidget;
|
||||
|
||||
QWidget* m_summaryWidget;
|
||||
QLabel* m_summary;
|
||||
QToolButton* m_summaryExpand;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -118,7 +118,7 @@ DynamicWidget::loadDynamicPlaylist( const Tomahawk::dynplaylist_ptr& playlist )
|
||||
foreach( const dyncontrol_ptr& control, playlist->generator()->controls() ) {
|
||||
qDebug() << "CONTROL:" << control->selectedType() << control->match() << control->input();
|
||||
}
|
||||
m_controls->setControls( m_playlist->generator(), m_playlist->generator()->controls(), m_playlist->author()->isLocal() );
|
||||
m_controls->setControls( m_playlist, m_playlist->author()->isLocal() );
|
||||
|
||||
m_playlist = playlist;
|
||||
m_view->setOnDemand( m_playlist->mode() == OnDemand );
|
||||
@ -139,7 +139,7 @@ DynamicWidget::loadDynamicPlaylist( const Tomahawk::dynplaylist_ptr& playlist )
|
||||
m_model->loadPlaylist( m_playlist );
|
||||
|
||||
if( !m_playlist.isNull() )
|
||||
m_controls->setControls( m_playlist->generator(), m_playlist->generator()->controls(), m_playlist->author()->isLocal() );
|
||||
m_controls->setControls( m_playlist, m_playlist->author()->isLocal() );
|
||||
|
||||
m_generatorCombo->setWritable( playlist->author()->isLocal() );
|
||||
m_generatorCombo->setLabel( qobject_cast< QComboBox* >( m_generatorCombo->writableWidget() )->currentText() );
|
||||
|
Loading…
x
Reference in New Issue
Block a user