1 /* Gnome Music Player Client (GMPC)
2 * Copyright (C) 2004-2012 Qball Cow <qball@gmpclient.org>
3 * Project homepage: http://gmpclient.org/
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "gmpc-mpddata-model.h"
25 #include "gmpc-extras.h"
28 #include "gmpc-mpddata-model-private.h"
31 #define BLOCK_SIZE 200
32 #define LOG_DOMAIN "MpdData.Model.Playlist"
35 * This is a Special version of Gmpc:MpdData:Model that is made to show the current playlist, and keep in sync with it.
36 * This is to replace the old playlist-lis backend.
37 * Use this model with Gmpc:MpdData:Treeview
40 class Gmpc:MpdData:Model:Playlist from Gmpc:MpdData:Model
41 (interface Gtk:Tree:Model)
42 (interface Gtk:Tree:Drag:Dest)
44 private GmpcConnection *conn = {NULL};
45 private unsigned int status_changed = 0;
46 private unsigned int connection_changed = 0;
48 private long long playlist_id = {0};
49 private unsigned long total_time = {0};
50 private unsigned long loaded_songs = {0};
51 private int old_highlight = -1;
53 private guint update_timeout = {0};
54 private guint last_pos = {0};
56 private gboolean update_callback(self)
58 static const unsigned int step_size = 100;
59 unsigned int num_rows = GMPC_MPDDATA_MODEL(self)->num_rows;
60 if(self->_priv->loaded_songs <num_rows)
63 MpdData *data = GMPC_MPDDATA_MODEL(self)->_priv->data;
64 data = mpd_data_get_first(data);
65 if(!data) return TRUE;
66 for(i=0; i < (self->_priv->last_pos);i++)
68 data = (MpdData *)mpd_data_get_next_real(data,FALSE);
72 for(i=0;i< step_size && data;i++)
74 if(data->song == NULL)
76 data->song = mpd_playlist_get_song_from_pos(self->_priv->mi,i+self->_priv->last_pos);
79 self->_priv->loaded_songs++;
80 if(data->song->time>0)
82 self->_priv->total_time += data->song->time;
83 self_total_playtime_changed(self, self->_priv->loaded_songs, self->_priv->total_time);
88 data = mpd_data_get_next_real(data, FALSE);
90 self->_priv->last_pos += step_size;
94 self->_priv->last_pos = 0;
101 dispose (G:Object *obj)
103 gmpc_mpddata_model_set_mpd_data(GMPC_MPDDATA_MODEL(obj), NULL);
107 finalize (G:Object *obj)
109 Self *self = GMPC_MPDDATA_MODEL_PLAYLIST(obj);
110 if(self->_priv->status_changed > 0)
112 g_signal_handler_disconnect(G_OBJECT(gmpcconn),self->_priv->status_changed);
113 self->_priv->status_changed = 0;
115 if(self->_priv->connection_changed > 0)
117 g_signal_handler_disconnect(G_OBJECT(gmpcconn),self->_priv->connection_changed);
118 self->_priv->connection_changed = 0;
120 if(self->_priv->update_timeout > 0)
122 g_source_remove(self->_priv->update_timeout);
123 self->_priv->update_timeout = 0;
128 Gmpc:MpdData:Model:Playlist *new (Gmpc:Connection *conn (check type null), MpdObj *mi(check null))
130 Self *self = GET_NEW;
131 self->_priv->conn = conn;
132 self->_priv->status_changed = g_signal_connect_swapped(G_OBJECT(conn), "status_changed", G_CALLBACK(self_status_changed), self);
133 self->_priv->connection_changed = g_signal_connect_swapped(G_OBJECT(conn), "connection_changed", G_CALLBACK(self_connection_changed), self);
134 self->_priv->mi = mi;
136 if(cfg_get_single_value_as_int_with_default(config, "playlist", "background-loading", FALSE))
138 self->_priv->update_timeout = g_timeout_add_seconds(1, (GSourceFunc)(self_update_callback), self);
144 status_changed(self, MpdObj *mi, ChangedStatusType what, Gmpc:Connection *conn (check type))
146 if(what&MPD_CST_PLAYLIST)
148 MpdData *data = NULL;
149 int new_length = mpd_playlist_get_playlist_length(mi);
151 int old_length = GMPC_MPDDATA_MODEL(self)->num_rows;
152 /* if it was empty just add everything */
155 self->_priv->loaded_songs=0;
156 self->_priv->total_time =0;
157 self_total_playtime_changed(self, self->_priv->loaded_songs, self->_priv->total_time);
160 gmpc_mpddata_model_set_mpd_data(GMPC_MPDDATA_MODEL(self), NULL);
164 else if(old_length == 0)
167 for(i = new_length; i > 0; i--)
169 data= mpd_new_data_struct_append(data);
170 data->type = MPD_DATA_TYPE_SONG;
173 gmpc_mpddata_model_set_mpd_data(GMPC_MPDDATA_MODEL(self), mpd_data_get_first(data));
177 data = mpd_playlist_get_changes_posid(mi, self->_priv->playlist_id);
178 /* if the new length is shorter then the old, remove rows at the end */
179 if(new_length < old_length)
182 MpdData_real *odata = (MpdData_real *)mpd_data_get_first(GMPC_MPDDATA_MODEL(self)->_priv->data);
183 while(odata->next)odata = odata->next;
184 /* data should be last */
186 for(i=old_length-1;i>=new_length;i--)
188 GtkTreePath *path = gtk_tree_path_new();
189 gtk_tree_path_append_index(path, i);
190 gtk_tree_model_row_deleted(GTK_TREE_MODEL(self), path);
191 gtk_tree_path_free(path);
195 self->_priv->loaded_songs--;
196 if(odata->song->time > 0)
198 self->_priv->total_time -= odata->song->time;
199 self_total_playtime_changed(self, self->_priv->loaded_songs, self->_priv->total_time);
203 odata = (MpdData_real *)mpd_data_delete_item((MpdData *)odata);
204 GMPC_MPDDATA_MODEL(self)->num_rows--;
207 /* if it's longer, append rows */
208 else if ( new_length > old_length)
211 MpdData_real *odata = (MpdData_real *)mpd_data_get_first(GMPC_MPDDATA_MODEL(self)->_priv->data);
212 /* get the last element */
213 while(odata->next) odata = odata->next;
214 for(i=old_length; i< new_length;i++)
216 GtkTreePath *path = gtk_tree_path_new();
219 odata = (MpdData_real *)mpd_new_data_struct_append((MpdData *)odata);
220 odata->type = MPD_DATA_TYPE_SONG;
223 iter.stamp = GMPC_MPDDATA_MODEL(self)->_priv->stamp;
224 iter.user_data = NULL;
225 iter.user_data2 = GINT_TO_POINTER(GMPC_MPDDATA_MODEL(self)->num_rows);
227 gtk_tree_path_append_index(path, i);
229 gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
230 gtk_tree_path_free(path);
231 GMPC_MPDDATA_MODEL(self)->num_rows++;
235 /* Now mark the changed rows */
239 MpdData_real *list_iter =(MpdData_real *) mpd_data_get_first(GMPC_MPDDATA_MODEL(self)->_priv->data);
240 MpdData_real *data_iter =(MpdData_real *) mpd_data_get_first(data);
241 while(data_iter && list_iter)
244 GtkTreePath *path = gtk_tree_path_new();
245 /* get the right entry */
246 for(;list_iter != NULL && i!= data_iter->song->pos;i++)
247 list_iter = list_iter->next;
253 self->_priv->loaded_songs--;
254 if(list_iter->song->time > 0)
256 self->_priv->total_time -= list_iter->song->time;
257 self_total_playtime_changed(self, self->_priv->loaded_songs, self->_priv->total_time);
262 mpd_freeSong(list_iter->song);
263 list_iter->song = NULL;
265 iter.stamp = GMPC_MPDDATA_MODEL(self)->_priv->stamp;
266 iter.user_data = NULL;
267 iter.user_data2 = GINT_TO_POINTER(i);
268 gtk_tree_path_append_index(path, i);
269 gtk_tree_model_row_changed(GTK_TREE_MODEL(self), path, &iter);
273 gtk_tree_path_free(path);
274 data_iter = data_iter->next;
279 if(GMPC_MPDDATA_MODEL(self)->num_rows != new_length)
281 g_log(LOG_DOMAIN, G_LOG_LEVEL_WARNING, "Playlist out of sync: %i-%i \n",GMPC_MPDDATA_MODEL(self)->num_rows,new_length);
283 self->_priv->playlist_id = mpd_playlist_get_playlist_id(mi);
285 if(what&(MPD_CST_SONGID|MPD_CST_SONGPOS|MPD_CST_STATE))
288 GtkTreePath *path = NULL;
289 int i = mpd_player_get_current_song_pos(self->_priv->mi);
290 if(self->_priv->old_highlight >= 0)
292 /* get the right entry */
293 path = gtk_tree_path_new();
294 iter.stamp = GMPC_MPDDATA_MODEL(self)->_priv->stamp;
295 iter.user_data = NULL;
296 iter.user_data2 = GINT_TO_POINTER(self->_priv->old_highlight);
297 gtk_tree_path_append_index(path, self->_priv->old_highlight);
298 gtk_tree_model_row_changed(GTK_TREE_MODEL(self), path, &iter);
299 gtk_tree_path_free(path);
300 self->_priv->old_highlight = -1;
304 /* Needs to be set before updating */
305 self->_priv->old_highlight = i;
306 /* get the right entry */
307 path = gtk_tree_path_new();
308 iter.stamp = GMPC_MPDDATA_MODEL(self)->_priv->stamp;
309 iter.user_data = NULL;
310 iter.user_data2 = GINT_TO_POINTER(i);
311 gtk_tree_path_append_index(path, i);
312 gtk_tree_model_row_changed(GTK_TREE_MODEL(self), path, &iter);
313 self_current_song_changed(self, path, &iter);
314 gtk_tree_path_free(path);
320 signal last NONE (POINTER, POINTER)
322 current_song_changed(self, GtkTreePath *path,GtkTreeIter *iter)
327 signal last NONE (LONG, LONG)
329 total_playtime_changed(self, unsigned long loaded_songs, unsigned long total_playtime)
335 connection_changed(self, MpdObj *mi, int connect, Gmpc:Connection *conn (check type))
339 gmpc_mpddata_model_set_mpd_data(GMPC_MPDDATA_MODEL(self), NULL);
340 self->_priv->playlist_id = 0;
341 self->_priv->loaded_songs = 0;
342 self->_priv->total_time = 0;
343 self_total_playtime_changed(self, self->_priv->loaded_songs, self->_priv->total_time);
349 is_current_song(Gtk:Tree:Model *model(check null type), GtkTreeIter *iter (check null))
351 Self *self = GMPC_MPDDATA_MODEL_PLAYLIST(model);
352 int n = GPOINTER_TO_INT(iter->user_data2);
353 if(n == self->_priv->old_highlight)
361 * "override" the get_value method, because we need to fetch the value before it's available.
362 * So after we fetch it, let the Gmpc:MpdData:Model handle the hard work again.
364 interface Gtk:Tree:Model
366 get_value(Gtk:Tree:Model *model(check null type), GtkTreeIter *iter (check null), gint column (check >= 0 < MPDDATA_MODEL_N_COLUMNS), GValue *value (check null))
368 Self *self = GMPC_MPDDATA_MODEL_PLAYLIST(model);
369 MpdData_real *data = iter->user_data;
370 int n = GPOINTER_TO_INT(iter->user_data2);
372 g_warning("data == NULL :: %i:: %i:: %i :: %i",n, GMPC_MPDDATA_MODEL(self)->num_rows,
373 GMPC_MPDDATA_MODEL(self)->_priv->stamp,
378 g_value_init(value, GMPC_MPDDATA_MODEL(self)->types[column]);
381 if(data->song == NULL)
383 int rs = n - n%BLOCK_SIZE;
384 int end = ((rs+(BLOCK_SIZE-1)) >= GMPC_MPDDATA_MODEL(self)->num_rows)? GMPC_MPDDATA_MODEL(self)->num_rows-1:rs+(BLOCK_SIZE-1);
386 MpdData_real *edit =(MpdData_real *) data;
387 MpdData_real *miter,*data2 = (MpdData_real *)mpd_playlist_get_song_from_pos_range(self->_priv->mi,rs, end);
389 /* rewind to start block */
390 while(n>rs){ edit = edit->prev; rs++;}
392 data2 =(MpdData_real *) mpd_data_get_first((MpdData *)data2);
395 if(edit->song == NULL)
397 edit->song = data2->song;
399 self->_priv->loaded_songs++;
400 if(edit->song->time>0)
402 self->_priv->total_time += edit->song->time;
408 mpd_data_free((MpdData *)miter);
410 self_total_playtime_changed(self, self->_priv->loaded_songs, self->_priv->total_time);
413 * If the fetch failed, return an empty value, otherwise we will get crashes
415 if(data->song == NULL)
417 g_value_init(value, GMPC_MPDDATA_MODEL(self)->types[column]);
418 g_log(LOG_DOMAIN, G_LOG_LEVEL_WARNING,"failed to get song entry\n");
421 if(column == MPDDATA_MODEL_COL_ICON_ID)
423 if(n == mpd_player_get_current_song_pos(self->_priv->mi))
425 g_value_init(value, GMPC_MPDDATA_MODEL(self)->types[column]);
426 g_value_set_string(value, "gtk-media-play-ltr");
432 * Call the parent function again
434 if(n < GMPC_MPDDATA_MODEL(self)->num_rows)
435 gmpc_mpddata_model_get_value(model, iter, column, value);
440 interface Gtk:Tree:Drag:Dest
441 private gboolean row_drop_possible
442 (Gtk:Tree:Drag:Dest *drag_dest,
444 Gtk:Selection:Data *selection_data)
448 interface Gtk:Tree:Drag:Dest
449 private gboolean drag_data_received
450 (Gtk:Tree:Drag:Dest *drag_dest,
452 Gtk:Selection:Data *selection_data)
454 GtkTreePath *path=NULL;
455 GtkTreeModel *model=NULL;
458 gint *ind = NULL, *ind2 = NULL;
459 if(dest == NULL || !gtk_tree_get_row_drag_data(selection_data, &model, &path))
463 if(model != GTK_TREE_MODEL(drag_dest)) {
465 mpd_Song *song = NULL;
466 ind = gtk_tree_path_get_indices(dest);
467 if(gtk_tree_model_get_iter(model, &iter, path))
469 gtk_tree_model_get(model, &iter,MPDDATA_MODEL_COL_MPDSONG, &song, -1);
470 if(song && song->file) {
472 int length = mpd_playlist_get_playlist_length(connection);
473 mpd_playlist_add(connection,song->file);
475 mpd_playlist_move_pos(connection, length, destp);
480 if(GMPC_MPDDATA_MODEL(model)->num_rows < 2)
482 gtk_tree_path_free(path);
485 self = GMPC_MPDDATA_MODEL_PLAYLIST(model);
486 ind = gtk_tree_path_get_indices(dest);
487 ind2 = gtk_tree_path_get_indices(path);
490 int original = ind2[0];
491 int destination = ind[0];
492 /* if(destination >0 && ind[1] != '\0') destination--; */
493 if(destination > original) destination--;
494 mpd_playlist_move_pos(self->_priv->mi,original,destination);
496 gtk_tree_path_free(path);
502 trigger_total_playtime_signal(self)
504 self_total_playtime_changed(self, self->_priv->loaded_songs, self->_priv->total_time);
508 get_total_playtime(self, unsigned long *loaded_songs, unsigned long *total_time)
511 (*loaded_songs) = self->_priv->loaded_songs;
513 (*total_time)= self->_priv->total_time;