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