File indexing completed on 2024-05-19 04:49:51
0001 /**************************************************************************************** 0002 * Copyright (c) 2009 Téo Mrnjavac <teo@kde.org> * 0003 * Copyright (c) 2010 Nanno Langstraat <langstr@gmail.com> * 0004 * Copyright (c) 2013 Konrad Zemek <konrad.zemek@gmail.com> * 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 "SortAlgorithms.h" 0020 0021 #include "core/meta/Meta.h" 0022 #include "core/meta/Statistics.h" 0023 #include "core/support/Debug.h" 0024 #include "playlist/proxymodels/AbstractModel.h" 0025 0026 #include <QDateTime> 0027 #include <QRandomGenerator> 0028 0029 namespace Playlist 0030 { 0031 0032 void 0033 multilevelLessThan::setSortScheme( const SortScheme & scheme ) 0034 { 0035 m_scheme = scheme; 0036 m_randomSalt = QRandomGenerator::global()->generate(); //! Do a different random sort order every time. 0037 } 0038 0039 bool 0040 multilevelLessThan::operator()( const QAbstractItemModel* sourceModel, 0041 int sourceModelRowA, int sourceModelRowB ) const 0042 { 0043 // Handle "Last Played" as a special case because the time since last played is not 0044 // reported as an int in the data columns. Handle Title, Album, Artist as special 0045 // cases with Meta::Base::sortableName(). This is necessary in order to have the same 0046 // sort order policy regarding "The" in both the playlist and the collection browser. 0047 QSet< Playlist::Column > specialCases; 0048 specialCases << Playlist::LastPlayed << Playlist::Title << Playlist::Album 0049 << Playlist::Artist << Playlist::AlbumArtist; 0050 0051 foreach( const SortLevel &level, m_scheme ) 0052 { 0053 const bool inverted = ( level.order() == Qt::DescendingOrder ); 0054 const Playlist::Column currentCategory = level.category(); 0055 0056 const QModelIndex indexA = sourceModel->index( sourceModelRowA, currentCategory ); 0057 const QModelIndex indexB = sourceModel->index( sourceModelRowB, currentCategory ); 0058 0059 const Meta::TrackPtr trackA = indexA.data( TrackRole ).value<Meta::TrackPtr>(); 0060 const Meta::TrackPtr trackB = indexB.data( TrackRole ).value<Meta::TrackPtr>(); 0061 0062 if( trackA && trackB && specialCases.contains( currentCategory ) ) 0063 { 0064 switch( currentCategory ) 0065 { 0066 case Playlist::LastPlayed: 0067 { 0068 const QDateTime lastPlayedA = trackA->statistics()->lastPlayed(); 0069 const QDateTime lastPlayedB = trackB->statistics()->lastPlayed(); 0070 0071 // The track with higher lastPlayed value was played more recently 0072 // 0073 // '!=' is the XOR operation; it simply negates the result if 'inverted' 0074 // is true. It isn't necessary to do it this way, although later on it will 0075 // ease figuring out what's actually being returned. 0076 if( lastPlayedA != lastPlayedB ) 0077 return ( lastPlayedA > lastPlayedB ) != inverted; 0078 0079 break; 0080 } 0081 case Playlist::Title: 0082 { 0083 const int compareResult = compareBySortableName( trackA, trackB ); 0084 0085 if( compareResult != 0 ) 0086 return ( compareResult < 0 ) != inverted; 0087 0088 break; 0089 } 0090 case Playlist::Album: 0091 { 0092 const int compareResult 0093 = compareBySortableName( trackA->album(), trackB->album() ); 0094 0095 if( compareResult != 0 ) 0096 return ( compareResult < 0 ) != inverted; 0097 0098 // Fall through to sorting by album artist if albums have same name 0099 Q_FALLTHROUGH(); 0100 } 0101 case Playlist::AlbumArtist: 0102 { 0103 const Meta::ArtistPtr artistA = 0104 (trackA->album() ? trackA->album()->albumArtist() : Meta::ArtistPtr()); 0105 0106 const Meta::ArtistPtr artistB = 0107 (trackB->album() ? trackB->album()->albumArtist() : Meta::ArtistPtr()); 0108 0109 const int compareResult = compareBySortableName( artistA, artistB ); 0110 0111 if( compareResult != 0 ) 0112 return ( compareResult < 0 ) != inverted; 0113 0114 break; 0115 } 0116 case Playlist::Artist: 0117 { 0118 const int compareResult 0119 = compareBySortableName( trackA->artist(), trackB->artist() ); 0120 0121 if( compareResult != 0 ) 0122 return ( compareResult < 0 ) != inverted; 0123 0124 break; 0125 } 0126 default: 0127 warning() << "One of the cases in specialCases set has not received special treatment!"; 0128 break; 0129 } 0130 } 0131 else // either it's not a special case, or we don't have means (TrackPtrs) to handle it 0132 { 0133 const QVariant dataA = indexA.data( Qt::DisplayRole ); 0134 const QVariant dataB = indexB.data( Qt::DisplayRole ); 0135 0136 if( level.isString() ) 0137 { 0138 const int compareResult = 0139 dataA.toString().compare(dataB.toString(), 0140 Qt::CaseInsensitive); 0141 if( compareResult != 0 ) 0142 return ( compareResult < 0 ) != inverted; 0143 } 0144 else if( level.isFloat() ) 0145 { 0146 if( dataA.toDouble() != dataB.toDouble() ) 0147 return ( dataA.toDouble() < dataB.toDouble() ) != inverted; 0148 } 0149 else // if it's neither a string nor a float ==> it's an integer 0150 { 0151 if( dataA.toInt() != dataB.toInt() ) 0152 return ( dataA.toInt() < dataB.toInt() ) != inverted; 0153 } 0154 } 0155 } 0156 0157 // Tie breaker: order by row number 0158 return ( sourceModelRowA < sourceModelRowB ); 0159 } 0160 0161 template<typename T> 0162 int 0163 multilevelLessThan::compareBySortableName( const AmarokSharedPointer<T> &left, 0164 const AmarokSharedPointer<T> &right ) const 0165 { 0166 if( !left && right ) 0167 return -1; 0168 else if( left && !right ) 0169 return 1; 0170 else if( left && right ) 0171 return left->sortableName().compare( right->sortableName(), 0172 Qt::CaseInsensitive ); 0173 return 0; 0174 } 0175 0176 } //namespace Playlist