5 #include <libmpd/libmpd.h>
6 #include <libmpd/libmpd-internal.h>
10 #include "playlist3.h"
12 #include "config-defaults.h"
21 MPDDATA_MODEL_COL_MPDSONG = 0, /* get the mpd_Song */
22 MPDDATA_MODEL_COL_PLAYING, /* Shows if this song is the current song */
23 MPDDATA_MODEL_COL_PLAYING_FONT_WEIGHT, /* Shows if this song is the current song */
24 MPDDATA_MODEL_COL_PATH, /* Path to song/file/directory */
25 MPDDATA_MODEL_COL_MARKUP, /* a string that has FALSEmarkup */
26 MPDDATA_MODEL_COL_SONG_ARTIST, /* artist name */
27 MPDDATA_MODEL_COL_SONG_ALBUM, /* album name */
28 MPDDATA_MODEL_COL_SONG_TITLE, /* song title */
29 MPDDATA_MODEL_COL_SONG_TITLEFILE, /* song title */
30 MPDDATA_MODEL_COL_SONG_GENRE, /* song genre */
31 MPDDATA_MODEL_COL_SONG_TRACK, /* song track */
32 MPDDATA_MODEL_COL_SONG_NAME, /* stream name */
33 MPDDATA_MODEL_COL_SONG_COMPOSER, /* composer name */
34 MPDDATA_MODEL_COL_SONG_PERFORMER, /* performer */
35 MPDDATA_MODEL_COL_SONG_DATE, /* date */
36 MPDDATA_MODEL_COL_SONG_LENGTH_FORMAT, /* length formatted */
37 MPDDATA_MODEL_COL_SONG_DISC, /* disc */
38 MPDDATA_MODEL_COL_SONG_COMMENT, /* comment */
39 MPDDATA_MODEL_COL_SONG_POS, /* position */
40 MPDDATA_MODEL_COL_PATH_EXTENSION, /* Extention */
41 MPDDATA_MODEL_COL_PATH_DIRECTORY, /* Directory */
42 MPDDATA_MODEL_COL_SONG_ID, /* col id */
43 MPDDATA_MODEL_COL_ICON_ID, /* icon id */
44 MPDDATA_MODEL_COL_SONG_LENGTH, /* length */
45 MPDDATA_MODEL_TAG_TYPE, /* tag type */
46 MPDDATA_MODEL_ROW_TYPE, /* type of the row */
47 MPDDATA_MODEL_META_DATA, /* metadata */
48 MPDDATA_MODEL_USERDATA,
49 MPDDATA_MODEL_N_COLUMNS
53 class Gmpc:MpdData:Model from G:Object
54 (interface Gtk:Tree:Sortable)
55 (interface Gtk:Tree:Model)
57 private gint stamp = {g_random_int()};
58 public GType types[MPDDATA_MODEL_N_COLUMNS];
59 private MpdData *data = NULL;
60 public gint num_rows = 0;
61 private GdkPixbuf **images = NULL;
62 private gchar *req_artist = {NULL} destroywith g_free;
65 private GtkSortType sort_order = GTK_SORT_ASCENDING;
66 private int sort_column = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID;
67 private int old_sort_column = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID;
68 public gboolean has_up = FALSE;
69 private gchar *markup = {gmpc_signals_get_browser_markup(gmpc_signals)} destroywith g_free;
70 private gulong markup_signal = 0;
72 private gulong playtime = {0};
74 property BOOLEAN has_up
76 blurb = "Show an 'up row",
77 default_value = FALSE,
83 markup_changed(self, const char *markup, gpointer data)
86 GtkTreePath *path = NULL;
88 MpdData *data2 = mpd_data_get_first(self->_priv->data);
90 /* set the new markup */
91 if(self->_priv->markup )
92 g_free(self->_priv->markup);
93 self->_priv->markup = g_strdup(markup);
95 if(self->has_up) i = 1;
96 for(;data2; data2 = mpd_data_get_next_real(data2,FALSE))
98 path = gtk_tree_path_new();
99 gtk_tree_path_append_index(path,i);
100 iter.stamp = self->_priv->stamp;
101 iter.user_data = data2;
102 iter.user_data2 = GINT_TO_POINTER(i);
104 /* propegate change */
105 gtk_tree_model_row_changed(GTK_TREE_MODEL(self), path, &iter);
112 self->types[MPDDATA_MODEL_COL_MPDSONG] = G_TYPE_POINTER;
113 self->types[MPDDATA_MODEL_COL_MARKUP] = G_TYPE_STRING;
114 self->types[MPDDATA_MODEL_COL_PLAYING] = G_TYPE_BOOLEAN;
115 self->types[MPDDATA_MODEL_COL_PLAYING_FONT_WEIGHT] = G_TYPE_INT;
116 self->types[MPDDATA_MODEL_COL_PATH] = G_TYPE_STRING;
117 self->types[MPDDATA_MODEL_COL_SONG_ARTIST] = G_TYPE_STRING;
118 self->types[MPDDATA_MODEL_COL_SONG_ALBUM] = G_TYPE_STRING;
119 self->types[MPDDATA_MODEL_COL_SONG_TITLE] = G_TYPE_STRING;
120 self->types[MPDDATA_MODEL_COL_SONG_TITLEFILE] = G_TYPE_STRING;
121 self->types[MPDDATA_MODEL_COL_SONG_TRACK] = G_TYPE_STRING;
122 self->types[MPDDATA_MODEL_COL_SONG_GENRE] = G_TYPE_STRING;
123 self->types[MPDDATA_MODEL_COL_SONG_NAME] = G_TYPE_STRING;
124 self->types[MPDDATA_MODEL_COL_SONG_COMPOSER] = G_TYPE_STRING;
125 self->types[MPDDATA_MODEL_COL_SONG_PERFORMER]= G_TYPE_STRING;
126 self->types[MPDDATA_MODEL_COL_SONG_DATE] = G_TYPE_STRING;
127 self->types[MPDDATA_MODEL_COL_SONG_LENGTH] = G_TYPE_INT;
128 self->types[MPDDATA_MODEL_COL_SONG_LENGTH_FORMAT] = G_TYPE_STRING;
129 self->types[MPDDATA_MODEL_COL_SONG_DISC] = G_TYPE_STRING;
130 self->types[MPDDATA_MODEL_COL_SONG_COMMENT] = G_TYPE_STRING;
131 self->types[MPDDATA_MODEL_COL_SONG_POS] = G_TYPE_INT;
132 self->types[MPDDATA_MODEL_COL_PATH_EXTENSION] = G_TYPE_STRING;
133 self->types[MPDDATA_MODEL_COL_PATH_DIRECTORY] = G_TYPE_STRING;
134 self->types[MPDDATA_MODEL_COL_SONG_ID] = G_TYPE_INT;
135 self->types[MPDDATA_MODEL_COL_ICON_ID] = G_TYPE_STRING;
136 self->types[MPDDATA_MODEL_TAG_TYPE] = G_TYPE_INT;
137 self->types[MPDDATA_MODEL_ROW_TYPE] = G_TYPE_INT;
138 self->types[MPDDATA_MODEL_USERDATA] = G_TYPE_POINTER;
139 self->types[MPDDATA_MODEL_META_DATA] = G_TYPE_OBJECT;
141 self->_priv->markup_signal = g_signal_connect_swapped(G_OBJECT(gmpc_signals), "browser-markup-changed", G_CALLBACK(self_markup_changed), self);
146 Gmpc:MpdData:Model *new (void)
153 finalize (G:Object *obj)
156 Self *self = GMPC_MPDDATA_MODEL(obj);
157 if(self->_priv->markup_signal)
159 g_signal_handler_disconnect(G_OBJECT(gmpc_signals),self->_priv->markup_signal);
161 self->_priv->markup_signal = 0;
163 if(self->_priv->data)
165 mpd_data_free(self->_priv->data);
166 self->_priv->data = NULL;
168 if(self->_priv->images)
170 for(i=0;i<self->num_rows;i++)
172 if(self->_priv->images[i]) {
173 g_object_unref(self->_priv->images[i]);
174 self->_priv->images[i] = NULL;
177 q_free(self->_priv->images);
178 /* q_free should do it, but force it */
179 self->_priv->images = NULL;
183 /* function implemented for the Gtk:Tree:Model interface */
184 interface Gtk:Tree:Model
185 private GtkTreeModelFlags
186 get_flags (Gtk:Tree:Model *self (check null type))
188 /* Here would be the implementation */
189 return (GtkTreeModelFlags)GTK_TREE_MODEL_LIST_ONLY;
193 interface Gtk:Tree:Model
195 gboolean iter_children(Gtk:Tree:Model *model, GtkTreeIter *iter, GtkTreeIter *parent)
197 Self *self = GMPC_MPDDATA_MODEL(model);
200 if(self->num_rows == 0)
202 /* Set iter to first item in list */
203 iter->stamp = self->_priv->stamp;
204 iter->user_data = self->_priv->data;
205 iter->user_data2 = GINT_TO_POINTER(0);
206 iter->user_data3 = NULL; /* unused */
211 * Unused, not known in the model directly?
213 interface Gtk:Tree:Model
215 gint get_n_columns(Gtk:Tree:Model *model)
217 return MPDDATA_MODEL_N_COLUMNS;
220 interface Gtk:Tree:Model
222 get_iter(Gtk:Tree:Model *model (check null type),
223 Gtk:Tree:Iter *iter (check null),
224 Gtk:Tree:Path *path (check null))
226 MpdData *data = NULL;
227 Self *self = GMPC_MPDDATA_MODEL(model);
228 gint *indices, n,depth,i;
229 indices = gtk_tree_path_get_indices(path);
230 depth = gtk_tree_path_get_depth(path);
233 g_assert(depth == 1);
235 n = indices[0]; /* the n-th top level row */
236 if (n >= self->num_rows || n < 0)
239 data = (MpdData*)self->_priv->data;
247 data = mpd_data_get_next_real(data,FALSE);
249 g_assert(n<self->num_rows);
250 if(self->has_up && n == 0)
253 iter->stamp = self->_priv->stamp;
254 iter->user_data = data;
255 iter->user_data2 = GINT_TO_POINTER(n);
259 interface Gtk:Tree:Model
261 iter_next(Gtk:Tree:Model *model(check null type), GtkTreeIter *iter (check null))
263 Self *self = GMPC_MPDDATA_MODEL(model);
264 MpdData *data = iter->user_data;
265 int n = GPOINTER_TO_INT(iter->user_data2) ;
266 /* if the row is the last row in the list or bigger then the last row, return FALSE */
267 if(n >= self->num_rows)
271 /* if the row is the first row in the list, and it has a "go-up" entry,
272 * it can have a next entry
274 if(data == NULL && n == 0 && self->has_up == TRUE)
276 iter->user_data = (MpdData *)self->_priv->data;
277 iter->user_data2 = GINT_TO_POINTER(1);
278 if(iter->user_data == NULL)
282 if(mpd_data_get_next_real(data,FALSE) == NULL)
286 iter->user_data = (MpdData *)mpd_data_get_next_real(data,FALSE);
287 iter->user_data2 = GINT_TO_POINTER(n+1);
288 g_assert(iter->user_data != NULL);
292 interface Gtk:Tree:Model
294 iter_has_child(Gtk:Tree:Model *self (check null type), GtkTreeIter *iter)
298 interface Gtk:Tree:Model
300 iter_n_children(Gtk:Tree:Model *model (check null type), GtkTreeIter *iter)
302 Self *list = GMPC_MPDDATA_MODEL(model);
305 return list->num_rows;
308 interface Gtk:Tree:Model
310 iter_parent(Gtk:Tree:Model *model (check null type), Gtk:Tree:Iter *iter, Gtk:Tree:Iter *child)
316 cover_art_fetched(mpd_Song *song, MetaDataResult ret, char *coverpath,GtkTreeRowReference *ref)
321 GtkTreePath *path =gtk_tree_row_reference_get_path(ref);
322 GtkTreeModel *model = gtk_tree_row_reference_get_model(ref);
326 int size = cfg_get_single_value_as_int_with_default(config, "gmpc-mpddata-model", "icon-size", 64);
327 Self *self = GMPC_MPDDATA_MODEL(model);
329 gtk_tree_model_get_iter(model, &iter, path);
330 n = GPOINTER_TO_INT(iter.user_data2) ;
331 if(ret == META_DATA_AVAILABLE)
333 GdkPixbuf *pb = gdk_pixbuf_new_from_file_at_size(coverpath,size,size,NULL);
334 screenshot_add_border(&pb);
335 if(self->_priv->images[n]) g_object_unref(self->_priv->images[n]);
336 self->_priv->images[n] = pb;
338 else if(ret == META_DATA_UNAVAILABLE)
341 pb2 = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-no-cover", size, 0,NULL);
342 if(self->_priv->images[n]) g_object_unref(self->_priv->images[n]);
343 self->_priv->images[n] = pb2;
345 gtk_tree_model_row_changed(model,path, &iter);
348 if(ret == META_DATA_AVAILABLE || ret == META_DATA_UNAVAILABLE)
352 gtk_tree_row_reference_free(ref);
358 interface Gtk:Tree:Model
360 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))
362 Self *self = GMPC_MPDDATA_MODEL(model);
363 MpdData_real *data = iter->user_data;
364 int n = GPOINTER_TO_INT(iter->user_data2);
366 /* set value to the correct type */
367 g_value_init(value, self->types[column]);
372 g_assert((n < self->num_rows || n == 0));
373 g_assert(!(data == NULL && n != 0));
376 if(data == NULL && n == 0)
380 case MPDDATA_MODEL_COL_ICON_ID:
381 g_value_set_string(value, GTK_STOCK_GO_UP);
383 case MPDDATA_MODEL_ROW_TYPE:
387 g_value_set_int(value, -1);
389 case MPDDATA_MODEL_COL_SONG_TITLE:
390 case MPDDATA_MODEL_COL_MARKUP:
392 g_value_set_string(value,"..");
401 if(column == MPDDATA_MODEL_META_DATA)
403 if(self->_priv->images[n] == NULL)
405 int size = cfg_get_single_value_as_int_with_default(config, "gmpc-mpddata-model", "icon-size", 64);
406 if(data->type == MPD_DATA_TYPE_TAG && data->tag_type == MPD_TAG_ITEM_ARTIST)
410 GtkTreeRowReference *ref;
411 self->_priv->images[n] = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-loading-cover", size, 0,NULL);
412 song = mpd_newSong();
413 song->artist = g_strdup(data->tag);
415 path = gtk_tree_model_get_path(GTK_TREE_MODEL(self), iter);
416 ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(self),path);
417 gmpc_meta_watcher_get_meta_path_callback(gmw,song, META_ARTIST_ART, (MetaDataCallback)self_cover_art_fetched, (gpointer)ref);
419 gtk_tree_path_free(path);
422 else if(data->type == MPD_DATA_TYPE_TAG && data->tag_type == MPD_TAG_ITEM_ALBUM && self->_priv->req_artist)
426 GtkTreeRowReference *ref;
427 self->_priv->images[n] = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-loading-cover", size, 0,NULL);
428 song = mpd_newSong();
429 song->artist = g_strdup(self->_priv->req_artist);
430 song->album = g_strdup(data->tag);
431 path = gtk_tree_model_get_path(GTK_TREE_MODEL(self), iter);
432 ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(self),path);
433 gmpc_meta_watcher_get_meta_path_callback(gmw,song, META_ALBUM_ART, (MetaDataCallback)self_cover_art_fetched, ref);
435 gtk_tree_path_free(path);
439 self->_priv->images[n] = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-no-cover", size, 0,NULL);
442 g_value_set_object(value, self->_priv->images[n]);
444 if(column == MPDDATA_MODEL_USERDATA)
446 g_value_set_pointer(value,data->userdata);
449 /* handle row type, this is independent of the row type */
450 if(column == MPDDATA_MODEL_ROW_TYPE)
452 g_value_set_int(value, data->type);
455 if (data->type == MPD_DATA_TYPE_TAG) {
458 case MPDDATA_MODEL_COL_ICON_ID:
459 switch(data->tag_type)
461 case MPD_TAG_ITEM_ALBUM:
462 g_value_set_string(value, "media-album");
464 case MPD_TAG_ITEM_ARTIST:
465 g_value_set_string(value, "media-artist");
468 g_value_set_string(value, "media-tag");
471 case MPDDATA_MODEL_COL_SONG_TITLE:
472 case MPDDATA_MODEL_COL_MARKUP:
474 g_value_set_string(value, data->tag);
477 case MPDDATA_MODEL_COL_PATH:
478 g_value_set_string(value, data->tag);
480 case MPDDATA_MODEL_TAG_TYPE:
481 g_value_set_int(value, data->tag_type);
486 } else if(data->type == MPD_DATA_TYPE_DIRECTORY) {
489 case MPDDATA_MODEL_COL_ICON_ID:
490 g_value_set_string(value, GTK_STOCK_OPEN);
492 case MPDDATA_MODEL_COL_SONG_TITLE:
493 case MPDDATA_MODEL_COL_SONG_TITLEFILE:
494 case MPDDATA_MODEL_COL_MARKUP:
496 gchar *basename = g_path_get_basename(data->directory);
497 g_value_set_string(value, basename);
501 case MPDDATA_MODEL_COL_PATH:
502 g_value_set_string(value, data->directory);
508 else if(data->type == MPD_DATA_TYPE_PLAYLIST)
512 case MPDDATA_MODEL_COL_ICON_ID:
513 g_value_set_string(value, "media-playlist");
515 case MPDDATA_MODEL_COL_SONG_TITLE:
516 case MPDDATA_MODEL_COL_SONG_TITLEFILE:
517 case MPDDATA_MODEL_COL_MARKUP:
519 gchar *basename = g_path_get_basename(data->playlist);
520 g_value_set_string(value, basename);
524 case MPDDATA_MODEL_COL_PATH:
525 g_value_set_string(value, data->playlist);
531 else if(data->type == MPD_DATA_TYPE_SONG)
533 mpd_Song *song = data->song;
535 case MPDDATA_MODEL_COL_MPDSONG:
536 g_value_set_pointer(value, song);
538 case MPDDATA_MODEL_COL_PLAYING:
539 g_value_set_boolean(value, FALSE);
541 case MPDDATA_MODEL_COL_PLAYING_FONT_WEIGHT:
542 g_value_set_int(value, PANGO_WEIGHT_NORMAL);
544 case MPDDATA_MODEL_COL_MARKUP:
546 /* we want to go cache this stuff */
548 mpd_song_markup(buffer, 1024, GMPC_MPDDATA_MODEL(model)->_priv->markup, song);
549 g_value_set_string(value, buffer);
552 case MPDDATA_MODEL_COL_PATH:
553 g_value_set_string(value, song->file);
555 case MPDDATA_MODEL_COL_PATH_EXTENSION:
557 int j = strlen(song->file);
558 for(;j>0&&song->file[j] != '.';j--);
559 g_value_set_string(value, &(song->file)[j+1]);
562 case MPDDATA_MODEL_COL_PATH_DIRECTORY:
564 gchar *dir = g_path_get_dirname(song->file);
565 g_value_set_string(value, dir);
569 case MPDDATA_MODEL_COL_SONG_ARTIST:
570 g_value_set_string(value, song->artist);
572 case MPDDATA_MODEL_COL_SONG_ALBUM:
573 g_value_set_string(value, song->album);
575 case MPDDATA_MODEL_COL_SONG_TITLE:
576 /* If there is a song available use that, else use the filename */
578 g_value_set_string(value, song->title);
580 /* Use the markup stuff, this makes sure it gets processed equaly */
582 mpd_song_markup(buffer, 1024, "%shortfile%", song);
583 g_value_set_string(value, buffer);
586 case MPDDATA_MODEL_COL_SONG_TITLEFILE:
588 gchar *path = g_path_get_basename(song->file);
589 g_value_set_string(value, path);
593 case MPDDATA_MODEL_COL_SONG_GENRE:
594 g_value_set_string(value, song->genre);
596 case MPDDATA_MODEL_COL_SONG_TRACK:
597 g_value_set_string(value, song->track);
599 case MPDDATA_MODEL_COL_SONG_NAME:
600 g_value_set_string(value, song->name);
602 case MPDDATA_MODEL_COL_SONG_COMPOSER:
603 g_value_set_string(value, song->composer);
605 case MPDDATA_MODEL_COL_SONG_PERFORMER:
606 g_value_set_string(value, song->performer);
608 case MPDDATA_MODEL_COL_SONG_DATE:
609 g_value_set_string(value, song->date);
611 case MPDDATA_MODEL_COL_SONG_LENGTH:
612 g_value_set_int(value, song->time);
614 case MPDDATA_MODEL_COL_SONG_LENGTH_FORMAT:
616 if(song->time >= 0) {
617 gchar *strdata = g_strdup_printf("%02i:%02i",
618 song->time/60, song->time%60);
619 g_value_set_string(value, strdata);
622 g_value_set_string(value, "n/a");
626 case MPDDATA_MODEL_COL_SONG_DISC:
627 g_value_set_string(value, song->disc);
629 case MPDDATA_MODEL_COL_SONG_COMMENT:
630 g_value_set_string(value, song->comment);
632 case MPDDATA_MODEL_COL_SONG_POS:
633 g_value_set_int(value, song->pos+1);
635 case MPDDATA_MODEL_COL_SONG_ID:
636 g_value_set_int(value, song->id);
639 case MPDDATA_MODEL_COL_ICON_ID:
640 if (strstr(song->file, "://")) {
641 g_value_set_string(value, "media-stream");
643 g_value_set_string(value, "media-audiofile");
650 interface Gtk:Tree:Model
652 iter_nth_child(Gtk:Tree:Model *model(check null type), GtkTreeIter *iter (check null), GtkTreeIter *parent, gint n (check >=0))
654 Self *self = GMPC_MPDDATA_MODEL(model);
656 MpdData *data = NULL;
659 if (n >= self->num_rows || n < 0)
662 data = (MpdData *)self->_priv->data;
668 data = mpd_data_get_next_real(data, FALSE);
671 if(self->has_up && n == 0)
674 iter->stamp = self->_priv->stamp;
675 iter->user_data = data;
676 iter->user_data2 = GINT_TO_POINTER(n);
680 interface Gtk:Tree:Model
681 private GtkTreePath *
682 get_path(Gtk:Tree:Model *self (check null type), GtkTreeIter *iter (check null))
684 GtkTreePath *path = NULL;
685 path = gtk_tree_path_new();
686 gtk_tree_path_append_index(path, GPOINTER_TO_INT(iter->user_data2));
690 interface Gtk:Tree:Model
692 get_column_type(Gtk:Tree:Model *model(check null type), gint ind (check >= 0))
694 Self *self = GMPC_MPDDATA_MODEL(model);
695 return self->types[ind];
699 set_request_artist(self, const char *artist)
701 if(self->_priv->req_artist)
702 q_free(self->_priv->req_artist);
703 self->_priv->req_artist = (artist != NULL)?g_strdup(artist):NULL;
707 get_request_artist(self)
709 return self->_priv->req_artist;
712 long unsigned set_mpd_data(self, MpdData *data2)
715 long unsigned retval = 0;
717 GtkTreePath *path = NULL;
718 /* Do some cleanup, like removing rows, and so */
719 /* loop and remove */
720 while ( self->num_rows > 0 ) {
721 path = gtk_tree_path_new();
722 gtk_tree_path_append_index(path, self->num_rows - 1 );
723 /* propegate change */
724 gtk_tree_model_row_deleted(GTK_TREE_MODEL(self), path);
725 gtk_tree_path_free(path);
731 * Free if there is a list and set it to NULL
733 if(self->_priv->data)
734 mpd_data_free(self->_priv->data);
735 self->_priv->data = NULL;
737 if(self->num_rows != 0)
738 debug_printf(DEBUG_ERROR,"not every row cleared %i\n",self->num_rows);
741 /* Free possible stored images */
742 if(self->_priv->images)
744 for(i=0;i<self->num_rows;i++)
746 if(self->_priv->images[i])
747 g_object_unref(self->_priv->images[i]);
749 q_free(self->_priv->images);
753 self->_priv->playtime = 0;
754 self_playtime_changed(self, self->_priv->playtime);
758 self->_priv->data = mpd_data_get_first(data2);
760 if(self->_priv->sort_column == MPDDATA_MODEL_COL_ICON_ID)
762 self->_priv->data = misc_sort_mpddata_by_album_disc_track(self->_priv->data);
764 else if ( self->_priv->sort_column != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID )
766 self->_priv->data = misc_sort_mpddata(self->_priv->data,(GCompareDataFunc)self_sort_func,self );
770 path = gtk_tree_path_new();
771 gtk_tree_path_append_index(path, self->num_rows);
772 iter.stamp = self->_priv->stamp;
773 iter.user_data = NULL;
774 iter.user_data2 = GINT_TO_POINTER(self->num_rows);
776 /* propegate change */
777 gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
778 gtk_tree_path_free(path);
781 for(data2 = mpd_data_get_first(self->_priv->data);data2; data2 = mpd_data_get_next_real(data2,FALSE)) {
782 path = gtk_tree_path_new();
783 gtk_tree_path_append_index(path,self->num_rows);
784 iter.stamp = self->_priv->stamp;
785 iter.user_data = data2;
786 iter.user_data2 = GINT_TO_POINTER(self->num_rows);
788 /* propegate change */
789 gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
790 gtk_tree_path_free(path);
792 if(data2->type == MPD_DATA_TYPE_SONG)
794 if(data2->song && data2->song->time > 0)
795 retval+= data2->song->time;
798 self->_priv->images = g_malloc0((self->num_rows)*sizeof(*(self->_priv->images)));
800 self->_priv->playtime = retval;
801 self_playtime_changed(self, self->_priv->playtime);
806 interface Gtk:Tree:Sortable
808 gboolean get_sort_column_id (Gtk:Tree:Sortable *model , gint *sort_column_id, GtkSortType *order)
810 Self *self = GMPC_MPDDATA_MODEL(model);
812 *sort_column_id = self->_priv->sort_column;
814 *order = self->_priv->sort_order;
818 interface Gtk:Tree:Sortable
820 void set_sort_column_id (Gtk:Tree:Sortable *model , gint sort_column_id, GtkSortType order)
822 Self *self = GMPC_MPDDATA_MODEL(model);
823 int old_col = self->_priv->sort_column, old_ord = self->_priv->sort_order;
824 /* if(sort_column_id == -1)
826 */ self->_priv->sort_column = sort_column_id;
827 self->_priv->sort_order = order;
829 if(old_col != sort_column_id || old_ord != order)
830 gtk_tree_sortable_sort_column_changed(model);
831 self->_priv->old_sort_column = self->_priv->sort_column;
835 interface Gtk:Tree:Sortable
838 has_default_sort_func(Gtk:Tree:Sortable *model)
843 int sort_func(gpointer ppaa, gpointer ppbb, Self *self)
845 MpdData_real *a = *(MpdData_real **)ppaa;
846 MpdData_real *b = *(MpdData_real **)ppbb;
847 int fact = (self->_priv->sort_order == GTK_SORT_ASCENDING)?-1:1;
849 if(a->type != b->type )
851 int val = a->type - b->type;
854 else if(a->type == b->type)
858 GValue va = {0,},vb = {0,};
859 /* if the type is a directory or a tag, always sort on the title column */
860 if(a->type == MPD_DATA_TYPE_DIRECTORY || b->type == MPD_DATA_TYPE_TAG)
864 iter.user_data2 = GPOINTER_TO_INT(0);
865 gtk_tree_model_get_value(GTK_TREE_MODEL(self), &iter, MPDDATA_MODEL_COL_SONG_TITLE, &va);
867 gtk_tree_model_get_value(GTK_TREE_MODEL(self), &iter, MPDDATA_MODEL_COL_SONG_TITLE, &vb);
868 ca = g_value_get_string(&va);
869 cb = g_value_get_string(&vb);
872 aa = g_utf8_strdown(ca, -1);
873 bb = g_utf8_strdown(cb, -1);
874 val = g_utf8_collate(aa,bb);
878 val = (ca == NULL)?((cb==NULL)?0:-1):1;
885 /* Get values from tree view, so we don't have to replicate code to sort the right entries */
887 iter.user_data2 = GPOINTER_TO_INT(0);
888 gtk_tree_model_get_value(GTK_TREE_MODEL(self), &iter, self->_priv->sort_column, &va);
890 gtk_tree_model_get_value(GTK_TREE_MODEL(self), &iter, self->_priv->sort_column, &vb);
891 if(self->_priv->sort_column == MPDDATA_MODEL_COL_SONG_TRACK)
893 const char *ca = g_value_get_string(&va);
894 const char *cb = g_value_get_string(&vb);
895 gint ca1 = (ca)?atoi(ca):0;
896 gint cb1 = (cb)?atoi(cb):0;
899 return fact*(ca1-cb1);
901 else if(self->types[self->_priv->sort_column] == G_TYPE_INT)
903 val = g_value_get_int(&va) - g_value_get_int(&vb);
905 else if (self->types[self->_priv->sort_column] == G_TYPE_STRING)
907 const char *ca = g_value_get_string(&va);
908 const char *cb = g_value_get_string(&vb);
911 aa = g_utf8_strdown(ca, -1);
912 bb = g_utf8_strdown(cb, -1);
913 val = g_utf8_collate(aa,bb);
917 val = (ca == NULL)?((cb==NULL)?0:-1):1;
927 interface Gtk:Tree:Sortable
930 sort_column_changed(Gtk:Tree:Sortable *model)
932 Self *self = GMPC_MPDDATA_MODEL(model);
933 if(!self->_priv->data || !(((MpdData_real *)self->_priv->data)->next))
935 if(self->_priv->sort_column == MPDDATA_MODEL_COL_ICON_ID)
937 self->_priv->data = misc_sort_mpddata_by_album_disc_track(self->_priv->data);
939 else if(self->_priv->sort_column != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID)
941 self->_priv->data = misc_sort_mpddata(self->_priv->data,(GCompareDataFunc)self_sort_func,self );
943 self->_priv->old_sort_column = self->_priv->sort_column;
945 /* tell the view that rows have changed */
948 GtkTreePath *path = NULL;
950 MpdData *data2 = mpd_data_get_first(self->_priv->data);
951 if(self->has_up) i = 1;
952 for(;data2; data2 = mpd_data_get_next_real(data2,FALSE))
954 path = gtk_tree_path_new();
955 gtk_tree_path_append_index(path,i);
956 iter.stamp = self->_priv->stamp;
957 iter.user_data = data2;
958 iter.user_data2 = GINT_TO_POINTER(i);
960 /* propegate change */
961 gtk_tree_model_row_changed(GTK_TREE_MODEL(self), path, &iter);
969 test_sort_func(gpointer ppaa, gpointer ppbb, Self *self)
971 MpdData_real *a = *(MpdData_real **)ppaa;
972 MpdData_real *b = *(MpdData_real **)ppbb;
973 return strcmp(a->tag, b->tag);
977 * For now this assumes tag list only.
980 long unsigned set_mpd_data_slow (self, MpdData *data2)
982 MpdData_real *new_list = NULL;
985 MpdData_real *original = (MpdData_real *)self->_priv->data;
986 MpdData_real *new = (MpdData_real *) data2;
987 GtkTreePath *path =NULL;
990 /* Free possible stored images */
991 if(self->_priv->images)
993 for(i=0;i<self->num_rows;i++)
995 if(self->_priv->images[i])
996 g_object_unref(self->_priv->images[i]);
998 q_free(self->_priv->images);
1003 /* sort it identical */
1004 new = (MpdData_real *)misc_sort_mpddata(data2,(GCompareDataFunc)self_test_sort_func,self );
1008 MpdData_real *n = new->first;
1025 if(self->num_rows > items)
1027 /* remove current row */
1030 path = gtk_tree_path_new();
1031 gtk_tree_path_append_index(path,i);
1033 /* propegate change */
1034 gtk_tree_model_row_deleted(GTK_TREE_MODEL(self), path);
1035 gtk_tree_path_free(path);
1037 original = original->next;
1038 has_next = (original )?1:0;
1043 int compare = strcmp(new->tag, original->tag);
1047 MpdData_real *n = (MpdData_real *) mpd_new_data_struct_append((MpdData *)new_list);
1048 n->type = MPD_DATA_TYPE_TAG;
1049 n->tag_type = new->tag_type;
1055 path = gtk_tree_path_new();
1056 gtk_tree_path_append_index(path,i);
1057 iter.stamp = self->_priv->stamp;
1059 iter.user_data2 = GINT_TO_POINTER(i);
1061 /* propegate change */
1062 gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
1063 gtk_tree_path_free(path);
1070 else if (compare > 0)
1072 path = gtk_tree_path_new();
1073 gtk_tree_path_append_index(path,i);
1074 /* propegate change */
1075 gtk_tree_model_row_deleted(GTK_TREE_MODEL(self), path);
1076 gtk_tree_path_free(path);
1080 original = original->next;
1081 has_next = (original )?1:0;
1084 new_list = (MpdData_real *)mpd_new_data_struct_append((MpdData*)new_list);
1085 new_list->type = MPD_DATA_TYPE_TAG;
1086 new_list->tag_type = new->tag_type;
1087 new_list->tag = new->tag;
1091 original = original->next;
1092 has_next = (original )?1:0;
1098 /* add entries remaining in new */
1102 new_list = (MpdData_real *)mpd_new_data_struct_append((MpdData*)new_list);
1103 new_list->type = MPD_DATA_TYPE_TAG;
1104 new_list->tag_type = new->tag_type;
1105 new_list->tag = new->tag;
1109 path = gtk_tree_path_new();
1110 gtk_tree_path_append_index(path,self->num_rows);
1111 iter.stamp = self->_priv->stamp;
1112 iter.user_data = new_list;
1113 iter.user_data2 = GINT_TO_POINTER(self->num_rows);
1115 /* propegate change */
1116 gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
1117 gtk_tree_path_free(path);
1124 if(self->_priv->data)
1125 mpd_data_free(self->_priv->data);
1127 self->_priv->data = mpd_data_get_first((MpdData*)new_list);
1129 g_assert(self->_priv->data != NULL);
1131 self->_priv->images = g_malloc0((self->num_rows)*sizeof(*(self->_priv->images)));
1133 mpd_data_free(data2);
1134 g_assert(items == self->num_rows);
1138 signal last NONE (LONG)
1140 playtime_changed(self, gulong playtime)
1149 return self->_priv->playtime;
1153 get_pos( self, GtkTreeIter *iter)
1155 g_assert(iter->stamp == self->_priv->stamp);
1156 return GPOINTER_TO_INT(iter->user_data2);