PageRenderTime 45ms CodeModel.GetById 14ms app.highlight 25ms RepoModel.GetById 2ms app.codeStats 0ms

/src/sourcetree/items/categoryitems.cpp

http://github.com/tomahawk-player/tomahawk
C++ | 408 lines | 261 code | 77 blank | 70 comment | 46 complexity | 0b070d16eba307489627a96802cf2abb MD5 | raw file
  1/*
  2 *  Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
  3 *  Copyright 2010-2011, Jeff Mitchell <jeff@tomahawk-player.org>
  4 *
  5 *  This program is free software; you can redistribute it and/or modify
  6 *  it under the terms of the GNU General Public License as published by
  7 *  the Free Software Foundation; either version 2 of the License, or
  8 *  (at your option) any later version.
  9 *
 10 *  This program is distributed in the hope that it will be useful,
 11 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 12 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13 *  GNU General Public License for more details.
 14 *
 15 *  You should have received a copy of the GNU General Public License along
 16 *  with this program; if not, write to the Free Software Foundation, Inc.,
 17 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 18 */
 19
 20#include "CategoryItems.h"
 21
 22
 23#include "TomahawkApp.h"
 24#include "ViewManager.h"
 25#include "ViewPage.h"
 26#include "SourceList.h"
 27#include "SourceTreeView.h"
 28#include "utils/TomahawkUtils.h"
 29#include "widgets/NewPlaylistWidget.h"
 30#include "TomahawkWindow.h"
 31#include "playlist/dynamic/GeneratorInterface.h"
 32#include "utils/Logger.h"
 33#include "DropJob.h"
 34
 35#include <echonest/Playlist.h>
 36
 37#include <QMimeData>
 38
 39using namespace Tomahawk;
 40
 41
 42/// CategoryAddItem
 43
 44CategoryAddItem::CategoryAddItem( SourcesModel* model, SourceTreeItem* parent, SourcesModel::CategoryType type )
 45    : SourceTreeItem( model, parent, SourcesModel::CategoryAdd )
 46    , m_categoryType( type )
 47{
 48    m_icon = QIcon( RESPATH "images/add.png" );
 49}
 50
 51
 52CategoryAddItem::~CategoryAddItem()
 53{
 54}
 55
 56
 57QString
 58CategoryAddItem::text() const
 59{
 60    switch( m_categoryType )
 61    {
 62        case SourcesModel::PlaylistsCategory:
 63            return tr( "Create new Playlist" );
 64
 65        case SourcesModel::StationsCategory:
 66            return tr( "Create new Station" );
 67    }
 68
 69    return QString();
 70}
 71
 72
 73void
 74CategoryAddItem::activate()
 75{
 76    switch( m_categoryType )
 77    {
 78        case SourcesModel::PlaylistsCategory:
 79            APP->mainWindow()->createPlaylist();
 80            break;
 81
 82        case SourcesModel::StationsCategory:
 83            APP->mainWindow()->createStation();
 84            break;
 85    }
 86}
 87
 88
 89Qt::ItemFlags
 90CategoryAddItem::flags() const
 91{
 92    switch ( m_categoryType )
 93    {
 94        case SourcesModel::PlaylistsCategory:
 95            return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled;
 96
 97        case SourcesModel::StationsCategory:
 98            return Qt::ItemIsEnabled | Qt::ItemIsDropEnabled;
 99
100        default:
101            return Qt::ItemIsEnabled;
102            break;
103    }
104}
105
106
107QIcon
108CategoryAddItem::icon() const
109{
110    return m_icon;
111}
112
113
114bool
115CategoryAddItem::willAcceptDrag( const QMimeData* data ) const
116{
117    if ( ( m_categoryType == SourcesModel::PlaylistsCategory || m_categoryType == SourcesModel::StationsCategory ) && DropJob::acceptsMimeData( data ) )
118    {
119        return true;
120    }
121
122    return false;
123}
124
125
126SourceTreeItem::DropTypes
127CategoryAddItem::supportedDropTypes( const QMimeData* data ) const
128{
129    SourceTreeItem::DropTypes types = DropTypesNone;
130
131    if ( m_categoryType == SourcesModel::PlaylistsCategory )
132    {
133        if ( data->hasFormat( "application/tomahawk.query.list" ) )
134            return types | DropTypeThisTrack | DropTypeThisAlbum | DropTypeAllFromArtist | DropTypeLocalItems | DropTypeTop50;
135        else if ( data->hasFormat( "application/tomahawk.result.list" ) )
136            return types | DropTypeThisTrack | DropTypeThisAlbum | DropTypeAllFromArtist | DropTypeLocalItems | DropTypeTop50;
137        else if ( data->hasFormat( "application/tomahawk.metadata.album" ) )
138            return types | DropTypeThisAlbum | DropTypeAllFromArtist | DropTypeLocalItems | DropTypeTop50;
139        else if ( data->hasFormat( "application/tomahawk.metadata.artist" ) )
140            return types | DropTypeAllFromArtist | DropTypeLocalItems | DropTypeTop50;
141    }
142
143    return types;
144}
145
146
147bool
148CategoryAddItem::dropMimeData( const QMimeData* data, Qt::DropAction )
149{
150    // As DropJob always converts dropped items to query_ptrs for all tracks we need to extract album/artist metadata ourselves for stations
151    if ( m_categoryType == SourcesModel::StationsCategory &&
152         ( data->hasFormat( "application/tomahawk.metadata.artist" ) || data->hasFormat( "application/tomahawk.metadata.album" ) ) )
153    {
154        QByteArray mimeData;
155        if ( data->hasFormat( "application/tomahawk.metadata.artist" ) )
156            mimeData = data->data( "application/tomahawk.metadata.artist" );
157        else if ( data->hasFormat( "application/tomahawk.metadata.album" ) )
158            mimeData = data->data( "application/tomahawk.metadata.album" );
159
160        QDataStream stream( &mimeData, QIODevice::ReadOnly );
161
162        dynplaylist_ptr newpl = DynamicPlaylist::create( SourceList::instance()->getLocal(), uuid(), QString(), "", SourceList::instance()->getLocal()->friendlyName(), OnDemand, false );
163        newpl->setMode( OnDemand );
164
165        QString firstArtist;
166        // now we want to add each artist as a filter...
167        QList< dyncontrol_ptr > contrls;
168        while ( !stream.atEnd() )
169        {
170            QString artist;
171            stream >> artist;
172            if ( firstArtist.isEmpty() )
173                firstArtist = artist;
174
175            QString album;
176            if ( data->hasFormat( "application/tomahawk.metadata.album" ) )
177                stream >> album; // throw away album title... we only create artists filters for now
178
179            dyncontrol_ptr c = newpl->generator()->createControl( "Artist" );
180            c->setInput( QString( "%1" ).arg( artist ) );
181            contrls << c;
182        }
183
184        QString name = firstArtist.isEmpty() ? tr( "New Station" ) : tr( "%1 Station" ).arg( firstArtist );
185        newpl->createNewRevision( uuid(), newpl->currentrevision(), newpl->type(), contrls );
186        newpl->setProperty( "newname", name );
187        connect( newpl.data(), SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ), this, SLOT( playlistToRenameLoaded() ) );
188
189        ViewManager::instance()->show( newpl );
190        return true;
191    }
192
193    // This could be needed once echonest supports filtering by album.
194    // If they never will, or if they do and this code still is not used, throw it away!
195    // If you enable this, make sure to remove the checks for album above.
196
197    /* if ( m_categoryType == SourcesModel::StationsCategory && data->hasFormat( "application/tomahawk.metadata.album" ) )
198    {
199        QByteArray mimeData = data->data( "application/tomahawk.metadata.album" );
200        QDataStream stream( &mimeData, QIODevice::ReadOnly );
201
202        dynplaylist_ptr newpl = DynamicPlaylist::create( SourceList::instance()->getLocal(), uuid(), QString(), "", SourceList::instance()->getLocal()->friendlyName(), OnDemand, false );
203        newpl->setMode( OnDemand );
204
205        QString firstAlbum;
206        // now we want to add each artist as a filter...
207        QList< dyncontrol_ptr > contrls;
208        while ( !stream.atEnd() )
209        {
210            QString artist;
211            stream >> artist;
212            QString album;
213            stream >> album;
214
215            if ( firstAlbum.isEmpty() )
216            {
217                firstAlbum = album;
218            }
219
220            dyncontrol_ptr c = newpl->generator()->createControl( "Album" );
221            c->setInput( QString( "%1" ).arg( artist ) );
222            contrls << c;
223        }
224
225        QString name = firstAlbum.isEmpty() ? tr( "New Station" ) : tr( "%1 Station" ).arg( firstAlbum );
226        newpl->rename( name );
227        newpl->createNewRevision( uuid(), newpl->currentrevision(), newpl->type(), contrls );
228
229        ViewManager::instance()->show( newpl );
230        // Give a shot to try to rename it. The playlist has to be created first. ugly.
231        QTimer::singleShot( 300, APP->mainWindow()->sourceTreeView(), SLOT( renamePlaylist() ) );
232        return true;
233
234    } */
235
236    // Create a new playlist seeded with these items
237    DropJob *dj = new DropJob();
238
239    connect( dj, SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ), Qt::QueuedConnection );
240    if ( dropType() == DropTypeAllFromArtist )
241        dj->setGetWholeArtists( true );
242    if ( dropType() == DropTypeThisAlbum )
243        dj->setGetWholeAlbums( true );
244
245    if ( dropType() == DropTypeLocalItems )
246    {
247        dj->setGetWholeArtists( true );
248        dj->tracksFromMimeData( data, false, true );
249    }
250    else if ( dropType() == DropTypeTop50 )
251    {
252        dj->setGetWholeArtists( true );
253        dj->tracksFromMimeData( data, false, false, true );
254    }
255    else
256        dj->tracksFromMimeData( data, false, false );
257
258    return true;
259}
260
261
262void
263CategoryAddItem::playlistToRenameLoaded()
264{
265    Playlist* pl = qobject_cast< Playlist* >( sender() );
266    Q_ASSERT( pl );
267
268    QString name = sender()->property( "newname" ).toString();
269    if ( !name.isEmpty() )
270        pl->rename( name );
271    else
272        QTimer::singleShot( 400, APP->mainWindow()->sourceTreeView(), SLOT( renamePlaylist() ) );
273
274    disconnect( pl, SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ), this, SLOT( playlistToRenameLoaded() ) );
275    disconnect( pl, SIGNAL( revisionLoaded( Tomahawk::PlaylistRevision ) ), this, SLOT( playlistToRenameLoaded() ) );
276}
277
278
279void
280CategoryAddItem::parsedDroppedTracks( const QList< query_ptr >& tracks )
281{
282    if ( m_categoryType == SourcesModel::PlaylistsCategory )
283    {
284        playlist_ptr newpl = Playlist::create( SourceList::instance()->getLocal(), uuid(), "New Playlist", "", SourceList::instance()->getLocal()->friendlyName(), false, tracks );
285        ViewManager::instance()->show( newpl );
286
287        connect( newpl.data(), SIGNAL( revisionLoaded( Tomahawk::PlaylistRevision ) ), this, SLOT( playlistToRenameLoaded() ) );
288    }
289    else if ( m_categoryType == SourcesModel::StationsCategory )
290    {
291        // seed the playlist with these song or artist filters
292        QString name;
293        name = tracks.isEmpty() ? tr( "New Station" ) : tr( "%1 Station" ).arg( tracks.first()->track() );
294
295        dynplaylist_ptr newpl = DynamicPlaylist::create( SourceList::instance()->getLocal(), uuid(), name, "", SourceList::instance()->getLocal()->friendlyName(), OnDemand, false );
296        newpl->setMode( OnDemand );
297
298        // now we want to add each query as a song or similar artist filter...
299        QList< dyncontrol_ptr > controls;
300        foreach ( const Tomahawk::query_ptr& q, tracks )
301        {
302            dyncontrol_ptr c = newpl->generator()->createControl( "Song" );
303            c->setInput( QString( "%1 %2" ).arg( q->track() ).arg( q->artist() ) );
304            controls << c;
305        }
306
307        newpl->createNewRevision( uuid(), newpl->currentrevision(), newpl->type(), controls );
308
309        ViewManager::instance()->show( newpl );
310        connect( newpl.data(), SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ), this, SLOT( playlistToRenameLoaded() ) );
311    }
312}
313
314
315int
316CategoryAddItem::peerSortValue() const
317{
318    return INT_MAX; // after any siblings
319}
320
321
322/// CategoryItem
323
324CategoryItem::CategoryItem( SourcesModel* model, SourceTreeItem* parent, SourcesModel::CategoryType category, bool showAddItem )
325    : SourceTreeItem( model, parent, SourcesModel::Category )
326    , m_category( category )
327    , m_addItem( 0 )
328    , m_showAdd( showAddItem )
329{
330    // in the constructor we're still being added to the parent, so we don't exist to have rows addded yet. so this is safe.
331    //     beginRowsAdded( 0, 0 );
332    if ( m_showAdd )
333    {
334        m_addItem = new CategoryAddItem( model, this, m_category );
335    }
336    //     endRowsAdded();
337}
338
339
340void
341CategoryItem::insertItem( SourceTreeItem* item )
342{
343    insertItems( QList< SourceTreeItem* >() << item );
344}
345
346
347void
348CategoryItem::insertItems( QList< SourceTreeItem* > items )
349{
350    // add the items to the category, and connect to the signals
351    int curCount = children().size();
352    if ( m_showAdd ) // if there's an add item, add it before that
353        curCount--;
354
355    beginRowsAdded( curCount, curCount + items.size() - 1 );
356    foreach( SourceTreeItem* item, items )
357    {
358        insertChild( children().count() - 1, item );
359    }
360    endRowsAdded();
361}
362
363
364int
365CategoryItem::peerSortValue() const
366{
367    if ( m_category == SourcesModel::PlaylistsCategory )
368        return -100;
369    else if ( m_category == SourcesModel::StationsCategory )
370        return 100;
371    else
372        return 0;
373}
374
375
376void
377CategoryItem::activate()
378{
379    emit toggleExpandRequest( this );
380}
381
382
383QString
384CategoryItem::text() const
385{
386    switch( m_category )
387    {
388    case SourcesModel::PlaylistsCategory:
389        return tr( "Playlists" );
390    case SourcesModel::StationsCategory:
391        return tr( "Stations" );
392    }
393    return QString();
394}
395
396
397Qt::ItemFlags
398CategoryItem::flags() const
399{
400    return Qt::ItemIsEnabled;
401}
402
403
404SourcesModel::CategoryType
405CategoryItem::categoryType()
406{
407    return m_category;
408}