File indexing completed on 2024-05-05 04:48:34

0001 /****************************************************************************************
0002  * Copyright (c) 2010 Sergey Ivanov <123kash@gmail.com>                                 *
0003  * Copyright (c) 2013 Alberto Villa <avilla@FreeBSD.org>                                *
0004  *                                                                                      *
0005  * This program is free software; you can redistribute it and/or modify it under        *
0006  * the terms of the GNU General Public License as published by the Free Software        *
0007  * Foundation; either version 2 of the License, or (at your option) any later           *
0008  * version.                                                                             *
0009  *                                                                                      *
0010  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
0011  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
0012  * PARTICULAR PURPOSE. See the GNU General Public License for more details.             *
0013  *                                                                                      *
0014  * You should have received a copy of the GNU General Public License along with         *
0015  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
0016  ****************************************************************************************/
0017 
0018 #define DEBUG_PREFIX "MusicBrainzTagsModel"
0019 
0020 #include "MusicBrainzTagsModel.h"
0021 
0022 #include "AmarokMimeData.h"
0023 #include "MusicBrainzMeta.h"
0024 #include "MusicBrainzTagsItem.h"
0025 #include "core/support/Debug.h"
0026 #include "support/MetaConstants.h"
0027 #include <KLocalizedString>
0028 
0029 #include <QFont>
0030 
0031 MusicBrainzTagsModel::MusicBrainzTagsModel( QObject *parent )
0032     : QAbstractItemModel( parent )
0033 {
0034     QVariantMap headerData;
0035     headerData.insert( MusicBrainz::SIMILARITY, "%" );
0036     headerData.insert( Meta::Field::TITLE, i18n( "Title" ) );
0037     headerData.insert( Meta::Field::ARTIST, i18n( "Artist" ) );
0038     headerData.insert( Meta::Field::ALBUM, i18n( "Album" ) );
0039     headerData.insert( Meta::Field::ALBUMARTIST, i18n( "Album Artist" ) );
0040     headerData.insert( Meta::Field::YEAR, i18n( "Year" ) );
0041     m_rootItem = new MusicBrainzTagsItem( nullptr, Meta::TrackPtr(), headerData );
0042 }
0043 
0044 MusicBrainzTagsModel::~MusicBrainzTagsModel()
0045 {
0046     delete m_rootItem;
0047 }
0048 
0049 QModelIndex
0050 MusicBrainzTagsModel::index( int row, int column, const QModelIndex &parent ) const
0051 {
0052     if( !hasIndex( row, column, parent ) )
0053         return QModelIndex();
0054 
0055     MusicBrainzTagsItem *parentItem;
0056 
0057     if( !parent.isValid() )
0058         parentItem = m_rootItem;
0059     else
0060         parentItem = static_cast<MusicBrainzTagsItem *>( parent.internalPointer() );
0061 
0062     MusicBrainzTagsItem *childItem = parentItem->child( row );
0063 
0064     if( childItem )
0065         return createIndex( row, column, childItem );
0066     else
0067         return QModelIndex();
0068 }
0069 
0070 QModelIndex
0071 MusicBrainzTagsModel::parent( const QModelIndex &index ) const
0072 {
0073     if( !index.isValid() )
0074         return QModelIndex();
0075 
0076     MusicBrainzTagsItem *childItem = static_cast<MusicBrainzTagsItem *>( index.internalPointer() );
0077     MusicBrainzTagsItem *parentItem = childItem->parent();
0078 
0079     if( parentItem == m_rootItem )
0080         return QModelIndex();
0081 
0082     return this->index( parentItem->row(), 0 );
0083 }
0084 
0085 QVariant
0086 MusicBrainzTagsModel::data( const QModelIndex &index, int role ) const
0087 {
0088     if( !index.isValid() )
0089         return QVariant();
0090 
0091     MusicBrainzTagsItem *item = static_cast<MusicBrainzTagsItem *>( index.internalPointer() );
0092 
0093     if( role == Qt::DisplayRole )
0094         return item->data( index.column() );
0095     else if( role == MusicBrainzTagsModel::SortRole )
0096     {
0097         if( item->parent() == m_rootItem )
0098             return item->track()->prettyUrl();
0099         else
0100         {
0101             /*
0102              * Sort order is ascending, but we want the results to be sorted from the
0103              * highest score to the lower.
0104              */
0105             return item->score() * -1;
0106         }
0107     }
0108     else if( role == MusicBrainzTagsModel::TracksRole )
0109     {
0110         QStringList trackList = item->dataValue( MusicBrainz::TRACKID ).toStringList();
0111         trackList.removeDuplicates();
0112         return trackList;
0113     }
0114     else if( role == MusicBrainzTagsModel::ArtistsRole )
0115     {
0116         QVariantList artistList = item->dataValue( MusicBrainz::ARTISTID ).toList();
0117         return artistList;
0118     }
0119     else if( role == MusicBrainzTagsModel::ReleasesRole )
0120     {
0121         QStringList releaseList = item->dataValue( MusicBrainz::RELEASEID ).toStringList();
0122         releaseList.removeDuplicates();
0123         return releaseList;
0124     }
0125     else if( role == Qt::CheckStateRole &&
0126              index.column() == 0 &&
0127              index.flags() & Qt::ItemIsUserCheckable )
0128         return item->isChosen()? Qt::Checked : Qt::Unchecked;
0129     else if( role == MusicBrainzTagsModel::ChosenStateRole &&
0130              item->parent() == m_rootItem )
0131         return item->isChosen()? MusicBrainzTagsModel::Chosen : MusicBrainzTagsModel::Unchosen;
0132     else if( role == Qt::BackgroundRole &&
0133              item->dataContains( MusicBrainz::SIMILARITY ) )
0134     {
0135         if( item->dataContains( MusicBrainz::MUSICBRAINZ ) &&
0136             item->dataContains( MusicBrainz::MUSICDNS ) )
0137             return QColor( Qt::green );
0138 
0139         float sim = ( item->dataValue( MusicBrainz::SIMILARITY ).toFloat() - MusicBrainz::MINSIMILARITY ) /
0140                     ( 1.0 - MusicBrainz::MINSIMILARITY );
0141 
0142         quint8 c1 = 255, c2 = 255;
0143         if( sim < 0.5 )
0144             c2 = ( 170 + 170 * sim );
0145         else
0146             c1 = ( 255 - 170 * ( sim - 0.5 ) );
0147 
0148         if( item->dataContains( MusicBrainz::MUSICDNS ) )
0149             return QColor( 0, c2, c1 );
0150         else
0151             return QColor( c1, c2, 0 );
0152     }
0153     else if( role == Qt::ToolTipRole )
0154     {
0155         QStringList toolTip;
0156         if( item->parent() == m_rootItem )
0157             toolTip.append( item->track()->prettyUrl() );
0158         else
0159         {
0160             if( item->dataContains( MusicBrainz::MUSICBRAINZ ) )
0161                 toolTip.append( i18n( "MusicBrainz match ratio: %1%",
0162                                       100 * item->dataValue( MusicBrainz::MUSICBRAINZ ).toFloat() ) );
0163             if( item->dataContains( MusicBrainz::MUSICDNS ) )
0164                 toolTip.append( i18n( "MusicDNS match ratio: %1%",
0165                                       100 * item->dataValue( MusicBrainz::MUSICDNS ).toFloat() ) );
0166         }
0167 
0168         return toolTip.join( "\n" );
0169     }
0170     else if( role == Qt::FontRole )
0171     {
0172         QFont font;
0173         if( item->parent() == m_rootItem )
0174             font.setItalic( true );
0175         else if( item->isChosen() )
0176             font.setBold( true );
0177         return font;
0178     }
0179     else if( role == Qt::ForegroundRole && item->parent() != m_rootItem )
0180         return QColor( Qt::black );
0181 
0182     return QVariant();
0183 }
0184 
0185 bool
0186 MusicBrainzTagsModel::setData( const QModelIndex &index, const QVariant &value, int role )
0187 {
0188     if( !index.isValid() || role != Qt::CheckStateRole || index.column() != 0 )
0189         return false;
0190 
0191     MusicBrainzTagsItem *item = static_cast<MusicBrainzTagsItem *>( index.internalPointer() );
0192     MusicBrainzTagsItem *parentItem = item->parent();
0193     if( item == m_rootItem || parentItem == m_rootItem )
0194         return false;
0195 
0196     parentItem->clearChoices();
0197     item->setChosen( value.toBool() );
0198     QModelIndex parent = index.parent();
0199     Q_EMIT dataChanged( this->index( 0, 0, parent ),
0200                       this->index( rowCount( parent ) - 1, 0, parent ) );
0201     return true;
0202 }
0203 
0204 Qt::ItemFlags
0205 MusicBrainzTagsModel::flags( const QModelIndex &index ) const
0206 {
0207     if( !index.isValid() )
0208         return QAbstractItemModel::flags( index );
0209 
0210     if( !parent( index ).isValid() )
0211         // Disable items with no children.
0212         return QAbstractItemModel::flags( index ) ^
0213                ( ( !static_cast<MusicBrainzTagsItem *>( index.internalPointer() )->childCount() )?
0214                  Qt::ItemIsEnabled : Qt::NoItemFlags );
0215 
0216     return QAbstractItemModel::flags( index ) | Qt::ItemIsUserCheckable;
0217 }
0218 
0219 QVariant
0220 MusicBrainzTagsModel::headerData( int section, Qt::Orientation orientation, int role ) const
0221 {
0222     if( orientation == Qt::Horizontal && role == Qt::DisplayRole )
0223         return m_rootItem->data( section );
0224 
0225     return QVariant();
0226 }
0227 
0228 int
0229 MusicBrainzTagsModel::rowCount( const QModelIndex &parent ) const
0230 {
0231     MusicBrainzTagsItem *parentItem;
0232 
0233     if( parent.column() > 0 )
0234         return 0;
0235 
0236     if( !parent.isValid() )
0237         parentItem = m_rootItem;
0238     else
0239         parentItem = static_cast<MusicBrainzTagsItem *>( parent.internalPointer() );
0240 
0241     return parentItem->childCount();
0242 }
0243 
0244 int
0245 MusicBrainzTagsModel::columnCount( const QModelIndex &parent ) const
0246 {
0247     Q_UNUSED( parent );
0248     return 5;
0249 }
0250 
0251 void
0252 MusicBrainzTagsModel::addTrack( const Meta::TrackPtr &track, const QVariantMap &tags )
0253 {
0254     DEBUG_BLOCK
0255 
0256     if( track.isNull() )
0257         return;
0258 
0259     QMutexLocker lock( &m_modelLock );
0260 
0261     MusicBrainzTagsItem *trackItem = nullptr;
0262     QModelIndex trackIndex;
0263     for( int i = 0; i < m_rootItem->childCount(); ++i )
0264     {
0265         MusicBrainzTagsItem *item = m_rootItem->child( i );
0266         if( track == item->track() )
0267         {
0268             trackItem = item;
0269             trackIndex = index( i, 0 );
0270 
0271             break;
0272         }
0273     }
0274 
0275     if( !trackItem )
0276     {
0277         trackItem = new MusicBrainzTagsItem( m_rootItem, track );
0278 
0279         beginInsertRows( QModelIndex(), m_rootItem->childCount(), m_rootItem->childCount() );
0280         m_rootItem->appendChild( trackItem );
0281         endInsertRows();
0282 
0283         trackIndex = index( m_rootItem->childCount() - 1, 0 );
0284     }
0285 
0286     if( tags.isEmpty() )
0287     {
0288         warning() << "Search result contains no data for track: " << track->prettyName();
0289         return;
0290     }
0291 
0292     MusicBrainzTagsItem *similarItem = nullptr;
0293     for( int i = 0; i < trackItem->childCount(); ++i )
0294     {
0295         MusicBrainzTagsItem *item = trackItem->child( i );
0296         if( item->isSimilar( tags ) )
0297         {
0298             similarItem = item;
0299 
0300             item->mergeData( tags );
0301             Q_EMIT dataChanged( index( i, 0, trackIndex ), index(i, columnCount() - 1, trackIndex ) );
0302 
0303             break;
0304         }
0305     }
0306 
0307     if( !similarItem )
0308     {
0309         MusicBrainzTagsItem *item = new MusicBrainzTagsItem( trackItem, track, tags );
0310 
0311         beginInsertRows( trackIndex, trackItem->childCount(), trackItem->childCount() );
0312         trackItem->appendChild( item );
0313         endInsertRows();
0314     }
0315 }
0316 
0317 QMap<Meta::TrackPtr, QVariantMap>
0318 MusicBrainzTagsModel::chosenItems() const
0319 {
0320     QMap<Meta::TrackPtr, QVariantMap> result;
0321 
0322     for( int i = 0; i < m_rootItem->childCount(); i++ )
0323     {
0324         MusicBrainzTagsItem *item = m_rootItem->child( i )->chosenItem();
0325         if( item )
0326         {
0327             QVariantMap data = item->data();
0328             data.remove( MusicBrainz::ARTISTID );
0329             data.remove( MusicBrainz::MUSICBRAINZ );
0330             data.remove( MusicBrainz::MUSICDNS );
0331             data.remove( MusicBrainz::RELEASEID );
0332             data.remove( MusicBrainz::SIMILARITY );
0333             data.remove( MusicBrainz::TRACKCOUNT );
0334             data.remove( MusicBrainz::TRACKID );
0335             result.insert( item->track(), data );
0336         }
0337     }
0338 
0339     return result;
0340 }
0341 
0342 void
0343 MusicBrainzTagsModel::chooseBestMatches()
0344 {
0345     for( int i = 0; i < m_rootItem->childCount(); i++ )
0346     {
0347         MusicBrainzTagsItem *item = m_rootItem->child( i );
0348         if( item->chooseBestMatch() )
0349         {
0350             QModelIndex parent = index( i, 0 );
0351             Q_EMIT dataChanged( index( 0, 0, parent ),
0352                               index( rowCount( parent ) - 1, 0, parent ) );
0353         }
0354     }
0355 }
0356 
0357 void
0358 MusicBrainzTagsModel::chooseBestMatchesFromRelease( const QStringList &releases )
0359 {
0360     for( int i = 0; i < m_rootItem->childCount(); i++ )
0361     {
0362         MusicBrainzTagsItem *item = m_rootItem->child( i );
0363         if( item->chooseBestMatchFromRelease( releases ) )
0364         {
0365             QModelIndex parent = index( i, 0 );
0366             Q_EMIT dataChanged( index( 0, 0, parent ),
0367                               index( rowCount( parent ) - 1, 0, parent ) );
0368         }
0369     }
0370 }
0371 
0372 void
0373 MusicBrainzTagsModel::clearChoices()
0374 {
0375     for( int i = 0; i < m_rootItem->childCount(); i++ )
0376     {
0377         MusicBrainzTagsItem *item = m_rootItem->child( i );
0378         item->clearChoices();
0379         QModelIndex parent = index( i, 0 );
0380         Q_EMIT dataChanged( index( 0, 0, parent ),
0381                           index( rowCount( parent ) - 1, 0, parent ) );
0382     }
0383 }
0384