1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-07-31 19:30:21 +02:00

* Interactive create-new-playlist dialog. Work in progress.

This commit is contained in:
Christian Muehlhaeuser
2011-01-03 05:18:12 +01:00
parent 0af3ac8c80
commit a5b495127a
16 changed files with 450 additions and 76 deletions

View File

@@ -92,6 +92,7 @@ private:
AudioEngine* m_audioEngine;
SipHandler* m_sipHandler;
Servent* m_servent;
XMPPBot* m_xmppBot;
#ifndef NO_LIBLASTFM

View File

@@ -96,6 +96,8 @@ SET( tomahawkSourcesGui ${tomahawkSourcesGui}
infowidgets/sourceinfowidget.cpp
widgets/newplaylistwidget.cpp
transferview.cpp
tomahawkwindow.cpp
tomahawktrayicon.cpp
@@ -173,6 +175,8 @@ SET( tomahawkHeadersGui ${tomahawkHeadersGui}
infowidgets/sourceinfowidget.h
widgets/newplaylistwidget.h
transferview.h
tomahawkwindow.h
tomahawktrayicon.h
@@ -189,6 +193,7 @@ SET( tomahawkUI ${tomahawkUI}
sourcetree/sourcetreeitemwidget.ui
topbar/topbar.ui
infowidgets/sourceinfowidget.ui
widgets/newplaylistwidget.ui
)
INCLUDE_DIRECTORIES(

View File

@@ -151,11 +151,7 @@ DatabaseCommand_Resolve::exec( DatabaseImpl* lib )
res << Tomahawk::result_ptr( new Tomahawk::Result( m, coll ) );
}
// return results, if any found
if( res.length() > 0 )
{
emit results( qid, res );
}
emit results( qid, res );
}

View File

@@ -20,9 +20,7 @@ public:
virtual void exec(DatabaseImpl *lib);
signals:
void results( Tomahawk::QID qid, QList<Tomahawk::result_ptr> results );
public slots:

View File

@@ -36,14 +36,10 @@ Pipeline::databaseReady()
void Pipeline::indexReady()
{
qDebug() << Q_FUNC_INFO << "shuting this many pending queries:" << m_queries_pending.size();
qDebug() << Q_FUNC_INFO << "shunting this many pending queries:" << m_queries_pending.size();
m_index_ready = true;
foreach( const query_ptr& q, m_queries_pending )
{
q->setLastPipelineWeight( 101 );
shunt( q );
}
m_queries_pending.clear();
shuntNext();
}
@@ -77,7 +73,7 @@ Pipeline::addResolver( Resolver* r, bool sort )
void
Pipeline::add( const QList<query_ptr>& qlist )
Pipeline::add( const QList<query_ptr>& qlist, bool prioritized )
{
{
QMutexLocker lock( &m_mut );
@@ -91,36 +87,28 @@ Pipeline::add( const QList<query_ptr>& qlist )
}
}
/*
Since resolvers are async, we now dispatch to the highest weighted ones
and after timeout, dispatch to next highest etc, aborting when solved
If index not yet loaded, leave in the pending list instead.
(they are shunted when index is ready)
*/
if( m_index_ready )
if ( prioritized )
{
foreach( const query_ptr& q, qlist )
{
q->setLastPipelineWeight( 101 );
shunt( q ); // bump into next stage of pipeline (highest weights are 100)
}
for( int i = qlist.count() - 1; i >= 0; i-- )
m_queries_pending.insert( 0, qlist.at( i ) );
}
else
{
qDebug() << "Placing query in pending queue - index not ready yet";
m_queries_pending.append( qlist );
}
if ( m_index_ready )
shuntNext();
}
void
Pipeline::add( const query_ptr& q )
Pipeline::add( const query_ptr& q, bool prioritized )
{
//qDebug() << Q_FUNC_INFO << (qlonglong)q.data() << q->toString();
QList< query_ptr > qlist;
qlist << q;
add( qlist );
add( qlist, prioritized );
}
@@ -135,18 +123,36 @@ Pipeline::reportResults( QID qid, const QList< result_ptr >& results )
return;
}
const query_ptr& q = m_qids.value( qid );
//qDebug() << Q_FUNC_INFO << qid;
//qDebug() << "solved query:" << (qlonglong)q.data() << q->toString();
q->addResults( results );
//qDebug() << "Results for " << q->toString() << ", just added" << results.length();
foreach( const result_ptr& r, q->results() )
if ( !results.isEmpty() )
{
m_rids.insert( r->id(), r );
//qDebug() << "* " << (results.contains(r) ? "NEW" : "") << r->toString();
}
//qDebug() << Q_FUNC_INFO << qid;
//qDebug() << "solved query:" << (qlonglong)q.data() << q->toString();
const query_ptr& q = m_qids.value( qid );
q->addResults( results );
foreach( const result_ptr& r, q->results() )
{
m_rids.insert( r->id(), r );
}
}
}
void
Pipeline::shuntNext()
{
if ( m_queries_pending.isEmpty() )
return;
/*
Since resolvers are async, we now dispatch to the highest weighted ones
and after timeout, dispatch to next highest etc, aborting when solved
*/
query_ptr q = m_queries_pending.takeFirst();
q->setLastPipelineWeight( 101 );
shunt( q ); // bump into next stage of pipeline (highest weights are 100)
}
@@ -156,7 +162,7 @@ Pipeline::shunt( const query_ptr& q )
if( q->solved() )
{
qDebug() << "Query solved, pipeline aborted:" << q->toString()
<< "numresults:" << q->results().length();
<< "numresults:" << q->results().length();
return;
}
unsigned int lastweight = 0;
@@ -197,6 +203,8 @@ Pipeline::shunt( const query_ptr& q )
//qDebug() << "Reached end of pipeline for:" << q->toString();
// reached end of pipeline
}
shuntNext();
}

View File

@@ -49,12 +49,14 @@ public:
}
public slots:
void add( const query_ptr& q );
void add( const QList<query_ptr>& qlist );
void add( const query_ptr& q, bool prioritized = true );
void add( const QList<query_ptr>& qlist, bool prioritized = true );
void databaseReady();
private slots:
void shunt( const query_ptr& q );
void shuntNext();
void indexReady();
private:

View File

@@ -66,6 +66,8 @@ PlaylistManager::PlaylistManager( QObject* parent )
m_currentInterface = m_superCollectionView->proxyModel();
connect( &m_filterTimer, SIGNAL( timeout() ), SLOT( applyFilter() ) );
linkPlaylist();
}
@@ -248,6 +250,27 @@ PlaylistManager::show( const Tomahawk::source_ptr& source )
}
bool
PlaylistManager::show( QWidget* widget )
{
unlinkPlaylist();
connect( widget, SIGNAL( destroyed( QWidget* ) ), SLOT( onWidgetDestroyed( QWidget* ) ) );
m_stack->addWidget( widget );
m_stack->setCurrentWidget( widget );
m_superCollectionVisible = false;
m_statsAvailable = false;
m_modesAvailable = false;
m_currentInterface = 0;
linkPlaylist();
return true;
}
bool
PlaylistManager::showSuperCollection()
{
@@ -414,6 +437,9 @@ PlaylistManager::linkPlaylist()
connect( m_currentInterface->object(), SIGNAL( shuffleModeChanged( bool ) ),
this, SIGNAL( shuffleModeChanged( bool ) ) );
m_interfaceHistory.removeAll( m_currentInterface );
m_interfaceHistory << m_currentInterface;
}
applyFilter();
@@ -433,6 +459,29 @@ PlaylistManager::linkPlaylist()
}
void
PlaylistManager::onWidgetDestroyed( QWidget* widget )
{
qDebug() << "Destroyed child:" << widget;
bool resetWidget = ( m_stack->currentWidget() == widget );
m_stack->removeWidget( widget );
if ( resetWidget && m_interfaceHistory.count() )
{
unlinkPlaylist();
m_currentInterface = m_interfaceHistory.last();
qDebug() << "Last interface:" << m_currentInterface << m_currentInterface->widget();
if ( m_currentInterface->widget() )
m_stack->setCurrentWidget( m_currentInterface->widget() );
linkPlaylist();
}
}
void
PlaylistManager::setRepeatMode( PlaylistInterface::RepeatMode mode )
{

View File

@@ -39,8 +39,10 @@ public:
bool show( const Tomahawk::album_ptr& album );
bool show( const Tomahawk::collection_ptr& collection );
bool show( const Tomahawk::source_ptr& source );
bool showSuperCollection();
bool show( QWidget* widget );
bool showSuperCollection();
void showCurrentTrack();
signals:
@@ -71,6 +73,8 @@ public slots:
private slots:
void applyFilter();
void onWidgetDestroyed( QWidget* widget );
private:
void unlinkPlaylist();
void linkPlaylist();
@@ -97,6 +101,7 @@ private:
QHash< Tomahawk::source_ptr, SourceInfoWidget* > m_sourceViews;
PlaylistInterface* m_currentInterface;
QList<PlaylistInterface*> m_interfaceHistory;
QWidget* m_currentInfoWidget;

View File

@@ -110,7 +110,7 @@ TomahawkApp::TomahawkApp( int& argc, char *argv[] )
new Pipeline( this );
new SourceList( this );
new Servent( this );
m_servent = new Servent( this );
#ifdef TOMAHAWK_HEADLESS
m_headless = true;
@@ -212,6 +212,7 @@ TomahawkApp::~TomahawkApp()
qDebug() << Q_FUNC_INFO;
delete m_sipHandler;
delete m_servent;
#ifndef TOMAHAWK_HEADLESS
delete m_mainwindow;

View File

@@ -21,6 +21,8 @@
#include "sip/SipHandler.h"
#include "widgets/newplaylistwidget.h"
#include "audiocontrols.h"
#include "network/controlconnection.h"
#include "database/database.h"
@@ -324,7 +326,9 @@ TomahawkWindow::createPlaylist()
{
qDebug() << Q_FUNC_INFO;
bool ok;
playlistManager()->show( new NewPlaylistWidget() );
/* bool ok;
QString name = QInputDialog::getText( this, "Create New Playlist", "Name:", QLineEdit::Normal, "New Playlist", &ok );
if ( !ok || name.isEmpty() )
return;
@@ -333,7 +337,7 @@ TomahawkWindow::createPlaylist()
QString id = uuid();
QString info = ""; // FIXME
QString creator = "someone"; // FIXME
Playlist::create( author, id, name, info, creator, false /* shared */ );
Playlist::create( author, id, name, info, creator, false );*/
}

View File

@@ -0,0 +1,131 @@
#include "newplaylistwidget.h"
#include "ui_newplaylistwidget.h"
#include <QPushButton>
#include <QDialogButtonBox>
#include "tomahawk/tomahawkapp.h"
#include "utils/tomahawkutils.h"
#include "playlist/playlistmanager.h"
#include "playlist/playlistmodel.h"
#include "pipeline.h"
#include "xspfloader.h"
#include "sourcelist.h"
#define FILTER_TIMEOUT 280
NewPlaylistWidget::NewPlaylistWidget( QWidget* parent )
: QWidget( parent )
, ui( new Ui::NewPlaylistWidget )
{
ui->setupUi( this );
QPushButton* saveButton = new QPushButton( tr( "&Create Playlist" ) );
saveButton->setDefault( true );
ui->buttonBox->addButton( saveButton, QDialogButtonBox::AcceptRole );
connect( ui->tagEdit, SIGNAL( textChanged( QString ) ), SLOT( tagChanged() ) );
connect( ui->buttonBox, SIGNAL( accepted() ), SLOT( savePlaylist() ) );
connect( ui->buttonBox, SIGNAL( rejected() ), SLOT( cancel() ) );
m_suggestionsModel = new PlaylistModel( ui->suggestionsView );
ui->suggestionsView->setModel( m_suggestionsModel );
connect( &m_filterTimer, SIGNAL( timeout() ), SLOT( updateSuggestions() ) );
}
NewPlaylistWidget::~NewPlaylistWidget()
{
delete ui;
}
void
NewPlaylistWidget::changeEvent( QEvent* e )
{
QWidget::changeEvent( e );
switch ( e->type() )
{
case QEvent::LanguageChange:
ui->retranslateUi( this );
break;
default:
break;
}
}
void
NewPlaylistWidget::tagChanged()
{
m_tag = ui->tagEdit->text();
m_filterTimer.stop();
m_filterTimer.setInterval( FILTER_TIMEOUT );
m_filterTimer.setSingleShot( true );
m_filterTimer.start();
}
void
NewPlaylistWidget::updateSuggestions()
{
QUrl url( QString( "http://ws.audioscrobbler.com/1.0/tag/%1/toptracks.xspf" ).arg( m_tag ) );
XSPFLoader* loader = new XSPFLoader( false );
connect( loader, SIGNAL( ok( Tomahawk::playlist_ptr ) ), SLOT( suggestionsFound() ) );
loader->load( url );
}
void
NewPlaylistWidget::suggestionsFound()
{
XSPFLoader* loader = qobject_cast<XSPFLoader*>( sender() );
m_entries = loader->entries();
delete m_suggestionsModel;
m_suggestionsModel = new PlaylistModel( ui->suggestionsView );
ui->suggestionsView->setModel( m_suggestionsModel );
QList<Tomahawk::query_ptr> ql;
foreach( const Tomahawk::plentry_ptr& entry, m_entries )
{
m_suggestionsModel->appendTrack( entry->query() );
ql.append( entry->query() );
}
Tomahawk::Pipeline::instance()->add( ql );
loader->deleteLater();
}
void
NewPlaylistWidget::savePlaylist()
{
Tomahawk::playlist_ptr playlist;
playlist = Tomahawk::Playlist::create( SourceList::instance()->getLocal(), uuid(), ui->titleEdit->text(), "", "", false );
playlist->createNewRevision( uuid(), playlist->currentrevision(), m_entries );
APP->playlistManager()->show( playlist );
cancel();
}
void
NewPlaylistWidget::cancel()
{
emit destroyed( this );
deleteLater();
}

View File

@@ -0,0 +1,50 @@
#ifndef NEWPLAYLISTWIDGET_H
#define NEWPLAYLISTWIDGET_H
#include <QWidget>
#include <QTimer>
#include "album.h"
#include "result.h"
#include "playlistinterface.h"
class PlaylistModel;
namespace Ui
{
class NewPlaylistWidget;
}
class NewPlaylistWidget : public QWidget
{
Q_OBJECT
public:
NewPlaylistWidget( QWidget* parent = 0 );
~NewPlaylistWidget();
protected:
void changeEvent( QEvent* e );
signals:
void destroyed( QWidget* widget );
private slots:
void tagChanged();
void updateSuggestions();
void suggestionsFound();
void savePlaylist();
void cancel();
private:
Ui::NewPlaylistWidget *ui;
PlaylistModel* m_suggestionsModel;
QList< Tomahawk::plentry_ptr > m_entries;
QTimer m_filterTimer;
QString m_tag;
};
#endif // NEWPLAYLISTWIDGET_H

View File

@@ -0,0 +1,111 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>NewPlaylistWidget</class>
<widget class="QWidget" name="NewPlaylistWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>985</width>
<height>460</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="captionLabel">
<property name="font">
<font>
<pointsize>20</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Create A New Playlist</string>
</property>
<property name="scaledContents">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="font">
<font>
<pointsize>14</pointsize>
</font>
</property>
<property name="text">
<string>Enter a title for the new playlist:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="titleEdit">
<property name="minimumSize">
<size>
<width>0</width>
<height>26</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="font">
<font>
<pointsize>14</pointsize>
</font>
</property>
<property name="text">
<string>Tomahawk offers a variety of ways to help you create playlists and find music you enjoy!</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="font">
<font>
<pointsize>14</pointsize>
</font>
</property>
<property name="text">
<string>Just enter a genre or tag name and Tomahawk will suggest a few songs to get you started with your new playlist:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="tagEdit">
<property name="minimumSize">
<size>
<width>0</width>
<height>26</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="PlaylistView" name="suggestionsView"/>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>PlaylistView</class>
<extends>QTreeView</extends>
<header>playlistview.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@@ -148,10 +148,7 @@ void XMPPBot::handleMessage(const Message& msg, MessageSession* session)
connect( q.data(), SIGNAL( resultsAdded( QList<Tomahawk::result_ptr> ) ),
SLOT( onResultsAdded( QList<Tomahawk::result_ptr> ) ) );
QList<Tomahawk::query_ptr> ql;
ql.append( q );
Tomahawk::Pipeline::instance()->add( ql );
Tomahawk::Pipeline::instance()->add( q );
return;
}
else if ( body.startsWith( "stop" ) )

View File

@@ -77,23 +77,14 @@ XSPFLoader::gotBody()
xmldoc.setContent( m_body );
QDomElement docElement( xmldoc.documentElement() );
QString origTitle, title, info, creator;
QString origTitle;
origTitle = docElement.firstChildElement( "title" ).text();
info = docElement.firstChildElement( "creator" ).text();
creator = docElement.firstChildElement( "info" ).text();
m_info = docElement.firstChildElement( "creator" ).text();
m_creator = docElement.firstChildElement( "info" ).text();
title = origTitle;
if ( title.isEmpty() )
title = tr( "New Playlist" );
m_playlist = Playlist::create( SourceList::instance()->getLocal(),
uuid(),
title,
info,
creator,
false );
QList< plentry_ptr > entries;
m_title = origTitle;
if ( m_title.isEmpty() )
m_title = tr( "New Playlist" );
QDomNodeList tracklist = docElement.elementsByTagName( "track" );
for ( unsigned int i = 0; i < tracklist.length(); i++ )
@@ -113,18 +104,36 @@ XSPFLoader::gotBody()
v.insert( "track", e.firstChildElement( "title" ).text() );
p->setQuery( Tomahawk::query_ptr(new Tomahawk::Query(v)) );
entries << p;
m_entries << p;
}
if ( origTitle.isEmpty() && entries.isEmpty() )
if ( origTitle.isEmpty() && m_entries.isEmpty() )
{
QMessageBox::critical( APP->mainWindow(), tr( "XSPF Error" ), tr( "This is not a valid XSPF playlist." ) );
deleteLater();
return;
if ( m_autoCreate )
{
QMessageBox::critical( APP->mainWindow(), tr( "XSPF Error" ), tr( "This is not a valid XSPF playlist." ) );
deleteLater();
return;
}
else
{
emit failed();
return;
}
}
m_playlist->createNewRevision( uuid(), m_playlist->currentrevision(), entries );
emit ok( m_playlist );
if ( m_autoCreate )
{
m_playlist = Playlist::create( SourceList::instance()->getLocal(),
uuid(),
m_title,
m_info,
m_creator,
false );
deleteLater();
m_playlist->createNewRevision( uuid(), m_playlist->currentrevision(), m_entries );
deleteLater();
}
emit ok( m_playlist );
}

View File

@@ -20,8 +20,9 @@ class XSPFLoader : public QObject
Q_OBJECT
public:
explicit XSPFLoader( QObject* parent = 0 )
explicit XSPFLoader( bool autoCreate = true, QObject* parent = 0 )
: QObject( parent )
, m_autoCreate( autoCreate )
{}
virtual ~XSPFLoader()
@@ -29,6 +30,8 @@ public:
qDebug() << Q_FUNC_INFO;
}
QList< Tomahawk::plentry_ptr > entries() const { return m_entries; }
signals:
void failed();
void ok( const Tomahawk::playlist_ptr& );
@@ -45,6 +48,10 @@ private:
void reportError();
void gotBody();
bool m_autoCreate;
QList< Tomahawk::plentry_ptr > m_entries;
QString m_title, m_info, m_creator;
QByteArray m_body;
Tomahawk::playlist_ptr m_playlist;
};