File indexing completed on 2024-05-19 04:49:41
0001 /**************************************************************************************** 0002 * Copyright (c) 2011 Ralf Engels <ralf-engels@gmx.de> * 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) version 3 or * 0007 * any later version accepted by the membership of KDE e.V. (or its successor approved * 0008 * by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of * 0009 * version 3 of the license. * 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 "QuizPlayBias" 0020 0021 #include "QuizPlayBias.h" 0022 0023 #include "core/collections/Collection.h" 0024 #include "core/collections/QueryMaker.h" 0025 #include "core/meta/Meta.h" 0026 #include "core/support/Debug.h" 0027 #include "core-impl/collections/support/CollectionManager.h" 0028 #include "dynamic/TrackSet.h" 0029 0030 #include <QLabel> 0031 #include <QComboBox> 0032 #include <QVBoxLayout> 0033 0034 #include <QTimer> 0035 #include <QXmlStreamReader> 0036 #include <QXmlStreamWriter> 0037 0038 #include <KLocalizedString> 0039 0040 QString 0041 Dynamic::QuizPlayBiasFactory::i18nName() const 0042 { return i18nc("Name of the \"QuizPlay\" bias", "Quiz play"); } 0043 0044 QString 0045 Dynamic::QuizPlayBiasFactory::name() const 0046 { return Dynamic::QuizPlayBias::sName(); } 0047 0048 QString 0049 Dynamic::QuizPlayBiasFactory::i18nDescription() const 0050 { return i18nc("Description of the \"QuizPlay\" bias", 0051 "The \"QuizPlay\" bias adds tracks that start\n" 0052 "with a character the last track ended with."); } 0053 0054 Dynamic::BiasPtr 0055 Dynamic::QuizPlayBiasFactory::createBias() 0056 { return Dynamic::BiasPtr( new Dynamic::QuizPlayBias() ); } 0057 0058 0059 // Note: this whole bias does not work correctly for right-to-left languages. 0060 0061 0062 Dynamic::QuizPlayBias::QuizPlayBias() 0063 : SimpleMatchBias() 0064 , m_follow( TitleToTitle ) 0065 { } 0066 0067 void 0068 Dynamic::QuizPlayBias::fromXml( QXmlStreamReader *reader ) 0069 { 0070 while (!reader->atEnd()) { 0071 reader->readNext(); 0072 0073 if( reader->isStartElement() ) 0074 { 0075 QStringRef name = reader->name(); 0076 if( name == "follow" ) 0077 m_follow = followForName( reader->readElementText(QXmlStreamReader::SkipChildElements) ); 0078 else 0079 { 0080 debug()<<"Unexpected xml start element"<<reader->name()<<"in input"; 0081 reader->skipCurrentElement(); 0082 } 0083 } 0084 else if( reader->isEndElement() ) 0085 { 0086 break; 0087 } 0088 } 0089 } 0090 0091 void 0092 Dynamic::QuizPlayBias::toXml( QXmlStreamWriter *writer ) const 0093 { 0094 writer->writeTextElement( QStringLiteral("follow"), nameForFollow( m_follow ) ); 0095 } 0096 0097 QString 0098 Dynamic::QuizPlayBias::sName() 0099 { 0100 return QStringLiteral( "quizPlayBias" ); 0101 } 0102 0103 QString 0104 Dynamic::QuizPlayBias::name() const 0105 { 0106 return Dynamic::QuizPlayBias::sName(); 0107 } 0108 0109 QString 0110 Dynamic::QuizPlayBias::toString() const 0111 { 0112 switch( m_follow ) 0113 { 0114 case TitleToTitle: 0115 return i18nc("QuizPlay bias representation", 0116 "Tracks whose title start with a\n" 0117 "character the last track ended with"); 0118 case ArtistToArtist: 0119 return i18nc("QuizPlay bias representation", 0120 "Tracks whose artist name start\n" 0121 "with a character the last track ended with"); 0122 case AlbumToAlbum: 0123 return i18nc("QuizPlay bias representation", 0124 "Tracks whose album name start\n" 0125 "with a character the last track ended with"); 0126 } 0127 return QString(); 0128 } 0129 0130 QWidget* 0131 Dynamic::QuizPlayBias::widget( QWidget* parent ) 0132 { 0133 QWidget *widget = new QWidget( parent ); 0134 QVBoxLayout *layout = new QVBoxLayout( widget ); 0135 0136 QLabel *label = new QLabel( i18n( "Last character of the previous song is\n" 0137 "the first character of the next song" ) ); 0138 layout->addWidget( label ); 0139 0140 QComboBox *combo = new QComboBox(); 0141 combo->addItem( i18n( "of the track title (Title quiz)" ), 0142 nameForFollow( TitleToTitle ) ); 0143 combo->addItem( i18n( "of the artist (Artist quiz)" ), 0144 nameForFollow( ArtistToArtist ) ); 0145 combo->addItem( i18n( "of the album name (Album quiz)" ), 0146 nameForFollow( AlbumToAlbum ) ); 0147 switch( m_follow ) 0148 { 0149 case TitleToTitle: combo->setCurrentIndex(0); break; 0150 case ArtistToArtist: combo->setCurrentIndex(1); break; 0151 case AlbumToAlbum: combo->setCurrentIndex(2); break; 0152 } 0153 connect( combo, QOverload<int>::of(&QComboBox::currentIndexChanged), 0154 this, &QuizPlayBias::selectionChanged ); 0155 layout->addWidget( combo ); 0156 0157 return widget; 0158 } 0159 0160 Dynamic::TrackSet 0161 Dynamic::QuizPlayBias::matchingTracks( const Meta::TrackList& playlist, 0162 int contextCount, int finalCount, 0163 const Dynamic::TrackCollectionPtr &universe ) const 0164 { 0165 Q_UNUSED( contextCount ); 0166 Q_UNUSED( finalCount ); 0167 0168 if( playlist.isEmpty() ) 0169 return Dynamic::TrackSet( universe, true ); 0170 0171 // determine the last character we need to quiz 0172 Meta::TrackPtr lastTrack = playlist.last(); 0173 Meta::DataPtr lastData; 0174 if( m_follow == TitleToTitle ) 0175 lastData = Meta::DataPtr::staticCast<Meta::Track>(lastTrack); 0176 else if( m_follow == ArtistToArtist ) 0177 lastData = Meta::DataPtr::staticCast<Meta::Artist>(lastTrack->artist()); 0178 else if( m_follow == AlbumToAlbum ) 0179 lastData = Meta::DataPtr::staticCast<Meta::Album>(lastTrack->album()); 0180 0181 if( !lastData || lastData->name().isEmpty() ) 0182 { 0183 // debug() << "QuizPlay: no data for"<<lastTrack->name(); 0184 return Dynamic::TrackSet( universe, true ); 0185 } 0186 0187 m_currentCharacter = lastChar(lastData->name()).toLower(); 0188 // debug() << "QuizPlay: data for"<<lastTrack->name()<<"is"<<m_currentCharacter; 0189 0190 // -- look if we already buffered it 0191 if( m_characterTrackMap.contains( m_currentCharacter ) ) 0192 return m_characterTrackMap.value( m_currentCharacter ); 0193 0194 // -- start a new query 0195 m_tracks = Dynamic::TrackSet( universe, false ); 0196 QTimer::singleShot(0, 0197 const_cast<QuizPlayBias*>(this), 0198 &QuizPlayBias::newQuery); // create the new query from my parent thread 0199 0200 return Dynamic::TrackSet(); 0201 } 0202 0203 bool 0204 Dynamic::QuizPlayBias::trackMatches( int position, 0205 const Meta::TrackList& playlist, 0206 int contextCount ) const 0207 { 0208 Q_UNUSED( contextCount ); 0209 0210 if( position <= 0 || position >= playlist.count()) 0211 return true; 0212 0213 // -- determine the last character we need to quiz 0214 Meta::TrackPtr lastTrack = playlist[position-1]; 0215 Meta::DataPtr lastData; 0216 if( m_follow == TitleToTitle ) 0217 lastData = Meta::DataPtr::staticCast<Meta::Track>(lastTrack); 0218 else if( m_follow == ArtistToArtist ) 0219 lastData = Meta::DataPtr::staticCast<Meta::Artist>(lastTrack->artist()); 0220 else if( m_follow == AlbumToAlbum ) 0221 lastData = Meta::DataPtr::staticCast<Meta::Album>(lastTrack->album()); 0222 0223 if( !lastData || lastData->name().isEmpty() ) 0224 return true; 0225 0226 // -- determine the first character 0227 Meta::TrackPtr track = playlist[position]; 0228 Meta::DataPtr data; 0229 if( m_follow == TitleToTitle ) 0230 data = Meta::DataPtr::staticCast<Meta::Track>(track); 0231 else if( m_follow == ArtistToArtist ) 0232 data = Meta::DataPtr::staticCast<Meta::Artist>(track->artist()); 0233 else if( m_follow == AlbumToAlbum ) 0234 data = Meta::DataPtr::staticCast<Meta::Album>(track->album()); 0235 0236 if( !data || data->name().isEmpty() ) 0237 return false; 0238 0239 // -- now compare 0240 QString lastName = lastData->name(); 0241 QString name = data->name(); 0242 return lastChar( lastName ).toLower() == name[0].toLower(); 0243 } 0244 0245 0246 Dynamic::QuizPlayBias::FollowType 0247 Dynamic::QuizPlayBias::follow() const 0248 { 0249 return m_follow; 0250 } 0251 0252 void 0253 Dynamic::QuizPlayBias::setFollow( Dynamic::QuizPlayBias::FollowType value ) 0254 { 0255 m_follow = value; 0256 invalidate(); 0257 Q_EMIT changed( BiasPtr(this) ); 0258 } 0259 0260 void 0261 Dynamic::QuizPlayBias::updateFinished() 0262 { 0263 m_characterTrackMap.insert( m_currentCharacter, m_tracks ); 0264 SimpleMatchBias::updateFinished(); 0265 } 0266 0267 void 0268 Dynamic::QuizPlayBias::invalidate() 0269 { 0270 m_characterTrackMap.clear(); 0271 SimpleMatchBias::invalidate(); 0272 } 0273 0274 0275 void 0276 Dynamic::QuizPlayBias::selectionChanged( int which ) 0277 { 0278 if( QComboBox *box = qobject_cast<QComboBox*>(sender()) ) 0279 setFollow( followForName( box->itemData( which ).toString() ) ); 0280 } 0281 0282 void 0283 Dynamic::QuizPlayBias::newQuery() 0284 { 0285 // ok, I need a new query maker 0286 m_qm.reset( CollectionManager::instance()->queryMaker() ); 0287 0288 uint field = 0; 0289 switch( m_follow ) 0290 { 0291 case Dynamic::QuizPlayBias::TitleToTitle: field = Meta::valTitle; break; 0292 case Dynamic::QuizPlayBias::ArtistToArtist: field = Meta::valArtist; break; 0293 case Dynamic::QuizPlayBias::AlbumToAlbum: field = Meta::valAlbum; break; 0294 } 0295 m_qm->addFilter( field, QString(m_currentCharacter), true, false ); 0296 0297 m_qm->setQueryType( Collections::QueryMaker::Custom ); 0298 m_qm->addReturnValue( Meta::valUniqueId ); 0299 0300 connect( m_qm.data(), &Collections::QueryMaker::newResultReady, 0301 this, &QuizPlayBias::updateReady ); 0302 connect( m_qm.data(), &Collections::QueryMaker::queryDone, 0303 this, &QuizPlayBias::updateFinished ); 0304 m_qm.data()->run(); 0305 } 0306 0307 0308 QChar 0309 Dynamic::QuizPlayBias::lastChar( const QString &str ) 0310 { 0311 int endIndex = str.length(); 0312 int index; 0313 0314 index = str.indexOf( '[' ); 0315 if( index > 0 && index < endIndex ) 0316 endIndex = index; 0317 0318 index = str.indexOf( '(' ); 0319 if( index > 0 && index < endIndex ) 0320 endIndex = index; 0321 0322 index = str.indexOf( QLatin1String(" cd"), Qt::CaseInsensitive ); 0323 if( index > 0 && index < endIndex ) 0324 endIndex = index; 0325 0326 while( endIndex > 0 && 0327 (str[ endIndex - 1 ].isSpace() || str[ endIndex - 1 ].isPunct()) ) 0328 endIndex--; 0329 0330 if( endIndex > 0 ) 0331 return str[ endIndex - 1 ]; 0332 0333 return QChar(); 0334 } 0335 0336 0337 QString 0338 Dynamic::QuizPlayBias::nameForFollow( Dynamic::QuizPlayBias::FollowType match ) 0339 { 0340 switch( match ) 0341 { 0342 case Dynamic::QuizPlayBias::TitleToTitle: return QStringLiteral("titleQuiz"); 0343 case Dynamic::QuizPlayBias::ArtistToArtist: return QStringLiteral("artistQuiz"); 0344 case Dynamic::QuizPlayBias::AlbumToAlbum: return QStringLiteral("albumQuiz"); 0345 } 0346 return QString(); 0347 } 0348 0349 Dynamic::QuizPlayBias::FollowType 0350 Dynamic::QuizPlayBias::followForName( const QString &name ) 0351 { 0352 if( name == QLatin1String("titleQuiz") ) return TitleToTitle; 0353 else if( name == QLatin1String("artistQuiz") ) return ArtistToArtist; 0354 else if( name == QLatin1String("albumQuiz") ) return AlbumToAlbum; 0355 else return TitleToTitle; 0356 } 0357 0358 0359 0360