PageRenderTime 315ms CodeModel.GetById 81ms app.highlight 163ms RepoModel.GetById 58ms app.codeStats 8ms

/src/sourcetree/items/collectionitem.cpp

http://github.com/tomahawk-player/tomahawk
C++ | 518 lines | 382 code | 98 blank | 38 comment | 63 complexity | 48e3d20a17454d5860d47a2df717cce1 MD5 | raw file
  1/*
  2 *    Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
  3 *
  4 *    This program is free software; you can redistribute it and/or modify
  5 *    it under the terms of the GNU General Public License as published by
  6 *    the Free Software Foundation; either version 2 of the License, or
  7 *    (at your option) any later version.
  8 *
  9 *    This program is distributed in the hope that it will be useful,
 10 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 11 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 12 *    GNU General Public License for more details.
 13 *
 14 *    You should have received a copy of the GNU General Public License along
 15 *    with this program; if not, write to the Free Software Foundation, Inc.,
 16 *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 17 */
 18
 19#include "collectionitem.h"
 20
 21#include "categoryitems.h"
 22#include "playlistitems.h"
 23#include "viewmanager.h"
 24#include "playlist.h"
 25#include "genericpageitems.h"
 26#include "utils/tomahawkutils.h"
 27#include "utils/logger.h"
 28#include "widgets/SocialPlaylistWidget.h"
 29#include "playlist/customplaylistview.h"
 30#include "source.h"
 31#include "temporarypageitem.h"
 32#include <sourcelist.h>
 33
 34/// CollectionItem
 35
 36using namespace Tomahawk;
 37
 38CollectionItem::CollectionItem(  SourcesModel* mdl, SourceTreeItem* parent, const Tomahawk::source_ptr& source )
 39    : SourceTreeItem( mdl, parent, SourcesModel::Collection )
 40    , m_source( source )
 41    , m_playlists( 0 )
 42    , m_stations( 0 )
 43    , m_latchedOn( false )
 44    , m_sourceInfoItem( 0   )
 45    , m_coolPlaylistsItem( 0 )
 46    , m_lovedTracksItem()
 47    , m_sourceInfoPage( 0 )
 48    , m_coolPlaylistsPage( 0 )
 49    , m_lovedTracksPage( 0 )
 50    , m_whatsHotPage( 0 )
 51{
 52    m_lovedTracksItem = new GenericPageItem( model(), this, ( m_source.isNull() ? tr( "Top Loved Tracks" ) : tr( "Loved Tracks" ) ), QIcon( RESPATH "images/loved_playlist.png" ),
 53                                             boost::bind( &CollectionItem::lovedTracksClicked, this ),
 54                                             boost::bind( &CollectionItem::getLovedTracksPage, this ) );
 55    m_lovedTracksItem->setSortValue( -250 );
 56
 57    if ( m_source.isNull() )
 58    {
 59        // super collection
 60        connect( ViewManager::instance(), SIGNAL( tempPageActivated( Tomahawk::ViewPage*) ), this, SLOT( tempPageActivated( Tomahawk::ViewPage* ) ) );
 61
 62                // add misc children of root node
 63        GenericPageItem* recent = new GenericPageItem( model(), this, tr( "Dashboard" ), QIcon( RESPATH "images/dashboard.png" ),
 64                             boost::bind( &ViewManager::showWelcomePage, ViewManager::instance() ),
 65                             boost::bind( &ViewManager::welcomeWidget, ViewManager::instance() )
 66                                                    );
 67        recent->setSortValue( -300 );
 68
 69        GenericPageItem* hot = new GenericPageItem( model(), this, tr( "Charts" ), QIcon( RESPATH "images/charts.png" ),
 70                             boost::bind( &ViewManager::showWhatsHotPage, ViewManager::instance() ),
 71                             boost::bind( &ViewManager::whatsHotWidget, ViewManager::instance() )
 72                                                    );
 73        hot->setSortValue( -300 );
 74
 75
 76        // TODO finish implementing and making pretty
 77//         m_coolPlaylistsItem = new GenericPageItem( model(), this, tr( "Cool Stuff" ), QIcon( RESPATH "images/new-additions.png" ),
 78//                                                    boost::bind( &CollectionItem::coolPlaylistsClicked, this ),
 79//                                                    boost::bind( &CollectionItem::getCoolPlaylistsPage, this )
 80//                                                  );
 81//         m_coolPlaylistsItem->setSortValue( 200 );
 82
 83        m_superCol = TomahawkUtils::createAvatarFrame( QPixmap( RESPATH "images/supercollection.png" ) );
 84
 85        return;
 86    }
 87
 88    m_sourceInfoItem = new GenericPageItem( model(), this, tr( "New Additions" ), QIcon( RESPATH "images/new-additions.png" ),
 89                                            boost::bind( &CollectionItem::sourceInfoClicked, this ),
 90                                            boost::bind( &CollectionItem::getSourceInfoPage, this ) );
 91    m_sourceInfoItem->setSortValue( -300 );
 92
 93    // create category items if there are playlists to show, or stations to show
 94    QList< playlist_ptr > playlists = source->collection()->playlists();
 95    QList< dynplaylist_ptr > autoplaylists = source->collection()->autoPlaylists();
 96    QList< dynplaylist_ptr > stations = source->collection()->stations();
 97
 98    if ( !playlists.isEmpty() || !autoplaylists.isEmpty() || source->isLocal() )
 99    {
100        m_playlists = new CategoryItem( model(), this, SourcesModel::PlaylistsCategory, source->isLocal() );
101        onPlaylistsAdded( playlists );
102        onAutoPlaylistsAdded( autoplaylists );
103    }
104    if ( !stations.isEmpty() || source->isLocal() )
105    {
106        m_stations = new CategoryItem( model(), this, SourcesModel::StationsCategory, source->isLocal() );
107        onStationsAdded( stations );
108    }
109
110    if( ViewManager::instance()->pageForCollection( source->collection() ) )
111        model()->linkSourceItemToPage( this, ViewManager::instance()->pageForCollection( source->collection() ) );
112
113    m_defaultAvatar = TomahawkUtils::createAvatarFrame( QPixmap( RESPATH "images/user-avatar.png" ) );
114
115    // load auto playlists and stations!
116
117    connect( source.data(), SIGNAL( stats( QVariantMap ) ), this, SIGNAL( updated() ) );
118    connect( source.data(), SIGNAL( syncedWithDatabase() ), this, SIGNAL( updated() ) );
119    connect( source.data(), SIGNAL( playbackStarted( Tomahawk::query_ptr ) ), this, SIGNAL( updated() ) );
120    connect( source.data(), SIGNAL( stateChanged() ), this, SIGNAL( updated() ) );
121    connect( source.data(), SIGNAL( offline() ), this, SIGNAL( updated() ) );
122    connect( source.data(), SIGNAL( online() ), this, SIGNAL( updated() ) );
123    connect( SourceList::instance(), SIGNAL( sourceLatchedOn( Tomahawk::source_ptr, Tomahawk::source_ptr ) ), this, SLOT( latchedOn( Tomahawk::source_ptr, Tomahawk::source_ptr ) ) );
124    connect( SourceList::instance(), SIGNAL( sourceLatchedOff( Tomahawk::source_ptr, Tomahawk::source_ptr ) ), this, SLOT( latchedOff( Tomahawk::source_ptr, Tomahawk::source_ptr ) ) );
125
126    connect( source->collection().data(), SIGNAL( playlistsAdded( QList<Tomahawk::playlist_ptr> ) ),
127             SLOT( onPlaylistsAdded( QList<Tomahawk::playlist_ptr> ) ), Qt::QueuedConnection );
128    connect( source->collection().data(), SIGNAL( autoPlaylistsAdded( QList< Tomahawk::dynplaylist_ptr > ) ),
129             SLOT( onAutoPlaylistsAdded( QList<Tomahawk::dynplaylist_ptr> ) ), Qt::QueuedConnection );
130    connect( source->collection().data(), SIGNAL( stationsAdded( QList<Tomahawk::dynplaylist_ptr> ) ),
131             SLOT( onStationsAdded( QList<Tomahawk::dynplaylist_ptr> ) ), Qt::QueuedConnection );
132
133    if ( m_source->isLocal() )
134        QTimer::singleShot(0, this, SLOT(requestExpanding()));
135}
136
137
138Tomahawk::source_ptr
139CollectionItem::source() const
140{
141    return m_source;
142}
143
144
145QString
146CollectionItem::text() const
147{
148    return m_source.isNull() ? tr( "Super Collection" ) : m_source->friendlyName();
149}
150
151
152int
153CollectionItem::IDValue() const
154{
155    if( m_source.isNull() )
156        return -1;
157    if( m_source->isLocal() )
158        return 0;
159
160    return m_source->id();
161}
162
163
164int
165CollectionItem::peerSortValue() const
166{
167    if( m_source.isNull() )
168        return -1;
169    if( m_source->isLocal() )
170        return 0;
171
172    return 1;
173}
174
175
176void
177CollectionItem::activate()
178{
179    ViewPage* p = 0;
180    if ( source().isNull() )
181        p = ViewManager::instance()->showSuperCollection();
182    else
183        p = ViewManager::instance()->show( source()->collection() );
184
185    model()->linkSourceItemToPage( this, p );
186}
187
188
189QIcon
190CollectionItem::icon() const
191{
192    if ( m_source.isNull() )
193       return m_superCol;
194    else
195    {
196        if( m_source->avatar().isNull() )
197            return m_defaultAvatar;
198        else
199            return m_source->avatar( Source::FancyStyle );
200    }
201}
202
203bool
204CollectionItem::localLatchedOn() const
205{
206    // Don't show a listen icon if this is the local collection and we are latched on to someone who went offline
207    // we are technically still latched on (if they come back online we'll be still listening along) but it's not visible
208    // in the UI and confusing to the user why the red headphones are still there
209
210    if ( !m_source.isNull() && m_source->isLocal() &&
211         !m_latchedOnTo.isNull() && !m_latchedOnTo->isOnline() )
212        return false;
213
214    return m_latchedOn;
215}
216
217
218void
219CollectionItem::latchedOff( const source_ptr& from, const source_ptr& to )
220{
221    if ( from->isLocal() && ( m_source == to || m_source == from ) )
222    {
223        m_latchedOn = false;
224        m_latchedOnTo.clear();
225        emit updated();
226    }
227}
228
229void
230CollectionItem::latchedOn( const source_ptr& from, const source_ptr& to )
231{
232    if ( from->isLocal() && ( m_source == to || m_source == from ) )
233    {
234        m_latchedOn = true;
235        m_latchedOnTo = to;
236        emit updated();
237    }
238}
239
240
241void
242CollectionItem::playlistsAddedInternal( SourceTreeItem* parent, const QList< dynplaylist_ptr >& playlists )
243{
244    QList< SourceTreeItem* > items;
245    int addOffset = playlists.first()->author()->isLocal() ? 1 : 0;
246
247    int from = parent->children().count() - addOffset;
248    parent->beginRowsAdded( from, from + playlists.count() - 1 );
249    foreach( const dynplaylist_ptr& p, playlists )
250    {
251        DynamicPlaylistItem* plItem = new DynamicPlaylistItem( model(), parent, p, parent->children().count() - addOffset );
252//        qDebug() << "Dynamic Playlist added:" << p->title() << p->creator() << p->info();
253        p->loadRevision();
254        items << plItem;
255
256        if( p->mode() == Static ) {
257            if( m_source->isLocal() )
258                connect( p.data(), SIGNAL( aboutToBeDeleted( Tomahawk::dynplaylist_ptr ) ),
259                        SLOT( onAutoPlaylistDeleted( Tomahawk::dynplaylist_ptr ) ), Qt::QueuedConnection );
260            else
261                connect( p.data(), SIGNAL( deleted( Tomahawk::dynplaylist_ptr ) ),
262                        SLOT( onAutoPlaylistDeleted( Tomahawk::dynplaylist_ptr ) ), Qt::QueuedConnection );
263        } else {
264            if( m_source->isLocal() )
265                connect( p.data(), SIGNAL( aboutToBeDeleted( Tomahawk::dynplaylist_ptr ) ),
266                        SLOT( onStationDeleted( Tomahawk::dynplaylist_ptr ) ), Qt::QueuedConnection );
267            else
268                connect( p.data(), SIGNAL( deleted( Tomahawk::dynplaylist_ptr ) ),
269                        SLOT( onStationDeleted( Tomahawk::dynplaylist_ptr ) ), Qt::QueuedConnection );
270        }
271    }
272    parent->endRowsAdded();
273}
274
275
276template< typename T >
277void
278CollectionItem::playlistDeletedInternal( SourceTreeItem* parent, const T& p )
279{
280    Q_ASSERT( parent ); // How can we delete playlists if we have none?
281    int curCount = parent->children().count();
282    for( int i = 0; i < curCount; i++ )
283    {
284        PlaylistItem* pl = qobject_cast< PlaylistItem* >( parent->children().at( i ) );
285        if( pl && pl->playlist() == p )
286        {
287            parent->beginRowsRemoved( i, i );
288            parent->removeChild( pl );
289            parent->endRowsRemoved();
290
291            delete pl;
292            break;
293        }
294    }
295
296    if( ( parent == m_playlists || parent == m_stations ) &&
297         parent->children().isEmpty() && parent->parent() ) // Don't leave an empty Playlist or Station category
298    {
299        int idx = parent->parent()->children().indexOf( parent );
300        if( idx < 0 )
301            return;
302
303        parent->parent()->beginRowsRemoved( idx, idx );
304        parent->parent()->removeChild( parent );
305        parent->parent()->endRowsRemoved();
306
307        if( parent == m_playlists )
308            m_playlists = 0;
309        else if( parent == m_stations )
310            m_stations = 0;
311        delete parent;
312    }
313}
314
315
316void
317CollectionItem::onPlaylistsAdded( const QList< playlist_ptr >& playlists )
318{
319//    qDebug() << Q_FUNC_INFO << m_source->friendlyName() << playlists.count();
320
321    if( playlists.isEmpty() )
322        return;
323
324    if( !m_playlists )
325    {
326        // add the category too
327        int cur = children().count();
328        beginRowsAdded( cur, cur );
329        m_playlists = new CategoryItem( model(), this, SourcesModel::PlaylistsCategory, source()->isLocal() );
330        endRowsAdded();
331    }
332
333    QList< SourceTreeItem* > items;
334    int addOffset = playlists.first()->author()->isLocal() ? 1 : 0;
335
336    int from = m_playlists->children().count() - addOffset;
337    m_playlists->beginRowsAdded( from, from + playlists.count() - 1 );
338    foreach( const playlist_ptr& p, playlists )
339    {
340        PlaylistItem* plItem = new PlaylistItem( model(), m_playlists, p, m_playlists->children().count() - addOffset );
341//        qDebug() << "Playlist added:" << p->title() << p->creator() << p->info();
342        p->loadRevision();
343        items << plItem;
344
345        if( m_source->isLocal() )
346            connect( p.data(), SIGNAL( aboutToBeDeleted( Tomahawk::playlist_ptr ) ),
347                    SLOT( onPlaylistDeleted( Tomahawk::playlist_ptr ) ), Qt::QueuedConnection );
348        else
349            connect( p.data(), SIGNAL( deleted( Tomahawk::playlist_ptr ) ),
350                    SLOT( onPlaylistDeleted( Tomahawk::playlist_ptr ) ), Qt::QueuedConnection );
351
352    }
353    m_playlists->endRowsAdded();
354}
355
356
357void
358CollectionItem::onPlaylistDeleted( const  playlist_ptr& playlist )
359{
360    playlistDeletedInternal( m_playlists, playlist );
361}
362
363
364void
365CollectionItem::onAutoPlaylistsAdded( const QList< dynplaylist_ptr >& playlists )
366{
367    if( playlists.isEmpty() )
368        return;
369
370    if( !m_playlists )
371    {
372        // add the category too
373        int cur = children().count();
374        beginRowsAdded( cur, cur );
375        m_playlists = new CategoryItem( model(), this, SourcesModel::PlaylistsCategory, source()->isLocal() );
376        endRowsAdded();
377    }
378
379    playlistsAddedInternal( m_playlists, playlists );
380}
381
382
383void
384CollectionItem::onAutoPlaylistDeleted( const dynplaylist_ptr& playlist )
385{
386    if( !m_playlists )
387        qDebug() << "NO playlist category item for a deleting playlist..";
388
389    playlistDeletedInternal( m_playlists, playlist );
390}
391
392
393void
394CollectionItem::onStationsAdded( const QList< dynplaylist_ptr >& stations )
395{
396    if( stations.isEmpty() )
397        return;
398
399    if( !m_stations )
400    {
401        // add the category too
402        int cur = children().count();
403        beginRowsAdded( cur, cur );
404        m_stations = new CategoryItem( model(), this, SourcesModel::StationsCategory, source()->isLocal() );
405        endRowsAdded();
406    }
407
408    playlistsAddedInternal( m_stations, stations );
409}
410
411
412void
413CollectionItem::onStationDeleted( const dynplaylist_ptr& station )
414{
415    playlistDeletedInternal( m_stations, station );
416}
417
418
419void
420CollectionItem::requestExpanding()
421{
422    emit expandRequest(this);
423}
424
425
426void
427CollectionItem::tempPageActivated( Tomahawk::ViewPage* v )
428{
429    const int idx = children().count();
430    const int latest = children().last()->IDValue();
431
432    foreach ( TemporaryPageItem* page, m_tempItems )
433    {
434        if ( page->page() == v )
435        {
436            emit selectRequest( page );
437            return;
438        }
439    }
440
441    // Only keep 5 temporary pages at once
442    while ( m_tempItems.size() > 4 )
443    {
444        TemporaryPageItem* item = m_tempItems.takeFirst();
445        QTimer::singleShot( 0, item, SLOT( removeFromList() ) );
446    }
447    emit beginRowsAdded( idx, idx );
448    TemporaryPageItem* tempPage = new TemporaryPageItem( model(), this, v, latest + 1 );
449    connect( tempPage, SIGNAL( removed() ), this, SLOT( temporaryPageDestroyed() ) );
450    m_tempItems << tempPage;
451    endRowsAdded();
452    emit selectRequest( tempPage );
453}
454
455void
456CollectionItem::temporaryPageDestroyed()
457{
458    TemporaryPageItem* tempPage = qobject_cast< TemporaryPageItem* >( sender() );
459    Q_ASSERT( tempPage );
460    m_tempItems.removeAll( tempPage );
461}
462
463
464ViewPage*
465CollectionItem::sourceInfoClicked()
466{
467    if( m_source.isNull() )
468        return 0;
469
470    m_sourceInfoPage = ViewManager::instance()->show( m_source );
471    return m_sourceInfoPage;
472}
473
474
475ViewPage*
476CollectionItem::getSourceInfoPage() const
477{
478    return m_sourceInfoPage;
479}
480
481
482ViewPage*
483CollectionItem::coolPlaylistsClicked()
484{
485    if( !m_source.isNull() )
486        return 0;
487
488    if( !m_coolPlaylistsPage )
489        m_coolPlaylistsPage = new SocialPlaylistWidget( ViewManager::instance()->widget() );
490
491    ViewManager::instance()->show( m_coolPlaylistsPage );
492    return m_coolPlaylistsPage;
493}
494
495
496ViewPage*
497CollectionItem::getCoolPlaylistsPage() const
498{
499    return m_coolPlaylistsPage;
500}
501
502
503ViewPage*
504CollectionItem::lovedTracksClicked()
505{
506    if( !m_lovedTracksPage )
507        m_lovedTracksPage = new CustomPlaylistView( m_source.isNull() ? CustomPlaylistView::AllLovedTracks : CustomPlaylistView::SourceLovedTracks, m_source, ViewManager::instance()->widget() );
508
509    ViewManager::instance()->show( m_lovedTracksPage );
510    return m_lovedTracksPage;
511}
512
513
514ViewPage*
515CollectionItem::getLovedTracksPage() const
516{
517    return m_lovedTracksPage;
518}