/src/libtomahawk/playlist/TreeProxyModel.cpp
C++ | 406 lines | 297 code | 73 blank | 36 comment | 67 complexity | eb062b22d1611943d54af03a1d2f006b MD5 | raw file
1/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> === 2 * 3 * Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org> 4 * Copyright 2010-2012, Jeff Mitchell <jeff@tomahawk-player.org> 5 * Copyright 2013, Teo Mrnjavac <teo@kde.org> 6 * 7 * Tomahawk is free software: you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation, either version 3 of the License, or 10 * (at your option) any later version. 11 * 12 * Tomahawk is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with Tomahawk. If not, see <http://www.gnu.org/licenses/>. 19 */ 20 21#include "TreeProxyModel.h" 22 23#include "TreeProxyModelPlaylistInterface.h" 24#include "Source.h" 25#include "Query.h" 26#include "database/Database.h" 27#include "database/DatabaseImpl.h" 28#include "collection/AlbumsRequest.h" 29#include "collection/ArtistsRequest.h" 30#include "database/DatabaseCommand_AllAlbums.h" 31#include "PlayableItem.h" 32#include "utils/Logger.h" 33 34#include <QListView> 35 36TreeProxyModel::TreeProxyModel( QObject* parent ) 37 : PlayableProxyModel( parent ) 38 , m_artistsFilterCmd( 0 ) 39 , m_model( 0 ) 40{ 41 setPlaylistInterface( Tomahawk::playlistinterface_ptr( new Tomahawk::TreeProxyModelPlaylistInterface( this ) ) ); 42} 43 44 45void 46TreeProxyModel::setSourcePlayableModel( TreeModel* model ) 47{ 48 if ( m_model ) 49 { 50 disconnect( m_model, SIGNAL( rowsInserted( QModelIndex, int, int ) ), this, SLOT( onRowsInserted( QModelIndex, int, int ) ) ); 51 disconnect( m_model, SIGNAL( modelReset() ), this, SLOT( onModelReset() ) ); 52 } 53 54 PlayableProxyModel::setSourcePlayableModel( model ); 55 m_model = model; 56 57 if ( m_model ) 58 { 59 connect( m_model, SIGNAL( rowsInserted( QModelIndex, int, int ) ), SLOT( onRowsInserted( QModelIndex, int, int ) ) ); 60 connect( m_model, SIGNAL( modelReset() ), SLOT( onModelReset() ) ); 61 } 62} 63 64 65void 66TreeProxyModel::onRowsInserted( const QModelIndex& parent, int /* start */, int /* end */ ) 67{ 68 if ( m_filter.isEmpty() ) 69 return; 70 if ( sender() != m_model ) 71 return; 72 73 PlayableItem* pi = m_model->itemFromIndex( m_model->index( parent.row(), 0, parent.parent() ) ); 74 if ( pi->artist().isNull() ) 75 return; 76 77 Tomahawk::AlbumsRequest* cmd = 0; 78 if ( !m_model->collection().isNull() ) 79 cmd = m_model->collection()->requestAlbums( pi->artist() ); 80 else 81 cmd = new Tomahawk::DatabaseCommand_AllAlbums( Tomahawk::collection_ptr(), pi->artist() ); 82 83 cmd->setFilter( m_filter ); 84 85 connect( dynamic_cast< QObject* >( cmd ), SIGNAL( albums( QList<Tomahawk::album_ptr> ) ), 86 SLOT( onFilterAlbums( QList<Tomahawk::album_ptr> ) ) ); 87 88 cmd->enqueue(); 89} 90 91 92void 93TreeProxyModel::onModelReset() 94{ 95 m_cache.clear(); 96 m_artistsFilter.clear(); 97 m_albumsFilter.clear(); 98} 99 100 101void 102TreeProxyModel::setFilter( const QString& pattern ) 103{ 104 emit filteringStarted(); 105 106 m_filter = pattern; 107 108 beginResetModel(); 109 m_albumsFilter.clear(); 110 endResetModel(); 111 112 if ( m_artistsFilterCmd ) 113 { 114 disconnect( dynamic_cast< QObject* >( m_artistsFilterCmd ), SIGNAL( artists( QList<Tomahawk::artist_ptr> ) ), 115 this, SLOT( onFilterArtists( QList<Tomahawk::artist_ptr> ) ) ); 116 117 delete m_artistsFilterCmd; 118 m_artistsFilterCmd = 0; 119 } 120 121 if ( m_filter.isEmpty() ) 122 { 123 filterFinished(); 124 } 125 else 126 { 127 Tomahawk::ArtistsRequest* cmd = 0; 128 if ( !m_model->collection().isNull() ) 129 cmd = m_model->collection()->requestArtists(); 130 else 131 cmd = new Tomahawk::DatabaseCommand_AllArtists(); //for SuperCollection, TODO: replace with a proper proxy-ArtistsRequest 132 133 cmd->setFilter( pattern ); 134 m_artistsFilterCmd = cmd; 135 136 connect( dynamic_cast< QObject* >( cmd ), SIGNAL( artists( QList<Tomahawk::artist_ptr> ) ), 137 SLOT( onFilterArtists( QList<Tomahawk::artist_ptr> ) ) ); 138 139 cmd->enqueue(); 140 } 141} 142 143 144QString 145TreeProxyModel::filter() const 146{ 147 return m_filter; 148} 149 150 151void 152TreeProxyModel::onFilterArtists( const QList<Tomahawk::artist_ptr>& artists ) 153{ 154 bool finished = true; 155 m_artistsFilter = artists; 156 m_artistsFilterCmd = 0; 157 158 foreach ( const Tomahawk::artist_ptr& artist, artists ) 159 { 160 const QModelIndex idx = m_model->indexFromArtist( artist ); 161 if ( m_model->rowCount( idx ) ) 162 { 163 finished = false; 164 165 Tomahawk::AlbumsRequest* cmd = m_model->collection()->requestAlbums( artist ); 166 cmd->setFilter( m_filter ); 167 168 connect( dynamic_cast< QObject* >( cmd ), SIGNAL( albums( QList<Tomahawk::album_ptr> ) ), 169 SLOT( onFilterAlbums( QList<Tomahawk::album_ptr> ) ) ); 170 171 cmd->enqueue(); 172 } 173 } 174 175 if ( finished ) 176 filterFinished(); 177} 178 179 180void 181TreeProxyModel::onFilterAlbums( const QList<Tomahawk::album_ptr>& albums ) 182{ 183 foreach ( const Tomahawk::album_ptr& album, albums ) 184 { 185 m_albumsFilter << album; 186 } 187 188 filterFinished(); 189} 190 191 192void 193TreeProxyModel::filterFinished() 194{ 195 if ( m_artistsFilterCmd ) 196 { 197 disconnect( dynamic_cast< QObject* >( m_artistsFilterCmd ), SIGNAL( artists( QList<Tomahawk::artist_ptr> ) ), 198 this, SLOT( onFilterArtists( QList<Tomahawk::artist_ptr> ) ) ); 199 200 delete m_artistsFilterCmd; 201 m_artistsFilterCmd = 0; 202 } 203 204 setFilterRegExp( m_filter ); 205 emit filterChanged( m_filter ); 206 emit filteringFinished(); 207} 208 209 210bool 211TreeProxyModel::filterAcceptsRow( int sourceRow, const QModelIndex& sourceParent ) const 212{ 213 PlayableItem* item = sourceModel()->itemFromIndex( sourceModel()->index( sourceRow, 0, sourceParent ) ); 214 Q_ASSERT( item ); 215 216 //FIXME: m_cache lookup is broken 217 if ( /*m_model->mode() == Tomahawk::DatabaseMode &&*/ !item->query().isNull() ) 218 { 219/* QList< Tomahawk::query_ptr > rl = m_cache.values( sourceParent ); 220 foreach ( const Tomahawk::query_ptr& cachedQuery, rl ) 221 { 222 if ( cachedQuery.isNull() ) 223 continue; 224 225 if ( cachedQuery->track()->track() == item->query()->track()->track() && 226 ( cachedQuery->track()->albumpos() == item->query()->track()->albumpos() || cachedQuery->track()->albumpos() == 0 ) ) 227 { 228 return ( cachedQuery.data() == item->query().data() ); 229 } 230 }*/ 231 232 for ( int i = 0; i < sourceModel()->rowCount( sourceParent ); i++ ) 233 { 234 if ( i == sourceRow ) 235 continue; 236 237 PlayableItem* ti = sourceModel()->itemFromIndex( sourceModel()->index( i, 0, sourceParent ) ); 238 if ( ti && ti->name() == item->name() && !ti->query().isNull() ) 239 { 240 if ( ti->query()->track()->albumpos() == item->query()->track()->albumpos() || ti->query()->track()->albumpos() == 0 || item->query()->track()->albumpos() == 0 ) 241 { 242 if ( item->result().isNull() ) 243 return false; 244 245 if ( !ti->result().isNull() ) 246 { 247 if ( !item->result()->isOnline() && ti->result()->isOnline() ) 248 return false; 249 250 if ( ( !item->result()->isLocal() ) && 251 !ti->result()->isLocal() ) 252 { 253 return false; 254 } 255 } 256 } 257 } 258 } 259 } 260 261 bool accepted = false; 262 if ( m_filter.isEmpty() ) 263 accepted = true; 264 else if ( !item->artist().isNull() ) 265 accepted = m_artistsFilter.contains( item->artist() ); 266 else if ( !item->album().isNull() ) 267 accepted = m_albumsFilter.contains( item->album() ); 268 269 if ( !accepted ) 270 { 271 QStringList sl = m_filter.split( " ", QString::SkipEmptyParts ); 272 foreach( const QString& s, sl ) 273 { 274 if ( !item->name().contains( s, Qt::CaseInsensitive ) && 275 !item->albumName().contains( s, Qt::CaseInsensitive ) && 276 !item->artistName().contains( s, Qt::CaseInsensitive ) ) 277 { 278 return false; 279 } 280 } 281 } 282 283 m_cache.insertMulti( sourceParent, item->query() ); 284 return true; 285} 286 287 288bool 289TreeProxyModel::lessThan( const QModelIndex& left, const QModelIndex& right ) const 290{ 291 PlayableItem* p1 = sourceModel()->itemFromIndex( left ); 292 PlayableItem* p2 = sourceModel()->itemFromIndex( right ); 293 294 if ( !p1 ) 295 return true; 296 if ( !p2 ) 297 return false; 298 299/* if ( !p1->result().isNull() && p2->result().isNull() ) 300 return true; 301 if ( p1->result().isNull() && !p2->result().isNull() ) 302 return false;*/ 303 304 unsigned int albumpos1 = 0; 305 unsigned int albumpos2 = 0; 306 unsigned int discnumber1 = 0; 307 unsigned int discnumber2 = 0; 308 if ( !p1->query().isNull() ) 309 { 310 albumpos1 = p1->query()->track()->albumpos(); 311 discnumber1 = p1->query()->track()->discnumber(); 312 } 313 if ( !p2->query().isNull() ) 314 { 315 albumpos2 = p2->query()->track()->albumpos(); 316 discnumber2 = p2->query()->track()->discnumber(); 317 } 318 if ( !p1->result().isNull() ) 319 { 320 if ( albumpos1 == 0 ) 321 albumpos1 = p1->result()->track()->albumpos(); 322 if ( discnumber1 == 0 ) 323 discnumber1 = p1->result()->track()->discnumber(); 324 } 325 if ( !p2->result().isNull() ) 326 { 327 if ( albumpos2 == 0 ) 328 albumpos2 = p2->result()->track()->albumpos(); 329 if ( discnumber2 == 0 ) 330 discnumber2 = p2->result()->track()->discnumber(); 331 } 332 discnumber1 = qMax( 1, (int)discnumber1 ); 333 discnumber2 = qMax( 1, (int)discnumber2 ); 334 335 if ( discnumber1 != discnumber2 ) 336 { 337 return discnumber1 < discnumber2; 338 } 339 else 340 { 341 if ( albumpos1 != albumpos2 ) 342 return albumpos1 < albumpos2; 343 } 344 345 const QString& lefts = textForItem( p1 ); 346 const QString& rights = textForItem( p2 ); 347 if ( lefts == rights ) 348 return (qint64)&p1 < (qint64)&p2; 349 350 return QString::localeAwareCompare( lefts, rights ) < 0; 351} 352 353 354QString 355TreeProxyModel::textForItem( PlayableItem* item ) const 356{ 357 if ( !item ) 358 return QString(); 359 360 if ( !item->artist().isNull() ) 361 { 362 return item->artist()->sortname(); 363 } 364 else if ( !item->album().isNull() ) 365 { 366 return Tomahawk::DatabaseImpl::sortname( item->album()->name() ); 367 } 368 else if ( !item->result().isNull() ) 369 { 370 return item->result()->track()->trackSortname(); 371 } 372 else if ( !item->query().isNull() ) 373 { 374 return item->query()->track()->track(); 375 } 376 377 return QString(); 378} 379 380 381QModelIndex 382TreeProxyModel::indexFromArtist( const Tomahawk::artist_ptr& artist ) const 383{ 384 return mapFromSource( m_model->indexFromArtist( artist ) ); 385} 386 387 388QModelIndex 389TreeProxyModel::indexFromAlbum( const Tomahawk::album_ptr& album ) const 390{ 391 return mapFromSource( m_model->indexFromAlbum( album ) ); 392} 393 394 395QModelIndex 396TreeProxyModel::indexFromResult( const Tomahawk::result_ptr& result ) const 397{ 398 return mapFromSource( m_model->indexFromResult( result ) ); 399} 400 401 402QModelIndex 403TreeProxyModel::indexFromQuery( const Tomahawk::query_ptr& query ) const 404{ 405 return mapFromSource( m_model->indexFromQuery( query ) ); 406}