1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-04-07 01:22:49 +02:00

* Added basic search widget.

This commit is contained in:
Christian Muehlhaeuser 2011-06-04 20:47:45 +02:00
parent 34d9a723dd
commit 2e9dc42696
15 changed files with 281 additions and 42 deletions

@ -122,6 +122,7 @@ SET( tomahawkUI ${tomahawkUI}
diagnosticsdialog.ui
stackedsettingsdialog.ui
proxydialog.ui
searchwidget.ui
audiocontrols.ui
)

@ -161,6 +161,7 @@ set( libSources
utils/xspfgenerator.cpp
widgets/newplaylistwidget.cpp
widgets/searchwidget.cpp
widgets/playlisttypeselectordlg.cpp
widgets/welcomewidget.cpp
widgets/welcomeplaylistmodel.cpp
@ -325,6 +326,7 @@ set( libHeaders
utils/xspfgenerator.h
widgets/newplaylistwidget.h
widgets/searchwidget.h
widgets/playlisttypeselectordlg.h
widgets/welcomewidget.h
widgets/welcomeplaylistmodel.h
@ -343,6 +345,7 @@ set( libHeaders_NoMOC
set( libUI ${libUI}
widgets/playlisttypeselectordlg.ui
widgets/newplaylistwidget.ui
widgets/searchwidget.ui
widgets/welcomewidget.ui
widgets/infowidgets/sourceinfowidget.ui
playlist/topbar/topbar.ui

@ -36,23 +36,35 @@ DatabaseCommand_Resolve::DatabaseCommand_Resolve( const query_ptr& query )
void
DatabaseCommand_Resolve::exec( DatabaseImpl* lib )
{
QList<Tomahawk::result_ptr> res;
if ( !m_query->resultHint().isEmpty() )
{
qDebug() << "Using result-hint to speed up resolving:" << m_query->resultHint();
Tomahawk::result_ptr result = lib->resultFromHint( m_query );
/* qDebug() << "Result null:" << result.isNull();
qDebug() << "Collection null:" << result->collection().isNull();
qDebug() << "Source null:" << result->collection()->source().isNull();*/
/* qDebug() << "Result null:" << result.isNull();
* qDebug() << "Collection null:" << result->collection().isNull();
* qDebug() << "Source null:" << result->collection()->source().isNull();*/
if ( !result.isNull() && !result->collection().isNull() && result->collection()->source()->isOnline() )
{
QList<Tomahawk::result_ptr> res;
res << result;
emit results( m_query->id(), res );
return;
}
}
if ( m_query->isFullTextQuery() )
fullTextResolve( lib );
else
resolve( lib );
}
void
DatabaseCommand_Resolve::resolve( DatabaseImpl* lib )
{
QList<Tomahawk::result_ptr> res;
/*
Resolving is a 2 stage process.
1) find list of trk/art/alb IDs that are reasonable matches to the metadata given
@ -64,10 +76,10 @@ DatabaseCommand_Resolve::exec( DatabaseImpl* lib )
// STEP 1
QList< int > artists = lib->searchTable( "artist", m_query->artist(), 10 );
QList< int > tracks = lib->searchTable( "track", m_query->track(), 10 );
QList< int > albums = lib->searchTable( "album", m_query->album(), 10 );
QList< int > tracks = lib->searchTable( "track", m_query->track(), 10 );
QList< int > albums = lib->searchTable( "album", m_query->album(), 10 );
if( artists.length() == 0 || tracks.length() == 0 )
if ( artists.length() == 0 || tracks.length() == 0 )
{
qDebug() << "No candidates found in first pass, aborting resolve" << m_query->artist() << m_query->track();
emit results( m_query->id(), res );
@ -83,6 +95,9 @@ DatabaseCommand_Resolve::exec( DatabaseImpl* lib )
foreach( int i, tracks )
trksl.append( QString::number( i ) );
QString artsToken = QString( "file_join.artist IN (%1)" ).arg( artsl.join( "," ) );
QString trksToken = QString( "file_join.track IN (%1)" ).arg( trksl.join( "," ) );
QString sql = QString( "SELECT "
"url, mtime, size, md5, mimetype, duration, bitrate, file_join.artist, file_join.album, file_join.track, "
"artist.name as artname, "
@ -98,10 +113,9 @@ DatabaseCommand_Resolve::exec( DatabaseImpl* lib )
"artist.id = file_join.artist AND "
"track.id = file_join.track AND "
"file.id = file_join.file AND "
"file_join.artist IN (%1) AND "
"file_join.track IN (%2)" )
.arg( artsl.join( "," ) )
.arg( trksl.join( "," ) );
"(%1 AND %2)" )
.arg( artsToken )
.arg( trksToken );
files_query.prepare( sql );
files_query.exec();
@ -160,7 +174,136 @@ DatabaseCommand_Resolve::exec( DatabaseImpl* lib )
float score = how_similar( m_query, result );
result->setScore( score );
if( score < MINSCORE )
if ( m_query->fullTextQuery().isEmpty() && score < MINSCORE )
continue;
result->setCollection( s->collection() );
res << result;
}
emit results( m_query->id(), res );
}
void
DatabaseCommand_Resolve::fullTextResolve( DatabaseImpl* lib )
{
QList<Tomahawk::result_ptr> res;
/*
* Resolving is a 2 stage process.
* 1) find list of trk/art/alb IDs that are reasonable matches to the metadata given
* 2) find files in database by permitted sources and calculate score, ignoring
* results that are less than MINSCORE
*/
typedef QPair<int, float> scorepair_t;
// STEP 1
QList< int > artists = lib->searchTable( "artist", m_query->fullTextQuery(), 10 );
QList< int > tracks = lib->searchTable( "track", m_query->fullTextQuery(), 10 );
QList< int > albums = lib->searchTable( "album", m_query->fullTextQuery(), 10 );
if ( artists.length() == 0 && tracks.length() == 0 && albums.length() == 0 )
{
qDebug() << "No candidates found in first pass, aborting resolve" << m_query->artist() << m_query->track();
emit results( m_query->id(), res );
return;
}
// STEP 2
TomahawkSqlQuery files_query = lib->newquery();
QStringList artsl, trksl, albsl;
foreach( int i, artists )
artsl.append( QString::number( i ) );
foreach( int i, tracks )
trksl.append( QString::number( i ) );
foreach( int i, albums )
albsl.append( QString::number( i ) );
QString artsToken = QString( "file_join.artist IN (%1)" ).arg( artsl.join( "," ) );
QString trksToken = QString( "file_join.track IN (%1)" ).arg( trksl.join( "," ) );
QString albsToken = QString( "file_join.album IN (%1)" ).arg( albsl.join( "," ) );
QString sql = QString( "SELECT "
"url, mtime, size, md5, mimetype, duration, bitrate, file_join.artist, file_join.album, file_join.track, "
"artist.name as artname, "
"album.name as albname, "
"track.name as trkname, "
"file.source, "
"file_join.albumpos, "
"artist.id as artid, "
"album.id as albid "
"FROM file, file_join, artist, track "
"LEFT JOIN album ON album.id = file_join.album "
"WHERE "
"artist.id = file_join.artist AND "
"track.id = file_join.track AND "
"file.id = file_join.file AND "
"(%1 OR %2 OR %3)" )
.arg( artists.length() > 0 ? artsToken : QString( "0" ) )
.arg( tracks.length() > 0 ? trksToken : QString( "0" ) )
.arg( albums.length() > 0 ? albsToken : QString( "0" ) );
files_query.prepare( sql );
files_query.exec();
while( files_query.next() )
{
Tomahawk::result_ptr result( new Tomahawk::Result() );
source_ptr s;
const QString url_str = files_query.value( 0 ).toString();
if( files_query.value( 13 ).toUInt() == 0 )
{
s = SourceList::instance()->getLocal();
result->setUrl( url_str );
}
else
{
s = SourceList::instance()->get( files_query.value( 13 ).toUInt() );
if( s.isNull() )
{
qDebug() << "WTF: Could not find source" << files_query.value( 13 ).toUInt();
continue;
}
result->setUrl( QString( "servent://%1\t%2" ).arg( s->userName() ).arg( url_str ) );
}
Tomahawk::artist_ptr artist = Tomahawk::Artist::get( files_query.value( 15 ).toUInt(), files_query.value( 10 ).toString() );
Tomahawk::album_ptr album = Tomahawk::Album::get( files_query.value( 16 ).toUInt(), files_query.value( 11 ).toString(), artist );
result->setModificationTime( files_query.value( 1 ).toUInt() );
result->setSize( files_query.value( 2 ).toUInt() );
result->setMimetype( files_query.value( 4 ).toString() );
result->setDuration( files_query.value( 5 ).toUInt() );
result->setBitrate( files_query.value( 6 ).toUInt() );
result->setArtist( artist );
result->setAlbum( album );
result->setTrack( files_query.value( 12 ).toString() );
result->setRID( uuid() );
result->setAlbumPos( files_query.value( 14 ).toUInt() );
result->setId( files_query.value( 9 ).toUInt() );
result->setYear( files_query.value( 17 ).toUInt() );
TomahawkSqlQuery attrQuery = lib->newquery();
QVariantMap attr;
attrQuery.prepare( "SELECT k, v FROM track_attributes WHERE id = ?" );
attrQuery.bindValue( 0, result->dbid() );
attrQuery.exec();
while ( attrQuery.next() )
{
attr[ attrQuery.value( 0 ).toString() ] = attrQuery.value( 1 ).toString();
}
result->setAttributes( attr );
float score = how_similar( m_query, result );
result->setScore( score );
if ( m_query->fullTextQuery().isEmpty() && score < MINSCORE )
continue;
result->setCollection( s->collection() );

@ -1,5 +1,5 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
@ -44,6 +44,9 @@ signals:
public slots:
private:
void fullTextResolve( DatabaseImpl* lib );
void resolve( DatabaseImpl* lib );
Tomahawk::query_ptr m_query;
float how_similar( const Tomahawk::query_ptr& q, const Tomahawk::result_ptr& r );

@ -190,7 +190,7 @@ Pipeline::reportResults( QID qid, const QList< result_ptr >& results )
m_rids.insert( r->id(), r );
}
if ( q->solved() )
if ( q->solved() && !q->isFullTextQuery() )
{
// qDebug() << "FINISHED RESOLVING EARLY" << q->toString();
q->onResolvingFinished();
@ -206,7 +206,7 @@ Pipeline::reportResults( QID qid, const QList< result_ptr >& results )
if ( decQIDState( q ) == 0 )
{
if ( !q->solved() )
if ( !q->solved() || q->isFullTextQuery() )
q->onResolvingFinished();
if ( m_qidsTimeout.contains( q->id() ) )

@ -33,7 +33,7 @@
class ClearButton;
class SearchButton;
class SearchLineEdit : public LineEdit
class DLLEXPORT SearchLineEdit : public LineEdit
{
Q_OBJECT

@ -174,7 +174,7 @@ TrackProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourceParen
if ( q->numResults() )
r = q->results().first();
if ( !m_showOfflineResults && !r.isNull() && !r->collection()->source()->isOnline() )
if ( !m_showOfflineResults && !r.isNull() && !r->isOnline() )
return false;
if ( filterRegExp().isEmpty() )

@ -42,6 +42,17 @@ Query::get( const QString& artist, const QString& track, const QString& album, c
}
query_ptr
Query::get( const QString& query, const QID& qid )
{
query_ptr q = query_ptr( new Query( query, qid ) );
if ( !qid.isEmpty() )
Pipeline::instance()->resolve( q );
return q;
}
Query::Query( const QString& artist, const QString& track, const QString& album, const QID& qid )
: m_solved( false )
, m_playable( false )
@ -59,6 +70,21 @@ Query::Query( const QString& artist, const QString& track, const QString& album,
}
Query::Query( const QString& query, const QID& qid )
: m_solved( false )
, m_playable( false )
, m_resolveFinished( false )
, m_qid( qid )
, m_fullTextQuery( query )
, m_duration( -1 )
{
if ( !qid.isEmpty() )
{
connect( Database::instance(), SIGNAL( indexReady() ), SLOT( refreshResults() ), Qt::QueuedConnection );
}
}
void
Query::addResults( const QList< Tomahawk::result_ptr >& newresults )
{

@ -1,5 +1,5 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
*
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
*
* Tomahawk is free software: you can redistribute it and/or modify
@ -46,7 +46,10 @@ friend class ::DatabaseCommand_LoadPlaylistEntries;
public:
static query_ptr get( const QString& artist, const QString& track, const QString& album, const QID& qid = QString() );
static query_ptr get( const QString& query, const QID& qid );
explicit Query( const QString& artist, const QString& track, const QString& album, const QID& qid );
explicit Query( const QString& query, const QID& qid );
/// returns list of all results so far
QList< result_ptr > results() const;
@ -64,8 +67,9 @@ public:
/// true when any result has been found (score may be less than 1.0)
bool playable() const { return m_playable; }
bool isFullTextQuery() const { return !m_fullTextQuery.isEmpty(); }
bool resolvingFinished() const { return m_resolveFinished; }
unsigned int lastPipelineWeight() const { return m_lastpipelineweight; }
void setLastPipelineWeight( unsigned int w ) { m_lastpipelineweight = w; }
@ -75,7 +79,7 @@ public:
void setResultHint( const QString& resultHint ) { m_resultHint = resultHint; }
void setDuration( int duration ) { m_duration = duration; }
void setResolveFinished( bool resolved ) { m_resolveFinished = resolved; }
QVariant toVariant() const;
QString toString() const;
@ -83,6 +87,7 @@ public:
QString artist() const { return m_artist; }
QString album() const { return m_album; }
QString track() const { return m_track; }
QString fullTextQuery() const { return m_fullTextQuery; }
int duration() const { return m_duration; }
signals:
@ -92,7 +97,7 @@ signals:
void resultsChanged();
void solvedStateChanged( bool state );
void resolvingFinished( bool hasResults );
public slots:
/// (indirectly) called by resolver plugins when results are found
void addResults( const QList< Tomahawk::result_ptr >& );
@ -118,6 +123,8 @@ private:
QString m_artist;
QString m_album;
QString m_track;
QString m_fullTextQuery;
int m_duration;
QString m_resultHint;

@ -584,7 +584,7 @@ ViewManager::setPage( ViewPage* page, bool trackHistory )
// save the old playlist shuffle state in config before we change playlists
saveCurrentPlaylistSettings();
unlinkPlaylist();
if ( !m_pageHistory.contains( page ) )
@ -630,6 +630,7 @@ ViewManager::setPage( ViewPage* page, bool trackHistory )
updateView();
}
bool
ViewManager::isNewPlaylistPageVisible() const
{
@ -661,7 +662,7 @@ ViewManager::saveCurrentPlaylistSettings()
{
TomahawkSettings* s = TomahawkSettings::instance();
Tomahawk::playlist_ptr pl = playlistForInterface( currentPlaylistInterface() );
if ( !pl.isNull() ) {
s->setShuffleState( pl->guid(), currentPlaylistInterface()->shuffled() );
s->setRepeatMode( pl->guid(), currentPlaylistInterface()->repeatMode() );
@ -726,7 +727,7 @@ ViewManager::updateView()
m_infobar->setCaption( currentPage()->title() );
m_infobar->setDescription( currentPage()->description() );
m_infobar->setPixmap( currentPage()->pixmap() );
// turn on shuffle/repeat mode for the new playlist view if specified in config
loadCurrentPlaylistSettings();
}
@ -739,7 +740,7 @@ ViewManager::loadCurrentPlaylistSettings()
if ( !pl.isNull() ) {
currentPlaylistInterface()->setShuffled( s->shuffleState( pl->guid() ));
currentPlaylistInterface()->setRepeatMode( s->repeatMode( pl->guid() ));
} else {
} else {
Tomahawk::dynplaylist_ptr dynPl = dynamicPlaylistForInterface( currentPlaylistInterface() );
if ( !dynPl.isNull() ) {
currentPlaylistInterface()->setShuffled( s->shuffleState( dynPl->guid() ));

@ -125,11 +125,9 @@ NewPlaylistWidget::suggestionsFound()
m_suggestionsModel = new PlaylistModel( ui->suggestionsView );
ui->suggestionsView->setPlaylistModel( m_suggestionsModel );
QList<Tomahawk::query_ptr> ql;
foreach( const Tomahawk::query_ptr& query, m_queries )
{
m_suggestionsModel->append( query );
ql.append( query );
}
loader->deleteLater();

@ -75,11 +75,24 @@ QtScriptResolver::resolve( const Tomahawk::query_ptr& query )
}
// qDebug() << Q_FUNC_INFO << query->toString();
QString eval = QString( "resolve( '%1', '%2', '%3', '%4' );" )
.arg( query->id().replace( "'", "\\'" ) )
.arg( query->artist().replace( "'", "\\'" ) )
.arg( query->album().replace( "'", "\\'" ) )
.arg( query->track().replace( "'", "\\'" ) );
QString eval;
if ( !query->isFullTextQuery() )
{
eval = QString( "resolve( '%1', '%2', '%3', '%4' );" )
.arg( query->id().replace( "'", "\\'" ) )
.arg( query->artist().replace( "'", "\\'" ) )
.arg( query->album().replace( "'", "\\'" ) )
.arg( query->track().replace( "'", "\\'" ) );
}
else
{
eval = QString( "resolve( '%1', '%2', '%3', '%4' );" )
.arg( query->id().replace( "'", "\\'" ) )
.arg( query->fullTextQuery().replace( "'", "\\'" ) )
.arg( QString() )
.arg( QString() );
}
QList< Tomahawk::result_ptr > results;

@ -208,12 +208,22 @@ ScriptResolver::resolve( const Tomahawk::query_ptr& query )
{
QVariantMap m;
m.insert( "_msgtype", "rq" );
m.insert( "artist", query->artist() );
m.insert( "track", query->track() );
m.insert( "qid", query->id() );
if ( query->isFullTextQuery() )
{
m.insert( "fulltext", query->fullTextQuery() );
m.insert( "artist", query->artist() );
m.insert( "track", query->fullTextQuery() );
m.insert( "qid", query->id() );
}
else
{
m.insert( "artist", query->artist() );
m.insert( "track", query->track() );
m.insert( "qid", query->id() );
}
const QByteArray msg = m_serializer.serialize( QVariant( m ) );
// qDebug() << "ASKING SCRIPT RESOLVER TO RESOLVE:" << msg;
sendMsg( msg );
}

@ -18,6 +18,7 @@
#include "tomahawkwindow.h"
#include "ui_tomahawkwindow.h"
#include "ui_searchwidget.h"
#include "config.h"
@ -28,6 +29,7 @@
#include <QInputDialog>
#include <QPixmap>
#include <QPropertyAnimation>
#include <QLineEdit>
#include <QMessageBox>
#include <QNetworkAccessManager>
#include <QNetworkReply>
@ -50,6 +52,7 @@
#include "utils/widgetdragfilter.h"
#include "utils/xspfloader.h"
#include "widgets/newplaylistwidget.h"
#include "widgets/searchwidget.h"
#include "widgets/playlisttypeselectordlg.h"
#include "audiocontrols.h"
@ -74,6 +77,7 @@ using namespace Tomahawk;
TomahawkWindow::TomahawkWindow( QWidget* parent )
: QMainWindow( parent )
, ui( new Ui::TomahawkWindow )
, m_searchWidget( new Ui::SearchWidget )
, m_audioControls( new AudioControls( this ) )
, m_trayIcon( new TomahawkTrayIcon( this ) )
, m_sourcetree( 0 )
@ -92,7 +96,9 @@ TomahawkWindow::TomahawkWindow( QWidget* parent )
connect( m_audioControls, SIGNAL( playPressed() ), pm, SLOT( onPlayClicked() ) );
connect( m_audioControls, SIGNAL( pausePressed() ), pm, SLOT( onPauseClicked() ) );
m_searchBox = new QWidget();
ui->setupUi( this );
m_searchWidget->setupUi( m_searchBox );
delete ui->sidebarWidget;
delete ui->playlistWidget;
@ -186,6 +192,14 @@ TomahawkWindow::TomahawkWindow( QWidget* parent )
m_forwardAvailable = toolbar->addAction( QIcon( RESPATH "images/forward.png" ), tr( "Forward" ), ViewManager::instance(), SLOT( historyForward() ) );
m_forwardAvailable->setToolTip( tr( "Go forward one page" ) );
m_searchWidget->searchEdit->setStyleSheet( "QLineEdit { border: 1px solid gray; border-radius: 6px; margin-right: 2px; }" );
#ifdef Q_WS_MAC
ui->filterEdit->setAttribute( Qt::WA_MacShowFocusRect, 0 );
#endif
connect( m_searchWidget->searchEdit, SIGNAL( returnPressed() ), SLOT( onSearch() ) );
toolbar->addWidget( m_searchBox );
statusBar()->addPermanentWidget( m_audioControls, 1 );
// propagate sip menu
@ -488,14 +502,17 @@ TomahawkWindow::createPlaylist()
PlaylistTypeSelectorDlg playlistSelectorDlg;
int successfulReturn = playlistSelectorDlg.exec();
if ( !playlistSelectorDlg.playlistTypeIsAuto() && successfulReturn ) {
if ( !playlistSelectorDlg.playlistTypeIsAuto() && successfulReturn )
{
// only show if none is shown yet
if( !ViewManager::instance()->isNewPlaylistPageVisible() ) {
if ( !ViewManager::instance()->isNewPlaylistPageVisible() )
{
ViewManager::instance()->show( new NewPlaylistWidget() );
}
} else if ( playlistSelectorDlg.playlistTypeIsAuto() && successfulReturn ) {
}
else if ( playlistSelectorDlg.playlistTypeIsAuto() && successfulReturn )
{
// create Auto Playlist
QString playlistName = playlistSelectorDlg.playlistName();
APP->mainWindow()->createAutomaticPlaylist( playlistName );
@ -600,13 +617,23 @@ TomahawkWindow::checkForUpdates()
}
void
TomahawkWindow::onSearch()
{
ViewManager::instance()->show( new SearchWidget( m_searchWidget->searchEdit->text() ) );
m_searchWidget->searchEdit->setText( QString() );
}
void
TomahawkWindow::minimize()
{
if ( isMinimized() )
{
showNormal();
} else {
}
else
{
showMinimized();
}
}
@ -618,7 +645,9 @@ TomahawkWindow::maximize()
if ( isMaximized() )
{
showNormal();
} else {
}
else
{
showMaximized();
}
}

@ -38,6 +38,7 @@ class TomahawkTrayIcon;
namespace Ui
{
class TomahawkWindow;
class SearchWidget;
}
class TomahawkWindow : public QMainWindow
@ -88,6 +89,8 @@ private slots:
void onSipPluginAdded( SipPlugin* p );
void onSipPluginRemoved( SipPlugin* p );
void onSearch();
void minimize();
void maximize();
@ -97,6 +100,8 @@ private:
void setupSignals();
Ui::TomahawkWindow* ui;
Ui::SearchWidget* m_searchWidget;
QWidget* m_searchBox;
AudioControls* m_audioControls;
TomahawkTrayIcon* m_trayIcon;
SourceTreeView* m_sourcetree;