1 /****************************************************************************************
2 * Copyright (c) 2007 Nikolaj Hald Nielsen <nhn@kde.org> *
3 * Copyright (c) 2008 Seb Ruiz <ruiz@kde.org> *
4 * Copyright (c) 2013 Ralf Engels <ralf-engels@gmx.de> *
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 "CollectionSortFilterProxyModel.h"
20
21 #include "amarokconfig.h"
22 #include "browsers/CollectionTreeItem.h"
23 #include "core/meta/Meta.h"
24 #include "core/support/Debug.h"
25 #include "widgets/PrettyTreeRoles.h"
26
27 #include <QCollator>
28 #include <QVariant>
29 #include <QString>
30
CollectionSortFilterProxyModel(QObject * parent)31 CollectionSortFilterProxyModel::CollectionSortFilterProxyModel( QObject * parent )
32 : QSortFilterProxyModel( parent )
33 , m_col( new QCollator )
34 {
35 setSortLocaleAware( true );
36
37 setSortRole( PrettyTreeRoles::SortRole );
38 setFilterRole( PrettyTreeRoles::FilterRole );
39 setSortCaseSensitivity( Qt::CaseInsensitive );
40 setFilterCaseSensitivity( Qt::CaseInsensitive );
41
42 setDynamicSortFilter( true );
43
44 m_col->setCaseSensitivity( Qt::CaseInsensitive );
45 }
46
47
~CollectionSortFilterProxyModel()48 CollectionSortFilterProxyModel::~CollectionSortFilterProxyModel()
49 {
50 delete m_col;
51 }
52
53 bool
hasChildren(const QModelIndex & parent) const54 CollectionSortFilterProxyModel::hasChildren(const QModelIndex & parent) const
55 {
56 QModelIndex sourceParent = mapToSource(parent);
57 return sourceModel()->hasChildren(sourceParent);
58 }
59
60 bool
filterAcceptsRow(int source_row,const QModelIndex & source_parent) const61 CollectionSortFilterProxyModel::filterAcceptsRow( int source_row, const QModelIndex& source_parent ) const
62 {
63 bool stringAccepted = QSortFilterProxyModel::filterAcceptsRow( source_row, source_parent );
64
65 if( AmarokConfig::showYears())
66 {
67 QModelIndex index = sourceModel()->index( source_row, 0, source_parent );
68 if( treeItem( index )->isAlbumItem() )
69 {
70 bool yearLoaded = index.data( PrettyTreeRoles::YearRole ) >= 0;
71 return yearLoaded && stringAccepted;
72 }
73 }
74
75 return stringAccepted;
76 }
77
78 bool
lessThan(const QModelIndex & left,const QModelIndex & right) const79 CollectionSortFilterProxyModel::lessThan( const QModelIndex &left, const QModelIndex &right ) const
80 {
81 CollectionTreeItem *leftItem = treeItem( left );
82 CollectionTreeItem *rightItem = treeItem( right );
83
84 // various artists and no label items are always at the top
85 if( !leftItem || leftItem->isVariousArtistItem() || leftItem->isNoLabelItem() )
86 return true;
87 if( !rightItem || rightItem->isVariousArtistItem() || rightItem->isNoLabelItem() )
88 return false;
89
90 if( leftItem->isTrackItem() && rightItem->isTrackItem() )
91 return lessThanTrack( left, right );
92
93 if( leftItem->isAlbumItem() && rightItem->isAlbumItem() )
94 return lessThanAlbum( left, right );
95
96 if( leftItem->isDataItem() && rightItem->isDataItem() )
97 return lessThanItem( left, right );
98
99 return QSortFilterProxyModel::lessThan( left, right );
100 }
101
102 bool
lessThanTrack(const QModelIndex & left,const QModelIndex & right) const103 CollectionSortFilterProxyModel::lessThanTrack( const QModelIndex &left, const QModelIndex &right ) const
104 {
105 const Meta::TrackPtr leftTrack = Meta::TrackPtr::dynamicCast( treeItem(left)->data() );
106 const Meta::TrackPtr rightTrack = Meta::TrackPtr::dynamicCast( treeItem(right)->data() );
107 if( !leftTrack || !rightTrack )
108 {
109 DEBUG_BLOCK
110 error() << "Should never have compared these two indexes"
111 << left.data(Qt::DisplayRole) << "and" << right.data(Qt::DisplayRole);
112 return QSortFilterProxyModel::lessThan( left, right );
113 }
114
115 if( AmarokConfig::showTrackNumbers() )
116 {
117 //First compare by disc number
118 if ( leftTrack->discNumber() < rightTrack->discNumber() )
119 return true;
120 if ( leftTrack->discNumber() > rightTrack->discNumber() )
121 return false;
122
123 //Disc #'s are equal, compare by track number
124 if( leftTrack->trackNumber() < rightTrack->trackNumber() )
125 return true;
126 if( leftTrack->trackNumber() > rightTrack->trackNumber() )
127 return false;
128 }
129
130 // compare by name
131 {
132 int comp = m_col->compare( leftTrack->sortableName(), rightTrack->sortableName() );
133 if( comp < 0 )
134 return true;
135 if( comp > 0 )
136 return false;
137 }
138
139 return leftTrack.data() < rightTrack.data(); // prevent expanded tracks from switching places (if that ever happens)
140 }
141
142 bool
lessThanAlbum(const QModelIndex & left,const QModelIndex & right) const143 CollectionSortFilterProxyModel::lessThanAlbum( const QModelIndex &left, const QModelIndex &right ) const
144 {
145 Meta::AlbumPtr leftAlbum = Meta::AlbumPtr::dynamicCast( treeItem(left)->data() );
146 Meta::AlbumPtr rightAlbum = Meta::AlbumPtr::dynamicCast( treeItem(right)->data() );
147
148 if( !leftAlbum || !rightAlbum )
149 {
150 DEBUG_BLOCK
151 error() << "Should never have compared these two indexes"
152 << left.data(Qt::DisplayRole) << "and" << right.data(Qt::DisplayRole);
153 return QSortFilterProxyModel::lessThan( left, right );
154 }
155
156 // compare by year
157 if( AmarokConfig::showYears() )
158 {
159 int leftYear = left.data( PrettyTreeRoles::YearRole ).toInt();
160 int rightYear = right.data( PrettyTreeRoles::YearRole ).toInt();
161
162 if( leftYear < rightYear )
163 return false; // left album is newer
164 if( leftYear > rightYear )
165 return true;
166 }
167
168 // compare by name
169 {
170 int comp = m_col->compare( leftAlbum->sortableName(), rightAlbum->sortableName() );
171 if( comp < 0 )
172 return true;
173 if( comp > 0 )
174 return false;
175 }
176
177 return leftAlbum.data() < rightAlbum.data(); // prevent expanded albums from switching places
178 }
179
180 bool
lessThanItem(const QModelIndex & left,const QModelIndex & right) const181 CollectionSortFilterProxyModel::lessThanItem( const QModelIndex &left, const QModelIndex &right ) const
182 {
183 Meta::DataPtr leftData = treeItem(left)->data();
184 Meta::DataPtr rightData = treeItem(right)->data();
185
186 if( !leftData || !rightData )
187 {
188 DEBUG_BLOCK
189 error() << "Should never have compared these two indexes"
190 << left.data(Qt::DisplayRole) << "and" << right.data(Qt::DisplayRole);
191 return QSortFilterProxyModel::lessThan( left, right );
192 }
193
194 // compare by name
195 {
196 int comp = m_col->compare( leftData->sortableName(), rightData->sortableName() );
197 if( comp < 0 )
198 return true;
199 if( comp > 0 )
200 return false;
201 }
202
203 return leftData.data() < rightData.data(); // prevent expanded data from switching places
204 }
205
206 inline CollectionTreeItem*
treeItem(const QModelIndex & index) const207 CollectionSortFilterProxyModel::treeItem( const QModelIndex &index ) const
208 {
209 return static_cast<CollectionTreeItem*>( index.internalPointer() );
210 }
211