1 /****************************************************************************************
2 * Copyright (c) 2009 Téo Mrnjavac <teo@kde.org> *
3 * Copyright (c) 2010 Nanno Langstraat <langstr@gmail.com> *
4 * Copyright (c) 2013 Konrad Zemek <konrad.zemek@gmail.com> *
5 * *
6 * This program is free software; you can redistribute it and/or modify it under *
7 * the terms of the GNU General Public License as published by the Free Software *
8 * Foundation; either version 2 of the License, or (at your option) any later *
9 * version. *
10 * *
11 * This program is distributed in the hope that it will be useful, but WITHOUT ANY *
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A *
13 * PARTICULAR PURPOSE. See the GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License along with *
16 * this program. If not, see <http://www.gnu.org/licenses/>. *
17 ****************************************************************************************/
18
19 #include "SortAlgorithms.h"
20
21 #include "core/meta/Meta.h"
22 #include "core/meta/Statistics.h"
23 #include "core/support/Debug.h"
24 #include "playlist/proxymodels/AbstractModel.h"
25
26 #include <QDateTime>
27
28 namespace Playlist
29 {
30
31 void
setSortScheme(const SortScheme & scheme)32 multilevelLessThan::setSortScheme( const SortScheme & scheme )
33 {
34 m_scheme = scheme;
35 m_randomSalt = qrand(); //! Do a different random sort order every time.
36 }
37
38 bool
operator ()(const QAbstractItemModel * sourceModel,int sourceModelRowA,int sourceModelRowB) const39 multilevelLessThan::operator()( const QAbstractItemModel* sourceModel,
40 int sourceModelRowA, int sourceModelRowB ) const
41 {
42 // Handle "Last Played" as a special case because the time since last played is not
43 // reported as an int in the data columns. Handle Title, Album, Artist as special
44 // cases with Meta::Base::sortableName(). This is necessary in order to have the same
45 // sort order policy regarding "The" in both the playlist and the collection browser.
46 QSet< Playlist::Column > specialCases;
47 specialCases << Playlist::LastPlayed << Playlist::Title << Playlist::Album
48 << Playlist::Artist << Playlist::AlbumArtist;
49
50 foreach( const SortLevel &level, m_scheme )
51 {
52 const bool inverted = ( level.order() == Qt::DescendingOrder );
53 const Playlist::Column currentCategory = level.category();
54
55 const QModelIndex indexA = sourceModel->index( sourceModelRowA, currentCategory );
56 const QModelIndex indexB = sourceModel->index( sourceModelRowB, currentCategory );
57
58 const Meta::TrackPtr trackA = indexA.data( TrackRole ).value<Meta::TrackPtr>();
59 const Meta::TrackPtr trackB = indexB.data( TrackRole ).value<Meta::TrackPtr>();
60
61 if( trackA && trackB && specialCases.contains( currentCategory ) )
62 {
63 switch( currentCategory )
64 {
65 case Playlist::LastPlayed:
66 {
67 const QDateTime lastPlayedA = trackA->statistics()->lastPlayed();
68 const QDateTime lastPlayedB = trackB->statistics()->lastPlayed();
69
70 // The track with higher lastPlayed value was played more recently
71 //
72 // '!=' is the XOR operation; it simply negates the result if 'inverted'
73 // is true. It isn't necessary to do it this way, although later on it will
74 // ease figuring out what's actually being returned.
75 if( lastPlayedA != lastPlayedB )
76 return ( lastPlayedA > lastPlayedB ) != inverted;
77
78 break;
79 }
80 case Playlist::Title:
81 {
82 const int compareResult = compareBySortableName( trackA, trackB );
83
84 if( compareResult != 0 )
85 return ( compareResult < 0 ) != inverted;
86
87 break;
88 }
89 case Playlist::Album:
90 {
91 const int compareResult
92 = compareBySortableName( trackA->album(), trackB->album() );
93
94 if( compareResult != 0 )
95 return ( compareResult < 0 ) != inverted;
96
97 // Fall through to sorting by album artist if albums have same name
98 Q_FALLTHROUGH();
99 }
100 case Playlist::AlbumArtist:
101 {
102 const Meta::ArtistPtr artistA =
103 (trackA->album() ? trackA->album()->albumArtist() : Meta::ArtistPtr());
104
105 const Meta::ArtistPtr artistB =
106 (trackB->album() ? trackB->album()->albumArtist() : Meta::ArtistPtr());
107
108 const int compareResult = compareBySortableName( artistA, artistB );
109
110 if( compareResult != 0 )
111 return ( compareResult < 0 ) != inverted;
112
113 break;
114 }
115 case Playlist::Artist:
116 {
117 const int compareResult
118 = compareBySortableName( trackA->artist(), trackB->artist() );
119
120 if( compareResult != 0 )
121 return ( compareResult < 0 ) != inverted;
122
123 break;
124 }
125 default:
126 warning() << "One of the cases in specialCases set has not received special treatment!";
127 break;
128 }
129 }
130 else // either it's not a special case, or we don't have means (TrackPtrs) to handle it
131 {
132 const QVariant dataA = indexA.data( Qt::DisplayRole );
133 const QVariant dataB = indexB.data( Qt::DisplayRole );
134
135 if( level.isString() )
136 {
137 const int compareResult =
138 dataA.toString().compare(dataB.toString(),
139 Qt::CaseInsensitive);
140 if( compareResult != 0 )
141 return ( compareResult < 0 ) != inverted;
142 }
143 else if( level.isFloat() )
144 {
145 if( dataA.toDouble() != dataB.toDouble() )
146 return ( dataA.toDouble() < dataB.toDouble() ) != inverted;
147 }
148 else // if it's neither a string nor a float ==> it's an integer
149 {
150 if( dataA.toInt() != dataB.toInt() )
151 return ( dataA.toInt() < dataB.toInt() ) != inverted;
152 }
153 }
154 }
155
156 // Tie breaker: order by row number
157 return ( sourceModelRowA < sourceModelRowB );
158 }
159
160 template<typename T>
161 int
compareBySortableName(const AmarokSharedPointer<T> & left,const AmarokSharedPointer<T> & right) const162 multilevelLessThan::compareBySortableName( const AmarokSharedPointer<T> &left,
163 const AmarokSharedPointer<T> &right ) const
164 {
165 if( !left && right )
166 return -1;
167 else if( left && !right )
168 return 1;
169 else if( left && right )
170 return left->sortableName().compare( right->sortableName(),
171 Qt::CaseInsensitive );
172 return 0;
173 }
174
175 } //namespace Playlist
176