Added Search functionality, and fixed navigation
[spotify:spotify.git] / libqspotify / src / qspotifyplaylist.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QMLify research project.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file.  Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 **
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42     #include "qspotifyplaylist.h"
43 #include "qspotifytrack.h"
44
45 #include <libspotify/api.h>
46
47 #include <QHash>
48
49 #include <QDebug>
50
51 QSpotifyPlaylist *createPlaylist(sp_playlistcontainer *container, int row)
52 {
53     int type = sp_playlistcontainer_playlist_type(container,row);
54     QSpotifyPlaylist *pl = new QSpotifyPlaylist(sp_playlistcontainer_playlist(container, row),type);
55     return pl;
56 }
57
58 static void playlist_added(sp_playlistcontainer *plContainer, sp_playlist *pl, int position, void *userdata)
59 {
60     QSpotifyPlaylist *spotifyPlaylist = createPlaylist(plContainer,position);
61     Q_ASSERT(spotifyPlaylist->  assertIsTheSame(pl));
62     QSpotifyPlaylistContainer *container = static_cast<QSpotifyPlaylistContainer *>(userdata);
63     if (container) {
64         container->addPlaylist(spotifyPlaylist,position);
65     }
66 }
67
68 static void playlist_removed(sp_playlistcontainer *, sp_playlist *, int position, void *userdata)
69 {
70     QSpotifyPlaylistContainer *container = static_cast<QSpotifyPlaylistContainer *>(userdata);
71     if (container) {
72         container->removePlaylist(position);
73     }
74 }
75
76 static void playlist_moved(sp_playlistcontainer *, sp_playlist *, int position, int new_position, void *userdata)
77 {
78     QSpotifyPlaylistContainer *container = static_cast<QSpotifyPlaylistContainer *>(userdata);
79     if (container) {
80         container->movePlaylist(position,new_position);
81     }
82 }
83
84 void container_loaded(sp_playlistcontainer *, void *userdata)
85 {
86     QSpotifyPlaylistContainer *container = static_cast<QSpotifyPlaylistContainer *>(userdata);
87     if (container) {
88         container->setIsFullyLoaded();
89     }
90 }
91
92 static sp_playlistcontainer_callbacks pc_callbacks = {
93     &playlist_added,
94     &playlist_removed,
95     &playlist_moved,
96     &container_loaded
97 };
98
99 static void tracks_added(sp_playlist *, sp_track *const *tracks, int num_tracks, int position, void *userdata)
100 {
101     QList<QSpotifyTrack *>spotifyTracks;
102     for (int i = 0; i < num_tracks; i++){
103         sp_track *track = *(tracks + i);
104         QSpotifyTrack *spotifyTrack = new QSpotifyTrack(track);
105         spotifyTracks.append(spotifyTrack);
106     }
107     QSpotifyPlaylist *playlist = static_cast<QSpotifyPlaylist *>(userdata);
108     if (playlist) {
109         playlist->addTracks(spotifyTracks,position);
110     }
111 }
112
113 static void tracks_removed(sp_playlist *pl, const int *tracks, int num_tracks, void *userdata)
114 {
115     QSpotifyPlaylist *playlist = static_cast<QSpotifyPlaylist *>(userdata);
116     if (playlist) {
117         int i = 0;
118         while (i < num_tracks) {
119             //compress modifications of vector
120             int numberOfTracks = 1;
121             int position = *(tracks+i);
122             while(*(tracks+numberOfTracks) == (position+numberOfTracks))
123                 numberOfTracks++;
124
125             playlist->removeTracks(numberOfTracks,position);
126             i+=numberOfTracks;
127         }
128     }
129 }
130
131 static void tracks_moved(sp_playlist *, const int *tracks, int num_tracks, int new_position, void *userdata)
132 {
133     QSpotifyPlaylist *playlist = static_cast<QSpotifyPlaylist *>(userdata);
134     if (playlist) {
135         int i = 0;
136         while (i < num_tracks) {
137             int end = i+1;
138             int position = *(tracks+i);
139             while(*(tracks+end) == (position+end))
140                 end++;
141             int numberOfTracks = i-end;
142             playlist->moveTracks(numberOfTracks,position, new_position + i);
143             i+=numberOfTracks;
144         }
145     }
146 }
147
148 static void playlist_renamed(sp_playlist *, void *userdata)
149 {
150     QSpotifyPlaylist *playlist = static_cast<QSpotifyPlaylist *>(userdata);
151     if (playlist) {
152         QMetaObject::invokeMethod(playlist,"playlistRenamed");
153     }
154 }
155
156 static void playlist_state_changed(sp_playlist *, void *userdata)
157 {
158     QSpotifyPlaylist *playlist = static_cast<QSpotifyPlaylist *>(userdata);
159     if (playlist) {
160         QMetaObject::invokeMethod(playlist,"playlistStateChanged");
161     }
162 }
163
164 void playlist_update_in_progress(sp_playlist *, bool done, void *userdata)
165 {
166     QSpotifyPlaylist *playlist = static_cast<QSpotifyPlaylist *>(userdata);
167     if (playlist) {
168         if (done)
169             QMetaObject::invokeMethod(playlist,"playlistUpdateFinished");
170         else
171             QMetaObject::invokeMethod(playlist,"playlistUpdateInProgress");
172     }
173 }
174
175 void playlist_metadata_updated(sp_playlist *, void *userdata)
176 {
177     QSpotifyPlaylist *playlist = static_cast<QSpotifyPlaylist *>(userdata);
178     if (playlist) {
179         QMetaObject::invokeMethod(playlist,"playlistMetaDataUpdated");
180     }
181 }
182
183 static sp_playlist_callbacks pl_callbacks = {
184     &tracks_added,
185     &tracks_removed,
186     &tracks_moved,
187     &playlist_renamed,
188     &playlist_state_changed,
189     &playlist_update_in_progress,
190     &playlist_metadata_updated,
191     0,
192     0,
193     0,
194     0
195 };
196
197 QSpotifyPlaylist::QSpotifyPlaylist(sp_playlist *playlist, int type)
198     : m_playlist(playlist)
199     , m_playlistType(type)
200 {
201     sp_playlist_add_ref(playlist);
202     sp_playlist_add_callbacks(m_playlist,&pl_callbacks,this);
203     m_cacheList.resize(sp_playlist_num_tracks(m_playlist));
204     m_cacheList.fill(0);
205 }
206
207 QSpotifyPlaylist::~QSpotifyPlaylist()
208 {
209     sp_playlist_release(m_playlist);
210 }
211
212 QString QSpotifyPlaylist::name() const
213 {
214     const char *name = sp_playlist_name(m_playlist);
215     return QString::fromLocal8Bit(name);
216 }
217
218 QSpotifyTrack * QSpotifyPlaylist::trackAt(int i) const
219 {
220     if (i < 0 || i >= m_cacheList.size())
221         return 0;
222     qDebug() << "Returning track";
223     return m_cacheList[i];
224 }
225
226 QVariant QSpotifyPlaylist::data(const QModelIndex &index, int role) const
227 {
228     if (role != Qt::DisplayRole)
229         return QVariant();
230     if (index.row() >= m_cacheList.size()) {
231         return QVariant();
232     }
233
234     QSpotifyTrack *track = m_cacheList[index.row()];
235     if (!track) {
236         track = new QSpotifyTrack(sp_playlist_track(m_playlist,index.row()));
237         const_cast<QSpotifyPlaylist *>(this)->m_cacheList.replace(index.row(), track);
238     }
239     return qVariantFromValue<QObject *>(track);
240 }
241
242 int QSpotifyPlaylist::rowCount(const QModelIndex & parent) const
243 {
244     Q_UNUSED(parent)
245     return sp_playlist_num_tracks(m_playlist);
246 }
247
248 int QSpotifyPlaylist::playlistType() const
249 {
250     return m_playlistType;
251 }
252
253
254 void QSpotifyPlaylist::addTracks(QList<QSpotifyTrack *> tracks, int position)
255 {
256     QModelIndex index = this->index(position);
257     beginInsertRows(index,position,position+tracks.size());
258
259     QVector<QSpotifyTrack *> newCacheList;
260     newCacheList.reserve(m_cacheList.size() + tracks.size());
261     if (position)
262         newCacheList += m_cacheList.mid(0,position -1);
263
264     newCacheList += tracks.toVector();
265
266     if (position < m_cacheList.size())
267         newCacheList += m_cacheList.mid(position);
268
269     m_cacheList = newCacheList;
270
271     endInsertRows();
272 }
273
274 void QSpotifyPlaylist::removeTracks(int numberOfTracks, int position)
275 {
276     QModelIndex index = this->index(position);
277
278     beginRemoveRows(index,position, position + numberOfTracks);
279
280     QVector<QSpotifyTrack *> newCacheList;
281     newCacheList.reserve(m_cacheList.size()- numberOfTracks);
282
283     if (position)
284         newCacheList += m_cacheList.mid(0,position -1);
285
286     if (position + numberOfTracks < m_cacheList.size())
287         newCacheList += m_cacheList.mid(position + numberOfTracks);
288
289     m_cacheList = newCacheList;
290
291     endRemoveRows();
292 }
293
294 void QSpotifyPlaylist::moveTracks(int numberOfTracks, int oldPosition, int newPosition)
295 {
296     Q_UNUSED(numberOfTracks);
297     Q_UNUSED(oldPosition);
298     Q_UNUSED(newPosition);
299     qDebug() << "moving tracks isn't supported yet";
300 }
301
302
303 QSpotifyPlaylistContainer::QSpotifyPlaylistContainer(sp_playlistcontainer *pc, QObject *parent)
304     : QAbstractListModel(parent)
305     , m_pc(pc)
306     , m_isFullyLoaded(false)
307 {
308     sp_playlistcontainer_add_callbacks(pc,&pc_callbacks,this);
309     invalidateCache();
310 }
311
312 int QSpotifyPlaylistContainer::rowCount(const QModelIndex &parent) const
313 {
314     Q_UNUSED(parent);
315     return sp_playlistcontainer_num_playlists(m_pc);
316 }
317
318 QVariant QSpotifyPlaylistContainer::data ( const QModelIndex & index, int role = Qt::DisplayRole ) const
319 {
320     if (role == Qt::DisplayRole) {
321         if (!m_isFullyLoaded)
322             qDebug() << "asking for data at" << index.row() << "when not finished";
323         if (index.row() >= m_cacheList.size())
324             return QVariant();
325         QSpotifyPlaylist *pl = m_cacheList[index.row()];
326         if (!pl) {
327             pl = createPlaylist(m_pc,index.row());
328             const_cast<QSpotifyPlaylistContainer *>(this)->m_cacheList.replace(index.row(),pl);
329         }
330         return qVariantFromValue<QObject *>(pl);
331     }
332     return QVariant();
333 }
334
335 QSpotifyPlaylist * QSpotifyPlaylistContainer::playlistAt(int i) const
336 {
337     if (i < 0 || i >= m_cacheList.size())
338         return 0;
339     return m_cacheList[i];
340 }
341 void QSpotifyPlaylistContainer::invalidateCache()
342 {
343     qDebug() << "invalidating";
344     m_cacheList.clear();
345     m_cacheList.resize(rowCount());
346     m_cacheList.fill(0);
347 }
348
349 void QSpotifyPlaylistContainer::setIsFullyLoaded()
350 {
351     m_isFullyLoaded = true;
352     emit containerFullyLoaded();
353 }
354
355 void QSpotifyPlaylistContainer::addPlaylist(QSpotifyPlaylist *playlist, int pos)
356 {
357     QModelIndex index = this->index(pos);
358     beginInsertRows(index,pos+1, pos+2);
359     m_cacheList.insert(pos,playlist);
360     endInsertRows();
361 }
362
363 void QSpotifyPlaylistContainer::removePlaylist(int pos)
364 {
365     QModelIndex index = this->index(pos);
366     beginRemoveRows(index,pos, pos+1);
367     m_cacheList.remove(pos);
368     endRemoveRows();
369
370 }
371
372 void QSpotifyPlaylistContainer::movePlaylist(int oldPos, int newPos)
373 {
374     QModelIndex oldIndex = index(oldPos);
375     QModelIndex newIndex = index(newPos-1);
376     beginMoveRows(oldIndex,oldPos, oldPos,newIndex,newPos-1);
377     QSpotifyPlaylist *playlist = m_cacheList[oldPos];
378     m_cacheList.remove(oldPos);
379     m_cacheList.insert(newPos,playlist);
380     endMoveRows();
381 }
382
383