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