1 /****************************************************************************************
2 * Copyright (c) 2009 Téo Mrnjavac <teo@kde.org> *
3 * Copyright (c) 2010 Nanno Langstraat <langstr@gmail.com> *
4 * *
5 * This program is free software; you can redistribute it and/or modify it under *
6 * the terms of the GNU General Public License as published by the Free Software *
7 * Foundation; either version 2 of the License, or (at your option) any later *
8 * version. *
9 * *
10 * This program is distributed in the hope that it will be useful, but WITHOUT ANY *
11 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A *
12 * PARTICULAR PURPOSE. See the GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License along with *
15 * this program. If not, see <http://www.gnu.org/licenses/>. *
16 ****************************************************************************************/
17
18 #include "ProxyBase.h"
19
20 #include "core/meta/Meta.h"
21 #include "core/meta/Statistics.h"
22 #include "core-impl/playlists/types/file/PlaylistFileSupport.h"
23 #include "playlist/PlaylistModel.h"
24
25 namespace Playlist
26 {
27
ProxyBase(AbstractModel * belowModel,QObject * parent)28 ProxyBase::ProxyBase( AbstractModel *belowModel, QObject *parent )
29 : QSortFilterProxyModel( parent )
30 , m_belowModel( belowModel )
31 {
32 setSourceModel( m_belowModel->qaim() );
33
34 // Proxy the Playlist::AbstractModel signals.
35 // If you need to do something special in a subclass, disconnect() this signal and
36 // do your own connect() call.
37 connect( qobject_cast<Playlist::Model*>( sourceModel() ), &Playlist::Model::activeTrackChanged,
38 this, &ProxyBase::activeTrackChanged );
39 connect( qobject_cast<Playlist::Model*>( sourceModel() ), &Playlist::Model::queueChanged,
40 this, &ProxyBase::queueChanged );
41 }
42
~ProxyBase()43 ProxyBase::~ProxyBase()
44 {}
45
46 // Pass-through virtual public methods, that pretty much just forward stuff through the stack
47 // of proxies, start here.
48 // Please keep them sorted alphabetically. -- Téo
49
50 quint64
activeId() const51 ProxyBase::activeId() const
52 {
53 return m_belowModel->activeId();
54 }
55
56 int
activeRow() const57 ProxyBase::activeRow() const
58 {
59 // We map the active row form the source to this ProxyModel.
60 return rowFromSource( m_belowModel->activeRow() );
61 }
62
63 Meta::TrackPtr
activeTrack() const64 ProxyBase::activeTrack() const
65 {
66 return m_belowModel->activeTrack();
67 }
68
69 QSet<int>
allRowsForTrack(const Meta::TrackPtr & track) const70 ProxyBase::allRowsForTrack( const Meta::TrackPtr& track ) const
71 {
72 QSet<int> proxyModelRows;
73
74 foreach( int sourceModelRow, m_belowModel->allRowsForTrack( track ) )
75 {
76 int proxyModelRow = rowFromSource( sourceModelRow );
77 if ( proxyModelRow != -1 )
78 proxyModelRows.insert( proxyModelRow );
79 }
80
81 return proxyModelRows;
82 }
83
84 void
clearSearchTerm()85 ProxyBase::clearSearchTerm()
86 {
87 m_belowModel->clearSearchTerm();
88 }
89
90 bool
containsTrack(const Meta::TrackPtr & track) const91 ProxyBase::containsTrack( const Meta::TrackPtr& track ) const
92 {
93 return ( firstRowForTrack( track ) != -1 ); // Let him do the clever work.
94 }
95
96 int
currentSearchFields()97 ProxyBase::currentSearchFields()
98 {
99 return m_belowModel->currentSearchFields();
100 }
101
102 QString
currentSearchTerm()103 ProxyBase::currentSearchTerm()
104 {
105 return m_belowModel->currentSearchTerm();
106 }
107
108 bool
exportPlaylist(const QString & path,bool relative)109 ProxyBase::exportPlaylist( const QString &path, bool relative )
110 {
111 return Playlists::exportPlaylistFile( tracks(), QUrl::fromLocalFile(path), relative );
112 }
113
114 void
filterUpdated()115 ProxyBase::filterUpdated()
116 {
117 m_belowModel->filterUpdated();
118 }
119
120 int
find(const QString & searchTerm,int searchFields)121 ProxyBase::find( const QString &searchTerm, int searchFields )
122 {
123 ProxyBase *proxyBase = dynamic_cast< ProxyBase * >( m_belowModel );
124 if ( !proxyBase )
125 return -1;
126
127 return rowFromSource( proxyBase->find( searchTerm, searchFields ) );
128 }
129
130 int
findNext(const QString & searchTerm,int selectedRow,int searchFields)131 ProxyBase::findNext( const QString &searchTerm, int selectedRow, int searchFields )
132 {
133 ProxyBase *proxyBase = dynamic_cast< ProxyBase * >( m_belowModel );
134 if ( !proxyBase )
135 return -1;
136
137 return rowFromSource( proxyBase->findNext( searchTerm, rowToSource( selectedRow ), searchFields ) );
138 }
139
140 int
findPrevious(const QString & searchTerm,int selectedRow,int searchFields)141 ProxyBase::findPrevious( const QString &searchTerm, int selectedRow, int searchFields )
142 {
143 ProxyBase *proxyBase = dynamic_cast< ProxyBase * >( m_belowModel );
144 if ( !proxyBase )
145 return -1;
146
147 return rowFromSource( proxyBase->findPrevious( searchTerm, rowToSource( selectedRow ), searchFields ) );
148 }
149
150 int
firstRowForTrack(const Meta::TrackPtr & track) const151 ProxyBase::firstRowForTrack( const Meta::TrackPtr& track ) const
152 {
153 // First optimistically try 'firstRowForTrack()'. It'll usually work.
154 int proxyModelRow = rowFromSource( m_belowModel->firstRowForTrack( track ) );
155 if ( proxyModelRow != -1 )
156 return proxyModelRow;
157 else
158 {
159 // It might be that there are multiple hits in the source model, and we just got
160 // unlucky with a source row that's filtered out in this model. So, we need to
161 // check all hits.
162 foreach( int sourceModelRow, m_belowModel->allRowsForTrack( track ) )
163 {
164 proxyModelRow = rowFromSource( sourceModelRow );
165 if ( proxyModelRow != -1 )
166 return proxyModelRow;
167 }
168
169 return -1;
170 }
171 }
172
173 quint64
idAt(const int row) const174 ProxyBase::idAt( const int row ) const
175 {
176 if( rowExists( row ) )
177 return m_belowModel->idAt( rowToSource( row ) );
178 return 0;
179 }
180
181 bool
rowExists(int row) const182 ProxyBase::rowExists( int row ) const
183 {
184 QModelIndex index = this->index( row, 0 );
185 return index.isValid();
186 }
187
188 int
rowForId(const quint64 id) const189 ProxyBase::rowForId( const quint64 id ) const
190 {
191 return rowFromSource( m_belowModel->rowForId( id ) );
192 }
193
194 int
rowFromBottomModel(const int row)195 ProxyBase::rowFromBottomModel( const int row )
196 {
197 return rowFromSource( m_belowModel->rowFromBottomModel( row ) );
198 }
199
200 int
rowToBottomModel(const int row)201 ProxyBase::rowToBottomModel( const int row )
202 {
203 return m_belowModel->rowToBottomModel( rowToSource( row ) );
204 }
205
206 void
setActiveId(const quint64 id)207 ProxyBase::setActiveId( const quint64 id )
208 {
209 m_belowModel->setActiveId( id );
210 }
211
212 void
setActiveRow(int row)213 ProxyBase::setActiveRow( int row )
214 {
215 m_belowModel->setActiveRow( rowToSource( row ) );
216 }
217
218 void
setAllUnplayed()219 ProxyBase::setAllUnplayed()
220 {
221 m_belowModel->setAllUnplayed();
222 }
223
224 void
emitQueueChanged()225 ProxyBase::emitQueueChanged()
226 {
227 Q_ASSERT_X(false, "emitQueueChanged", "queueChanged() should be emitted at the bottom of "
228 "the model stack so it can be received from every model.");
229 }
230
231 int
queuePositionOfRow(int row)232 ProxyBase::queuePositionOfRow( int row )
233 {
234 return m_belowModel->queuePositionOfRow( rowToSource ( row ) );
235 }
236
237 void
showOnlyMatches(bool onlyMatches)238 ProxyBase::showOnlyMatches( bool onlyMatches )
239 {
240 ProxyBase *proxyBase = dynamic_cast< ProxyBase * >( m_belowModel );
241 if ( !proxyBase )
242 return ;
243
244 proxyBase->showOnlyMatches( onlyMatches );
245 }
246
247 Item::State
stateOfId(quint64 id) const248 ProxyBase::stateOfId( quint64 id ) const
249 {
250 return m_belowModel->stateOfId( id );
251 }
252
253 Item::State
stateOfRow(int row) const254 ProxyBase::stateOfRow( int row ) const
255 {
256 return m_belowModel->stateOfRow( rowToSource( row ) );
257 }
258
259 qint64
totalLength() const260 ProxyBase::totalLength() const
261 {
262 return m_belowModel->totalLength();
263 }
264
265 quint64
totalSize() const266 ProxyBase::totalSize() const
267 {
268 return m_belowModel->totalSize();
269 }
270
271 Meta::TrackPtr
trackAt(int row) const272 ProxyBase::trackAt(int row) const
273 {
274 return m_belowModel->trackAt( rowToSource( row ) );
275 }
276
277 Meta::TrackPtr
trackForId(const quint64 id) const278 ProxyBase::trackForId( const quint64 id ) const
279 {
280 return m_belowModel->trackForId( id );
281 }
282
283 Meta::TrackList
tracks()284 ProxyBase::tracks()
285 {
286 Meta::TrackList tl;
287 for( int i = 0; i < rowCount(); ++i )
288 tl << trackAt( i );
289 return tl;
290 }
291
292 //protected:
293
294 bool
rowMatch(int sourceModelRow,const QString & searchTerms,int searchFields) const295 ProxyBase::rowMatch( int sourceModelRow, const QString &searchTerms, int searchFields ) const
296 {
297 if ( !m_belowModel )
298 return false;
299
300 Meta::TrackPtr track = m_belowModel->trackAt( sourceModelRow );
301
302 QStringList searchList = searchTerms.split(QLatin1Char(' '), QString::SkipEmptyParts);
303
304 foreach( const QString& searchTerm, searchList )
305 {
306 bool match = false;
307
308 if ( searchFields & MatchTrack &&
309 track->prettyName().contains( searchTerm, Qt::CaseInsensitive )
310 )
311 match = true;
312
313 if ( searchFields & MatchArtist &&
314 track->artist() &&
315 track->artist()->prettyName().contains( searchTerm, Qt::CaseInsensitive )
316 )
317 match = true;
318
319 if ( searchFields & MatchAlbum &&
320 track->album() &&
321 track->album()->prettyName().contains( searchTerm, Qt::CaseInsensitive )
322 )
323 match = true;
324
325 if ( searchFields & MatchGenre &&
326 track->genre() &&
327 track->genre()->prettyName().contains( searchTerm, Qt::CaseInsensitive )
328 )
329 match = true;
330
331 if ( searchFields & MatchComposer &&
332 track->composer() &&
333 track->composer()->prettyName().contains( searchTerm, Qt::CaseInsensitive )
334 )
335 match = true;
336
337 if ( searchFields & MatchYear &&
338 track->year() &&
339 track->year()->prettyName().contains( searchTerm, Qt::CaseInsensitive )
340 )
341 match = true;
342
343 if( searchFields & MatchRating )
344 {
345 bool ok;
346 int rating = QString( searchTerm ).remove( QStringLiteral("rating:") ).toInt( &ok );
347 if( ok && ( track->statistics()->rating() == rating ) )
348 match = true;
349 }
350
351 if( !match )
352 return false;
353 }
354
355 return true;
356 }
357
358 int
rowFromSource(int sourceModelRow) const359 ProxyBase::rowFromSource( int sourceModelRow ) const
360 {
361 QModelIndex sourceModelIndex = sourceModel()->index( sourceModelRow, 0 );
362 QModelIndex proxyModelIndex = mapFromSource( sourceModelIndex ); // Call 'map' even for a 1:1 passthrough proxy: QSFPM might need it.
363
364 if ( proxyModelIndex.isValid() )
365 return proxyModelIndex.row();
366 else
367 return -1;
368 }
369
370 int
rowToSource(int proxyModelRow) const371 ProxyBase::rowToSource( int proxyModelRow ) const
372 {
373 QModelIndex proxyModelIndex = this->index( proxyModelRow, 0 );
374 QModelIndex sourceModelIndex = mapToSource( proxyModelIndex ); // Call 'map' even for a 1:1 passthrough proxy: QSFPM might need it.
375
376 if( sourceModelIndex.isValid() )
377 return sourceModelIndex.row();
378 else
379 if( proxyModelRow == rowCount() )
380 return sourceModel()->rowCount();
381 else
382 return -1;
383 }
384
385 } //namespace Playlist
386
387