File indexing completed on 2024-05-19 04:48:40

0001 /****************************************************************************************
0002  * Copyright (c) 2010 Bart Cerneels <bart.cerneels@kde.org>                             *
0003  *                                                                                      *
0004  * This program is free software; you can redistribute it and/or modify it under        *
0005  * the terms of the GNU General Public License as published by the Free Software        *
0006  * Foundation; either version 2 of the License, or (at your option) any later           *
0007  * version.                                                                             *
0008  *                                                                                      *
0009  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
0010  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
0011  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
0012  *                                                                                      *
0013  * You should have received a copy of the GNU General Public License along with         *
0014  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
0015  ****************************************************************************************/
0016 
0017 #include "PlaylistsByProviderProxy.h"
0018 
0019 #include "AmarokMimeData.h"
0020 #include "PlaylistBrowserModel.h"
0021 
0022 #include "core/playlists/PlaylistProvider.h"
0023 #include "core/support/Debug.h"
0024 #include "playlistmanager/PlaylistManager.h"
0025 #include "widgets/PrettyTreeRoles.h"
0026 
0027 #include <QIcon>
0028 
0029 #include <QStack>
0030 
0031 PlaylistsByProviderProxy::PlaylistsByProviderProxy( int playlistCategory, QObject *parent )
0032     : QtGroupingProxy( parent )
0033     , m_playlistCategory( playlistCategory )
0034 {
0035     // we need this to track providers with no playlists
0036     connect( The::playlistManager(), &PlaylistManager::providerAdded,
0037              this, &PlaylistsByProviderProxy::slotProviderAdded );
0038     connect( The::playlistManager(), &PlaylistManager::providerRemoved,
0039              this, &PlaylistsByProviderProxy::slotProviderRemoved );
0040 }
0041 
0042 //TODO: remove this constructor
0043 PlaylistsByProviderProxy::PlaylistsByProviderProxy( QAbstractItemModel *model, int column, int playlistCategory )
0044         : QtGroupingProxy( model, QModelIndex(), column )
0045         , m_playlistCategory( playlistCategory )
0046 {
0047     setSourceModel( model );
0048 
0049     // we need this to track providers with no playlists
0050     connect( The::playlistManager(), &PlaylistManager::providerAdded,
0051              this, &PlaylistsByProviderProxy::slotProviderAdded );
0052     connect( The::playlistManager(), &PlaylistManager::providerRemoved,
0053              this, &PlaylistsByProviderProxy::slotProviderRemoved );
0054 }
0055 
0056 QVariant
0057 PlaylistsByProviderProxy::data( const QModelIndex &idx, int role ) const
0058 {
0059     //TODO: actions for empty providers
0060 
0061     //TODO: filter out actions not from the provider, possibly using QAction separators marking
0062     // the source of the actions (makes sense in the UI as well.
0063 
0064     //Turn the QVariantList of the source into a comma separated string, but only for the real items
0065     if( !isGroup( idx ) && idx.column() == PlaylistBrowserNS::PlaylistBrowserModel::ProviderColumn
0066         && role == Qt::DisplayRole )
0067     {
0068         QVariant indexData = QtGroupingProxy::data( idx, role );
0069         if( indexData.type() != QVariant::List )
0070             return indexData;
0071 
0072         QString providerString = indexData.toStringList().join( QStringLiteral(", ") );
0073         return QVariant( providerString );
0074     }
0075 
0076     return QtGroupingProxy::data( idx, role );
0077 }
0078 
0079 Qt::ItemFlags
0080 PlaylistsByProviderProxy::flags( const QModelIndex &idx ) const
0081 {
0082     //TODO: check if provider supports addPlaylist for DropEnabled
0083     if( isGroup( idx ) )
0084         return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled;
0085 
0086     return QtGroupingProxy::flags( idx );
0087 }
0088 
0089 bool
0090 PlaylistsByProviderProxy::removeRows( int row, int count, const QModelIndex &parent )
0091 {
0092     DEBUG_BLOCK
0093     bool result;
0094     debug() << "in parent " << parent << "remove " << count << " starting at row " << row;
0095     QModelIndex originalIdx = mapToSource( parent );
0096     result = sourceModel()->removeRows( row, count, originalIdx );
0097     if( result )
0098     {
0099         beginRemoveRows( parent, row, row + count - 1 );
0100         endRemoveRows();
0101     }
0102     return result;
0103 }
0104 
0105 //TODO: move the next 3 implementation to QtGroupingProxy
0106 QStringList
0107 PlaylistsByProviderProxy::mimeTypes() const
0108 {
0109     //nothing to add
0110     return sourceModel()->mimeTypes();
0111 }
0112 
0113 QMimeData *
0114 PlaylistsByProviderProxy::mimeData( const QModelIndexList &indexes ) const
0115 {
0116     DEBUG_BLOCK
0117     QModelIndexList sourceIndexes;
0118     foreach( const QModelIndex &idx, indexes )
0119     {
0120         if( isGroup( idx ) )
0121             continue; // drags not enabled for playlist providers
0122         QModelIndex originalIdx = mapToSource( idx );
0123         if( originalIdx.isValid() )
0124             sourceIndexes << originalIdx;
0125     }
0126 
0127     if( sourceIndexes.isEmpty() )
0128         return nullptr;
0129     return sourceModel()->mimeData( sourceIndexes );
0130 }
0131 
0132 bool
0133 PlaylistsByProviderProxy::dropMimeData( const QMimeData *data, Qt::DropAction action,
0134                                         int row, int column, const QModelIndex &parent )
0135 {
0136     DEBUG_BLOCK
0137     debug() << "Dropped on" << parent << "row" << row << "column" << column << "action" << action;
0138     if( action == Qt::IgnoreAction )
0139         return true;
0140 
0141     if( !isGroup( parent ) ) // drops on empty space fall here, it is okay
0142     {
0143         QModelIndex sourceIndex = mapToSource( parent );
0144         return sourceModel()->dropMimeData( data, action, row, column, sourceIndex );
0145     }
0146 
0147     const AmarokMimeData *amarokData = dynamic_cast<const AmarokMimeData *>( data );
0148     if( !amarokData )
0149     {
0150         debug() << __PRETTY_FUNCTION__ << "supports only drag & drop originating in Amarok.";
0151         return false;
0152     }
0153 
0154     Playlists::PlaylistProvider *provider =
0155         parent.data( PlaylistBrowserNS::PlaylistBrowserModel::ProviderRole )
0156         .value<Playlists::PlaylistProvider *>();
0157     if( !provider )
0158     {
0159         warning() << "Dropped tracks to a group with no (or multiple) providers!";
0160         return false;
0161     }
0162 
0163     if( amarokData->hasFormat( AmarokMimeData::PLAYLIST_MIME ) )
0164     {
0165         debug() << "Dropped playlists to provider" << provider->prettyName();
0166         foreach( Playlists::PlaylistPtr pl, amarokData->playlists() )
0167         {
0168             // few PlaylistProviders implement addPlaylist(), use save() instead:
0169             The::playlistManager()->save( pl->tracks(), pl->name(), provider, false /* editName */ );
0170         }
0171         return true;
0172     }
0173     if( amarokData->hasFormat( AmarokMimeData::TRACK_MIME ) )
0174     {
0175         debug() << "Dropped tracks to provider" << provider->prettyName();
0176         Meta::TrackList tracks = amarokData->tracks();
0177         QString playlistName = Amarok::generatePlaylistName( tracks );
0178         return The::playlistManager()->save( tracks, playlistName, provider );
0179     }
0180 
0181     debug() << __PRETTY_FUNCTION__ << "Unsupported drop mime-data:" << data->formats();
0182     return false;
0183 }
0184 
0185 Qt::DropActions
0186 PlaylistsByProviderProxy::supportedDropActions() const
0187 {
0188     //always add CopyAction because playlists can copied to a Provider
0189     return sourceModel()->supportedDropActions() | Qt::CopyAction;
0190 }
0191 
0192 Qt::DropActions
0193 PlaylistsByProviderProxy::supportedDragActions() const
0194 {
0195     //always add CopyAction because playlists can be put into a different group
0196     return sourceModel()->supportedDragActions() | Qt::CopyAction;
0197 }
0198 
0199 void
0200 PlaylistsByProviderProxy::setSourceModel( QAbstractItemModel *model )
0201 {
0202     if( sourceModel() )
0203         sourceModel()->disconnect();
0204 
0205     QtGroupingProxy::setSourceModel( model );
0206 
0207     connect( sourceModel(), SIGNAL(renameIndex(QModelIndex)),
0208              SLOT(slotRenameIndex(QModelIndex)) );
0209 }
0210 
0211 void
0212 PlaylistsByProviderProxy::buildTree()
0213 {
0214     //clear that data anyway since provider can disappear and should no longer be listed.
0215     m_groupMaps.clear();
0216 
0217     //add the empty providers at the top of the list
0218     PlaylistProviderList providerList =
0219             The::playlistManager()->providersForCategory( m_playlistCategory );
0220 
0221     foreach( Playlists::PlaylistProvider *provider, providerList )
0222     {
0223         slotProviderAdded( provider, provider->category() );
0224     }
0225 
0226     QtGroupingProxy::buildTree();
0227 }
0228 
0229 void
0230 PlaylistsByProviderProxy::slotRenameIndex( const QModelIndex &sourceIdx )
0231 {
0232     QModelIndex idx = mapFromSource( sourceIdx );
0233     if( idx.isValid() )
0234         Q_EMIT renameIndex( idx );
0235 }
0236 
0237 void
0238 PlaylistsByProviderProxy::slotProviderAdded( Playlists::PlaylistProvider *provider, int category )
0239 {
0240     DEBUG_BLOCK
0241     if( category != m_playlistCategory )
0242         return;
0243 
0244     if( provider->playlistCount() > 0
0245         || ( provider->playlistCount() < 0 /* not counted */
0246              && !provider->playlists().isEmpty() ) )
0247             return; // non-empty providers are handled by PlaylistBrowserModel
0248 
0249     ItemData itemData;
0250     itemData.insert( Qt::DisplayRole, provider->prettyName() );
0251     itemData.insert( Qt::DecorationRole, provider->icon() );
0252     itemData.insert( PrettyTreeRoles::DecoratorRole, QVariant::fromValue( provider->providerActions() ) );
0253     itemData.insert( PrettyTreeRoles::DecoratorRoleCount, provider->providerActions().count() );
0254 
0255     itemData.insert( PlaylistBrowserNS::PlaylistBrowserModel::ProviderRole,
0256                         QVariant::fromValue<Playlists::PlaylistProvider*>( provider ) );
0257     RowData rowData;
0258     rowData.insert( PlaylistBrowserNS::PlaylistBrowserModel::PlaylistItemColumn, itemData );
0259     //Provider column is used for filtering.
0260     rowData.insert( PlaylistBrowserNS::PlaylistBrowserModel::ProviderColumn, itemData );
0261 
0262     addEmptyGroup( rowData );
0263 }
0264 
0265 void
0266 PlaylistsByProviderProxy::slotProviderRemoved( Playlists::PlaylistProvider *provider, int category )
0267 {
0268     DEBUG_BLOCK
0269     if( category != m_playlistCategory )
0270         return;
0271 
0272     for( int i = 0; i < rowCount(); i++ )
0273     {
0274         QModelIndex idx = index( i, PlaylistBrowserNS::PlaylistBrowserModel::PlaylistItemColumn );
0275         Playlists::PlaylistProvider *rowProvider = data( idx, PlaylistBrowserNS::PlaylistBrowserModel::ProviderRole )
0276             .value<Playlists::PlaylistProvider *>();
0277         if( rowProvider != provider )
0278             continue;
0279 
0280         removeGroup( idx );
0281     }
0282 }