1 /* Gnome Music Player Client (GMPC)
2 * Copyright (C) 2004-2011 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.
25 #include <libmpd/libmpd.h>
26 #include <libmpd/libmpd-internal.h>
30 //#include "playlist3.h"
32 #include "config-defaults.h"
38 #define GMPC_MPD_DATA_MODEL(a) GMPC_MPDDATA_MODEL(a)
41 MPDDATA_MODEL_COL_MPDSONG = 0, /* get the mpd_Song */
42 MPDDATA_MODEL_COL_PLAYING, /* Shows if this song is the current song */
43 MPDDATA_MODEL_COL_PLAYING_FONT_WEIGHT, /* Shows if this song is the current song */
44 MPDDATA_MODEL_COL_PATH, /* Path to song/file/directory */
45 MPDDATA_MODEL_COL_MARKUP, /* a string that has FALSEmarkup */
46 MPDDATA_MODEL_COL_SONG_ARTIST, /* artist name */
47 MPDDATA_MODEL_COL_SONG_ALBUM, /* album name */
48 MPDDATA_MODEL_COL_SONG_TITLE, /* song title */
49 MPDDATA_MODEL_COL_SONG_TITLEFILE, /* song title */
50 MPDDATA_MODEL_COL_SONG_GENRE, /* song genre */
51 MPDDATA_MODEL_COL_SONG_TRACK, /* song track */
52 MPDDATA_MODEL_COL_SONG_NAME, /* stream name */
53 MPDDATA_MODEL_COL_SONG_COMPOSER, /* composer name */
54 MPDDATA_MODEL_COL_SONG_PERFORMER, /* performer */
55 MPDDATA_MODEL_COL_SONG_DATE, /* date */
56 MPDDATA_MODEL_COL_SONG_LENGTH_FORMAT, /* length formatted */
57 MPDDATA_MODEL_COL_SONG_DISC, /* disc */
58 MPDDATA_MODEL_COL_SONG_COMMENT, /* comment */
59 MPDDATA_MODEL_COL_SONG_POS, /* position */
60 MPDDATA_MODEL_COL_SONG_ALBUMARTIST,
61 MPDDATA_MODEL_COL_PATH_EXTENSION, /* Extention */
62 MPDDATA_MODEL_COL_PATH_DIRECTORY, /* Directory */
63 MPDDATA_MODEL_COL_SONG_ID, /* col id */
64 MPDDATA_MODEL_COL_ICON_ID, /* icon id */
65 MPDDATA_MODEL_COL_SONG_LENGTH, /* length */
66 MPDDATA_MODEL_TAG_TYPE, /* tag type */
67 MPDDATA_MODEL_ROW_TYPE, /* type of the row */
68 MPDDATA_MODEL_META_DATA, /* metadata */
69 MPDDATA_MODEL_USERDATA,
70 MPDDATA_MODEL_N_COLUMNS
75 #define LOG_DOMAIN "MpdData.Model"
78 class Gmpc:MpdData:Model from G:Object
79 (interface Gtk:Tree:Sortable)
80 (interface Gtk:Tree:Drag:Source)
81 (interface Gtk:Tree:Model)
83 private gint stamp = {g_random_int()};
84 public GType types[MPDDATA_MODEL_N_COLUMNS];
85 private MpdData *data = NULL;
86 public gint num_rows = 0;
87 private GmpcPixbufLoaderAsync **images = NULL;
88 private gchar *req_artist = {NULL} destroywith g_free;
89 private GdkPixbuf *blank = {NULL} destroywith g_object_unref;
90 public gint icon_size = {cfg_get_single_value_as_int_with_default(config, "gmpc-mpddata-model", "icon-size", 32)};
93 private GtkSortType sort_order = GTK_SORT_ASCENDING;
94 private int sort_column = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID;
95 private int old_sort_column = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID;
96 public gboolean has_up = FALSE;
97 private gchar *markup = {cfg_get_single_value_as_string_with_default(config, "playlist", "browser_markup",DEFAULT_MARKUP_BROWSER)} destroywith g_free;
99 private gulong playtime = {0};
102 private gboolean use_images = TRUE;
104 property BOOLEAN has_up
106 blurb = "Show an 'up row",
107 default_value = FALSE,
113 self->types[MPDDATA_MODEL_COL_MPDSONG] = G_TYPE_POINTER;
114 self->types[MPDDATA_MODEL_COL_MARKUP] = G_TYPE_STRING;
115 self->types[MPDDATA_MODEL_COL_PLAYING] = G_TYPE_BOOLEAN;
116 self->types[MPDDATA_MODEL_COL_PLAYING_FONT_WEIGHT] = G_TYPE_INT;
117 self->types[MPDDATA_MODEL_COL_PATH] = G_TYPE_STRING;
118 self->types[MPDDATA_MODEL_COL_SONG_ARTIST] = G_TYPE_STRING;
119 self->types[MPDDATA_MODEL_COL_SONG_ALBUM] = G_TYPE_STRING;
120 self->types[MPDDATA_MODEL_COL_SONG_TITLE] = G_TYPE_STRING;
121 self->types[MPDDATA_MODEL_COL_SONG_TITLEFILE] = G_TYPE_STRING;
122 self->types[MPDDATA_MODEL_COL_SONG_TRACK] = G_TYPE_STRING;
123 self->types[MPDDATA_MODEL_COL_SONG_GENRE] = G_TYPE_STRING;
124 self->types[MPDDATA_MODEL_COL_SONG_NAME] = G_TYPE_STRING;
125 self->types[MPDDATA_MODEL_COL_SONG_COMPOSER] = G_TYPE_STRING;
126 self->types[MPDDATA_MODEL_COL_SONG_PERFORMER]= G_TYPE_STRING;
127 self->types[MPDDATA_MODEL_COL_SONG_DATE] = G_TYPE_STRING;
128 self->types[MPDDATA_MODEL_COL_SONG_LENGTH] = G_TYPE_INT;
129 self->types[MPDDATA_MODEL_COL_SONG_LENGTH_FORMAT] = G_TYPE_STRING;
130 self->types[MPDDATA_MODEL_COL_SONG_DISC] = G_TYPE_STRING;
131 self->types[MPDDATA_MODEL_COL_SONG_COMMENT] = G_TYPE_STRING;
132 self->types[MPDDATA_MODEL_COL_SONG_POS] = G_TYPE_INT;
133 self->types[MPDDATA_MODEL_COL_SONG_ALBUMARTIST] = G_TYPE_STRING;
134 self->types[MPDDATA_MODEL_COL_PATH_EXTENSION] = G_TYPE_STRING;
135 self->types[MPDDATA_MODEL_COL_PATH_DIRECTORY] = G_TYPE_STRING;
136 self->types[MPDDATA_MODEL_COL_SONG_ID] = G_TYPE_INT;
137 self->types[MPDDATA_MODEL_COL_ICON_ID] = G_TYPE_STRING;
138 self->types[MPDDATA_MODEL_TAG_TYPE] = G_TYPE_INT;
139 self->types[MPDDATA_MODEL_ROW_TYPE] = G_TYPE_INT;
140 self->types[MPDDATA_MODEL_USERDATA] = G_TYPE_POINTER;
141 self->types[MPDDATA_MODEL_META_DATA] = GDK_TYPE_PIXBUF;
144 self->_priv->blank = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, self->icon_size, self->icon_size);
145 gdk_pixbuf_fill(self->_priv->blank, 0x00000000);
150 Gmpc:MpdData:Model *new (void)
157 finalize (G:Object *obj)
160 Self *self = GMPC_MPDDATA_MODEL(obj);
162 if(self->_priv->data)
164 mpd_data_free(self->_priv->data);
165 self->_priv->data = NULL;
167 if(self->_priv->images && self->_priv->use_images)
169 for(i=0;i<self->num_rows;i++)
171 if(self->_priv->images[i]) {
172 g_object_unref(self->_priv->images[i]);
173 self->_priv->images[i] = NULL;
176 q_free(self->_priv->images);
177 /* q_free should do it, but force it */
178 self->_priv->images = NULL;
182 /* function implemented for the Gtk:Tree:Model interface */
183 interface Gtk:Tree:Model
184 private GtkTreeModelFlags
185 get_flags (Gtk:Tree:Model *self (check null type))
187 /* Here would be the implementation */
188 return (GtkTreeModelFlags)GTK_TREE_MODEL_LIST_ONLY;
192 interface Gtk:Tree:Model
194 gboolean iter_children(Gtk:Tree:Model *model, GtkTreeIter *iter, GtkTreeIter *parent)
196 Self *self = GMPC_MPDDATA_MODEL(model);
199 if(self->num_rows == 0)
201 /* Set iter to first item in list */
202 iter->stamp = self->_priv->stamp;
203 iter->user_data = self->_priv->data;
204 iter->user_data2 = GINT_TO_POINTER(0);
205 iter->user_data3 = NULL; /* unused */
210 * Unused, not known in the model directly?
212 interface Gtk:Tree:Model
214 gint get_n_columns(Gtk:Tree:Model *model)
216 return MPDDATA_MODEL_N_COLUMNS;
219 interface Gtk:Tree:Model
221 get_iter(Gtk:Tree:Model *model (check null type),
222 Gtk:Tree:Iter *iter (check null),
223 Gtk:Tree:Path *path (check null))
225 Self *self = GMPC_MPDDATA_MODEL(model);
226 MpdData_real *data = NULL;
228 const gint *indices = gtk_tree_path_get_indices(path);
229 gint depth = gtk_tree_path_get_depth(path);
230 gint n = indices[0]; /* the n-th top level row */
233 g_assert(depth == 1);
235 if (n >= self->num_rows || n < 0)
238 g_assert(n<self->num_rows);
239 /* If it is the first item in a has_up list, do nothing */
240 if(self->has_up && n == 0){
244 data = (MpdData_real *)self->_priv->data;
254 iter->stamp = self->_priv->stamp;
255 iter->user_data = data;
256 iter->user_data2 = GINT_TO_POINTER(n);
260 interface Gtk:Tree:Model
262 iter_next(Gtk:Tree:Model *model(check null type), GtkTreeIter *iter (check null))
264 Self *self = GMPC_MPDDATA_MODEL(model);
265 MpdData *data = iter->user_data;
266 int n = GPOINTER_TO_INT(iter->user_data2) ;
267 /* if the row is the last row in the list or bigger then the last row, return FALSE */
268 if(n >= self->num_rows)
272 /* if the row is the first row in the list, and it has a "go-up" entry,
273 * it can have a next entry
275 if(data == NULL && n == 0 && self->has_up == TRUE)
277 iter->user_data = (MpdData *)self->_priv->data;
278 iter->user_data2 = GINT_TO_POINTER(1);
279 if(iter->user_data == NULL)
283 if(mpd_data_get_next_real(data,FALSE) == NULL)
287 iter->user_data = (MpdData *)mpd_data_get_next_real(data,FALSE);
288 iter->user_data2 = GINT_TO_POINTER(n+1);
289 g_assert(iter->user_data != NULL);
293 interface Gtk:Tree:Model
295 iter_has_child(Gtk:Tree:Model *self (check null type), GtkTreeIter *iter)
299 interface Gtk:Tree:Model
301 iter_n_children(Gtk:Tree:Model *model (check null type), GtkTreeIter *iter)
303 Self *list = GMPC_MPDDATA_MODEL(model);
306 return list->num_rows;
309 interface Gtk:Tree:Model
311 iter_parent(Gtk:Tree:Model *model (check null type), Gtk:Tree:Iter *iter, Gtk:Tree:Iter *child)
317 cover_art_fetched(mpd_Song *song, MetaDataResult ret,MetaData *met,GtkTreeRowReference *ref)
322 GtkTreePath *path =gtk_tree_row_reference_get_path(ref);
323 GtkTreeModel *model = gtk_tree_row_reference_get_model(ref);
327 Self *self = GMPC_MPDDATA_MODEL(model);
330 g_assert(self->_priv->use_images == TRUE);
332 gtk_tree_model_get_iter(model, &iter, path);
333 n = GPOINTER_TO_INT(iter.user_data2) ;
335 if(self->_priv->images[n] == NULL){
336 self->_priv->images[n]= gmpc_pixbuf_loader_async_new();
337 gmpc_pixbuf_loader_async_set_rref(GMPC_PIXBUF_LOADER_ASYNC(self->_priv->images[n]), ref);
340 if(ret == META_DATA_AVAILABLE)
343 GdkPixbuf *pb = NULL;
344 if(met->content_type == META_DATA_CONTENT_URI)
346 const gchar *coverpath = meta_data_get_uri(met);
347 MpdData_real *data = iter.user_data;
348 gmpc_pixbuf_loader_async_set_from_file(GMPC_PIXBUF_LOADER_ASYNC(self->_priv->images[n]),
349 coverpath, self->icon_size, self->icon_size,
350 ((data->type == MPD_DATA_TYPE_TAG && data->tag_type == MPD_TAG_ITEM_ALBUM) || (data->type == MPD_DATA_TYPE_SONG)?GMPC_MODIFICATION_TYPE_CASING: GMPC_MODIFICATION_TYPE_NONE));
351 } else if (met->content_type == META_DATA_CONTENT_RAW) {
352 MpdData_real *data = iter.user_data;
353 gmpc_pixbuf_loader_async_set_from_raw(GMPC_PIXBUF_LOADER_ASYNC(self->_priv->images[n]),
354 met->content, met->size, self->icon_size, self->icon_size,
355 ((data->type == MPD_DATA_TYPE_TAG && data->tag_type == MPD_TAG_ITEM_ALBUM) || (data->type == MPD_DATA_TYPE_SONG)?GMPC_MODIFICATION_TYPE_CASING: GMPC_MODIFICATION_TYPE_NONE));
358 pb = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-no-cover", self->icon_size, 0,NULL);
359 gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n], pb);
363 else if(ret == META_DATA_UNAVAILABLE)
366 pb2 = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-no-cover", self->icon_size, 0,NULL);
367 gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n], pb2);
370 gtk_tree_model_row_changed(model,path, &iter);
371 gtk_tree_path_free(path);
374 if(ret == META_DATA_AVAILABLE || ret == META_DATA_UNAVAILABLE)
378 gtk_tree_row_reference_free(ref);
384 interface Gtk:Tree:Model
386 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))
388 Self *self = GMPC_MPDDATA_MODEL(model);
389 MpdData_real *data = iter->user_data;
390 int n = GPOINTER_TO_INT(iter->user_data2);
392 if(self->_priv->data == NULL)
393 g_warning("self->_priv->data == NULL");
394 /* set value to the correct type */
395 g_value_init(value, self->types[column]);
400 g_assert((n < self->num_rows || n == 0));
401 g_assert(!(data == NULL && n != 0));
404 if(data == NULL && n == 0)
408 case MPDDATA_MODEL_COL_ICON_ID:
409 g_value_set_string(value, GTK_STOCK_GO_UP);
411 case MPDDATA_MODEL_ROW_TYPE:
415 g_value_set_int(value, -1);
417 case MPDDATA_MODEL_COL_SONG_TITLE:
418 case MPDDATA_MODEL_COL_MARKUP:
420 g_value_set_string(value,"..");
429 if(column == MPDDATA_MODEL_META_DATA)
431 if(self->_priv->use_images && self->_priv->images)
433 if(self->_priv->images[n] == NULL)
436 GtkTreeRowReference *ref;
437 path = gtk_tree_model_get_path(GTK_TREE_MODEL(self), iter);
438 ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(self),path);
440 self->_priv->images[n]= gmpc_pixbuf_loader_async_new();
441 gmpc_pixbuf_loader_async_set_rref(GMPC_PIXBUF_LOADER_ASYNC(self->_priv->images[n]), ref);
442 if(data->type == MPD_DATA_TYPE_TAG && (data->tag_type == MPD_TAG_ITEM_ARTIST || data->tag_type == MPD_TAG_ITEM_ALBUM_ARTIST) )
445 GdkPixbuf *pb = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-loading-cover", self->icon_size, 0,NULL);
446 gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n], pb);
448 song = mpd_newSong();
449 song->artist = g_strdup(data->tag);
451 gmpc_meta_watcher_get_meta_path_callback(gmw,song, META_ARTIST_ART, (MetaDataCallback)self_cover_art_fetched, (gpointer)ref);
455 else if(data->type == MPD_DATA_TYPE_TAG && data->tag_type == MPD_TAG_ITEM_ALBUM && self->_priv->req_artist)
458 GdkPixbuf *pb = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-loading-cover", self->icon_size, 0,NULL);
459 gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n],pb);
461 song = mpd_newSong();
462 song->artist = g_strdup(self->_priv->req_artist);
463 song->albumartist = g_strdup(self->_priv->req_artist);
464 song->album = g_strdup(data->tag);
465 gmpc_meta_watcher_get_meta_path_callback(gmw,song, META_ALBUM_ART, (MetaDataCallback)self_cover_art_fetched, ref);
468 else if(data->type == MPD_DATA_TYPE_SONG&& data->song && data->song->album && (data->song->artist || self->_priv->req_artist))
470 if(data->prev && data->prev->type == MPD_DATA_TYPE_SONG && data->prev->song->album && g_utf8_collate(data->song->album, data->prev->song->album) ==0)
472 gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n],self->_priv->blank);
475 GdkPixbuf *pb = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-loading-cover", self->icon_size, 0,NULL);
476 gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n],pb);
478 song = mpd_newSong();
479 song->artist = g_strdup((data->song->artist)?data->song->artist:self->_priv->req_artist);
480 song->albumartist = g_strdup((data->song->albumartist)?data->song->albumartist:self->_priv->req_artist);
481 song->album = g_strdup(data->song->album);
482 gmpc_meta_watcher_get_meta_path_callback(gmw,song, META_ALBUM_ART, (MetaDataCallback)self_cover_art_fetched, ref);
488 GdkPixbuf *pb = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-no-cover", self->icon_size, 0,NULL);
489 gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n],pb);
492 gtk_tree_path_free(path);
494 g_value_set_object(value, gmpc_pixbuf_loader_async_get_pixbuf(self->_priv->images[n]));
496 g_value_set_object(value,NULL);
499 if(column == MPDDATA_MODEL_USERDATA)
501 g_value_set_pointer(value,data->userdata);
504 /* handle row type, this is independent of the row type */
505 if(column == MPDDATA_MODEL_ROW_TYPE)
507 g_value_set_int(value, data->type);
510 if (data->type == MPD_DATA_TYPE_TAG) {
513 case MPDDATA_MODEL_COL_ICON_ID:
514 switch(data->tag_type)
516 case MPD_TAG_ITEM_ALBUM:
517 g_value_set_string(value, "media-album");
519 case MPD_TAG_ITEM_ARTIST:
520 g_value_set_string(value, "media-artist");
522 case MPD_TAG_ITEM_GENRE:
523 g_value_set_string(value, "media-genre");
525 case MPD_TAG_ITEM_TRACK:
526 g_value_set_string(value, "media-num-tracks");
529 g_value_set_string(value, "media-tag");
532 case MPDDATA_MODEL_COL_SONG_TITLE:
533 case MPDDATA_MODEL_COL_MARKUP:
535 g_value_set_string(value, data->tag);
538 case MPDDATA_MODEL_COL_PATH:
539 g_value_set_string(value, data->tag);
541 case MPDDATA_MODEL_TAG_TYPE:
542 g_value_set_int(value, data->tag_type);
547 } else if(data->type == MPD_DATA_TYPE_DIRECTORY) {
550 case MPDDATA_MODEL_COL_ICON_ID:
551 g_value_set_string(value, GTK_STOCK_OPEN);
553 case MPDDATA_MODEL_COL_SONG_TITLE:
554 case MPDDATA_MODEL_COL_SONG_TITLEFILE:
555 case MPDDATA_MODEL_COL_MARKUP:
557 gchar *basename = g_path_get_basename(data->directory);
558 g_value_set_string(value, basename);
562 case MPDDATA_MODEL_COL_PATH:
563 g_value_set_string(value, data->directory);
569 else if(data->type == MPD_DATA_TYPE_PLAYLIST)
573 case MPDDATA_MODEL_COL_ICON_ID:
574 g_value_set_string(value, "media-playlist");
576 case MPDDATA_MODEL_COL_SONG_TITLE:
577 case MPDDATA_MODEL_COL_SONG_TITLEFILE:
578 case MPDDATA_MODEL_COL_MARKUP:
580 gchar *basename = g_path_get_basename(data->playlist->path);
581 g_value_set_string(value, basename);
585 case MPDDATA_MODEL_COL_PATH:
586 g_value_set_string(value, data->playlist->path);
592 else if(data->type == MPD_DATA_TYPE_SONG)
594 mpd_Song *song = data->song;
596 case MPDDATA_MODEL_COL_MPDSONG:
597 g_value_set_pointer(value, song);
599 case MPDDATA_MODEL_COL_PLAYING:
600 g_value_set_boolean(value, FALSE);
602 case MPDDATA_MODEL_COL_PLAYING_FONT_WEIGHT:
603 g_value_set_int(value, PANGO_WEIGHT_NORMAL);
605 case MPDDATA_MODEL_COL_MARKUP:
607 /* we want to go cache this stuff */
609 mpd_song_markup(buffer, 1024, GMPC_MPDDATA_MODEL(model)->_priv->markup, song);
610 g_value_set_string(value, buffer);
613 case MPDDATA_MODEL_COL_PATH:
614 g_value_set_string(value, song->file);
616 case MPDDATA_MODEL_COL_PATH_EXTENSION:
618 int j = strlen(song->file);
619 for(;j>0&&song->file[j] != '.';j--);
620 g_value_set_string(value, &(song->file)[j+1]);
623 case MPDDATA_MODEL_COL_PATH_DIRECTORY:
625 gchar *dir = g_path_get_dirname(song->file);
626 g_value_set_string(value, dir);
630 case MPDDATA_MODEL_COL_SONG_ARTIST:
631 g_value_set_string(value, song->artist);
633 case MPDDATA_MODEL_COL_SONG_ALBUM:
634 g_value_set_string(value, song->album);
636 case MPDDATA_MODEL_COL_SONG_TITLE:
637 /* If there is a song available use that, else use the filename */
639 g_value_set_string(value, song->title);
640 } else if (song->name) {
641 g_value_set_string(value, song->name);
643 /* Use the markup stuff, this makes sure it gets processed equaly */
645 mpd_song_markup(buffer, 1024, "%shortfile%", song);
646 g_value_set_string(value, buffer);
649 case MPDDATA_MODEL_COL_SONG_TITLEFILE:
651 gchar *path = g_path_get_basename(song->file);
652 g_value_set_string(value, path);
656 case MPDDATA_MODEL_COL_SONG_GENRE:
657 g_value_set_string(value, song->genre);
659 case MPDDATA_MODEL_COL_SONG_TRACK:
660 g_value_set_string(value, song->track);
662 case MPDDATA_MODEL_COL_SONG_NAME:
663 g_value_set_string(value, song->name);
665 case MPDDATA_MODEL_COL_SONG_COMPOSER:
666 g_value_set_string(value, song->composer);
668 case MPDDATA_MODEL_COL_SONG_PERFORMER:
669 g_value_set_string(value, song->performer);
671 case MPDDATA_MODEL_COL_SONG_DATE:
672 g_value_set_string(value, song->date);
674 case MPDDATA_MODEL_COL_SONG_LENGTH:
675 g_value_set_int(value, song->time);
677 case MPDDATA_MODEL_COL_SONG_LENGTH_FORMAT:
679 if(song->time >= 0) {
680 gchar *strdata = g_strdup_printf("%02i:%02i",
681 song->time/60, song->time%60);
682 g_value_set_string(value, strdata);
685 g_value_set_string(value, "n/a");
689 case MPDDATA_MODEL_COL_SONG_DISC:
690 g_value_set_string(value, song->disc);
692 case MPDDATA_MODEL_COL_SONG_COMMENT:
693 g_value_set_string(value, song->comment);
695 case MPDDATA_MODEL_COL_SONG_POS:
696 g_value_set_int(value, song->pos+1);
698 case MPDDATA_MODEL_COL_SONG_ALBUMARTIST:
699 g_value_set_string(value, song->albumartist);
701 case MPDDATA_MODEL_COL_SONG_ID:
702 g_value_set_int(value, song->id);
705 case MPDDATA_MODEL_COL_ICON_ID:
706 if (strstr(song->file, "://")) {
707 g_value_set_string(value, "media-stream");
709 g_value_set_string(value, "media-audiofile");
718 interface Gtk:Tree:Model
720 iter_nth_child(Gtk:Tree:Model *model(check null type), GtkTreeIter *iter (check null), GtkTreeIter *parent, gint n (check >=0))
722 Self *self = GMPC_MPDDATA_MODEL(model);
724 MpdData_real *data = NULL;
727 if (n >= self->num_rows || n < 0)
730 data = (MpdData_real *)self->_priv->data;
736 data = data->next;//mpd_data_get_next_real(data, FALSE);
737 g_assert(data != NULL);
740 if(self->has_up && n == 0)
743 iter->stamp = self->_priv->stamp;
744 iter->user_data = data;
745 iter->user_data2 = GINT_TO_POINTER(n);
749 interface Gtk:Tree:Model
750 private GtkTreePath *
751 get_path(Gtk:Tree:Model *self (check null type), GtkTreeIter *iter (check null))
753 GtkTreePath *path = NULL;
754 path = gtk_tree_path_new();
755 gtk_tree_path_append_index(path, GPOINTER_TO_INT(iter->user_data2));
759 interface Gtk:Tree:Model
761 get_column_type(Gtk:Tree:Model *model(check null type), gint ind (check >= 0))
763 Self *self = GMPC_MPDDATA_MODEL(model);
764 return self->types[ind];
768 set_request_artist(self, const char *artist)
771 GtkTreePath *path = NULL;
773 if(self->_priv->req_artist)
774 q_free(self->_priv->req_artist);
775 self->_priv->req_artist = (artist != NULL && artist[0] != '\0')?g_strdup(artist):NULL;
776 /* Free possible stored images */
777 if(self->_priv->images && self->_priv->use_images)
779 MpdData *data2 = mpd_data_get_first(self->_priv->data);
780 for(i=0;i<self->num_rows;i++)
782 if(self->_priv->images[i]){
783 g_object_unref(self->_priv->images[i]);
784 self->_priv->images[i] = NULL;
786 /* Update the view */
787 path = gtk_tree_path_new();
788 gtk_tree_path_append_index(path,i);
789 iter.stamp = self->_priv->stamp;
790 iter.user_data = data2;
791 iter.user_data2 = GINT_TO_POINTER(i);
792 /* propegate change */
793 gtk_tree_model_row_changed(GTK_TREE_MODEL(self), path, &iter);
794 gtk_tree_path_free(path);
796 data2 = mpd_data_get_next_real(data2,FALSE);
802 get_request_artist(self)
804 return self->_priv->req_artist;
807 long unsigned set_mpd_data(self, MpdData *data2)
810 long unsigned retval = 0;
812 GtkTreePath *path = NULL;
813 int old_num_rows = self->num_rows;
814 /* Do some cleanup, like removing rows, and so */
815 /* loop and remove */
816 while ( self->num_rows > 0 ) {
817 path = gtk_tree_path_new();
818 gtk_tree_path_append_index(path, self->num_rows - 1 );
819 /* propegate change */
820 gtk_tree_model_row_deleted(GTK_TREE_MODEL(self), path);
821 gtk_tree_path_free(path);
827 * Free if there is a list and set it to NULL
829 if(self->_priv->data){
830 mpd_data_free(mpd_data_get_first(self->_priv->data));
832 self->_priv->data = NULL;
834 if(self->num_rows != 0)
835 g_log(LOG_DOMAIN, G_LOG_LEVEL_WARNING,"not every row cleared %i\n",self->num_rows);
837 /* Free possible stored images */
838 if(self->_priv->images && self->_priv->use_images)
840 for(i=0;i< old_num_rows;i++)
842 if(self->_priv->images[i])
843 g_object_unref(self->_priv->images[i]);
845 q_free(self->_priv->images);
849 self->_priv->playtime = 0;
850 self_playtime_changed(self, self->_priv->playtime);
854 self->_priv->data = mpd_data_get_first(data2);
856 if(self->_priv->sort_column == MPDDATA_MODEL_COL_ICON_ID)
858 self->_priv->data = misc_sort_mpddata_by_album_disc_track(self->_priv->data);
860 else if ( self->_priv->sort_column != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID )
862 self->_priv->data = misc_sort_mpddata(self->_priv->data,(GCompareDataFunc)self_sort_func,self );
866 path = gtk_tree_path_new();
867 gtk_tree_path_append_index(path, self->num_rows);
868 iter.stamp = self->_priv->stamp;
869 iter.user_data = NULL;
870 iter.user_data2 = GINT_TO_POINTER(self->num_rows);
873 /* propegate change */
874 gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
875 gtk_tree_path_free(path);
877 for(data2 = mpd_data_get_first(self->_priv->data);data2; data2 = mpd_data_get_next_real(data2,FALSE)) {
878 path = gtk_tree_path_new();
879 gtk_tree_path_append_index(path,self->num_rows);
880 iter.stamp = self->_priv->stamp;
881 iter.user_data = data2;
882 iter.user_data2 = GINT_TO_POINTER(self->num_rows);
885 /* propegate change */
886 gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
887 gtk_tree_path_free(path);
888 if(data2->type == MPD_DATA_TYPE_SONG)
890 if(data2->song && data2->song->time > 0)
891 retval+= data2->song->time;
894 if(self->_priv->use_images)
896 self->_priv->images = g_malloc0((self->num_rows)*sizeof(*(self->_priv->images)));
899 self->_priv->playtime = retval;
900 self_playtime_changed(self, self->_priv->playtime);
905 interface Gtk:Tree:Sortable
907 gboolean get_sort_column_id (Gtk:Tree:Sortable *model , gint *sort_column_id, GtkSortType *order)
909 Self *self = GMPC_MPDDATA_MODEL(model);
911 *sort_column_id = self->_priv->sort_column;
913 *order = self->_priv->sort_order;
917 interface Gtk:Tree:Sortable
919 void set_sort_column_id (Gtk:Tree:Sortable *model , gint sort_column_id, GtkSortType order)
921 Self *self = GMPC_MPDDATA_MODEL(model);
922 gint old_col = self->_priv->sort_column;
923 GtkSortType old_ord = self->_priv->sort_order;
924 self->_priv->sort_column = sort_column_id;
925 self->_priv->sort_order = order;
927 if(old_col != sort_column_id || old_ord != order)
928 gtk_tree_sortable_sort_column_changed(model);
929 self->_priv->old_sort_column = self->_priv->sort_column;
933 interface Gtk:Tree:Sortable
936 has_default_sort_func(Gtk:Tree:Sortable *model)
941 int sort_func(gpointer ppaa, gpointer ppbb, Self *self)
943 MpdData_real *a = *(MpdData_real **)ppaa;
944 MpdData_real *b = *(MpdData_real **)ppbb;
945 int fact = (self->_priv->sort_order == GTK_SORT_ASCENDING)?-1:1;
947 if(a->type != b->type )
949 int val = a->type - b->type;
952 else if(a->type == b->type)
956 GValue va = {0,},vb = {0,};
957 /* Get values from tree view, so we don't have to replicate code to sort the right entries */
959 iter.user_data2 = GPOINTER_TO_INT(0);
960 gtk_tree_model_get_value(GTK_TREE_MODEL(self), &iter, self->_priv->sort_column, &va);
962 gtk_tree_model_get_value(GTK_TREE_MODEL(self), &iter, self->_priv->sort_column, &vb);
963 /* if the type is a directory or a tag, always sort on the title column */
964 if(a->type == MPD_DATA_TYPE_DIRECTORY || b->type == MPD_DATA_TYPE_TAG)
967 ca = g_value_get_string(&va);
968 cb = g_value_get_string(&vb);
971 aa = g_utf8_strdown(ca, -1);
972 bb = g_utf8_strdown(cb, -1);
973 val = g_utf8_collate(aa,bb);
977 val = (ca == NULL)?((cb==NULL)?0:-1):1;
984 if(self->_priv->sort_column == MPDDATA_MODEL_COL_SONG_TRACK)
986 const char *ca = g_value_get_string(&va);
987 const char *cb = g_value_get_string(&vb);
988 gint ca1 = (ca)?atoi(ca):0;
989 gint cb1 = (cb)?atoi(cb):0;
992 return fact*(ca1-cb1);
994 else if(self->types[self->_priv->sort_column] == G_TYPE_INT)
996 val = g_value_get_int(&va) - g_value_get_int(&vb);
998 else if (self->types[self->_priv->sort_column] == G_TYPE_STRING)
1000 const char *ca = g_value_get_string(&va);
1001 const char *cb = g_value_get_string(&vb);
1004 aa = g_utf8_normalize(ca, -1,G_NORMALIZE_ALL);
1005 bb = g_utf8_normalize(cb, -1,G_NORMALIZE_ALL);
1006 val = g_utf8_collate(aa,bb);
1010 val = (ca == NULL)?((cb==NULL)?0:-1):1;
1015 a->type == MPD_DATA_TYPE_SONG &&
1016 b->type == MPD_DATA_TYPE_SONG &&
1020 val = strcmp (a->song->file, b->song->file);
1030 interface Gtk:Tree:Sortable
1033 sort_column_changed(Gtk:Tree:Sortable *model)
1035 Self *self = GMPC_MPDDATA_MODEL(model);
1036 if(!self->_priv->data || !(((MpdData_real *)self->_priv->data)->next))
1038 if(self->_priv->sort_column == MPDDATA_MODEL_COL_ICON_ID)
1040 self->_priv->data = misc_sort_mpddata_by_album_disc_track(self->_priv->data);
1042 else if(self->_priv->sort_column != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID)
1044 self->_priv->data = misc_sort_mpddata(self->_priv->data,(GCompareDataFunc)self_sort_func,self );
1046 self->_priv->old_sort_column = self->_priv->sort_column;
1048 /* tell the view that rows have changed */
1051 GtkTreePath *path = NULL;
1053 MpdData *data2 = mpd_data_get_first(self->_priv->data);
1054 if(self->has_up) i = 1;
1055 for(;data2; data2 = mpd_data_get_next_real(data2,FALSE))
1057 path = gtk_tree_path_new();
1058 gtk_tree_path_append_index(path,i);
1059 iter.stamp = self->_priv->stamp;
1060 iter.user_data = data2;
1061 iter.user_data2 = GINT_TO_POINTER(i);
1063 /* propegate change */
1064 gtk_tree_model_row_changed(GTK_TREE_MODEL(self), path, &iter);
1065 gtk_tree_path_free(path);
1073 test_sort_func(gpointer ppaa, gpointer ppbb, Self *self)
1075 MpdData_real *a = *(MpdData_real **)ppaa;
1076 MpdData_real *b = *(MpdData_real **)ppbb;
1077 if(a->type == MPD_DATA_TYPE_TAG && b->type == MPD_DATA_TYPE_TAG)
1079 if(a->tag_type != b->tag_type)
1080 return a->tag_type - b->tag_type;
1081 if(a->tag== NULL && b->tag != NULL)
1083 else if(b->tag == NULL && a->tag != NULL)
1085 else if (a->tag && b->tag)
1088 if(a->tag && b->tag) {
1090 sa = g_utf8_strdown(a->tag, -1);
1091 sb = g_utf8_strdown(b->tag, -1);
1092 val = g_utf8_collate(sa,sb);
1096 val = (a == NULL)?((b==NULL)?0:-1):1;
1101 return a->type - b->type;
1105 * For now this assumes tag list only.
1108 long unsigned set_mpd_data_slow (self, MpdData *data2)
1110 MpdData_real *new_list = NULL;
1113 MpdData_real *original = (MpdData_real *)mpd_data_get_first(self->_priv->data);
1114 MpdData_real *new = (MpdData_real *) data2;
1115 GtkTreePath *path =NULL;
1117 int old_list_size = self->num_rows;
1119 /* sort it identical */
1120 data2 = misc_sort_mpddata(data2,(GCompareDataFunc)self_test_sort_func,self );
1121 new = (MpdData_real *)data2;
1124 MpdData_real *n = new->first;
1132 /* Free possible stored images */
1134 * TODO: Don't free the list, but grow it to MAX(items, self->num_rows);
1135 * Then after updating, resize it to new size
1137 /* Grow the list when needed */
1139 if(self->_priv->use_images)
1141 /* Clean them, they might not be in the right place anymore */
1142 for(i=0;i< old_list_size; i++)
1144 if(self->_priv->images[i] != NULL) g_object_unref(self->_priv->images[i]);
1145 self->_priv->images[i] = NULL;
1147 if(items > self->num_rows)
1149 self->_priv->images = g_realloc(self->_priv->images, items*sizeof(GdkPixbuf *));
1150 /* set the new ones to NUll */
1151 for(i=self->num_rows;i<items;i++)
1153 self->_priv->images[i] = NULL;
1163 if(self->_priv->data && original == NULL) g_warning("weirdness\n");
1164 if(self->_priv->data)
1172 if(self->num_rows > items)
1174 MpdData_real *n = original->next;
1175 /* remove current row */
1178 path = gtk_tree_path_new();
1179 gtk_tree_path_append_index(path,i);
1181 /* propegate change */
1182 gtk_tree_model_row_deleted(GTK_TREE_MODEL(self), path);
1183 gtk_tree_path_free(path);
1185 original =(MpdData_real *) mpd_data_delete_item((MpdData *)original);
1186 self->_priv->data = mpd_data_get_first((MpdData *)original);
1189 has_next = (original )?1:0;
1196 compare = g_utf8_collate(new->tag,original->tag);
1200 MpdData_real *n = (MpdData_real *) mpd_new_data_struct();
1201 n->type = MPD_DATA_TYPE_TAG;
1202 n->tag_type = new->tag_type;
1209 n->prev = original->prev;
1212 original->prev->next = n;
1214 n->first = original->first;
1216 if(n->prev == NULL){
1217 MpdData_real *fiter;
1218 for(fiter = n;fiter;fiter = fiter->next){
1223 self->_priv->data = mpd_data_get_first((MpdData *)original);
1225 path = gtk_tree_path_new();
1226 gtk_tree_path_append_index(path,i);
1227 iter.stamp = self->_priv->stamp;
1229 iter.user_data2 = GINT_TO_POINTER(i);
1231 gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
1232 gtk_tree_path_free(path);
1234 /* mark for insert */
1239 else if (compare > 0)
1241 MpdData_real *n = original->next;
1242 path = gtk_tree_path_new();
1243 gtk_tree_path_append_index(path,i);
1244 /* propegate change */
1247 gtk_tree_model_row_deleted(GTK_TREE_MODEL(self), path);
1248 gtk_tree_path_free(path);
1251 original = (MpdData_real *)mpd_data_delete_item((MpdData *)original);
1252 self->_priv->data = mpd_data_get_first((MpdData *)original);
1254 //original = original->next;
1255 has_next = (original )?1:0;
1259 original = original->next;
1260 has_next = (original )?1:0;
1266 /* add entries remaining in new */
1270 new_list = (MpdData_real *) self->_priv->data;
1271 while(new_list && new_list->next) new_list = new_list->next;
1273 new_list = (MpdData_real *)mpd_new_data_struct_append((MpdData*)new_list);
1274 new_list->type = MPD_DATA_TYPE_TAG;
1275 new_list->tag_type = new->tag_type;
1276 new_list->tag = new->tag;
1279 self->_priv->data = (MpdData *)new_list->first;
1280 if(self->_priv->data == NULL) g_warning("self->_priv->data == NULL");
1283 path = gtk_tree_path_new();
1284 gtk_tree_path_append_index(path,i);
1285 iter.stamp = self->_priv->stamp;
1286 iter.user_data = new_list;
1287 iter.user_data2 = GINT_TO_POINTER(i);
1289 gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
1290 gtk_tree_path_free(path);
1299 if(self->_priv->use_images){
1300 if(self->num_rows < old_list_size){
1301 for(i=self->num_rows;i< old_list_size; i++)
1304 if(self->_priv->images[i] != NULL) g_object_unref(self->_priv->images[i]);
1306 self->_priv->images = g_realloc(self->_priv->images, (self->num_rows)*sizeof(GdkPixbuf *));
1313 mpd_data_free(data2);
1315 g_assert(items == self->num_rows);
1319 signal last NONE (LONG)
1321 playtime_changed(self, gulong playtime)
1330 return self->_priv->playtime;
1334 get_pos( self, GtkTreeIter *iter)
1336 g_assert(iter->stamp == self->_priv->stamp);
1337 return GPOINTER_TO_INT(iter->user_data2);
1344 g_assert(self->_priv->data == NULL);
1345 self->_priv->use_images = FALSE;
1350 steal_mpd_data(self)
1354 int old_num_rows = self->num_rows;
1355 MpdData *data = self->_priv->data;
1356 while ( self->num_rows > 0 ) {
1357 path = gtk_tree_path_new();
1358 gtk_tree_path_append_index(path, self->num_rows - 1 );
1359 /* propegate change */
1360 gtk_tree_model_row_deleted(GTK_TREE_MODEL(self), path);
1361 gtk_tree_path_free(path);
1366 self->_priv->data = NULL;
1367 /* Free possible stored images */
1368 if(self->_priv->images && self->_priv->use_images)
1370 for(i=0;i< old_num_rows;i++)
1372 if(self->_priv->images[i])
1373 g_object_unref(self->_priv->images[i]);
1375 q_free(self->_priv->images);
1377 self->_priv->playtime = 0;
1378 self_playtime_changed(self, self->_priv->playtime);
1380 return mpd_data_get_first(data);
1383 /* Drag and stop handling */
1386 interface Gtk:Tree:Drag:Source
1388 gboolean row_draggable (Gtk:Tree:Drag:Source *drag_source, Gtk:Tree:Path *path)
1391 if(gtk_tree_model_get_iter(GTK_TREE_MODEL(drag_source), &iter, path))
1393 MpdData *data = iter.user_data;
1394 if(data->type == MPD_DATA_TYPE_SONG) {
1401 interface Gtk:Tree:Drag:Source
1403 gboolean drag_data_delete (Gtk:Tree:Drag:Source *drag_source, Gtk:Tree:Path *path)
1407 interface Gtk:Tree:Drag:Source
1409 gboolean drag_data_get (Gtk:Tree:Drag:Source *drag_source, Gtk:Tree:Path *path, Gtk:Selection:Data *selection_data)
1411 if(!gtk_tree_set_row_drag_data(selection_data,GTK_TREE_MODEL(drag_source), path)) {