File indexing completed on 2024-05-05 04:47:28

0001 /****************************************************************************************
0002  * Copyright (c) 2007 Alexandre Pereira de Oliveira <aleprj@gmail.com>                  *
0003  * Copyright (c) 2007-2009 Maximilian Kossick <maximilian.kossick@googlemail.com>       *
0004  * Copyright (c) 2007 Nikolaj Hald Nielsen <nhn@kde.org>                                *
0005  *                                                                                      *
0006  * This program is free software; you can redistribute it and/or modify it under        *
0007  * the terms of the GNU General Public License as published by the Free Software        *
0008  * Foundation; either version 2 of the License, or (at your option) any later           *
0009  * version.                                                                             *
0010  *                                                                                      *
0011  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
0012  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
0013  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
0014  *                                                                                      *
0015  * You should have received a copy of the GNU General Public License along with         *
0016  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
0017  ****************************************************************************************/
0018 
0019 #define DEBUG_PREFIX "CollectionTreeItemModel"
0020 
0021 #include "CollectionTreeItemModel.h"
0022 
0023 #include <amarokconfig.h>
0024 #include "AmarokMimeData.h"
0025 #include "CollectionTreeItem.h"
0026 #include "core/support/Debug.h"
0027 #include "core/support/Amarok.h"
0028 #include "core/collections/Collection.h"
0029 #include "core/collections/CollectionLocation.h"
0030 #include "core/collections/QueryMaker.h"
0031 #include "core/meta/Meta.h"
0032 #include "core-impl/collections/support/CollectionManager.h"
0033 #include "core-impl/collections/support/FileCollectionLocation.h"
0034 
0035 #include <KLocalizedString>
0036 
0037 #include <QTimer>
0038 #include <QMap>
0039 
0040 CollectionTreeItemModel::CollectionTreeItemModel( const QList<CategoryId::CatMenuId> &levelType )
0041     : CollectionTreeItemModelBase()
0042 {
0043     m_rootItem = new CollectionTreeItem( this );
0044     CollectionManager *collMgr = CollectionManager::instance();
0045     connect( collMgr, &CollectionManager::collectionAdded, this, &CollectionTreeItemModel::collectionAdded, Qt::QueuedConnection );
0046     connect( collMgr, &CollectionManager::collectionRemoved, this, &CollectionTreeItemModel::collectionRemoved );
0047 
0048     QList<Collections::Collection *> collections = CollectionManager::instance()->viewableCollections();
0049     foreach( Collections::Collection *coll, collections )
0050     {
0051         connect( coll, &Collections::Collection::updated, this, &CollectionTreeItemModel::slotFilterWithoutAutoExpand );
0052         m_collections.insert( coll->collectionId(), CollectionRoot( coll, new CollectionTreeItem( coll, m_rootItem, this ) ) );
0053     }
0054 
0055     setLevels( levelType );
0056 }
0057 
0058 Qt::ItemFlags
0059 CollectionTreeItemModel::flags( const QModelIndex &idx ) const
0060 {
0061     if( !idx.isValid() )
0062         return {};
0063 
0064     Qt::ItemFlags flags = CollectionTreeItemModelBase::flags( idx );
0065     if( idx.parent().isValid() )
0066         return flags; // has parent -> not a collection -> no drops
0067 
0068     // we depend on someone (probably CollectionTreeView) to call
0069     // CollectionTreeItemModelBase::setDragSourceCollections() every time a drag is
0070     // initiated or enters collection browser widget
0071     CollectionTreeItem *item = static_cast<CollectionTreeItem*>( idx.internalPointer() );
0072     Q_ASSERT(item->type() == CollectionTreeItem::Collection);
0073     if( m_dragSourceCollections.contains( item->parentCollection() ) )
0074         return flags; // attempt to drag tracks from the same collection, don't allow this (bug 291068)
0075 
0076     if( !item->parentCollection()->isWritable() )
0077         return flags; // not writeable, disallow drops
0078 
0079     // all paranoid checks passed, tracks can be dropped to this item
0080     return flags | Qt::ItemIsDropEnabled;
0081 }
0082 
0083 QVariant
0084 CollectionTreeItemModel::data(const QModelIndex &index, int role) const
0085 {
0086     if (!index.isValid())
0087         return QVariant();
0088 
0089     CollectionTreeItem *item = static_cast<CollectionTreeItem*>(index.internalPointer());
0090     // subtract one here because there is a collection level for this model
0091     return dataForItem( item, role, item->level() - 1 );
0092 }
0093 
0094 bool
0095 CollectionTreeItemModel::dropMimeData( const QMimeData *data, Qt::DropAction action,
0096                                        int row, int column, const QModelIndex &parent )
0097 {
0098     Q_UNUSED(row)
0099     Q_UNUSED(column)
0100     //no drops on empty areas
0101     if( !parent.isValid() )
0102         return false;
0103 
0104     CollectionTreeItem *item = static_cast<CollectionTreeItem*>( parent.internalPointer() );
0105     Q_ASSERT(item->type() == CollectionTreeItem::Collection);
0106 
0107     Collections::Collection *targetCollection = item->parentCollection();
0108     Q_ASSERT(targetCollection);
0109 
0110     //TODO: accept external drops.
0111     const AmarokMimeData *mimeData = qobject_cast<const AmarokMimeData *>( data );
0112     Q_ASSERT(mimeData);
0113 
0114     //TODO: optimize for copy from same provider.
0115     Meta::TrackList tracks = mimeData->tracks();
0116     QMultiMap<Collections::Collection *, Meta::TrackPtr> collectionTrackMap;
0117 
0118     foreach( Meta::TrackPtr track, tracks )
0119     {
0120         Collections::Collection *sourceCollection = track->collection();
0121         collectionTrackMap.insertMulti( sourceCollection, track );
0122     }
0123 
0124     foreach( Collections::Collection *sourceCollection, collectionTrackMap.uniqueKeys() )
0125     {
0126         if( sourceCollection == targetCollection )
0127             continue; // should be already caught by ...Model::flags(), but hey
0128 
0129         Collections::CollectionLocation *sourceLocation;
0130         if( sourceCollection )
0131         {
0132             sourceLocation = sourceCollection->location();
0133             Q_ASSERT(sourceLocation);
0134         }
0135         else
0136         {
0137             sourceLocation = new Collections::FileCollectionLocation();
0138         }
0139 
0140         // we need to create target collection location per each source collection location
0141         // -- prepareSomething() takes ownership of the pointer.
0142         Collections::CollectionLocation *targetLocation = targetCollection->location();
0143         Q_ASSERT(targetLocation);
0144 
0145         if( action == Qt::CopyAction )
0146         {
0147             sourceLocation->prepareCopy( collectionTrackMap.values( sourceCollection ),
0148                                          targetLocation );
0149         }
0150         else if( action == Qt::MoveAction )
0151         {
0152             sourceLocation->prepareMove( collectionTrackMap.values( sourceCollection ),
0153                                          targetLocation );
0154         }
0155     }
0156     return true;
0157 }
0158 
0159 
0160 bool
0161 CollectionTreeItemModel::canFetchMore( const QModelIndex &parent ) const
0162 {
0163     if ( !parent.isValid() )
0164         return false;       //children of the root item are the collections, and they are always known
0165 
0166     CollectionTreeItem *item = static_cast<CollectionTreeItem*>( parent.internalPointer() );
0167     return item->level() <= m_levelType.count() && item->requiresUpdate();
0168 }
0169 
0170 void
0171 CollectionTreeItemModel::fetchMore( const QModelIndex &parent )
0172 {
0173     if ( !parent.isValid() )
0174         return;
0175 
0176     CollectionTreeItem *item = static_cast<CollectionTreeItem*>( parent.internalPointer() );
0177     ensureChildrenLoaded( item );
0178 }
0179 
0180 Qt::DropActions
0181 CollectionTreeItemModel::supportedDropActions() const
0182 {
0183     // this also causes supportedDragActions() to contain move action
0184     return CollectionTreeItemModelBase::supportedDropActions() | Qt::MoveAction;
0185 }
0186 
0187 void
0188 CollectionTreeItemModel::collectionAdded( Collections::Collection *newCollection )
0189 {
0190     if( !newCollection )
0191         return;
0192 
0193     connect( newCollection, &Collections::Collection::updated, this, &CollectionTreeItemModel::slotFilterWithoutAutoExpand ) ;
0194 
0195     QString collectionId = newCollection->collectionId();
0196     if( m_collections.contains( collectionId ) )
0197         return;
0198 
0199     //inserts new collection at the end.
0200     beginInsertRows( QModelIndex(), m_rootItem->childCount(), m_rootItem->childCount() );
0201     m_collections.insert( collectionId, CollectionRoot( newCollection, new CollectionTreeItem( newCollection, m_rootItem, this ) ) );
0202     endInsertRows();
0203 
0204     if( m_collections.count() == 1 )
0205         QTimer::singleShot( 0, this, &CollectionTreeItemModel::requestCollectionsExpansion );
0206 }
0207 
0208 void
0209 CollectionTreeItemModel::collectionRemoved( const QString &collectionId )
0210 {
0211     int count = m_rootItem->childCount();
0212     for( int i = 0; i < count; i++ )
0213     {
0214         CollectionTreeItem *item = m_rootItem->child( i );
0215         if( item && !item->isDataItem() && item->parentCollection()->collectionId() == collectionId )
0216         {
0217             beginRemoveRows( QModelIndex(), i, i );
0218             m_rootItem->removeChild( i );
0219             m_collections.remove( collectionId );
0220             m_expandedCollections.remove( item->parentCollection() );
0221             endRemoveRows();
0222         }
0223     }
0224 }
0225 
0226 void
0227 CollectionTreeItemModel::filterChildren()
0228 {
0229     int count = m_rootItem->childCount();
0230     for ( int i = 0; i < count; i++ )
0231     {
0232         markSubTreeAsDirty( m_rootItem->child( i ) );
0233         ensureChildrenLoaded( m_rootItem->child( i ) );
0234     }
0235 }
0236 
0237 void
0238 CollectionTreeItemModel::requestCollectionsExpansion()
0239 {
0240     for( int i = 0, count = m_rootItem->childCount(); i < count; i++ )
0241     {
0242         Q_EMIT expandIndex( itemIndex( m_rootItem->child( i ) ) );
0243     }
0244 }
0245