File indexing completed on 2024-05-19 04:49:40

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 "AlbumPlayBias"
0020 
0021 #include "AlbumPlayBias.h"
0022 
0023 #include "core/meta/Meta.h"
0024 #include "core/support/Debug.h"
0025 #include "dynamic/TrackSet.h"
0026 
0027 #include <KLocalizedString>
0028 
0029 #include <QComboBox>
0030 #include <QFormLayout>
0031 #include <QXmlStreamReader>
0032 #include <QXmlStreamWriter>
0033 
0034 QString
0035 Dynamic::AlbumPlayBiasFactory::i18nName() const
0036 { return i18nc("Name of the \"AlbumPlay\" bias", "Album play"); }
0037 
0038 QString
0039 Dynamic::AlbumPlayBiasFactory::name() const
0040 { return Dynamic::AlbumPlayBias::sName(); }
0041 
0042 QString
0043 Dynamic::AlbumPlayBiasFactory::i18nDescription() const
0044 { return i18nc("Description of the \"AlbumPlay\" bias",
0045                    "The \"AlbumPlay\" bias adds tracks that belong to one album."); }
0046 
0047 Dynamic::BiasPtr
0048 Dynamic::AlbumPlayBiasFactory::createBias()
0049 { return Dynamic::BiasPtr( new Dynamic::AlbumPlayBias() ); }
0050 
0051 
0052 
0053 
0054 Dynamic::AlbumPlayBias::AlbumPlayBias()
0055     : m_follow( DirectlyFollow )
0056 { }
0057 
0058 void
0059 Dynamic::AlbumPlayBias::fromXml( QXmlStreamReader *reader )
0060 {
0061     while (!reader->atEnd()) {
0062         reader->readNext();
0063 
0064         if( reader->isStartElement() )
0065         {
0066             QStringRef name = reader->name();
0067             if( name == "follow" )
0068                 m_follow = followForName( reader->readElementText(QXmlStreamReader::SkipChildElements) );
0069             else
0070             {
0071                 debug()<<"Unexpected xml start element"<<reader->name()<<"in input";
0072                 reader->skipCurrentElement();
0073             }
0074         }
0075         else if( reader->isEndElement() )
0076         {
0077             break;
0078         }
0079     }
0080 }
0081 
0082 void
0083 Dynamic::AlbumPlayBias::toXml( QXmlStreamWriter *writer ) const
0084 {
0085     writer->writeTextElement( QStringLiteral("follow"), nameForFollow( m_follow ) );
0086 }
0087 
0088 QString
0089 Dynamic::AlbumPlayBias::sName()
0090 {
0091     return QStringLiteral( "albumPlayBias" );
0092 }
0093 
0094 QString
0095 Dynamic::AlbumPlayBias::name() const
0096 {
0097     return Dynamic::AlbumPlayBias::sName();
0098 }
0099 
0100 QString
0101 Dynamic::AlbumPlayBias::toString() const
0102 {
0103     switch( m_follow )
0104     {
0105     case DirectlyFollow:
0106         return i18nc("AlbumPlay bias representation",
0107                      "The next track from the album");
0108     case Follow:
0109         return i18nc("AlbumPlay bias representation",
0110                      "Any later track from the album");
0111     case DontCare:
0112         return i18nc("AlbumPlay bias representation",
0113                      "Tracks from the same album");
0114     }
0115     return QString();
0116 }
0117 
0118 
0119 QWidget*
0120 Dynamic::AlbumPlayBias::widget( QWidget* parent )
0121 {
0122     QComboBox *combo = new QComboBox( parent );
0123     combo->addItem( i18n( "Track directly follows previous track in album" ),
0124                     nameForFollow( DirectlyFollow ) );
0125     combo->addItem( i18n( "Track comes after previous track in album" ),
0126                     nameForFollow( Follow ) );
0127     combo->addItem( i18n( "Track is in the same album as previous track" ),
0128                     nameForFollow( DontCare ) );
0129     switch( m_follow )
0130     {
0131     case DirectlyFollow: combo->setCurrentIndex(0); break;
0132     case Follow:         combo->setCurrentIndex(1); break;
0133     case DontCare:       combo->setCurrentIndex(2); break;
0134     }
0135     connect( combo, QOverload<int>::of(&QComboBox::currentIndexChanged),
0136              this, &AlbumPlayBias::selectionChanged );
0137 
0138     return combo;
0139 }
0140 
0141 Dynamic::TrackSet
0142 Dynamic::AlbumPlayBias::matchingTracks( const Meta::TrackList& playlist,
0143                                         int contextCount, int finalCount,
0144                                         const Dynamic::TrackCollectionPtr &universe ) const
0145 {
0146     Q_UNUSED( contextCount );
0147     Q_UNUSED( finalCount );
0148 
0149     if( playlist.isEmpty() ) // no track means we can't find any tracks in the same album
0150         return Dynamic::TrackSet( universe, false );
0151 
0152     Meta::TrackPtr track = playlist.last();
0153     Meta::AlbumPtr album = track->album();
0154 
0155     if( !album ) // no album means we can't find any tracks in the same album
0156         return Dynamic::TrackSet( universe, false );
0157 
0158     Meta::TrackList albumTracks = album->tracks();
0159 
0160     if( ( albumTracks.count() <= 1 ) || // the album has only one track (or even less) so there can't be any other tracks in the same album
0161         ( m_follow != DontCare && sameTrack( track, albumTracks.last() ) ) ) // track is the last one and we want to find a later one.
0162         return Dynamic::TrackSet( universe, false );
0163 
0164     // we assume that the album tracks are sorted by cd and track number which
0165     // is at least true for the SqlCollection
0166     TrackSet result( universe, false );
0167     if( m_follow == DirectlyFollow )
0168     {
0169         for( int i = 1; i < albumTracks.count(); i++ )
0170             if( sameTrack( albumTracks[i-1], track ) )
0171                 result.unite( albumTracks[i] );
0172     }
0173     else if( m_follow == Follow )
0174     {
0175         bool found = false;
0176         for( int i = 0; i < albumTracks.count(); i++ )
0177         {
0178             if( found )
0179                 result.unite( albumTracks[i] );
0180             if( sameTrack( albumTracks[i], track ) )
0181                 found = true;
0182         }
0183     }
0184     else if( m_follow == DontCare )
0185     {
0186         for( int i = 0; i < albumTracks.count(); i++ )
0187         {
0188             if( !sameTrack( albumTracks[i], track ) )
0189                 result.unite( albumTracks[i] );
0190         }
0191     }
0192 
0193     return result;
0194 }
0195 
0196 bool
0197 Dynamic::AlbumPlayBias::trackMatches( int position,
0198                                       const Meta::TrackList& playlist,
0199                                       int contextCount ) const
0200 {
0201     Q_UNUSED( contextCount );
0202 
0203     if( position <= 0 || playlist.count() <= position )
0204         return true;
0205 
0206     Meta::TrackPtr track = playlist[position-1];
0207     Meta::AlbumPtr album = track->album();
0208     Meta::TrackPtr currentTrack = playlist[position];
0209     Meta::AlbumPtr currentAlbum = currentTrack->album();
0210 
0211     if( !album || album->tracks().isEmpty() )
0212         return false;
0213 
0214     Meta::TrackList albumTracks = album->tracks();
0215     if( sameTrack( track, albumTracks.last() ) && m_follow != DontCare )
0216         return false;
0217 
0218     // we assume that the album tracks are sorted by cd and track number which
0219     // is at least true for the SqlCollection
0220     if( m_follow == DirectlyFollow )
0221     {
0222         for( int i = 1; i < albumTracks.count(); i++ )
0223             if( sameTrack( albumTracks[i-1], track ) )
0224                 return sameTrack( albumTracks[i], currentTrack );
0225         return false;
0226     }
0227     else if( m_follow == Follow )
0228     {
0229         bool found = false;
0230         for( int i = 0; i < albumTracks.count(); i++ )
0231         {
0232             if( found && sameTrack( albumTracks[i], currentTrack ) )
0233                 return true;
0234             if( sameTrack( albumTracks[i], track ) )
0235                 found = true;
0236         }
0237         return false;
0238     }
0239     else if( m_follow == DontCare )
0240     {
0241         return album == currentAlbum;
0242     }
0243     return false;
0244 }
0245 
0246 
0247 Dynamic::AlbumPlayBias::FollowType
0248 Dynamic::AlbumPlayBias::follow() const
0249 {
0250     return m_follow;
0251 }
0252 
0253 void
0254 Dynamic::AlbumPlayBias::setFollow( Dynamic::AlbumPlayBias::FollowType value )
0255 {
0256     m_follow = value;
0257     invalidate();
0258     Q_EMIT changed( BiasPtr(this) );
0259 }
0260 
0261 void
0262 Dynamic::AlbumPlayBias::selectionChanged( int which )
0263 {
0264     if( QComboBox *box = qobject_cast<QComboBox*>(sender()) )
0265         setFollow( followForName( box->itemData( which ).toString() ) );
0266 }
0267 
0268 QString
0269 Dynamic::AlbumPlayBias::nameForFollow( Dynamic::AlbumPlayBias::FollowType match )
0270 {
0271     switch( match )
0272     {
0273     case Dynamic::AlbumPlayBias::DirectlyFollow: return QStringLiteral("directlyFollow");
0274     case Dynamic::AlbumPlayBias::Follow:         return QStringLiteral("follow");
0275     case Dynamic::AlbumPlayBias::DontCare:       return QStringLiteral("dontCare");
0276     }
0277     return QString();
0278 }
0279 
0280 Dynamic::AlbumPlayBias::FollowType
0281 Dynamic::AlbumPlayBias::followForName( const QString &name )
0282 {
0283     if( name == QLatin1String("directlyFollow") ) return DirectlyFollow;
0284     else if( name == QLatin1String("follow") )    return Follow;
0285     else if( name == QLatin1String("dontCare") )  return DontCare;
0286     else return DontCare;
0287 }
0288 
0289 bool
0290 Dynamic::AlbumPlayBias::sameTrack( Meta::TrackPtr track1, Meta::TrackPtr track2 ) const
0291 {
0292     // We compare items which may be MetaProxy::Track or Meta::Track. For the
0293     // same underlying track, MetaProxy::Track == Meta;:Track will be true, but
0294     // Meta::Track == MetaProxy::Track false. Check both ways, and if either
0295     // returns true, it's the same track.
0296     return ( *track1 == *track2 ) || ( *track2 == *track1 );
0297 }
0298 
0299 
0300