From 8c10bdfb07a1bbf50a486dbca09d9f75dba67f2e Mon Sep 17 00:00:00 2001 From: Christian Muehlhaeuser Date: Sun, 12 Jun 2011 05:57:57 +0200 Subject: [PATCH] * WIP: Fulltext resolving, score bars and score column. * Added social_attributes table to the database. * Added db migration script. * Fixed database migration. --- data/sql/dbmigrate-23_to_24.sql | 22 ++++ resources.qrc | 1 + .../database/databasecommand_resolve.cpp | 75 +++++------ src/libtomahawk/database/databaseimpl.cpp | 123 +++++++++--------- src/libtomahawk/database/databaseimpl.h | 9 +- src/libtomahawk/database/schema.sql | 20 ++- src/libtomahawk/database/schema.sql.h | 15 ++- .../playlist/collectionproxymodel.cpp | 106 --------------- .../playlist/collectionproxymodel.h | 5 +- src/libtomahawk/playlist/collectionview.cpp | 14 +- .../playlist/playlistitemdelegate.cpp | 98 ++++++++++---- .../playlist/playlistitemdelegate.h | 11 +- src/libtomahawk/playlist/playlistmodel.cpp | 1 + src/libtomahawk/playlist/playlistview.cpp | 18 +-- src/libtomahawk/playlist/playlistview.h | 1 - src/libtomahawk/playlist/trackheader.cpp | 8 +- src/libtomahawk/playlist/trackmodel.cpp | 8 +- src/libtomahawk/playlist/trackmodel.h | 3 +- src/libtomahawk/playlist/trackproxymodel.cpp | 108 +++++++++++++++ src/libtomahawk/playlist/trackproxymodel.h | 1 + src/libtomahawk/playlist/trackview.cpp | 5 +- src/libtomahawk/widgets/searchwidget.cpp | 1 + src/sip/jabber/jabber.cpp | 2 +- thirdparty/jreen | 2 +- 24 files changed, 384 insertions(+), 273 deletions(-) create mode 100644 data/sql/dbmigrate-23_to_24.sql diff --git a/data/sql/dbmigrate-23_to_24.sql b/data/sql/dbmigrate-23_to_24.sql new file mode 100644 index 000000000..31c35f9ab --- /dev/null +++ b/data/sql/dbmigrate-23_to_24.sql @@ -0,0 +1,22 @@ +-- Script to migate from db version 23 to 24. +-- Added the social_attributes table. +-- +-- Separate each command with %% + +CREATE TABLE IF NOT EXISTS social_attributes ( + id INTEGER REFERENCES track(id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED, -- track id + source INTEGER REFERENCES source(id) ON DELETE CASCADE ON UPDATE CASCADE, -- DEFERRABLE INITIALLY DEFERRED, + k TEXT NOT NULL, + v TEXT NOT NULL, + timestamp INTEGER NOT NULL DEFAULT 0 +); + +CREATE INDEX social_attrib_id ON social_attributes(id); + +CREATE INDEX social_attrib_source ON social_attributes(source); + +CREATE INDEX social_attrib_k ON social_attributes(k); + +CREATE INDEX social_attrib_timestamp ON social_attributes(timestamp); + +UPDATE settings SET v = '24' WHERE k == 'schema_version'; diff --git a/resources.qrc b/resources.qrc index 5194ea273..4e88a8098 100644 --- a/resources.qrc +++ b/resources.qrc @@ -102,5 +102,6 @@ ./data/www/auth.na.html ./data/www/tomahawk_banner_small.png ./data/sql/dbmigrate-22_to_23.sql + ./data/sql/dbmigrate-23_to_24.sql diff --git a/src/libtomahawk/database/databasecommand_resolve.cpp b/src/libtomahawk/database/databasecommand_resolve.cpp index 876a9685f..5908f4ada 100644 --- a/src/libtomahawk/database/databasecommand_resolve.cpp +++ b/src/libtomahawk/database/databasecommand_resolve.cpp @@ -36,6 +36,13 @@ DatabaseCommand_Resolve::DatabaseCommand_Resolve( const query_ptr& query ) void DatabaseCommand_Resolve::exec( DatabaseImpl* lib ) { + /* + * 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 + */ + if ( !m_query->resultHint().isEmpty() ) { qDebug() << "Using result-hint to speed up resolving:" << m_query->resultHint(); @@ -64,20 +71,12 @@ void DatabaseCommand_Resolve::resolve( DatabaseImpl* lib ) { QList 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 scorepair_t; // 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< QPair > artists = lib->searchTable( "artist", m_query->artist(), 10 ); + QList< QPair > tracks = lib->searchTable( "track", m_query->track(), 10 ); + QList< QPair > albums = lib->searchTable( "album", m_query->album(), 10 ); if ( artists.length() == 0 || tracks.length() == 0 ) { @@ -90,10 +89,10 @@ DatabaseCommand_Resolve::resolve( DatabaseImpl* lib ) TomahawkSqlQuery files_query = lib->newquery(); QStringList artsl, trksl; - foreach( int i, artists ) - artsl.append( QString::number( i ) ); - foreach( int i, tracks ) - trksl.append( QString::number( i ) ); + for ( int k = 0; k < artists.count(); k++ ) + artsl.append( QString::number( artists.at( k ).first ) ); + for ( int k = 0; k < tracks.count(); k++ ) + trksl.append( QString::number( tracks.at( k ).first ) ); QString artsToken = QString( "file_join.artist IN (%1)" ).arg( artsl.join( "," ) ); QString trksToken = QString( "file_join.track IN (%1)" ).arg( trksl.join( "," ) ); @@ -189,20 +188,12 @@ void DatabaseCommand_Resolve::fullTextResolve( DatabaseImpl* lib ) { QList 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 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 ); + QList< QPair > artists = lib->searchTable( "artist", m_query->fullTextQuery(), 10 ); + QList< QPair > tracks = lib->searchTable( "track", m_query->fullTextQuery(), 10 ); + QList< QPair > albums = lib->searchTable( "album", m_query->fullTextQuery(), 10 ); if ( artists.length() == 0 && tracks.length() == 0 && albums.length() == 0 ) { @@ -215,12 +206,12 @@ DatabaseCommand_Resolve::fullTextResolve( DatabaseImpl* lib ) 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 ) ); + for ( int k = 0; k < artists.count(); k++ ) + artsl.append( QString::number( artists.at( k ).first ) ); + for ( int k = 0; k < tracks.count(); k++ ) + trksl.append( QString::number( tracks.at( k ).first ) ); + for ( int k = 0; k < albums.count(); k++ ) + albsl.append( QString::number( albums.at( k ).first ) ); QString artsToken = QString( "file_join.artist IN (%1)" ).arg( artsl.join( "," ) ); QString trksToken = QString( "file_join.track IN (%1)" ).arg( trksl.join( "," ) ); @@ -241,10 +232,8 @@ DatabaseCommand_Resolve::fullTextResolve( DatabaseImpl* lib ) "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" ) ); + "%1" ) + .arg( tracks.length() > 0 ? trksToken : QString( "0" ) ); files_query.prepare( sql ); files_query.exec(); @@ -288,6 +277,15 @@ DatabaseCommand_Resolve::fullTextResolve( DatabaseImpl* lib ) result->setId( files_query.value( 9 ).toUInt() ); result->setYear( files_query.value( 17 ).toUInt() ); + for ( int k = 0; k < tracks.count(); k++ ) + { + if ( tracks.at( k ).first == (int)result->dbid() ) + { + result->setScore( tracks.at( k ).second ); + break; + } + } + TomahawkSqlQuery attrQuery = lib->newquery(); QVariantMap attr; @@ -301,11 +299,6 @@ DatabaseCommand_Resolve::fullTextResolve( DatabaseImpl* lib ) 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() ); res << result; } diff --git a/src/libtomahawk/database/databaseimpl.cpp b/src/libtomahawk/database/databaseimpl.cpp index b41852ad4..d5409859c 100644 --- a/src/libtomahawk/database/databaseimpl.cpp +++ b/src/libtomahawk/database/databaseimpl.cpp @@ -38,7 +38,7 @@ */ #include "schema.sql.h" -#define CURRENT_SCHEMA_VERSION 23 +#define CURRENT_SCHEMA_VERSION 24 DatabaseImpl::DatabaseImpl( const QString& dbname, Database* parent ) @@ -47,66 +47,46 @@ DatabaseImpl::DatabaseImpl( const QString& dbname, Database* parent ) , m_lastalbid( 0 ) , m_lasttrkid( 0 ) { - db = QSqlDatabase::addDatabase( "QSQLITE", "tomahawk" ); - db.setDatabaseName( dbname ); - if ( !db.open() ) - { - qDebug() << "FAILED TO OPEN DB"; - throw "failed to open db"; // TODO - } - - QSqlQuery qry = QSqlQuery( db ); - bool schemaUpdated = false; - qry.exec( "SELECT v FROM settings WHERE k='schema_version'" ); - if ( qry.next() ) + int version = getDatabaseVersion( dbname ); + + if ( version != CURRENT_SCHEMA_VERSION ) { - int v = qry.value( 0 ).toInt(); - qDebug() << "Current schema is" << v << this->thread(); - if ( v != CURRENT_SCHEMA_VERSION ) + QString newname = QString( "%1.v%2" ).arg( dbname ).arg( version ); + qDebug() << endl << "****************************" << endl; + qDebug() << "Schema version too old: " << version << ". Current version is:" << CURRENT_SCHEMA_VERSION; + qDebug() << "Moving" << dbname << newname; + qDebug() << "If the migration fails, you can recover your DB by copying" << newname << "back to" << dbname; + qDebug() << endl << "****************************" << endl; + + QFile::copy( dbname, newname ); { + db = QSqlDatabase::addDatabase( "QSQLITE", "tomahawk" ); + db.setDatabaseName( dbname ); + if( !db.open() ) + throw "db moving failed"; - QString newname = QString("%1.v%2").arg(dbname).arg(v); - qDebug() << endl << "****************************" << endl; - qDebug() << "Schema version too old: " << v << ". Current version is:" << CURRENT_SCHEMA_VERSION; - qDebug() << "Moving" << dbname << newname; - qDebug() << "If the migration fails, you can recover your DB by copying" << newname << "back to" << dbname; - qDebug() << endl << "****************************" << endl; + TomahawkSqlQuery query = newquery(); + query.exec( "PRAGMA auto_vacuum = FULL" ); - qry.clear(); - qry.finish(); - - db.close(); - db.removeDatabase( "tomahawk" ); - - if( QFile::copy( dbname, newname ) ) - { - db = QSqlDatabase::addDatabase( "QSQLITE", "tomahawk" ); - db.setDatabaseName( dbname ); - if( !db.open() ) - throw "db moving failed"; - - TomahawkSqlQuery query = newquery(); - query.exec( "PRAGMA auto_vacuum = FULL" ); - schemaUpdated = updateSchema( v ); - - if( !schemaUpdated ) - { - Q_ASSERT( false ); - QTimer::singleShot( 0, qApp, SLOT( quit() ) ); - } - - } - else + schemaUpdated = updateSchema( version ); + if ( !schemaUpdated ) { Q_ASSERT( false ); QTimer::singleShot( 0, qApp, SLOT( quit() ) ); - return; } } } else { + db = QSqlDatabase::addDatabase( "QSQLITE", "tomahawk" ); + db.setDatabaseName( dbname ); + if ( !db.open() ) + { + qDebug() << "Failed to open database" << dbname; + throw "failed to open db"; // TODO + } + schemaUpdated = updateSchema( 0 ); } @@ -422,30 +402,23 @@ DatabaseImpl::albumId( int artistid, const QString& name_orig, bool& isnew ) } -QList< int > +QList< QPair > DatabaseImpl::searchTable( const QString& table, const QString& name, uint limit ) { Q_UNUSED( limit ); - QList< int > results; - if( table != "artist" && table != "track" && table != "album" ) - return results; - - QMap< int, float > resultsmap = m_fuzzyIndex->search( table, name ); QList< QPair > resultslist; + if( table != "artist" && table != "track" && table != "album" ) + return resultslist; + + QMap< int, float > resultsmap = m_fuzzyIndex->search( table, name ); foreach( int i, resultsmap.keys() ) { resultslist << QPair( i, (float)resultsmap.value( i ) ); } qSort( resultslist.begin(), resultslist.end(), DatabaseImpl::scorepairSorter ); - for( int k = 0; k < resultslist.count(); k++ ) - { - results << resultslist.at( k ).first; - } - -// qDebug() << "Returning" << results.count() << "results"; - return results; + return resultslist; } @@ -639,3 +612,31 @@ DatabaseImpl::resultFromHint( const Tomahawk::query_ptr& origquery ) return res; } + + +int +DatabaseImpl::getDatabaseVersion( const QString& dbname ) +{ + int version = -1; + { + QSqlDatabase db = QSqlDatabase::addDatabase( "QSQLITE", "tomahawk" ); + db.setDatabaseName( dbname ); + if ( !db.open() ) + { + qDebug() << "Failed to open database" << dbname; + throw "failed to open db"; // TODO + } + + QSqlQuery qry = QSqlQuery( db ); + qry.exec( "SELECT v FROM settings WHERE k='schema_version'" ); + if ( qry.next() ) + { + version = qry.value( 0 ).toInt(); + qDebug() << "Database schema of" << dbname << "is" << version; + } + } + + QSqlDatabase::removeDatabase( "tomahawk" ); + + return version; +} diff --git a/src/libtomahawk/database/databaseimpl.h b/src/libtomahawk/database/databaseimpl.h index 243f04edd..70a029cb9 100644 --- a/src/libtomahawk/database/databaseimpl.h +++ b/src/libtomahawk/database/databaseimpl.h @@ -55,7 +55,7 @@ public: int trackId( int artistid, const QString& name_orig, bool& isnew ); int albumId( int artistid, const QString& name_orig, bool& isnew ); - QList< int > searchTable( const QString& table, const QString& name, uint limit = 10 ); + QList< QPair > searchTable( const QString& table, const QString& name, uint limit = 10 ); QList< int > getTrackFids( int tid ); static QString sortname( const QString& str ); @@ -84,19 +84,18 @@ signals: public slots: private: + static int getDatabaseVersion( const QString& dbname ); + QString cleanSql( const QString& sql ); - - bool m_ready; - bool updateSchema( int oldVersion ); + bool m_ready; QSqlDatabase db; QString m_lastart, m_lastalb, m_lasttrk; int m_lastartid, m_lastalbid, m_lasttrkid; QString m_dbid; - FuzzyIndex* m_fuzzyIndex; }; diff --git a/src/libtomahawk/database/schema.sql b/src/libtomahawk/database/schema.sql index e2f6ce90d..1f5e5593b 100644 --- a/src/libtomahawk/database/schema.sql +++ b/src/libtomahawk/database/schema.sql @@ -227,6 +227,24 @@ CREATE TABLE IF NOT EXISTS track_attributes ( CREATE INDEX track_attrib_id ON track_attributes(id); CREATE INDEX track_attrib_k ON track_attributes(k); +-- social attributes connected to the track. +-- like love, hate, comments, recommendations +-- love=[comment], hate=[comment], comment=Some text +-- NB: since all values are text, numeric values should be zero-padded to a set amount +-- so that we can always do range queries. + +CREATE TABLE IF NOT EXISTS social_attributes ( + id INTEGER REFERENCES track(id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED, -- track id + source INTEGER REFERENCES source(id) ON DELETE CASCADE ON UPDATE CASCADE, -- DEFERRABLE INITIALLY DEFERRED, + k TEXT NOT NULL, + v TEXT NOT NULL, + timestamp INTEGER NOT NULL DEFAULT 0 +); +CREATE INDEX social_attrib_id ON social_attributes(id); +CREATE INDEX social_attrib_source ON social_attributes(source); +CREATE INDEX social_attrib_k ON social_attributes(k); +CREATE INDEX social_attrib_timestamp ON social_attributes(timestamp); + -- playback history @@ -264,4 +282,4 @@ CREATE TABLE IF NOT EXISTS settings ( v TEXT NOT NULL DEFAULT '' ); -INSERT INTO settings(k,v) VALUES('schema_version', '23'); +INSERT INTO settings(k,v) VALUES('schema_version', '24'); diff --git a/src/libtomahawk/database/schema.sql.h b/src/libtomahawk/database/schema.sql.h index 864766dc6..bd085067a 100644 --- a/src/libtomahawk/database/schema.sql.h +++ b/src/libtomahawk/database/schema.sql.h @@ -1,5 +1,5 @@ /* - This file was automatically generated from schema.sql on Fri Apr 15 15:55:52 EDT 2011. + This file was automatically generated from ./schema.sql on Sun Jun 12 05:17:25 CEST 2011. */ static const char * tomahawk_schema_sql = @@ -151,6 +151,17 @@ static const char * tomahawk_schema_sql = ");" "CREATE INDEX track_attrib_id ON track_attributes(id);" "CREATE INDEX track_attrib_k ON track_attributes(k);" +"CREATE TABLE IF NOT EXISTS social_attributes (" +" id INTEGER REFERENCES track(id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED, " +" source INTEGER REFERENCES source(id) ON DELETE CASCADE ON UPDATE CASCADE, " +" k TEXT NOT NULL," +" v TEXT NOT NULL," +" timestamp INTEGER NOT NULL DEFAULT 0" +");" +"CREATE INDEX social_attrib_id ON social_attributes(id);" +"CREATE INDEX social_attrib_source ON social_attributes(source);" +"CREATE INDEX social_attrib_k ON social_attributes(k);" +"CREATE INDEX social_attrib_timestamp ON social_attributes(timestamp);" "CREATE TABLE IF NOT EXISTS playback_log (" " id INTEGER PRIMARY KEY AUTOINCREMENT," " source INTEGER REFERENCES source(id) ON DELETE CASCADE ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED," @@ -172,7 +183,7 @@ static const char * tomahawk_schema_sql = " k TEXT NOT NULL PRIMARY KEY," " v TEXT NOT NULL DEFAULT ''" ");" -"INSERT INTO settings(k,v) VALUES('schema_version', '23');" +"INSERT INTO settings(k,v) VALUES('schema_version', '24');" ; const char * get_tomahawk_sql() diff --git a/src/libtomahawk/playlist/collectionproxymodel.cpp b/src/libtomahawk/playlist/collectionproxymodel.cpp index 46972c6c8..a7eb2f572 100644 --- a/src/libtomahawk/playlist/collectionproxymodel.cpp +++ b/src/libtomahawk/playlist/collectionproxymodel.cpp @@ -29,109 +29,3 @@ CollectionProxyModel::CollectionProxyModel( QObject* parent ) : TrackProxyModel( parent ) { } - - -bool -CollectionProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) const -{ - TrackModelItem* p1 = itemFromIndex( left ); - TrackModelItem* p2 = itemFromIndex( right ); - - if ( !p1 ) - return true; - if ( !p2 ) - return false; - - const Tomahawk::query_ptr& q1 = p1->query(); - const Tomahawk::query_ptr& q2 = p2->query(); - - QString artist1 = q1->artist(); - QString artist2 = q2->artist(); - QString album1 = q1->album(); - QString album2 = q2->album(); - QString track1 = q1->track(); - QString track2 = q2->track(); - unsigned int albumpos1 = 0, albumpos2 = 0; - unsigned int bitrate1 = 0, bitrate2 = 0; - unsigned int mtime1 = 0, mtime2 = 0; - unsigned int id1 = 0, id2 = 0; - unsigned int size1 = 0, size2 = 0; - - if ( q1->numResults() ) - { - const Tomahawk::result_ptr& r = q1->results().at( 0 ); - artist1 = r->artist()->name(); - album1 = r->album()->name(); - track1 = r->track(); - albumpos1 = r->albumpos(); - bitrate1 = r->bitrate(); - mtime1 = r->modificationTime(); - id1 = r->dbid(); - size1 = r->size(); - } - if ( q2->numResults() ) - { - const Tomahawk::result_ptr& r = q2->results().at( 0 ); - artist2 = r->artist()->name(); - album2 = r->album()->name(); - track2 = r->track(); - albumpos2 = r->albumpos(); - bitrate2 = r->bitrate(); - mtime2 = r->modificationTime(); - id2 = r->dbid(); - size2 = r->size(); - } - - if ( left.column() == TrackModel::Artist ) // sort by artist - { - if ( artist1 == artist2 ) - { - if ( album1 == album2 ) - { - if ( albumpos1 == albumpos2 ) - return id1 < id2; - - return albumpos1 < albumpos2; - } - - return QString::localeAwareCompare( album1, album2 ) < 0; - } - - return QString::localeAwareCompare( artist1, artist2 ) < 0; - } - else if ( left.column() == TrackModel::Album ) // sort by album - { - if ( album1 == album2 ) - { - if ( albumpos1 == albumpos2 ) - return id1 < id2; - - return albumpos1 < albumpos2; - } - - return QString::localeAwareCompare( album1, album2 ) < 0; - } - else if ( left.column() == TrackModel::Bitrate ) // sort by bitrate - { - if ( bitrate1 == bitrate2 ) - return id1 < id2; - - return bitrate1 < bitrate2; - } - else if ( left.column() == TrackModel::Age ) // sort by mtime - { - if ( mtime1 == mtime2 ) - return id1 < id2; - - return mtime1 < mtime2; - } - else if ( left.column() == TrackModel::Filesize ) // sort by file size - { - if ( size1 == size2 ) - return id1 < id2; - - return size1 < size2; - } - return QString::localeAwareCompare( sourceModel()->data( left ).toString(), - sourceModel()->data( right ).toString() ) < 0; -} diff --git a/src/libtomahawk/playlist/collectionproxymodel.h b/src/libtomahawk/playlist/collectionproxymodel.h index 63c615f40..61abd3df4 100644 --- a/src/libtomahawk/playlist/collectionproxymodel.h +++ b/src/libtomahawk/playlist/collectionproxymodel.h @@ -1,5 +1,5 @@ /* === This file is part of Tomahawk Player - === - * + * * Copyright 2010-2011, Christian Muehlhaeuser * * Tomahawk is free software: you can redistribute it and/or modify @@ -31,9 +31,6 @@ public: explicit CollectionProxyModel( QObject* parent = 0 ); virtual PlaylistInterface::ViewMode viewMode() const { return PlaylistInterface::Flat; } - -protected: - bool lessThan( const QModelIndex& left, const QModelIndex& right ) const; }; #endif // COLLECTIONPROXYMODEL_H diff --git a/src/libtomahawk/playlist/collectionview.cpp b/src/libtomahawk/playlist/collectionview.cpp index 1b4d02c0f..8d3895389 100644 --- a/src/libtomahawk/playlist/collectionview.cpp +++ b/src/libtomahawk/playlist/collectionview.cpp @@ -63,8 +63,12 @@ void CollectionView::setTrackModel( TrackModel* model ) { TrackView::setTrackModel( model ); + setColumnHidden( TrackModel::Score, true ); // Hide age column per default setGuid( "collectionview" ); + setSortingEnabled( true ); + sortByColumn( 0, Qt::AscendingOrder ); + connect( model, SIGNAL( trackCountChanged( unsigned int ) ), SLOT( onTrackCountChanged( unsigned int ) ) ); } @@ -83,14 +87,14 @@ CollectionView::setupMenus() m_playItemAction = m_itemMenu.addAction( tr( "&Play" ) ); m_addItemsToQueueAction = m_itemMenu.addAction( tr( "Add to &Queue" ) ); - m_itemMenu.addSeparator(); + m_itemMenu.addSeparator(); - foreach( QAction* a, actions() ) - m_itemMenu.addAction( a ); + foreach( QAction* a, actions() ) + m_itemMenu.addAction( a ); // m_addItemsToPlaylistAction = m_itemMenu.addAction( tr( "&Add to Playlist" ) ); - connect( m_playItemAction, SIGNAL( triggered() ), SLOT( playItem() ) ); - connect( m_addItemsToQueueAction, SIGNAL( triggered() ), SLOT( addItemsToQueue() ) ); + connect( m_playItemAction, SIGNAL( triggered() ), SLOT( playItem() ) ); + connect( m_addItemsToQueueAction, SIGNAL( triggered() ), SLOT( addItemsToQueue() ) ); // connect( m_addItemsToPlaylistAction, SIGNAL( triggered() ), SLOT( addItemsToPlaylist() ) ); } diff --git a/src/libtomahawk/playlist/playlistitemdelegate.cpp b/src/libtomahawk/playlist/playlistitemdelegate.cpp index e06bdb9c4..23c8280c7 100644 --- a/src/libtomahawk/playlist/playlistitemdelegate.cpp +++ b/src/libtomahawk/playlist/playlistitemdelegate.cpp @@ -18,6 +18,7 @@ #include "playlistitemdelegate.h" +#include #include #include @@ -36,6 +37,7 @@ PlaylistItemDelegate::PlaylistItemDelegate( TrackView* parent, TrackProxyModel* proxy ) : QStyledItemDelegate( (QObject*)parent ) + , m_style( Detailed ) , m_view( parent ) , m_model( proxy ) { @@ -43,6 +45,13 @@ PlaylistItemDelegate::PlaylistItemDelegate( TrackView* parent, TrackProxyModel* } +void +PlaylistItemDelegate::setStyle( PlaylistItemDelegate::PlaylistItemStyle style ) +{ + m_style = style; +} + + void PlaylistItemDelegate::updateRowSize( const QModelIndex& index ) { @@ -70,6 +79,29 @@ PlaylistItemDelegate::createEditor( QWidget* parent, const QStyleOptionViewItem& void PlaylistItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const +{ + switch ( m_style ) + { + case Detailed: + paintDetailed( painter, option, index ); + break; + + case Short: + paintShort( painter, option, index ); + break; + } +} + + +void +PlaylistItemDelegate::paintShort( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const +{ + +} + + +void +PlaylistItemDelegate::paintDetailed( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const { TrackModelItem* item = m_model->itemFromIndex( m_model->mapToSource( index ) ); if ( !item || item->query().isNull() ) @@ -82,13 +114,46 @@ PlaylistItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& opti opacity = qMax( (float)0.3, opacity ); QColor textColor = TomahawkUtils::alphaBlend( option.palette.color( QPalette::Foreground ), option.palette.color( QPalette::Background ), opacity ); - if ( item->isPlaying() ) - { -// painter->setRenderHint( QPainter::Antialiasing ); - painter->save(); + QStyleOptionViewItemV4 opt = option; + initStyleOption( &opt, index ); + if ( item->isPlaying() ) + opt.state |= QStyle::State_Selected; + if ( item->isPlaying() || index.column() == TrackModel::Score ) + opt.text.clear(); + if ( opt.state & QStyle::State_Selected ) + opt.palette.setColor( QPalette::Text, opt.palette.color( QPalette::HighlightedText ) ); + + qApp->style()->drawControl( QStyle::CE_ItemViewItem, &opt, painter ); + + painter->save(); + if ( index.column() == TrackModel::Score ) + { + if ( opt.state & QStyle::State_Selected ) + painter->setPen( opt.palette.brightText().color() ); + else + painter->setPen( opt.palette.highlight().color() ); + + QRect r = opt.rect.adjusted( 3, 3, -6, -5 ); + painter->drawRect( r ); + + QRect fillR = r; + int fillerWidth = (int)( index.data().toFloat() * (float)fillR.width() ); + fillR.adjust( 0, 0, -( fillR.width() - fillerWidth ), 0 ); + + if ( opt.state & QStyle::State_Selected ) + painter->setBrush( opt.palette.brightText().color() ); + else + painter->setBrush( opt.palette.highlight().color() ); + + painter->drawRect( fillR ); + } + else if ( item->isPlaying() ) + { { - QRect r = option.rect.adjusted( 3, 0, 0, 0 ); + QRect r = opt.rect.adjusted( 3, 0, 0, 0 ); + + // Paint Now Playing Speaker Icon if ( m_view->header()->visualIndex( index.column() ) == 0 ) { r.adjust( 0, 0, 0, -3 ); @@ -96,34 +161,23 @@ PlaylistItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& opti r.adjust( 22, 0, 0, 3 ); } - painter->setPen( option.palette.text().color() ); + painter->setPen( opt.palette.text().color() ); QTextOption to( Qt::AlignVCenter ); QString text = painter->fontMetrics().elidedText( index.data().toString(), Qt::ElideRight, r.width() - 3 ); painter->drawText( r.adjusted( 0, 1, 0, 0 ), text, to ); } -// if ( m_view->header()->visualIndex( index.column() ) == m_view->header()->visibleSectionCount() - 1 ) + // Paint Now Playing Frame + return; //FIXME { - QRect r = QRect( 3, option.rect.y() + 1, m_view->viewport()->width() - 6, option.rect.height() - 2 ); - painter->setPen( option.palette.highlight().color() ); + QRect r = QRect( 3, opt.rect.y() + 1, m_view->viewport()->width() - 6, opt.rect.height() - 2 ); + painter->setPen( opt.palette.highlight().color() ); QPen pen = painter->pen(); pen.setWidth( 1.0 ); painter->setPen( pen ); painter->drawRoundedRect( r, 3.0, 3.0 ); } - - painter->restore(); - } - else - { - if ( const QStyleOptionViewItem *vioption = qstyleoption_cast(&option)) - { - QStyleOptionViewItemV4 o( *vioption ); - o.palette.setColor( QPalette::Text, textColor ); - QStyledItemDelegate::paint( painter, o, index ); - } - else - QStyledItemDelegate::paint( painter, option, index ); } + painter->restore(); } diff --git a/src/libtomahawk/playlist/playlistitemdelegate.h b/src/libtomahawk/playlist/playlistitemdelegate.h index 5ba66bbd9..eabe03c1b 100644 --- a/src/libtomahawk/playlist/playlistitemdelegate.h +++ b/src/libtomahawk/playlist/playlistitemdelegate.h @@ -1,5 +1,5 @@ /* === This file is part of Tomahawk Player - === - * + * * Copyright 2010-2011, Christian Muehlhaeuser * * Tomahawk is free software: you can redistribute it and/or modify @@ -31,8 +31,12 @@ class DLLEXPORT PlaylistItemDelegate : public QStyledItemDelegate Q_OBJECT public: + enum PlaylistItemStyle + { Detailed = 0, Short = 1 }; + PlaylistItemDelegate( TrackView* parent = 0, TrackProxyModel* proxy = 0 ); + void setStyle( PlaylistItemDelegate::PlaylistItemStyle style ); void updateRowSize( const QModelIndex& index ); public slots: @@ -41,13 +45,16 @@ public slots: protected: void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const; QSize sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const; - QWidget* createEditor( QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index ) const; private: + void paintDetailed( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const; + void paintShort( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const; + unsigned int m_removalProgress; QPixmap m_nowPlayingIcon; + PlaylistItemStyle m_style; TrackView* m_view; TrackProxyModel* m_model; }; diff --git a/src/libtomahawk/playlist/playlistmodel.cpp b/src/libtomahawk/playlist/playlistmodel.cpp index 65c157d9c..d480db8e1 100644 --- a/src/libtomahawk/playlist/playlistmodel.cpp +++ b/src/libtomahawk/playlist/playlistmodel.cpp @@ -509,6 +509,7 @@ PlaylistModel::removeIndex( const QModelIndex& index, bool moreToCome ) } } + bool PlaylistModel::isTemporary() const { diff --git a/src/libtomahawk/playlist/playlistview.cpp b/src/libtomahawk/playlist/playlistview.cpp index 8d59cb6d4..1a3b4084a 100644 --- a/src/libtomahawk/playlist/playlistview.cpp +++ b/src/libtomahawk/playlist/playlistview.cpp @@ -73,10 +73,8 @@ PlaylistView::setPlaylistModel( PlaylistModel* model ) else { setGuid( "playlistview" ); - - m_model->title(); - m_model->description(); } + connect( m_model, SIGNAL( trackCountChanged( unsigned int ) ), SLOT( onTrackCountChanged( unsigned int ) ) ); connect( m_model, SIGNAL( playlistDeleted() ), SLOT( onDeleted() ) ); connect( m_model, SIGNAL( playlistChanged() ), SLOT( onChanged() ) ); @@ -148,12 +146,6 @@ PlaylistView::keyPressEvent( QKeyEvent* event ) } -void -PlaylistView::addItemsToPlaylist() -{ -} - - void PlaylistView::deleteItems() { @@ -189,6 +181,7 @@ PlaylistView::onDeleted() deleteLater(); } + void PlaylistView::onChanged() { @@ -197,12 +190,9 @@ PlaylistView::onChanged() emit nameChanged( m_model->playlist()->title() ); } + bool PlaylistView::isTemporaryPage() const { - if ( m_model ) { - return m_model->isTemporary(); - } else { - return false; - } + return ( m_model && m_model->isTemporary() ); } diff --git a/src/libtomahawk/playlist/playlistview.h b/src/libtomahawk/playlist/playlistview.h index c42ff1248..12bbd934b 100644 --- a/src/libtomahawk/playlist/playlistview.h +++ b/src/libtomahawk/playlist/playlistview.h @@ -65,7 +65,6 @@ private slots: void onCustomContextMenu( const QPoint& pos ); void onTrackCountChanged( unsigned int tracks ); - void addItemsToPlaylist(); void deleteItems(); void onDeleted(); diff --git a/src/libtomahawk/playlist/trackheader.cpp b/src/libtomahawk/playlist/trackheader.cpp index 750f207d2..962665d72 100644 --- a/src/libtomahawk/playlist/trackheader.cpp +++ b/src/libtomahawk/playlist/trackheader.cpp @@ -1,5 +1,5 @@ /* === This file is part of Tomahawk Player - === - * + * * Copyright 2010-2011, Christian Muehlhaeuser * * Tomahawk is free software: you can redistribute it and/or modify @@ -79,11 +79,15 @@ TrackHeader::checkState() QByteArray state = TomahawkSettings::instance()->playlistColumnSizes( m_parent->guid() ); if ( !state.isEmpty() ) + { restoreState( state ); + setSortIndicatorShown( true ); + setSortIndicator( -1, Qt::AscendingOrder ); + } else { QList< double > m_columnWeights; - m_columnWeights << 0.21 << 0.22 << 0.20 << 0.05 << 0.05 << 0.05 << 0.05 << 0.05; // << 0.12; + m_columnWeights << 0.20 << 0.20 << 0.18 << 0.05 << 0.05 << 0.05 << 0.05 << 0.05 << 0.12; // << 0.05; for ( int i = 0; i < count() - 1; i++ ) { diff --git a/src/libtomahawk/playlist/trackmodel.cpp b/src/libtomahawk/playlist/trackmodel.cpp index 51ae0590a..79bff2a00 100644 --- a/src/libtomahawk/playlist/trackmodel.cpp +++ b/src/libtomahawk/playlist/trackmodel.cpp @@ -82,7 +82,7 @@ int TrackModel::columnCount( const QModelIndex& parent ) const { Q_UNUSED( parent ); - return 9; + return 10; } @@ -183,6 +183,10 @@ TrackModel::data( const QModelIndex& index, int role ) const case Origin: return query->results().first()->friendlySource(); break; + + case Score: + return query->results().first()->score(); + break; } } @@ -195,7 +199,7 @@ TrackModel::headerData( int section, Qt::Orientation orientation, int role ) con { Q_UNUSED( orientation ); QStringList headers; - headers << tr( "Artist" ) << tr( "Track" ) << tr( "Album" ) << tr( "Duration" ) << tr( "Bitrate" ) << tr( "Age" ) << tr( "Year" ) << tr( "Size" ) << tr( "Origin" ); + headers << tr( "Artist" ) << tr( "Track" ) << tr( "Album" ) << tr( "Duration" ) << tr( "Bitrate" ) << tr( "Age" ) << tr( "Year" ) << tr( "Size" ) << tr( "Origin" ) << tr( "Score" ); if ( role == Qt::DisplayRole && section >= 0 ) { return headers.at( section ); diff --git a/src/libtomahawk/playlist/trackmodel.h b/src/libtomahawk/playlist/trackmodel.h index 843e8f72a..5a935c8df 100644 --- a/src/libtomahawk/playlist/trackmodel.h +++ b/src/libtomahawk/playlist/trackmodel.h @@ -42,7 +42,8 @@ public: Age = 5, Year = 6, Filesize = 7, - Origin = 8 + Origin = 8, + Score = 9 }; explicit TrackModel( QObject* parent = 0 ); diff --git a/src/libtomahawk/playlist/trackproxymodel.cpp b/src/libtomahawk/playlist/trackproxymodel.cpp index dbc22c543..224f995cb 100644 --- a/src/libtomahawk/playlist/trackproxymodel.cpp +++ b/src/libtomahawk/playlist/trackproxymodel.cpp @@ -270,3 +270,111 @@ TrackProxyModel::removeIndexes( const QList& indexes ) sourceModel()->removeIndex( idx, b ); } } + + +bool +TrackProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) const +{ + qDebug() << Q_FUNC_INFO; + + TrackModelItem* p1 = itemFromIndex( left ); + TrackModelItem* p2 = itemFromIndex( right ); + + if ( !p1 ) + return true; + if ( !p2 ) + return false; + + const Tomahawk::query_ptr& q1 = p1->query(); + const Tomahawk::query_ptr& q2 = p2->query(); + + QString artist1 = q1->artist(); + QString artist2 = q2->artist(); + QString album1 = q1->album(); + QString album2 = q2->album(); + QString track1 = q1->track(); + QString track2 = q2->track(); + unsigned int albumpos1 = 0, albumpos2 = 0; + unsigned int bitrate1 = 0, bitrate2 = 0; + unsigned int mtime1 = 0, mtime2 = 0; + unsigned int id1 = 0, id2 = 0; + unsigned int size1 = 0, size2 = 0; + + if ( q1->numResults() ) + { + const Tomahawk::result_ptr& r = q1->results().at( 0 ); + artist1 = r->artist()->name(); + album1 = r->album()->name(); + track1 = r->track(); + albumpos1 = r->albumpos(); + bitrate1 = r->bitrate(); + mtime1 = r->modificationTime(); + id1 = r->dbid(); + size1 = r->size(); + } + if ( q2->numResults() ) + { + const Tomahawk::result_ptr& r = q2->results().at( 0 ); + artist2 = r->artist()->name(); + album2 = r->album()->name(); + track2 = r->track(); + albumpos2 = r->albumpos(); + bitrate2 = r->bitrate(); + mtime2 = r->modificationTime(); + id2 = r->dbid(); + size2 = r->size(); + } + + if ( left.column() == TrackModel::Artist ) // sort by artist + { + if ( artist1 == artist2 ) + { + if ( album1 == album2 ) + { + if ( albumpos1 == albumpos2 ) + return id1 < id2; + + return albumpos1 < albumpos2; + } + + return QString::localeAwareCompare( album1, album2 ) < 0; + } + + return QString::localeAwareCompare( artist1, artist2 ) < 0; + } + else if ( left.column() == TrackModel::Album ) // sort by album + { + if ( album1 == album2 ) + { + if ( albumpos1 == albumpos2 ) + return id1 < id2; + + return albumpos1 < albumpos2; + } + + return QString::localeAwareCompare( album1, album2 ) < 0; + } + else if ( left.column() == TrackModel::Bitrate ) // sort by bitrate + { + if ( bitrate1 == bitrate2 ) + return id1 < id2; + + return bitrate1 < bitrate2; + } + else if ( left.column() == TrackModel::Age ) // sort by mtime + { + if ( mtime1 == mtime2 ) + return id1 < id2; + + return mtime1 < mtime2; + } + else if ( left.column() == TrackModel::Filesize ) // sort by file size + { + if ( size1 == size2 ) + return id1 < id2; + + return size1 < size2; + } + return QString::localeAwareCompare( sourceModel()->data( left ).toString(), + sourceModel()->data( right ).toString() ) < 0; +} diff --git a/src/libtomahawk/playlist/trackproxymodel.h b/src/libtomahawk/playlist/trackproxymodel.h index 853a512d8..ee8b65753 100644 --- a/src/libtomahawk/playlist/trackproxymodel.h +++ b/src/libtomahawk/playlist/trackproxymodel.h @@ -77,6 +77,7 @@ public slots: protected: bool filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent ) const; + bool lessThan( const QModelIndex& left, const QModelIndex& right ) const; private: TrackModel* m_model; diff --git a/src/libtomahawk/playlist/trackview.cpp b/src/libtomahawk/playlist/trackview.cpp index 729394e4a..0ee1c8a8d 100644 --- a/src/libtomahawk/playlist/trackview.cpp +++ b/src/libtomahawk/playlist/trackview.cpp @@ -50,7 +50,6 @@ TrackView::TrackView( QWidget* parent ) , m_resizing( false ) , m_dragging( false ) { - setSortingEnabled( false ); setAlternatingRowColors( true ); setSelectionMode( QAbstractItemView::ExtendedSelection ); setSelectionBehavior( QAbstractItemView::SelectRows ); @@ -63,9 +62,11 @@ TrackView::TrackView( QWidget* parent ) setRootIsDecorated( false ); setUniformRowHeights( true ); setMinimumWidth( 300 ); -// setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOn ); + // setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOn ); setHeader( m_header ); + setSortingEnabled( true ); + sortByColumn( -1 ); #ifndef Q_WS_WIN QFont f = font(); diff --git a/src/libtomahawk/widgets/searchwidget.cpp b/src/libtomahawk/widgets/searchwidget.cpp index 34350f4df..e4a734915 100644 --- a/src/libtomahawk/widgets/searchwidget.cpp +++ b/src/libtomahawk/widgets/searchwidget.cpp @@ -42,6 +42,7 @@ SearchWidget::SearchWidget( const QString& search, QWidget* parent ) m_resultsModel = new PlaylistModel( ui->resultsView ); ui->resultsView->setPlaylistModel( m_resultsModel ); ui->resultsView->overlay()->setEnabled( false ); + ui->resultsView->sortByColumn( PlaylistModel::Score, Qt::DescendingOrder ); m_queries << Tomahawk::Query::get( search, uuid() ); diff --git a/src/sip/jabber/jabber.cpp b/src/sip/jabber/jabber.cpp index 29ee22a16..721c654d0 100644 --- a/src/sip/jabber/jabber.cpp +++ b/src/sip/jabber/jabber.cpp @@ -685,7 +685,7 @@ void JabberPlugin::onPresenceReceived( const Jreen::RosterItem::Ptr &item, const Jreen::IQ featuresIq( Jreen::IQ::Get, jid ); featuresIq.addExtension( new Jreen::Disco::Info( node ) ); - + Jreen::IQReply *reply = m_client->send(featuresIq); reply->setData(RequestDisco); connect(reply, SIGNAL(received(Jreen::IQ)), SLOT(onNewIq(Jreen::IQ))); diff --git a/thirdparty/jreen b/thirdparty/jreen index 8f995f246..2957d0ff0 160000 --- a/thirdparty/jreen +++ b/thirdparty/jreen @@ -1 +1 @@ -Subproject commit 8f995f246637f533feb7124744e113034a32b505 +Subproject commit 2957d0ff03d9561af8afc4bd3a45947392868875