1 /* Gnome Music Player Client (GMPC)
2 * Copyright (C) 2004-2009 Qball Cow <qball@sarine.nl>
3 * Project homepage: http://gmpc.wikia.com/
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.
24 #include <libmpd/libmpd.h>
25 #include <libmpd/libmpd-internal.h>
29 //#include "playlist3.h"
31 #include "config-defaults.h"
37 #define GMPC_MPD_DATA_MODEL(a) GMPC_MPDDATA_MODEL(a)
40 MPDDATA_MODEL_COL_MPDSONG = 0, /* get the mpd_Song */
41 MPDDATA_MODEL_COL_PLAYING, /* Shows if this song is the current song */
42 MPDDATA_MODEL_COL_PLAYING_FONT_WEIGHT, /* Shows if this song is the current song */
43 MPDDATA_MODEL_COL_PATH, /* Path to song/file/directory */
44 MPDDATA_MODEL_COL_MARKUP, /* a string that has FALSEmarkup */
45 MPDDATA_MODEL_COL_SONG_ARTIST, /* artist name */
46 MPDDATA_MODEL_COL_SONG_ALBUM, /* album name */
47 MPDDATA_MODEL_COL_SONG_TITLE, /* song title */
48 MPDDATA_MODEL_COL_SONG_TITLEFILE, /* song title */
49 MPDDATA_MODEL_COL_SONG_GENRE, /* song genre */
50 MPDDATA_MODEL_COL_SONG_TRACK, /* song track */
51 MPDDATA_MODEL_COL_SONG_NAME, /* stream name */
52 MPDDATA_MODEL_COL_SONG_COMPOSER, /* composer name */
53 MPDDATA_MODEL_COL_SONG_PERFORMER, /* performer */
54 MPDDATA_MODEL_COL_SONG_DATE, /* date */
55 MPDDATA_MODEL_COL_SONG_LENGTH_FORMAT, /* length formatted */
56 MPDDATA_MODEL_COL_SONG_DISC, /* disc */
57 MPDDATA_MODEL_COL_SONG_COMMENT, /* comment */
58 MPDDATA_MODEL_COL_SONG_POS, /* position */
59 MPDDATA_MODEL_COL_SONG_ALBUMARTIST,
60 MPDDATA_MODEL_COL_PATH_EXTENSION, /* Extention */
61 MPDDATA_MODEL_COL_PATH_DIRECTORY, /* Directory */
62 MPDDATA_MODEL_COL_SONG_ID, /* col id */
63 MPDDATA_MODEL_COL_ICON_ID, /* icon id */
64 MPDDATA_MODEL_COL_SONG_LENGTH, /* length */
65 MPDDATA_MODEL_TAG_TYPE, /* tag type */
66 MPDDATA_MODEL_ROW_TYPE, /* type of the row */
67 MPDDATA_MODEL_META_DATA, /* metadata */
68 MPDDATA_MODEL_USERDATA,
69 MPDDATA_MODEL_N_COLUMNS
73 class Gmpc:MpdData:Model from G:Object
74 (interface Gtk:Tree:Sortable)
75 (interface Gtk:Tree:Model)
77 private gint stamp = {g_random_int()};
78 public GType types[MPDDATA_MODEL_N_COLUMNS];
79 private MpdData *data = NULL;
80 public gint num_rows = 0;
81 private GdkPixbuf **images = NULL;
82 private gchar *req_artist = {NULL} destroywith g_free;
83 public gint icon_size = {cfg_get_single_value_as_int_with_default(config, "gmpc-mpddata-model", "icon-size", 32)};
86 private GtkSortType sort_order = GTK_SORT_ASCENDING;
87 private int sort_column = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID;
88 private int old_sort_column = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID;
89 public gboolean has_up = FALSE;
90 private gchar *markup = {cfg_get_single_value_as_string_with_default(config, "playlist", "browser_markup",DEFAULT_MARKUP_BROWSER)} destroywith g_free;
92 private gulong playtime = {0};
95 private gboolean use_images = TRUE;
97 property BOOLEAN has_up
99 blurb = "Show an 'up row",
100 default_value = FALSE,
106 self->types[MPDDATA_MODEL_COL_MPDSONG] = G_TYPE_POINTER;
107 self->types[MPDDATA_MODEL_COL_MARKUP] = G_TYPE_STRING;
108 self->types[MPDDATA_MODEL_COL_PLAYING] = G_TYPE_BOOLEAN;
109 self->types[MPDDATA_MODEL_COL_PLAYING_FONT_WEIGHT] = G_TYPE_INT;
110 self->types[MPDDATA_MODEL_COL_PATH] = G_TYPE_STRING;
111 self->types[MPDDATA_MODEL_COL_SONG_ARTIST] = G_TYPE_STRING;
112 self->types[MPDDATA_MODEL_COL_SONG_ALBUM] = G_TYPE_STRING;
113 self->types[MPDDATA_MODEL_COL_SONG_TITLE] = G_TYPE_STRING;
114 self->types[MPDDATA_MODEL_COL_SONG_TITLEFILE] = G_TYPE_STRING;
115 self->types[MPDDATA_MODEL_COL_SONG_TRACK] = G_TYPE_STRING;
116 self->types[MPDDATA_MODEL_COL_SONG_GENRE] = G_TYPE_STRING;
117 self->types[MPDDATA_MODEL_COL_SONG_NAME] = G_TYPE_STRING;
118 self->types[MPDDATA_MODEL_COL_SONG_COMPOSER] = G_TYPE_STRING;
119 self->types[MPDDATA_MODEL_COL_SONG_PERFORMER]= G_TYPE_STRING;
120 self->types[MPDDATA_MODEL_COL_SONG_DATE] = G_TYPE_STRING;
121 self->types[MPDDATA_MODEL_COL_SONG_LENGTH] = G_TYPE_INT;
122 self->types[MPDDATA_MODEL_COL_SONG_LENGTH_FORMAT] = G_TYPE_STRING;
123 self->types[MPDDATA_MODEL_COL_SONG_DISC] = G_TYPE_STRING;
124 self->types[MPDDATA_MODEL_COL_SONG_COMMENT] = G_TYPE_STRING;
125 self->types[MPDDATA_MODEL_COL_SONG_POS] = G_TYPE_INT;
126 self->types[MPDDATA_MODEL_COL_SONG_ALBUMARTIST] = G_TYPE_STRING;
127 self->types[MPDDATA_MODEL_COL_PATH_EXTENSION] = G_TYPE_STRING;
128 self->types[MPDDATA_MODEL_COL_PATH_DIRECTORY] = G_TYPE_STRING;
129 self->types[MPDDATA_MODEL_COL_SONG_ID] = G_TYPE_INT;
130 self->types[MPDDATA_MODEL_COL_ICON_ID] = G_TYPE_STRING;
131 self->types[MPDDATA_MODEL_TAG_TYPE] = G_TYPE_INT;
132 self->types[MPDDATA_MODEL_ROW_TYPE] = G_TYPE_INT;
133 self->types[MPDDATA_MODEL_USERDATA] = G_TYPE_POINTER;
134 self->types[MPDDATA_MODEL_META_DATA] = GDK_TYPE_PIXBUF;
139 Gmpc:MpdData:Model *new (void)
146 finalize (G:Object *obj)
149 Self *self = GMPC_MPDDATA_MODEL(obj);
151 if(self->_priv->data)
153 mpd_data_free(self->_priv->data);
154 self->_priv->data = NULL;
156 if(self->_priv->images && self->_priv->use_images)
158 for(i=0;i<self->num_rows;i++)
160 if(self->_priv->images[i]) {
161 g_object_unref(self->_priv->images[i]);
162 self->_priv->images[i] = NULL;
165 q_free(self->_priv->images);
166 /* q_free should do it, but force it */
167 self->_priv->images = NULL;
171 /* function implemented for the Gtk:Tree:Model interface */
172 interface Gtk:Tree:Model
173 private GtkTreeModelFlags
174 get_flags (Gtk:Tree:Model *self (check null type))
176 /* Here would be the implementation */
177 return (GtkTreeModelFlags)GTK_TREE_MODEL_LIST_ONLY;
181 interface Gtk:Tree:Model
183 gboolean iter_children(Gtk:Tree:Model *model, GtkTreeIter *iter, GtkTreeIter *parent)
185 Self *self = GMPC_MPDDATA_MODEL(model);
188 if(self->num_rows == 0)
190 /* Set iter to first item in list */
191 iter->stamp = self->_priv->stamp;
192 iter->user_data = self->_priv->data;
193 iter->user_data2 = GINT_TO_POINTER(0);
194 iter->user_data3 = NULL; /* unused */
199 * Unused, not known in the model directly?
201 interface Gtk:Tree:Model
203 gint get_n_columns(Gtk:Tree:Model *model)
205 return MPDDATA_MODEL_N_COLUMNS;
208 interface Gtk:Tree:Model
210 get_iter(Gtk:Tree:Model *model (check null type),
211 Gtk:Tree:Iter *iter (check null),
212 Gtk:Tree:Path *path (check null))
214 Self *self = GMPC_MPDDATA_MODEL(model);
215 MpdData_real *data = NULL;
217 const gint *indices = gtk_tree_path_get_indices(path);
218 gint depth = gtk_tree_path_get_depth(path);
219 gint n = indices[0]; /* the n-th top level row */
222 g_assert(depth == 1);
224 if (n >= self->num_rows || n < 0)
227 g_assert(n<self->num_rows);
228 /* If it is the first item in a has_up list, do nothing */
229 if(self->has_up && n == 0){
233 data = (MpdData_real *)self->_priv->data;
243 iter->stamp = self->_priv->stamp;
244 iter->user_data = data;
245 iter->user_data2 = GINT_TO_POINTER(n);
249 interface Gtk:Tree:Model
251 iter_next(Gtk:Tree:Model *model(check null type), GtkTreeIter *iter (check null))
253 Self *self = GMPC_MPDDATA_MODEL(model);
254 MpdData *data = iter->user_data;
255 int n = GPOINTER_TO_INT(iter->user_data2) ;
256 /* if the row is the last row in the list or bigger then the last row, return FALSE */
257 if(n >= self->num_rows)
261 /* if the row is the first row in the list, and it has a "go-up" entry,
262 * it can have a next entry
264 if(data == NULL && n == 0 && self->has_up == TRUE)
266 iter->user_data = (MpdData *)self->_priv->data;
267 iter->user_data2 = GINT_TO_POINTER(1);
268 if(iter->user_data == NULL)
272 if(mpd_data_get_next_real(data,FALSE) == NULL)
276 iter->user_data = (MpdData *)mpd_data_get_next_real(data,FALSE);
277 iter->user_data2 = GINT_TO_POINTER(n+1);
278 g_assert(iter->user_data != NULL);
282 interface Gtk:Tree:Model
284 iter_has_child(Gtk:Tree:Model *self (check null type), GtkTreeIter *iter)
288 interface Gtk:Tree:Model
290 iter_n_children(Gtk:Tree:Model *model (check null type), GtkTreeIter *iter)
292 Self *list = GMPC_MPDDATA_MODEL(model);
295 return list->num_rows;
298 interface Gtk:Tree:Model
300 iter_parent(Gtk:Tree:Model *model (check null type), Gtk:Tree:Iter *iter, Gtk:Tree:Iter *child)
306 cover_art_fetched(mpd_Song *song, MetaDataResult ret,MetaData *met,GtkTreeRowReference *ref)
311 GtkTreePath *path =gtk_tree_row_reference_get_path(ref);
312 GtkTreeModel *model = gtk_tree_row_reference_get_model(ref);
316 Self *self = GMPC_MPDDATA_MODEL(model);
319 g_assert(self->_priv->use_images == TRUE);
321 gtk_tree_model_get_iter(model, &iter, path);
322 n = GPOINTER_TO_INT(iter.user_data2) ;
323 if(ret == META_DATA_AVAILABLE)
326 GdkPixbuf *pb = NULL;
327 if(met->content_type == META_DATA_CONTENT_URI)
329 const gchar *coverpath = meta_data_get_uri(met);
330 pb = gdk_pixbuf_new_from_file_at_size(coverpath,self->icon_size,self->icon_size,NULL);
331 screenshot_add_border(&pb);
333 pb = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-no-cover", self->icon_size, 0,NULL);
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", self->icon_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);
346 gtk_tree_path_free(path);
349 if(ret == META_DATA_AVAILABLE || ret == META_DATA_UNAVAILABLE)
353 gtk_tree_row_reference_free(ref);
359 interface Gtk:Tree:Model
361 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))
363 Self *self = GMPC_MPDDATA_MODEL(model);
364 MpdData_real *data = iter->user_data;
365 int n = GPOINTER_TO_INT(iter->user_data2);
367 if(self->_priv->data == NULL)
368 g_warning("self->_priv->data == NULL");
369 /* set value to the correct type */
370 g_value_init(value, self->types[column]);
375 g_assert((n < self->num_rows || n == 0));
376 g_assert(!(data == NULL && n != 0));
379 if(data == NULL && n == 0)
383 case MPDDATA_MODEL_COL_ICON_ID:
384 g_value_set_string(value, GTK_STOCK_GO_UP);
386 case MPDDATA_MODEL_ROW_TYPE:
390 g_value_set_int(value, -1);
392 case MPDDATA_MODEL_COL_SONG_TITLE:
393 case MPDDATA_MODEL_COL_MARKUP:
395 g_value_set_string(value,"..");
404 if(column == MPDDATA_MODEL_META_DATA)
406 if(self->_priv->use_images && self->_priv->images)
408 if(self->_priv->images[n] == NULL)
410 if(data->type == MPD_DATA_TYPE_TAG && (data->tag_type == MPD_TAG_ITEM_ARTIST || data->tag_type == MPD_TAG_ITEM_ALBUM_ARTIST) )
414 GtkTreeRowReference *ref;
415 self->_priv->images[n] = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-loading-cover", self->icon_size, 0,NULL);
416 song = mpd_newSong();
417 song->artist = g_strdup(data->tag);
419 path = gtk_tree_model_get_path(GTK_TREE_MODEL(self), iter);
420 ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(self),path);
421 gmpc_meta_watcher_get_meta_path_callback(gmw,song, META_ARTIST_ART, (MetaDataCallback)self_cover_art_fetched, (gpointer)ref);
423 gtk_tree_path_free(path);
426 else if(data->type == MPD_DATA_TYPE_TAG && data->tag_type == MPD_TAG_ITEM_ALBUM && self->_priv->req_artist)
430 GtkTreeRowReference *ref;
431 self->_priv->images[n] = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-loading-cover", self->icon_size, 0,NULL);
432 song = mpd_newSong();
433 song->artist = g_strdup(self->_priv->req_artist);
434 song->albumartist = g_strdup(self->_priv->req_artist);
435 song->album = g_strdup(data->tag);
436 path = gtk_tree_model_get_path(GTK_TREE_MODEL(self), iter);
437 ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(self),path);
438 gmpc_meta_watcher_get_meta_path_callback(gmw,song, META_ALBUM_ART, (MetaDataCallback)self_cover_art_fetched, ref);
440 gtk_tree_path_free(path);
442 else if(data->type == MPD_DATA_TYPE_SONG&& data->song && data->song->album && (data->song->artist || self->_priv->req_artist))
446 GtkTreeRowReference *ref;
447 self->_priv->images[n] = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-loading-cover", self->icon_size, 0,NULL);
448 song = mpd_newSong();
449 song->artist = g_strdup((data->song->artist)?data->song->artist:self->_priv->req_artist);
450 song->albumartist = g_strdup((data->song->albumartist)?data->song->albumartist:self->_priv->req_artist);
451 song->album = g_strdup(data->song->album);
452 path = gtk_tree_model_get_path(GTK_TREE_MODEL(self), iter);
453 ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(self),path);
454 gmpc_meta_watcher_get_meta_path_callback(gmw,song, META_ALBUM_ART, (MetaDataCallback)self_cover_art_fetched, ref);
456 gtk_tree_path_free(path);
460 self->_priv->images[n] = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-no-cover", self->icon_size, 0,NULL);
463 g_value_set_object(value, self->_priv->images[n]);
465 g_value_set_object(value,NULL);
468 if(column == MPDDATA_MODEL_USERDATA)
470 g_value_set_pointer(value,data->userdata);
473 /* handle row type, this is independent of the row type */
474 if(column == MPDDATA_MODEL_ROW_TYPE)
476 g_value_set_int(value, data->type);
479 if (data->type == MPD_DATA_TYPE_TAG) {
482 case MPDDATA_MODEL_COL_ICON_ID:
483 switch(data->tag_type)
485 case MPD_TAG_ITEM_ALBUM:
486 g_value_set_string(value, "media-album");
488 case MPD_TAG_ITEM_ARTIST:
489 g_value_set_string(value, "media-artist");
492 g_value_set_string(value, "media-tag");
495 case MPDDATA_MODEL_COL_SONG_TITLE:
496 case MPDDATA_MODEL_COL_MARKUP:
498 g_value_set_string(value, data->tag);
501 case MPDDATA_MODEL_COL_PATH:
502 g_value_set_string(value, data->tag);
504 case MPDDATA_MODEL_TAG_TYPE:
505 g_value_set_int(value, data->tag_type);
510 } else if(data->type == MPD_DATA_TYPE_DIRECTORY) {
513 case MPDDATA_MODEL_COL_ICON_ID:
514 g_value_set_string(value, GTK_STOCK_OPEN);
516 case MPDDATA_MODEL_COL_SONG_TITLE:
517 case MPDDATA_MODEL_COL_SONG_TITLEFILE:
518 case MPDDATA_MODEL_COL_MARKUP:
520 gchar *basename = g_path_get_basename(data->directory);
521 g_value_set_string(value, basename);
525 case MPDDATA_MODEL_COL_PATH:
526 g_value_set_string(value, data->directory);
532 else if(data->type == MPD_DATA_TYPE_PLAYLIST)
536 case MPDDATA_MODEL_COL_ICON_ID:
537 g_value_set_string(value, "media-playlist");
539 case MPDDATA_MODEL_COL_SONG_TITLE:
540 case MPDDATA_MODEL_COL_SONG_TITLEFILE:
541 case MPDDATA_MODEL_COL_MARKUP:
543 gchar *basename = g_path_get_basename(data->playlist->path);
544 g_value_set_string(value, basename);
548 case MPDDATA_MODEL_COL_PATH:
549 g_value_set_string(value, data->playlist->path);
555 else if(data->type == MPD_DATA_TYPE_SONG)
557 mpd_Song *song = data->song;
559 case MPDDATA_MODEL_COL_MPDSONG:
560 g_value_set_pointer(value, song);
562 case MPDDATA_MODEL_COL_PLAYING:
563 g_value_set_boolean(value, FALSE);
565 case MPDDATA_MODEL_COL_PLAYING_FONT_WEIGHT:
566 g_value_set_int(value, PANGO_WEIGHT_NORMAL);
568 case MPDDATA_MODEL_COL_MARKUP:
570 /* we want to go cache this stuff */
572 mpd_song_markup(buffer, 1024, GMPC_MPDDATA_MODEL(model)->_priv->markup, song);
573 g_value_set_string(value, buffer);
576 case MPDDATA_MODEL_COL_PATH:
577 g_value_set_string(value, song->file);
579 case MPDDATA_MODEL_COL_PATH_EXTENSION:
581 int j = strlen(song->file);
582 for(;j>0&&song->file[j] != '.';j--);
583 g_value_set_string(value, &(song->file)[j+1]);
586 case MPDDATA_MODEL_COL_PATH_DIRECTORY:
588 gchar *dir = g_path_get_dirname(song->file);
589 g_value_set_string(value, dir);
593 case MPDDATA_MODEL_COL_SONG_ARTIST:
594 g_value_set_string(value, song->artist);
596 case MPDDATA_MODEL_COL_SONG_ALBUM:
597 g_value_set_string(value, song->album);
599 case MPDDATA_MODEL_COL_SONG_TITLE:
600 /* If there is a song available use that, else use the filename */
602 g_value_set_string(value, song->title);
604 /* Use the markup stuff, this makes sure it gets processed equaly */
606 mpd_song_markup(buffer, 1024, "%shortfile%", song);
607 g_value_set_string(value, buffer);
610 case MPDDATA_MODEL_COL_SONG_TITLEFILE:
612 gchar *path = g_path_get_basename(song->file);
613 g_value_set_string(value, path);
617 case MPDDATA_MODEL_COL_SONG_GENRE:
618 g_value_set_string(value, song->genre);
620 case MPDDATA_MODEL_COL_SONG_TRACK:
621 g_value_set_string(value, song->track);
623 case MPDDATA_MODEL_COL_SONG_NAME:
624 g_value_set_string(value, song->name);
626 case MPDDATA_MODEL_COL_SONG_COMPOSER:
627 g_value_set_string(value, song->composer);
629 case MPDDATA_MODEL_COL_SONG_PERFORMER:
630 g_value_set_string(value, song->performer);
632 case MPDDATA_MODEL_COL_SONG_DATE:
633 g_value_set_string(value, song->date);
635 case MPDDATA_MODEL_COL_SONG_LENGTH:
636 g_value_set_int(value, song->time);
638 case MPDDATA_MODEL_COL_SONG_LENGTH_FORMAT:
640 if(song->time >= 0) {
641 gchar *strdata = g_strdup_printf("%02i:%02i",
642 song->time/60, song->time%60);
643 g_value_set_string(value, strdata);
646 g_value_set_string(value, "n/a");
650 case MPDDATA_MODEL_COL_SONG_DISC:
651 g_value_set_string(value, song->disc);
653 case MPDDATA_MODEL_COL_SONG_COMMENT:
654 g_value_set_string(value, song->comment);
656 case MPDDATA_MODEL_COL_SONG_POS:
657 g_value_set_int(value, song->pos+1);
659 case MPDDATA_MODEL_COL_SONG_ALBUMARTIST:
660 g_value_set_string(value, song->albumartist);
662 case MPDDATA_MODEL_COL_SONG_ID:
663 g_value_set_int(value, song->id);
666 case MPDDATA_MODEL_COL_ICON_ID:
667 if (strstr(song->file, "://")) {
668 g_value_set_string(value, "media-stream");
670 g_value_set_string(value, "media-audiofile");
679 interface Gtk:Tree:Model
681 iter_nth_child(Gtk:Tree:Model *model(check null type), GtkTreeIter *iter (check null), GtkTreeIter *parent, gint n (check >=0))
683 Self *self = GMPC_MPDDATA_MODEL(model);
685 MpdData_real *data = NULL;
688 if (n >= self->num_rows || n < 0)
691 data = (MpdData_real *)self->_priv->data;
697 data = data->next;//mpd_data_get_next_real(data, FALSE);
698 g_assert(data != NULL);
701 if(self->has_up && n == 0)
704 iter->stamp = self->_priv->stamp;
705 iter->user_data = data;
706 iter->user_data2 = GINT_TO_POINTER(n);
710 interface Gtk:Tree:Model
711 private GtkTreePath *
712 get_path(Gtk:Tree:Model *self (check null type), GtkTreeIter *iter (check null))
714 GtkTreePath *path = NULL;
715 path = gtk_tree_path_new();
716 gtk_tree_path_append_index(path, GPOINTER_TO_INT(iter->user_data2));
720 interface Gtk:Tree:Model
722 get_column_type(Gtk:Tree:Model *model(check null type), gint ind (check >= 0))
724 Self *self = GMPC_MPDDATA_MODEL(model);
725 return self->types[ind];
729 set_request_artist(self, const char *artist)
732 GtkTreePath *path = NULL;
734 if(self->_priv->req_artist)
735 q_free(self->_priv->req_artist);
736 self->_priv->req_artist = (artist != NULL && artist[0] != '\0')?g_strdup(artist):NULL;
737 /* Free possible stored images */
738 if(self->_priv->images && self->_priv->use_images)
740 MpdData *data2 = mpd_data_get_first(self->_priv->data);
741 for(i=0;i<self->num_rows;i++)
743 if(self->_priv->images[i]){
744 g_object_unref(self->_priv->images[i]);
745 self->_priv->images[i] = NULL;
747 /* Update the view */
748 path = gtk_tree_path_new();
749 gtk_tree_path_append_index(path,i);
750 iter.stamp = self->_priv->stamp;
751 iter.user_data = data2;
752 iter.user_data2 = GINT_TO_POINTER(i);
753 /* propegate change */
754 gtk_tree_model_row_changed(GTK_TREE_MODEL(self), path, &iter);
755 gtk_tree_path_free(path);
757 data2 = mpd_data_get_next_real(data2,FALSE);
763 get_request_artist(self)
765 return self->_priv->req_artist;
768 long unsigned set_mpd_data(self, MpdData *data2)
771 long unsigned retval = 0;
773 GtkTreePath *path = NULL;
774 int old_num_rows = self->num_rows;
775 /* Do some cleanup, like removing rows, and so */
776 /* loop and remove */
777 while ( self->num_rows > 0 ) {
778 path = gtk_tree_path_new();
779 gtk_tree_path_append_index(path, self->num_rows - 1 );
780 /* propegate change */
781 gtk_tree_model_row_deleted(GTK_TREE_MODEL(self), path);
782 gtk_tree_path_free(path);
788 * Free if there is a list and set it to NULL
790 if(self->_priv->data)
791 mpd_data_free(self->_priv->data);
792 self->_priv->data = NULL;
794 if(self->num_rows != 0)
795 debug_printf(DEBUG_ERROR,"not every row cleared %i\n",self->num_rows);
797 /* Free possible stored images */
798 if(self->_priv->images && self->_priv->use_images)
800 for(i=0;i< old_num_rows;i++)
802 if(self->_priv->images[i])
803 g_object_unref(self->_priv->images[i]);
805 q_free(self->_priv->images);
809 self->_priv->playtime = 0;
810 self_playtime_changed(self, self->_priv->playtime);
814 self->_priv->data = mpd_data_get_first(data2);
816 if(self->_priv->sort_column == MPDDATA_MODEL_COL_ICON_ID)
818 self->_priv->data = misc_sort_mpddata_by_album_disc_track(self->_priv->data);
820 else if ( self->_priv->sort_column != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID )
822 self->_priv->data = misc_sort_mpddata(self->_priv->data,(GCompareDataFunc)self_sort_func,self );
826 path = gtk_tree_path_new();
827 gtk_tree_path_append_index(path, self->num_rows);
828 iter.stamp = self->_priv->stamp;
829 iter.user_data = NULL;
830 iter.user_data2 = GINT_TO_POINTER(self->num_rows);
833 /* propegate change */
834 gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
835 gtk_tree_path_free(path);
837 for(data2 = mpd_data_get_first(self->_priv->data);data2; data2 = mpd_data_get_next_real(data2,FALSE)) {
838 path = gtk_tree_path_new();
839 gtk_tree_path_append_index(path,self->num_rows);
840 iter.stamp = self->_priv->stamp;
841 iter.user_data = data2;
842 iter.user_data2 = GINT_TO_POINTER(self->num_rows);
845 /* propegate change */
846 gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
847 gtk_tree_path_free(path);
848 if(data2->type == MPD_DATA_TYPE_SONG)
850 if(data2->song && data2->song->time > 0)
851 retval+= data2->song->time;
854 if(self->_priv->use_images)
856 self->_priv->images = g_malloc0((self->num_rows)*sizeof(*(self->_priv->images)));
859 self->_priv->playtime = retval;
860 self_playtime_changed(self, self->_priv->playtime);
865 interface Gtk:Tree:Sortable
867 gboolean get_sort_column_id (Gtk:Tree:Sortable *model , gint *sort_column_id, GtkSortType *order)
869 Self *self = GMPC_MPDDATA_MODEL(model);
871 *sort_column_id = self->_priv->sort_column;
873 *order = self->_priv->sort_order;
877 interface Gtk:Tree:Sortable
879 void set_sort_column_id (Gtk:Tree:Sortable *model , gint sort_column_id, GtkSortType order)
881 Self *self = GMPC_MPDDATA_MODEL(model);
882 gint old_col = self->_priv->sort_column;
883 GtkSortType old_ord = self->_priv->sort_order;
884 self->_priv->sort_column = sort_column_id;
885 self->_priv->sort_order = order;
887 if(old_col != sort_column_id || old_ord != order)
888 gtk_tree_sortable_sort_column_changed(model);
889 self->_priv->old_sort_column = self->_priv->sort_column;
893 interface Gtk:Tree:Sortable
896 has_default_sort_func(Gtk:Tree:Sortable *model)
901 int sort_func(gpointer ppaa, gpointer ppbb, Self *self)
903 MpdData_real *a = *(MpdData_real **)ppaa;
904 MpdData_real *b = *(MpdData_real **)ppbb;
905 int fact = (self->_priv->sort_order == GTK_SORT_ASCENDING)?-1:1;
907 if(a->type != b->type )
909 int val = a->type - b->type;
912 else if(a->type == b->type)
916 GValue va = {0,},vb = {0,};
917 /* Get values from tree view, so we don't have to replicate code to sort the right entries */
919 iter.user_data2 = GPOINTER_TO_INT(0);
920 gtk_tree_model_get_value(GTK_TREE_MODEL(self), &iter, self->_priv->sort_column, &va);
922 gtk_tree_model_get_value(GTK_TREE_MODEL(self), &iter, self->_priv->sort_column, &vb);
923 /* if the type is a directory or a tag, always sort on the title column */
924 if(a->type == MPD_DATA_TYPE_DIRECTORY || b->type == MPD_DATA_TYPE_TAG)
927 ca = g_value_get_string(&va);
928 cb = g_value_get_string(&vb);
931 aa = g_utf8_strdown(ca, -1);
932 bb = g_utf8_strdown(cb, -1);
933 val = g_utf8_collate(aa,bb);
937 val = (ca == NULL)?((cb==NULL)?0:-1):1;
944 if(self->_priv->sort_column == MPDDATA_MODEL_COL_SONG_TRACK)
946 const char *ca = g_value_get_string(&va);
947 const char *cb = g_value_get_string(&vb);
948 gint ca1 = (ca)?atoi(ca):0;
949 gint cb1 = (cb)?atoi(cb):0;
952 return fact*(ca1-cb1);
954 else if(self->types[self->_priv->sort_column] == G_TYPE_INT)
956 val = g_value_get_int(&va) - g_value_get_int(&vb);
958 else if (self->types[self->_priv->sort_column] == G_TYPE_STRING)
960 const char *ca = g_value_get_string(&va);
961 const char *cb = g_value_get_string(&vb);
964 aa = g_utf8_normalize(ca, -1,G_NORMALIZE_ALL);
965 bb = g_utf8_normalize(cb, -1,G_NORMALIZE_ALL);
966 val = g_utf8_collate(aa,bb);
970 val = (ca == NULL)?((cb==NULL)?0:-1):1;
980 interface Gtk:Tree:Sortable
983 sort_column_changed(Gtk:Tree:Sortable *model)
985 Self *self = GMPC_MPDDATA_MODEL(model);
986 if(!self->_priv->data || !(((MpdData_real *)self->_priv->data)->next))
988 if(self->_priv->sort_column == MPDDATA_MODEL_COL_ICON_ID)
990 self->_priv->data = misc_sort_mpddata_by_album_disc_track(self->_priv->data);
992 else if(self->_priv->sort_column != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID)
994 self->_priv->data = misc_sort_mpddata(self->_priv->data,(GCompareDataFunc)self_sort_func,self );
996 self->_priv->old_sort_column = self->_priv->sort_column;
998 /* tell the view that rows have changed */
1001 GtkTreePath *path = NULL;
1003 MpdData *data2 = mpd_data_get_first(self->_priv->data);
1004 if(self->has_up) i = 1;
1005 for(;data2; data2 = mpd_data_get_next_real(data2,FALSE))
1007 path = gtk_tree_path_new();
1008 gtk_tree_path_append_index(path,i);
1009 iter.stamp = self->_priv->stamp;
1010 iter.user_data = data2;
1011 iter.user_data2 = GINT_TO_POINTER(i);
1013 /* propegate change */
1014 gtk_tree_model_row_changed(GTK_TREE_MODEL(self), path, &iter);
1015 gtk_tree_path_free(path);
1023 test_sort_func(gpointer ppaa, gpointer ppbb, Self *self)
1025 MpdData_real *a = *(MpdData_real **)ppaa;
1026 MpdData_real *b = *(MpdData_real **)ppbb;
1027 return strcmp(a->tag, b->tag);
1031 * For now this assumes tag list only.
1034 long unsigned set_mpd_data_slow (self, MpdData *data2)
1036 MpdData_real *new_list = NULL;
1039 MpdData_real *original = (MpdData_real *)mpd_data_get_first(self->_priv->data);
1040 MpdData_real *new = (MpdData_real *) data2;
1041 GtkTreePath *path =NULL;
1043 int old_list_size = self->num_rows;
1045 /* sort it identical */
1046 data2 = misc_sort_mpddata(data2,(GCompareDataFunc)self_test_sort_func,self );
1047 new = (MpdData_real *)data2;
1050 MpdData_real *n = new->first;
1058 /* Free possible stored images */
1060 * TODO: Don't free the list, but grow it to MAX(items, self->num_rows);
1061 * Then after updating, resize it to new size
1063 /* Grow the list when needed */
1065 if(self->_priv->use_images)
1067 /* Clean them, they might not be in the right place anymore */
1068 for(i=0;i< old_list_size; i++)
1070 if(self->_priv->images[i] != NULL) g_object_unref(self->_priv->images[i]);
1071 self->_priv->images[i] = NULL;
1073 if(items > self->num_rows)
1075 self->_priv->images = g_realloc(self->_priv->images, items*sizeof(GdkPixbuf *));
1076 /* set the new ones to NUll */
1077 for(i=self->num_rows;i<items;i++)
1079 self->_priv->images[i] = NULL;
1089 if(self->_priv->data && original == NULL) g_warning("weirdness\n");
1090 if(self->_priv->data)
1098 if(self->num_rows > items)
1100 MpdData_real *n = original->next;
1101 /* remove current row */
1104 path = gtk_tree_path_new();
1105 gtk_tree_path_append_index(path,i);
1107 /* propegate change */
1108 gtk_tree_model_row_deleted(GTK_TREE_MODEL(self), path);
1109 gtk_tree_path_free(path);
1111 original =(MpdData_real *) mpd_data_delete_item((MpdData *)original);
1112 self->_priv->data = mpd_data_get_first((MpdData *)original);
1115 has_next = (original )?1:0;
1120 int compare = strcmp(new->tag, original->tag);
1124 MpdData_real *n = (MpdData_real *) mpd_new_data_struct();
1125 n->type = MPD_DATA_TYPE_TAG;
1126 n->tag_type = new->tag_type;
1133 n->prev = original->prev;
1136 original->prev->next = n;
1138 n->first = original->first;
1140 if(n->prev == NULL){
1141 MpdData_real *fiter;
1142 for(fiter = n;fiter;fiter = fiter->next){
1147 self->_priv->data = mpd_data_get_first((MpdData *)original);
1149 path = gtk_tree_path_new();
1150 gtk_tree_path_append_index(path,i);
1151 iter.stamp = self->_priv->stamp;
1153 iter.user_data2 = GINT_TO_POINTER(i);
1155 gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
1156 gtk_tree_path_free(path);
1158 /* mark for insert */
1163 else if (compare > 0)
1165 MpdData_real *n = original->next;
1166 path = gtk_tree_path_new();
1167 gtk_tree_path_append_index(path,i);
1168 /* propegate change */
1171 gtk_tree_model_row_deleted(GTK_TREE_MODEL(self), path);
1172 gtk_tree_path_free(path);
1175 original = (MpdData_real *)mpd_data_delete_item((MpdData *)original);
1176 self->_priv->data = mpd_data_get_first((MpdData *)original);
1178 //original = original->next;
1179 has_next = (original )?1:0;
1183 original = original->next;
1184 has_next = (original )?1:0;
1190 /* add entries remaining in new */
1194 new_list = (MpdData_real *) self->_priv->data;
1195 while(new_list && new_list->next) new_list = new_list->next;
1197 new_list = (MpdData_real *)mpd_new_data_struct_append((MpdData*)new_list);
1198 new_list->type = MPD_DATA_TYPE_TAG;
1199 new_list->tag_type = new->tag_type;
1200 new_list->tag = new->tag;
1203 self->_priv->data = (MpdData *)new_list->first;
1204 if(self->_priv->data == NULL) g_warning("self->_priv->data == NULL");
1207 path = gtk_tree_path_new();
1208 gtk_tree_path_append_index(path,i);
1209 iter.stamp = self->_priv->stamp;
1210 iter.user_data = new_list;
1211 iter.user_data2 = GINT_TO_POINTER(i);
1213 gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
1214 gtk_tree_path_free(path);
1223 if(self->_priv->use_images){
1224 if(self->num_rows < old_list_size){
1225 for(i=self->num_rows;i< old_list_size; i++)
1228 if(self->_priv->images[i] != NULL) g_object_unref(self->_priv->images[i]);
1230 self->_priv->images = g_realloc(self->_priv->images, (self->num_rows)*sizeof(GdkPixbuf *));
1237 mpd_data_free(data2);
1239 g_assert(items == self->num_rows);
1243 signal last NONE (LONG)
1245 playtime_changed(self, gulong playtime)
1254 return self->_priv->playtime;
1258 get_pos( self, GtkTreeIter *iter)
1260 g_assert(iter->stamp == self->_priv->stamp);
1261 return GPOINTER_TO_INT(iter->user_data2);
1268 g_assert(self->_priv->data == NULL);
1269 self->_priv->use_images = FALSE;
1274 steal_mpd_data(self)
1278 int old_num_rows = self->num_rows;
1279 MpdData *data = self->_priv->data;
1280 while ( self->num_rows > 0 ) {
1281 path = gtk_tree_path_new();
1282 gtk_tree_path_append_index(path, self->num_rows - 1 );
1283 /* propegate change */
1284 gtk_tree_model_row_deleted(GTK_TREE_MODEL(self), path);
1285 gtk_tree_path_free(path);
1290 self->_priv->data = NULL;
1291 /* Free possible stored images */
1292 if(self->_priv->images && self->_priv->use_images)
1294 for(i=0;i< old_num_rows;i++)
1296 if(self->_priv->images[i])
1297 g_object_unref(self->_priv->images[i]);
1299 q_free(self->_priv->images);
1301 self->_priv->playtime = 0;
1302 self_playtime_changed(self, self->_priv->playtime);
1304 return mpd_data_get_first(data);