/src/sourcetree/items/categoryitems.cpp

http://github.com/tomahawk-player/tomahawk · C++ · 408 lines · 261 code · 77 blank · 70 comment · 49 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. #include "CategoryItems.h"
  20. #include "TomahawkApp.h"
  21. #include "ViewManager.h"
  22. #include "ViewPage.h"
  23. #include "SourceList.h"
  24. #include "SourceTreeView.h"
  25. #include "utils/TomahawkUtils.h"
  26. #include "widgets/NewPlaylistWidget.h"
  27. #include "TomahawkWindow.h"
  28. #include "playlist/dynamic/GeneratorInterface.h"
  29. #include "utils/Logger.h"
  30. #include "DropJob.h"
  31. #include <echonest/Playlist.h>
  32. #include <QMimeData>
  33. using namespace Tomahawk;
  34. /// CategoryAddItem
  35. CategoryAddItem::CategoryAddItem( SourcesModel* model, SourceTreeItem* parent, SourcesModel::CategoryType type )
  36. : SourceTreeItem( model, parent, SourcesModel::CategoryAdd )
  37. , m_categoryType( type )
  38. {
  39. m_icon = QIcon( RESPATH "images/add.png" );
  40. }
  41. CategoryAddItem::~CategoryAddItem()
  42. {
  43. }
  44. QString
  45. CategoryAddItem::text() const
  46. {
  47. switch( m_categoryType )
  48. {
  49. case SourcesModel::PlaylistsCategory:
  50. return tr( "Create new Playlist" );
  51. case SourcesModel::StationsCategory:
  52. return tr( "Create new Station" );
  53. }
  54. return QString();
  55. }
  56. void
  57. CategoryAddItem::activate()
  58. {
  59. switch( m_categoryType )
  60. {
  61. case SourcesModel::PlaylistsCategory:
  62. APP->mainWindow()->createPlaylist();
  63. break;
  64. case SourcesModel::StationsCategory:
  65. APP->mainWindow()->createStation();
  66. break;
  67. }
  68. }
  69. Qt::ItemFlags
  70. CategoryAddItem::flags() const
  71. {
  72. switch ( m_categoryType )
  73. {
  74. case SourcesModel::PlaylistsCategory:
  75. return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled;
  76. case SourcesModel::StationsCategory:
  77. return Qt::ItemIsEnabled | Qt::ItemIsDropEnabled;
  78. default:
  79. return Qt::ItemIsEnabled;
  80. break;
  81. }
  82. }
  83. QIcon
  84. CategoryAddItem::icon() const
  85. {
  86. return m_icon;
  87. }
  88. bool
  89. CategoryAddItem::willAcceptDrag( const QMimeData* data ) const
  90. {
  91. if ( ( m_categoryType == SourcesModel::PlaylistsCategory || m_categoryType == SourcesModel::StationsCategory ) && DropJob::acceptsMimeData( data ) )
  92. {
  93. return true;
  94. }
  95. return false;
  96. }
  97. SourceTreeItem::DropTypes
  98. CategoryAddItem::supportedDropTypes( const QMimeData* data ) const
  99. {
  100. SourceTreeItem::DropTypes types = DropTypesNone;
  101. if ( m_categoryType == SourcesModel::PlaylistsCategory )
  102. {
  103. if ( data->hasFormat( "application/tomahawk.query.list" ) )
  104. return types | DropTypeThisTrack | DropTypeThisAlbum | DropTypeAllFromArtist | DropTypeLocalItems | DropTypeTop50;
  105. else if ( data->hasFormat( "application/tomahawk.result.list" ) )
  106. return types | DropTypeThisTrack | DropTypeThisAlbum | DropTypeAllFromArtist | DropTypeLocalItems | DropTypeTop50;
  107. else if ( data->hasFormat( "application/tomahawk.metadata.album" ) )
  108. return types | DropTypeThisAlbum | DropTypeAllFromArtist | DropTypeLocalItems | DropTypeTop50;
  109. else if ( data->hasFormat( "application/tomahawk.metadata.artist" ) )
  110. return types | DropTypeAllFromArtist | DropTypeLocalItems | DropTypeTop50;
  111. }
  112. return types;
  113. }
  114. bool
  115. CategoryAddItem::dropMimeData( const QMimeData* data, Qt::DropAction )
  116. {
  117. // As DropJob always converts dropped items to query_ptrs for all tracks we need to extract album/artist metadata ourselves for stations
  118. if ( m_categoryType == SourcesModel::StationsCategory &&
  119. ( data->hasFormat( "application/tomahawk.metadata.artist" ) || data->hasFormat( "application/tomahawk.metadata.album" ) ) )
  120. {
  121. QByteArray mimeData;
  122. if ( data->hasFormat( "application/tomahawk.metadata.artist" ) )
  123. mimeData = data->data( "application/tomahawk.metadata.artist" );
  124. else if ( data->hasFormat( "application/tomahawk.metadata.album" ) )
  125. mimeData = data->data( "application/tomahawk.metadata.album" );
  126. QDataStream stream( &mimeData, QIODevice::ReadOnly );
  127. dynplaylist_ptr newpl = DynamicPlaylist::create( SourceList::instance()->getLocal(), uuid(), QString(), "", SourceList::instance()->getLocal()->friendlyName(), OnDemand, false );
  128. newpl->setMode( OnDemand );
  129. QString firstArtist;
  130. // now we want to add each artist as a filter...
  131. QList< dyncontrol_ptr > contrls;
  132. while ( !stream.atEnd() )
  133. {
  134. QString artist;
  135. stream >> artist;
  136. if ( firstArtist.isEmpty() )
  137. firstArtist = artist;
  138. QString album;
  139. if ( data->hasFormat( "application/tomahawk.metadata.album" ) )
  140. stream >> album; // throw away album title... we only create artists filters for now
  141. dyncontrol_ptr c = newpl->generator()->createControl( "Artist" );
  142. c->setInput( QString( "%1" ).arg( artist ) );
  143. contrls << c;
  144. }
  145. QString name = firstArtist.isEmpty() ? tr( "New Station" ) : tr( "%1 Station" ).arg( firstArtist );
  146. newpl->createNewRevision( uuid(), newpl->currentrevision(), newpl->type(), contrls );
  147. newpl->setProperty( "newname", name );
  148. connect( newpl.data(), SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ), this, SLOT( playlistToRenameLoaded() ) );
  149. ViewManager::instance()->show( newpl );
  150. return true;
  151. }
  152. // This could be needed once echonest supports filtering by album.
  153. // If they never will, or if they do and this code still is not used, throw it away!
  154. // If you enable this, make sure to remove the checks for album above.
  155. /* if ( m_categoryType == SourcesModel::StationsCategory && data->hasFormat( "application/tomahawk.metadata.album" ) )
  156. {
  157. QByteArray mimeData = data->data( "application/tomahawk.metadata.album" );
  158. QDataStream stream( &mimeData, QIODevice::ReadOnly );
  159. dynplaylist_ptr newpl = DynamicPlaylist::create( SourceList::instance()->getLocal(), uuid(), QString(), "", SourceList::instance()->getLocal()->friendlyName(), OnDemand, false );
  160. newpl->setMode( OnDemand );
  161. QString firstAlbum;
  162. // now we want to add each artist as a filter...
  163. QList< dyncontrol_ptr > contrls;
  164. while ( !stream.atEnd() )
  165. {
  166. QString artist;
  167. stream >> artist;
  168. QString album;
  169. stream >> album;
  170. if ( firstAlbum.isEmpty() )
  171. {
  172. firstAlbum = album;
  173. }
  174. dyncontrol_ptr c = newpl->generator()->createControl( "Album" );
  175. c->setInput( QString( "%1" ).arg( artist ) );
  176. contrls << c;
  177. }
  178. QString name = firstAlbum.isEmpty() ? tr( "New Station" ) : tr( "%1 Station" ).arg( firstAlbum );
  179. newpl->rename( name );
  180. newpl->createNewRevision( uuid(), newpl->currentrevision(), newpl->type(), contrls );
  181. ViewManager::instance()->show( newpl );
  182. // Give a shot to try to rename it. The playlist has to be created first. ugly.
  183. QTimer::singleShot( 300, APP->mainWindow()->sourceTreeView(), SLOT( renamePlaylist() ) );
  184. return true;
  185. } */
  186. // Create a new playlist seeded with these items
  187. DropJob *dj = new DropJob();
  188. connect( dj, SIGNAL( tracks( QList< Tomahawk::query_ptr > ) ), this, SLOT( parsedDroppedTracks( QList< Tomahawk::query_ptr > ) ), Qt::QueuedConnection );
  189. if ( dropType() == DropTypeAllFromArtist )
  190. dj->setGetWholeArtists( true );
  191. if ( dropType() == DropTypeThisAlbum )
  192. dj->setGetWholeAlbums( true );
  193. if ( dropType() == DropTypeLocalItems )
  194. {
  195. dj->setGetWholeArtists( true );
  196. dj->tracksFromMimeData( data, false, true );
  197. }
  198. else if ( dropType() == DropTypeTop50 )
  199. {
  200. dj->setGetWholeArtists( true );
  201. dj->tracksFromMimeData( data, false, false, true );
  202. }
  203. else
  204. dj->tracksFromMimeData( data, false, false );
  205. return true;
  206. }
  207. void
  208. CategoryAddItem::playlistToRenameLoaded()
  209. {
  210. Playlist* pl = qobject_cast< Playlist* >( sender() );
  211. Q_ASSERT( pl );
  212. QString name = sender()->property( "newname" ).toString();
  213. if ( !name.isEmpty() )
  214. pl->rename( name );
  215. else
  216. QTimer::singleShot( 400, APP->mainWindow()->sourceTreeView(), SLOT( renamePlaylist() ) );
  217. disconnect( pl, SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ), this, SLOT( playlistToRenameLoaded() ) );
  218. disconnect( pl, SIGNAL( revisionLoaded( Tomahawk::PlaylistRevision ) ), this, SLOT( playlistToRenameLoaded() ) );
  219. }
  220. void
  221. CategoryAddItem::parsedDroppedTracks( const QList< query_ptr >& tracks )
  222. {
  223. if ( m_categoryType == SourcesModel::PlaylistsCategory )
  224. {
  225. playlist_ptr newpl = Playlist::create( SourceList::instance()->getLocal(), uuid(), "New Playlist", "", SourceList::instance()->getLocal()->friendlyName(), false, tracks );
  226. ViewManager::instance()->show( newpl );
  227. connect( newpl.data(), SIGNAL( revisionLoaded( Tomahawk::PlaylistRevision ) ), this, SLOT( playlistToRenameLoaded() ) );
  228. }
  229. else if ( m_categoryType == SourcesModel::StationsCategory )
  230. {
  231. // seed the playlist with these song or artist filters
  232. QString name;
  233. name = tracks.isEmpty() ? tr( "New Station" ) : tr( "%1 Station" ).arg( tracks.first()->track() );
  234. dynplaylist_ptr newpl = DynamicPlaylist::create( SourceList::instance()->getLocal(), uuid(), name, "", SourceList::instance()->getLocal()->friendlyName(), OnDemand, false );
  235. newpl->setMode( OnDemand );
  236. // now we want to add each query as a song or similar artist filter...
  237. QList< dyncontrol_ptr > controls;
  238. foreach ( const Tomahawk::query_ptr& q, tracks )
  239. {
  240. dyncontrol_ptr c = newpl->generator()->createControl( "Song" );
  241. c->setInput( QString( "%1 %2" ).arg( q->track() ).arg( q->artist() ) );
  242. controls << c;
  243. }
  244. newpl->createNewRevision( uuid(), newpl->currentrevision(), newpl->type(), controls );
  245. ViewManager::instance()->show( newpl );
  246. connect( newpl.data(), SIGNAL( dynamicRevisionLoaded( Tomahawk::DynamicPlaylistRevision ) ), this, SLOT( playlistToRenameLoaded() ) );
  247. }
  248. }
  249. int
  250. CategoryAddItem::peerSortValue() const
  251. {
  252. return INT_MAX; // after any siblings
  253. }
  254. /// CategoryItem
  255. CategoryItem::CategoryItem( SourcesModel* model, SourceTreeItem* parent, SourcesModel::CategoryType category, bool showAddItem )
  256. : SourceTreeItem( model, parent, SourcesModel::Category )
  257. , m_category( category )
  258. , m_addItem( 0 )
  259. , m_showAdd( showAddItem )
  260. {
  261. // 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.
  262. // beginRowsAdded( 0, 0 );
  263. if ( m_showAdd )
  264. {
  265. m_addItem = new CategoryAddItem( model, this, m_category );
  266. }
  267. // endRowsAdded();
  268. }
  269. void
  270. CategoryItem::insertItem( SourceTreeItem* item )
  271. {
  272. insertItems( QList< SourceTreeItem* >() << item );
  273. }
  274. void
  275. CategoryItem::insertItems( QList< SourceTreeItem* > items )
  276. {
  277. // add the items to the category, and connect to the signals
  278. int curCount = children().size();
  279. if ( m_showAdd ) // if there's an add item, add it before that
  280. curCount--;
  281. beginRowsAdded( curCount, curCount + items.size() - 1 );
  282. foreach( SourceTreeItem* item, items )
  283. {
  284. insertChild( children().count() - 1, item );
  285. }
  286. endRowsAdded();
  287. }
  288. int
  289. CategoryItem::peerSortValue() const
  290. {
  291. if ( m_category == SourcesModel::PlaylistsCategory )
  292. return -100;
  293. else if ( m_category == SourcesModel::StationsCategory )
  294. return 100;
  295. else
  296. return 0;
  297. }
  298. void
  299. CategoryItem::activate()
  300. {
  301. emit toggleExpandRequest( this );
  302. }
  303. QString
  304. CategoryItem::text() const
  305. {
  306. switch( m_category )
  307. {
  308. case SourcesModel::PlaylistsCategory:
  309. return tr( "Playlists" );
  310. case SourcesModel::StationsCategory:
  311. return tr( "Stations" );
  312. }
  313. return QString();
  314. }
  315. Qt::ItemFlags
  316. CategoryItem::flags() const
  317. {
  318. return Qt::ItemIsEnabled;
  319. }
  320. SourcesModel::CategoryType
  321. CategoryItem::categoryType()
  322. {
  323. return m_category;
  324. }