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) 2009 Seb Ruiz <ruiz@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 #include "CollectionTreeItem.h"
0020 
0021 #include "amarokconfig.h"
0022 #include "browsers/CollectionTreeItemModelBase.h"
0023 #include "core/capabilities/ActionsCapability.h"
0024 #include "core/meta/Meta.h"
0025 #include "core/support/Debug.h"
0026 #include "widgets/PrettyTreeRoles.h"
0027 
0028 #include <QIcon>
0029 #include <KLocalizedString>
0030 
0031 Q_DECLARE_METATYPE( QAction* )
0032 Q_DECLARE_METATYPE( QList<QAction*> )
0033 
0034 CollectionTreeItem::CollectionTreeItem( CollectionTreeItemModelBase *model )
0035 : m_data( nullptr )
0036     , m_parent( nullptr )
0037     , m_model( model )
0038     , m_parentCollection( nullptr )
0039     , m_updateRequired( false )
0040     , m_trackCount( -1 )
0041     , m_type( Root )
0042     //, m_name( "Root" )
0043     , m_isCounting( false )
0044 {
0045 }
0046 
0047 CollectionTreeItem::CollectionTreeItem( const Meta::DataPtr &data, CollectionTreeItem *parent, CollectionTreeItemModelBase *model  )
0048     : m_data( data )
0049     , m_parent( parent )
0050     , m_model( model )
0051     , m_parentCollection( nullptr )
0052     , m_updateRequired( true )
0053     , m_trackCount( -1 )
0054     , m_type( Data )
0055     //, m_name( data ? data->name() : "NullData" )
0056     , m_isCounting( false )
0057 {
0058     if ( m_parent )
0059         m_parent->appendChild( this );
0060 }
0061 
0062 CollectionTreeItem::CollectionTreeItem( Collections::Collection *parentCollection, CollectionTreeItem *parent, CollectionTreeItemModelBase *model  )
0063     : m_data( nullptr )
0064     , m_parent( parent )
0065     , m_model( model )
0066     , m_parentCollection( parentCollection )
0067     , m_updateRequired( true )
0068     , m_trackCount( -1 )
0069     , m_type( Collection )
0070     //, m_name( parentCollection ? parentCollection->collectionId() : "NullColl" )
0071     , m_isCounting( false )
0072 {
0073     if ( m_parent )
0074         m_parent->appendChild( this );
0075 
0076     connect( parentCollection, &Collections::Collection::updated, this, &CollectionTreeItem::collectionUpdated );
0077 }
0078 
0079 CollectionTreeItem::CollectionTreeItem( Type type, const Meta::DataList &data, CollectionTreeItem *parent, CollectionTreeItemModelBase *model  )
0080     : m_data( nullptr )
0081     , m_parent( parent )
0082     , m_model( model )
0083     , m_parentCollection( nullptr )
0084     , m_updateRequired( false )  //the node already has all children
0085     , m_trackCount( -1 )
0086     , m_type( type )
0087     , m_isCounting( false )
0088 {
0089     if( m_parent )
0090         m_parent->m_childItems.insert( 0, this );
0091 
0092     foreach( Meta::DataPtr datap, data )
0093         new CollectionTreeItem( datap, this, m_model );
0094 }
0095 
0096 CollectionTreeItem::~CollectionTreeItem()
0097 {
0098     qDeleteAll( m_childItems );
0099 }
0100 
0101 void
0102 CollectionTreeItem::appendChild(CollectionTreeItem *child)
0103 {
0104     m_childItems.append(child);
0105 }
0106 
0107 void
0108 CollectionTreeItem::removeChild( int index )
0109 {
0110     CollectionTreeItem *child = m_childItems[index];
0111     m_childItems.removeAt( index );
0112     child->prepareForRemoval();
0113     child->deleteLater();
0114 }
0115 
0116 void
0117 CollectionTreeItem::prepareForRemoval()
0118 {
0119     m_parent = nullptr;
0120     m_model->itemAboutToBeDeleted( this );
0121     foreach( CollectionTreeItem *item, m_childItems )
0122     {
0123         item->prepareForRemoval();
0124     }
0125 }
0126 
0127 CollectionTreeItem*
0128 CollectionTreeItem::child( int row )
0129 {
0130     return m_childItems.value( row );
0131 }
0132 
0133 QVariant
0134 CollectionTreeItem::data( int role ) const
0135 {
0136     if( isNoLabelItem() )
0137     {
0138         switch( role )
0139         {
0140         case Qt::DisplayRole:
0141             return i18nc( "No labels are assigned to the given item are any of its subitems", "No Labels" );
0142         case Qt::DecorationRole:
0143             return QIcon::fromTheme( QStringLiteral("label-amarok") );
0144         }
0145         return QVariant();
0146     }
0147     else if( m_parentCollection )
0148     {
0149         static const QString counting = i18n( "Counting..." );
0150         switch( role )
0151         {
0152         case Qt::DisplayRole:
0153         case PrettyTreeRoles::FilterRole:
0154         case PrettyTreeRoles::SortRole:
0155             return m_parentCollection->prettyName();
0156         case Qt::DecorationRole:
0157             return m_parentCollection->icon();
0158         case PrettyTreeRoles::ByLineRole:
0159             if( m_isCounting )
0160                 return counting;
0161             if( m_trackCount < 0 )
0162             {
0163                 m_isCounting = true;
0164 
0165                 Collections::QueryMaker *qm = m_parentCollection->queryMaker();
0166                 connect( qm, &Collections::QueryMaker::newResultReady,
0167                          this, &CollectionTreeItem::tracksCounted );
0168 
0169                 qm->setAutoDelete( true )
0170                   ->setQueryType( Collections::QueryMaker::Custom )
0171                   ->addReturnFunction( Collections::QueryMaker::Count, Meta::valUrl )
0172                   ->run();
0173 
0174                 return counting;
0175             }
0176             return i18np( "1 track", "%1 tracks", m_trackCount );
0177         case PrettyTreeRoles::HasCapacityRole:
0178             return m_parentCollection->hasCapacity();
0179         case PrettyTreeRoles::UsedCapacityRole:
0180             return m_parentCollection->usedCapacity();
0181         case PrettyTreeRoles::TotalCapacityRole:
0182             return m_parentCollection->totalCapacity();
0183         case PrettyTreeRoles::DecoratorRoleCount:
0184             return decoratorActions().size();
0185         case PrettyTreeRoles::DecoratorRole:
0186             QVariant v;
0187             v.setValue( decoratorActions() );
0188             return v;
0189         }
0190     }
0191     return QVariant();
0192 }
0193 
0194 QList<QAction*>
0195 CollectionTreeItem::decoratorActions() const
0196 {
0197     QList<QAction*> decoratorActions;
0198     QScopedPointer<Capabilities::ActionsCapability> dc( m_parentCollection->create<Capabilities::ActionsCapability>() );
0199     if( dc )
0200         decoratorActions = dc->actions();
0201     return decoratorActions;
0202 }
0203 
0204 void
0205 CollectionTreeItem::tracksCounted( QStringList res )
0206 {
0207     if( !res.isEmpty() )
0208         m_trackCount = res.first().toInt();
0209     else
0210         m_trackCount = 0;
0211     m_isCounting = false;
0212     Q_EMIT dataUpdated();
0213 }
0214 
0215 void
0216 CollectionTreeItem::collectionUpdated()
0217 {
0218     m_trackCount = -1;
0219 }
0220 
0221 int
0222 CollectionTreeItem::row() const
0223 {
0224     if( m_parent )
0225     {
0226         const QList<CollectionTreeItem*> &children = m_parent->m_childItems;
0227         if( !children.isEmpty() && children.contains( const_cast<CollectionTreeItem*>(this) ) )
0228             return children.indexOf( const_cast<CollectionTreeItem*>(this) );
0229         else
0230             return -1;
0231     }
0232     return 0;
0233 }
0234 
0235 int
0236 CollectionTreeItem::level() const
0237 {
0238     if( m_parent )
0239         return m_parent->level() + 1;
0240     return -1;
0241 }
0242 
0243 bool
0244 CollectionTreeItem::isDataItem() const
0245 {
0246     return m_type == Data;
0247 }
0248 
0249 bool
0250 CollectionTreeItem::isVariousArtistItem() const
0251 {
0252     return m_type == CollectionTreeItem::VariousArtist;
0253 }
0254 
0255 bool
0256 CollectionTreeItem::isNoLabelItem() const
0257 {
0258     return m_type == CollectionTreeItem::NoLabel;
0259 }
0260 
0261 bool
0262 CollectionTreeItem::isAlbumItem() const
0263 {
0264     return m_type == Data && !Meta::AlbumPtr::dynamicCast( m_data ).isNull();
0265 }
0266 
0267 bool
0268 CollectionTreeItem::isTrackItem() const
0269 {
0270     return m_type == Data && !Meta::TrackPtr::dynamicCast( m_data ).isNull();
0271 }
0272 
0273 Collections::QueryMaker*
0274 CollectionTreeItem::queryMaker() const
0275 {
0276     Collections::Collection* coll = parentCollection();
0277     if( coll )
0278         return coll->queryMaker();
0279     return nullptr;
0280 }
0281 
0282 void
0283 CollectionTreeItem::addMatch( Collections::QueryMaker *qm, CategoryId::CatMenuId levelCategory ) const
0284 {
0285     if( !qm )
0286         return;
0287 
0288     if( isVariousArtistItem() )
0289         qm->setAlbumQueryMode( Collections::QueryMaker::OnlyCompilations );
0290     if( isNoLabelItem() )
0291         qm->setLabelQueryMode( Collections::QueryMaker::OnlyWithoutLabels );
0292     else if( Meta::TrackPtr track = Meta::TrackPtr::dynamicCast( m_data ) )
0293         qm->addMatch( track );
0294     else if( Meta::ArtistPtr artist = Meta::ArtistPtr::dynamicCast( m_data ) )
0295     {
0296         Collections::QueryMaker::ArtistMatchBehaviour behaviour =
0297                 ( levelCategory == CategoryId::AlbumArtist ) ? Collections::QueryMaker::AlbumArtists :
0298                                                                Collections::QueryMaker::TrackArtists;
0299         qm->addMatch( artist, behaviour );
0300     }
0301     else if( Meta::AlbumPtr album = Meta::AlbumPtr::dynamicCast( m_data ) )
0302         qm->addMatch( album );
0303     else if( Meta::ComposerPtr composer = Meta::ComposerPtr::dynamicCast( m_data ) )
0304         qm->addMatch( composer );
0305     else if( Meta::GenrePtr genre = Meta::GenrePtr::dynamicCast( m_data ) )
0306         qm->addMatch( genre );
0307     else if( Meta::YearPtr year = Meta::YearPtr::dynamicCast( m_data ) )
0308         qm->addMatch( year );
0309     else if( Meta::LabelPtr label = Meta::LabelPtr::dynamicCast( m_data ) )
0310         qm->addMatch( label );
0311 }
0312 
0313 
0314 QList<QUrl>
0315 CollectionTreeItem::urls() const
0316 {
0317     /*QueryBuilder qb = queryBuilder();
0318     qb.addReturnValue( QueryBuilder::tabSong, QueryBuilder::valURL );
0319     QStringList values = qb.run();
0320     QList<QUrl> list;
0321     foreach( QString s, values ) {
0322         list += QUrl( s );
0323     }
0324     return list;*/
0325     QList<QUrl> list;
0326     return list;
0327 }
0328 
0329 bool
0330 CollectionTreeItem::operator<( const CollectionTreeItem& other ) const
0331 {
0332     if( isVariousArtistItem() )
0333         return true;
0334     return m_data->sortableName() < other.m_data->sortableName();
0335 }
0336 
0337 const Meta::DataPtr
0338 CollectionTreeItem::data() const
0339 {
0340     return m_data;
0341 }
0342 
0343 Meta::TrackList
0344 CollectionTreeItem::descendentTracks()
0345 {
0346     QList<Meta::TrackPtr> descendentTracks;
0347     Meta::TrackPtr track;
0348     if( isDataItem() )
0349         track = Meta::TrackPtr::dynamicCast( m_data );
0350 
0351     if( !track.isNull() )
0352         descendentTracks << track;
0353     else
0354     {
0355         foreach( CollectionTreeItem *child, m_childItems )
0356             descendentTracks << child->descendentTracks();
0357     }
0358     return descendentTracks;
0359 }
0360 
0361 bool
0362 CollectionTreeItem::allDescendentTracksLoaded() const
0363 {
0364     if( isTrackItem() )
0365         return true;
0366 
0367     if( requiresUpdate() )
0368         return false;
0369 
0370     foreach( CollectionTreeItem *item, m_childItems )
0371     {
0372         if( !item->allDescendentTracksLoaded() )
0373             return false;
0374     }
0375     return true;
0376 }
0377 
0378 void
0379 CollectionTreeItem::setRequiresUpdate( bool updateRequired )
0380 {
0381     m_updateRequired = updateRequired;
0382 }
0383 
0384 bool
0385 CollectionTreeItem::requiresUpdate() const
0386 {
0387     return m_updateRequired;
0388 }
0389 
0390 CollectionTreeItem::Type
0391 CollectionTreeItem::type() const
0392 {
0393     return m_type;
0394 }
0395 
0396 QList<CollectionTreeItem*>
0397 CollectionTreeItem::children() const
0398 {
0399     return m_childItems;
0400 }
0401 
0402