1 /* Gnome Music Player Client (GMPC)
2 * Copyright (C) 2004-2012 Qball Cow <qball@gmpclient.org>
3 * Project homepage: http://gmpclient.org/
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
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_RAW) {
345 MpdData_real *data = iter.user_data;
346 gmpc_pixbuf_loader_async_set_from_raw(GMPC_PIXBUF_LOADER_ASYNC(self->_priv->images[n]),
347 met->content, met->size, self->icon_size, self->icon_size,
348 ((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),
352 pb = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-no-cover", self->icon_size, 0,NULL);
353 gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n], pb);
357 else if(ret == META_DATA_UNAVAILABLE)
360 pb2 = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-no-cover", self->icon_size, 0,NULL);
361 gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n], pb2);
364 gtk_tree_model_row_changed(model,path, &iter);
365 gtk_tree_path_free(path);
368 if(ret == META_DATA_AVAILABLE || ret == META_DATA_UNAVAILABLE)
372 gtk_tree_row_reference_free(ref);
378 interface Gtk:Tree:Model
380 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))
382 Self *self = GMPC_MPDDATA_MODEL(model);
383 MpdData_real *data = iter->user_data;
384 int n = GPOINTER_TO_INT(iter->user_data2);
386 if(self->_priv->data == NULL)
387 g_warning("self->_priv->data == NULL");
388 /* set value to the correct type */
389 g_value_init(value, self->types[column]);
394 g_assert((n < self->num_rows || n == 0));
395 g_assert(!(data == NULL && n != 0));
398 if(data == NULL && n == 0)
402 case MPDDATA_MODEL_COL_ICON_ID:
403 g_value_set_string(value, GTK_STOCK_GO_UP);
405 case MPDDATA_MODEL_ROW_TYPE:
409 g_value_set_int(value, -1);
411 case MPDDATA_MODEL_COL_SONG_TITLE:
412 case MPDDATA_MODEL_COL_MARKUP:
414 g_value_set_string(value,"..");
423 if(column == MPDDATA_MODEL_META_DATA)
425 if(self->_priv->use_images && self->_priv->images)
427 if(self->_priv->images[n] == NULL)
430 GtkTreeRowReference *ref;
431 path = gtk_tree_model_get_path(GTK_TREE_MODEL(self), iter);
432 ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(self),path);
434 self->_priv->images[n]= gmpc_pixbuf_loader_async_new();
435 gmpc_pixbuf_loader_async_set_rref(GMPC_PIXBUF_LOADER_ASYNC(self->_priv->images[n]), ref);
436 if(data->type == MPD_DATA_TYPE_TAG && (data->tag_type == MPD_TAG_ITEM_ARTIST || data->tag_type == MPD_TAG_ITEM_ALBUM_ARTIST) )
439 GdkPixbuf *pb = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-loading-cover", self->icon_size, 0,NULL);
440 gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n], pb);
442 song = mpd_newSong();
443 song->artist = g_strdup(data->tag);
445 meta_data_get_path_callback(song, META_ARTIST_ART, (MetaDataCallback)self_cover_art_fetched, (gpointer)ref);
449 else if(data->type == MPD_DATA_TYPE_TAG && data->tag_type == MPD_TAG_ITEM_ALBUM && self->_priv->req_artist)
452 GdkPixbuf *pb = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-loading-cover", self->icon_size, 0,NULL);
453 gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n],pb);
455 song = mpd_newSong();
456 song->artist = g_strdup(self->_priv->req_artist);
457 song->albumartist = g_strdup(self->_priv->req_artist);
458 song->album = g_strdup(data->tag);
459 meta_data_get_path_callback(song, META_ALBUM_ART, (MetaDataCallback)self_cover_art_fetched, ref);
462 else if(data->type == MPD_DATA_TYPE_SONG&& data->song && data->song->album && (data->song->artist || self->_priv->req_artist))
464 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)
466 gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n],self->_priv->blank);
469 GdkPixbuf *pb = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-loading-cover", self->icon_size, 0,NULL);
470 gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n],pb);
472 song = mpd_newSong();
473 song->artist = g_strdup((data->song->artist)?data->song->artist:self->_priv->req_artist);
474 song->albumartist = g_strdup((data->song->albumartist)?data->song->albumartist:self->_priv->req_artist);
475 song->album = g_strdup(data->song->album);
476 meta_data_get_path_callback(song, META_ALBUM_ART, (MetaDataCallback)self_cover_art_fetched, ref);
482 GdkPixbuf *pb = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-no-cover", self->icon_size, 0,NULL);
483 gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n],pb);
486 gtk_tree_path_free(path);
488 g_value_set_object(value, gmpc_pixbuf_loader_async_get_pixbuf(self->_priv->images[n]));
490 g_value_set_object(value,NULL);
493 if(column == MPDDATA_MODEL_USERDATA)
495 g_value_set_pointer(value,data->userdata);
498 /* handle row type, this is independent of the row type */
499 if(column == MPDDATA_MODEL_ROW_TYPE)
501 g_value_set_int(value, data->type);
504 if (data->type == MPD_DATA_TYPE_TAG) {
507 case MPDDATA_MODEL_COL_ICON_ID:
508 switch(data->tag_type)
510 case MPD_TAG_ITEM_ALBUM:
511 g_value_set_string(value, "media-album");
513 case MPD_TAG_ITEM_ARTIST:
514 g_value_set_string(value, "media-artist");
516 case MPD_TAG_ITEM_GENRE:
517 g_value_set_string(value, "media-genre");
519 case MPD_TAG_ITEM_TRACK:
520 g_value_set_string(value, "media-num-tracks");
523 g_value_set_string(value, "media-tag");
526 case MPDDATA_MODEL_COL_SONG_TITLE:
527 case MPDDATA_MODEL_COL_MARKUP:
529 g_value_set_string(value, data->tag);
532 case MPDDATA_MODEL_COL_PATH:
533 g_value_set_string(value, data->tag);
535 case MPDDATA_MODEL_TAG_TYPE:
536 g_value_set_int(value, data->tag_type);
541 } else if(data->type == MPD_DATA_TYPE_DIRECTORY) {
544 case MPDDATA_MODEL_COL_ICON_ID:
545 g_value_set_string(value, GTK_STOCK_OPEN);
547 case MPDDATA_MODEL_COL_SONG_TITLE:
548 case MPDDATA_MODEL_COL_SONG_TITLEFILE:
549 case MPDDATA_MODEL_COL_MARKUP:
551 gchar *basename = g_path_get_basename(data->directory);
552 g_value_set_string(value, basename);
556 case MPDDATA_MODEL_COL_PATH:
557 g_value_set_string(value, data->directory);
563 else if(data->type == MPD_DATA_TYPE_PLAYLIST)
567 case MPDDATA_MODEL_COL_ICON_ID:
568 g_value_set_string(value, "media-playlist");
570 case MPDDATA_MODEL_COL_SONG_TITLE:
571 case MPDDATA_MODEL_COL_SONG_TITLEFILE:
572 case MPDDATA_MODEL_COL_MARKUP:
574 gchar *basename = g_path_get_basename(data->playlist->path);
575 g_value_set_string(value, basename);
579 case MPDDATA_MODEL_COL_PATH:
580 g_value_set_string(value, data->playlist->path);
586 else if(data->type == MPD_DATA_TYPE_SONG)
588 mpd_Song *song = data->song;
590 case MPDDATA_MODEL_COL_MPDSONG:
591 g_value_set_pointer(value, song);
593 case MPDDATA_MODEL_COL_PLAYING:
594 g_value_set_boolean(value, FALSE);
596 case MPDDATA_MODEL_COL_PLAYING_FONT_WEIGHT:
597 g_value_set_int(value, PANGO_WEIGHT_NORMAL);
599 case MPDDATA_MODEL_COL_MARKUP:
601 /* we want to go cache this stuff */
603 mpd_song_markup(buffer, 1024, GMPC_MPDDATA_MODEL(model)->_priv->markup, song);
604 g_value_set_string(value, buffer);
607 case MPDDATA_MODEL_COL_PATH:
608 g_value_set_string(value, song->file);
610 case MPDDATA_MODEL_COL_PATH_EXTENSION:
612 int j = strlen(song->file);
613 for(;j>0&&song->file[j] != '.';j--);
614 g_value_set_string(value, &(song->file)[j+1]);
617 case MPDDATA_MODEL_COL_PATH_DIRECTORY:
619 gchar *dir = g_path_get_dirname(song->file);
620 g_value_set_string(value, dir);
624 case MPDDATA_MODEL_COL_SONG_ARTIST:
625 g_value_set_string(value, song->artist);
627 case MPDDATA_MODEL_COL_SONG_ALBUM:
628 g_value_set_string(value, song->album);
630 case MPDDATA_MODEL_COL_SONG_TITLE:
631 /* If there is a song available use that, else use the filename */
633 g_value_set_string(value, song->title);
634 } else if (song->name) {
635 g_value_set_string(value, song->name);
637 /* Use the markup stuff, this makes sure it gets processed equaly */
639 mpd_song_markup(buffer, 1024, "%shortfile%", song);
640 g_value_set_string(value, buffer);
643 case MPDDATA_MODEL_COL_SONG_TITLEFILE:
645 gchar *path = g_path_get_basename(song->file);
646 g_value_set_string(value, path);
650 case MPDDATA_MODEL_COL_SONG_GENRE:
651 g_value_set_string(value, song->genre);
653 case MPDDATA_MODEL_COL_SONG_TRACK:
654 g_value_set_string(value, song->track);
656 case MPDDATA_MODEL_COL_SONG_NAME:
657 g_value_set_string(value, song->name);
659 case MPDDATA_MODEL_COL_SONG_COMPOSER:
660 g_value_set_string(value, song->composer);
662 case MPDDATA_MODEL_COL_SONG_PERFORMER:
663 g_value_set_string(value, song->performer);
665 case MPDDATA_MODEL_COL_SONG_DATE:
666 g_value_set_string(value, song->date);
668 case MPDDATA_MODEL_COL_SONG_LENGTH:
669 g_value_set_int(value, song->time);
671 case MPDDATA_MODEL_COL_SONG_LENGTH_FORMAT:
673 if(song->time >= 0) {
674 gchar *strdata = g_strdup_printf("%02i:%02i",
675 song->time/60, song->time%60);
676 g_value_set_string(value, strdata);
679 g_value_set_string(value, "n/a");
683 case MPDDATA_MODEL_COL_SONG_DISC:
684 g_value_set_string(value, song->disc);
686 case MPDDATA_MODEL_COL_SONG_COMMENT:
687 g_value_set_string(value, song->comment);
689 case MPDDATA_MODEL_COL_SONG_POS:
690 g_value_set_int(value, song->pos+1);
692 case MPDDATA_MODEL_COL_SONG_ALBUMARTIST:
693 g_value_set_string(value, song->albumartist);
695 case MPDDATA_MODEL_COL_SONG_ID:
696 g_value_set_int(value, song->id);
699 case MPDDATA_MODEL_COL_ICON_ID:
700 if (strstr(song->file, "://")) {
701 g_value_set_string(value, "media-stream");
703 g_value_set_string(value, "media-audiofile");
712 interface Gtk:Tree:Model
714 iter_nth_child(Gtk:Tree:Model *model(check null type), GtkTreeIter *iter (check null), GtkTreeIter *parent, gint n (check >=0))
716 Self *self = GMPC_MPDDATA_MODEL(model);
718 MpdData_real *data = NULL;
721 if (n >= self->num_rows || n < 0)
724 data = (MpdData_real *)self->_priv->data;
730 data = data->next;//mpd_data_get_next_real(data, FALSE);
731 g_assert(data != NULL);
734 if(self->has_up && n == 0)
737 iter->stamp = self->_priv->stamp;
738 iter->user_data = data;
739 iter->user_data2 = GINT_TO_POINTER(n);
743 interface Gtk:Tree:Model
744 private GtkTreePath *
745 get_path(Gtk:Tree:Model *self (check null type), GtkTreeIter *iter (check null))
747 GtkTreePath *path = NULL;
748 path = gtk_tree_path_new();
749 gtk_tree_path_append_index(path, GPOINTER_TO_INT(iter->user_data2));
753 interface Gtk:Tree:Model
755 get_column_type(Gtk:Tree:Model *model(check null type), gint ind (check >= 0))
757 Self *self = GMPC_MPDDATA_MODEL(model);
758 return self->types[ind];
762 set_request_artist(self, const char *artist)
765 GtkTreePath *path = NULL;
767 if(self->_priv->req_artist)
768 q_free(self->_priv->req_artist);
769 self->_priv->req_artist = (artist != NULL && artist[0] != '\0')?g_strdup(artist):NULL;
770 /* Free possible stored images */
771 if(self->_priv->images && self->_priv->use_images)
773 MpdData *data2 = mpd_data_get_first(self->_priv->data);
774 for(i=0;i<self->num_rows;i++)
776 if(self->_priv->images[i]){
777 g_object_unref(self->_priv->images[i]);
778 self->_priv->images[i] = NULL;
780 /* Update the view */
781 path = gtk_tree_path_new();
782 gtk_tree_path_append_index(path,i);
783 iter.stamp = self->_priv->stamp;
784 iter.user_data = data2;
785 iter.user_data2 = GINT_TO_POINTER(i);
786 /* propegate change */
787 gtk_tree_model_row_changed(GTK_TREE_MODEL(self), path, &iter);
788 gtk_tree_path_free(path);
790 data2 = mpd_data_get_next_real(data2,FALSE);
796 get_request_artist(self)
798 return self->_priv->req_artist;
801 long unsigned set_mpd_data(self, MpdData *data2)
804 long unsigned retval = 0;
806 GtkTreePath *path = NULL;
807 int old_num_rows = self->num_rows;
808 /* Do some cleanup, like removing rows, and so */
809 /* loop and remove */
810 while ( self->num_rows > 0 ) {
811 path = gtk_tree_path_new();
812 gtk_tree_path_append_index(path, self->num_rows - 1 );
813 /* propegate change */
814 gtk_tree_model_row_deleted(GTK_TREE_MODEL(self), path);
815 gtk_tree_path_free(path);
821 * Free if there is a list and set it to NULL
823 if(self->_priv->data){
824 mpd_data_free(mpd_data_get_first(self->_priv->data));
826 self->_priv->data = NULL;
828 if(self->num_rows != 0)
829 g_log(LOG_DOMAIN, G_LOG_LEVEL_WARNING,"not every row cleared %i\n",self->num_rows);
831 /* Free possible stored images */
832 if(self->_priv->images && self->_priv->use_images)
834 for(i=0;i< old_num_rows;i++)
836 if(self->_priv->images[i])
837 g_object_unref(self->_priv->images[i]);
839 q_free(self->_priv->images);
843 self->_priv->playtime = 0;
844 self_playtime_changed(self, self->_priv->playtime);
848 self->_priv->data = mpd_data_get_first(data2);
850 if(self->_priv->sort_column == MPDDATA_MODEL_COL_ICON_ID)
852 self->_priv->data = misc_sort_mpddata_by_album_disc_track(self->_priv->data);
854 else if ( self->_priv->sort_column != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID )
856 self->_priv->data = misc_sort_mpddata(self->_priv->data,(GCompareDataFunc)self_sort_func,self );
860 path = gtk_tree_path_new();
861 gtk_tree_path_append_index(path, self->num_rows);
862 iter.stamp = self->_priv->stamp;
863 iter.user_data = NULL;
864 iter.user_data2 = GINT_TO_POINTER(self->num_rows);
867 /* propegate change */
868 gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
869 gtk_tree_path_free(path);
871 for(data2 = mpd_data_get_first(self->_priv->data);data2; data2 = mpd_data_get_next_real(data2,FALSE)) {
872 path = gtk_tree_path_new();
873 gtk_tree_path_append_index(path,self->num_rows);
874 iter.stamp = self->_priv->stamp;
875 iter.user_data = data2;
876 iter.user_data2 = GINT_TO_POINTER(self->num_rows);
879 /* propegate change */
880 gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
881 gtk_tree_path_free(path);
882 if(data2->type == MPD_DATA_TYPE_SONG)
884 if(data2->song && data2->song->time > 0)
885 retval+= data2->song->time;
888 if(self->_priv->use_images)
890 self->_priv->images = g_malloc0((self->num_rows)*sizeof(*(self->_priv->images)));
893 self->_priv->playtime = retval;
894 self_playtime_changed(self, self->_priv->playtime);
899 interface Gtk:Tree:Sortable
901 gboolean get_sort_column_id (Gtk:Tree:Sortable *model , gint *sort_column_id, GtkSortType *order)
903 Self *self = GMPC_MPDDATA_MODEL(model);
905 *sort_column_id = self->_priv->sort_column;
907 *order = self->_priv->sort_order;
911 interface Gtk:Tree:Sortable
913 void set_sort_column_id (Gtk:Tree:Sortable *model , gint sort_column_id, GtkSortType order)
915 Self *self = GMPC_MPDDATA_MODEL(model);
916 gint old_col = self->_priv->sort_column;
917 GtkSortType old_ord = self->_priv->sort_order;
918 self->_priv->sort_column = sort_column_id;
919 self->_priv->sort_order = order;
921 if(old_col != sort_column_id || old_ord != order)
922 gtk_tree_sortable_sort_column_changed(model);
923 self->_priv->old_sort_column = self->_priv->sort_column;
927 interface Gtk:Tree:Sortable
930 has_default_sort_func(Gtk:Tree:Sortable *model)
935 int sort_func(gpointer ppaa, gpointer ppbb, Self *self)
937 MpdData_real *a = *(MpdData_real **)ppaa;
938 MpdData_real *b = *(MpdData_real **)ppbb;
939 int fact = (self->_priv->sort_order == GTK_SORT_ASCENDING)?-1:1;
941 if(a->type != b->type )
943 int val = a->type - b->type;
946 else if(a->type == b->type)
950 GValue va = {0,},vb = {0,};
951 /* Get values from tree view, so we don't have to replicate code to sort the right entries */
953 iter.user_data2 = GPOINTER_TO_INT(0);
954 gtk_tree_model_get_value(GTK_TREE_MODEL(self), &iter, self->_priv->sort_column, &va);
956 gtk_tree_model_get_value(GTK_TREE_MODEL(self), &iter, self->_priv->sort_column, &vb);
957 /* if the type is a directory or a tag, always sort on the title column */
958 if(a->type == MPD_DATA_TYPE_DIRECTORY || b->type == MPD_DATA_TYPE_TAG)
961 ca = g_value_get_string(&va);
962 cb = g_value_get_string(&vb);
965 aa = g_utf8_strdown(ca, -1);
966 bb = g_utf8_strdown(cb, -1);
967 val = g_utf8_collate(aa,bb);
971 val = (ca == NULL)?((cb==NULL)?0:-1):1;
978 if(self->_priv->sort_column == MPDDATA_MODEL_COL_SONG_TRACK)
980 const char *ca = g_value_get_string(&va);
981 const char *cb = g_value_get_string(&vb);
982 gint ca1 = (ca)?atoi(ca):0;
983 gint cb1 = (cb)?atoi(cb):0;
986 return fact*(ca1-cb1);
988 else if(self->types[self->_priv->sort_column] == G_TYPE_INT)
990 val = g_value_get_int(&va) - g_value_get_int(&vb);
992 else if (self->types[self->_priv->sort_column] == G_TYPE_STRING)
994 const char *ca = g_value_get_string(&va);
995 const char *cb = g_value_get_string(&vb);
998 aa = g_utf8_normalize(ca, -1,G_NORMALIZE_ALL);
999 bb = g_utf8_normalize(cb, -1,G_NORMALIZE_ALL);
1000 val = g_utf8_collate(aa,bb);
1004 val = (ca == NULL)?((cb==NULL)?0:-1):1;
1009 a->type == MPD_DATA_TYPE_SONG &&
1010 b->type == MPD_DATA_TYPE_SONG &&
1014 val = strcmp (a->song->file, b->song->file);
1024 interface Gtk:Tree:Sortable
1027 sort_column_changed(Gtk:Tree:Sortable *model)
1029 Self *self = GMPC_MPDDATA_MODEL(model);
1030 if(!self->_priv->data || !(((MpdData_real *)self->_priv->data)->next))
1032 if(self->_priv->sort_column == MPDDATA_MODEL_COL_ICON_ID)
1034 self->_priv->data = misc_sort_mpddata_by_album_disc_track(self->_priv->data);
1036 else if(self->_priv->sort_column != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID)
1038 self->_priv->data = misc_sort_mpddata(self->_priv->data,(GCompareDataFunc)self_sort_func,self );
1040 self->_priv->old_sort_column = self->_priv->sort_column;
1042 /* tell the view that rows have changed */
1045 GtkTreePath *path = NULL;
1047 MpdData *data2 = mpd_data_get_first(self->_priv->data);
1048 if(self->has_up) i = 1;
1049 for(;data2; data2 = mpd_data_get_next_real(data2,FALSE))
1051 path = gtk_tree_path_new();
1052 gtk_tree_path_append_index(path,i);
1053 iter.stamp = self->_priv->stamp;
1054 iter.user_data = data2;
1055 iter.user_data2 = GINT_TO_POINTER(i);
1057 /* propegate change */
1058 gtk_tree_model_row_changed(GTK_TREE_MODEL(self), path, &iter);
1059 gtk_tree_path_free(path);
1067 test_sort_func(gpointer ppaa, gpointer ppbb, Self *self)
1069 MpdData_real *a = *(MpdData_real **)ppaa;
1070 MpdData_real *b = *(MpdData_real **)ppbb;
1071 if(a->type == MPD_DATA_TYPE_TAG && b->type == MPD_DATA_TYPE_TAG)
1073 if(a->tag_type != b->tag_type)
1074 return a->tag_type - b->tag_type;
1075 if(a->tag== NULL && b->tag != NULL)
1077 else if(b->tag == NULL && a->tag != NULL)
1079 else if (a->tag && b->tag)
1082 if(a->tag && b->tag) {
1084 sa = g_utf8_strdown(a->tag, -1);
1085 sb = g_utf8_strdown(b->tag, -1);
1086 val = g_utf8_collate(sa,sb);
1090 val = (a == NULL)?((b==NULL)?0:-1):1;
1095 return a->type - b->type;
1099 * For now this assumes tag list only.
1102 long unsigned set_mpd_data_slow (self, MpdData *data2)
1104 MpdData_real *new_list = NULL;
1107 MpdData_real *original = (MpdData_real *)mpd_data_get_first(self->_priv->data);
1108 MpdData_real *new = (MpdData_real *) data2;
1109 GtkTreePath *path =NULL;
1111 int old_list_size = self->num_rows;
1113 /* sort it identical */
1114 data2 = misc_sort_mpddata(data2,(GCompareDataFunc)self_test_sort_func,self );
1115 new = (MpdData_real *)data2;
1118 MpdData_real *n = new->first;
1126 /* Free possible stored images */
1128 * TODO: Don't free the list, but grow it to MAX(items, self->num_rows);
1129 * Then after updating, resize it to new size
1131 /* Grow the list when needed */
1133 if(self->_priv->use_images)
1135 /* Clean them, they might not be in the right place anymore */
1136 for(i=0;i< old_list_size; i++)
1138 if(self->_priv->images[i] != NULL) g_object_unref(self->_priv->images[i]);
1139 self->_priv->images[i] = NULL;
1141 if(items > self->num_rows)
1143 self->_priv->images = g_realloc(self->_priv->images, items*sizeof(GdkPixbuf *));
1144 /* set the new ones to NUll */
1145 for(i=self->num_rows;i<items;i++)
1147 self->_priv->images[i] = NULL;
1157 if(self->_priv->data && original == NULL) g_warning("weirdness\n");
1158 if(self->_priv->data)
1166 if(self->num_rows > items)
1168 MpdData_real *n = original->next;
1169 /* remove current row */
1172 path = gtk_tree_path_new();
1173 gtk_tree_path_append_index(path,i);
1175 /* propegate change */
1176 gtk_tree_model_row_deleted(GTK_TREE_MODEL(self), path);
1177 gtk_tree_path_free(path);
1179 original =(MpdData_real *) mpd_data_delete_item((MpdData *)original);
1180 self->_priv->data = mpd_data_get_first((MpdData *)original);
1183 has_next = (original )?1:0;
1189 compare = g_utf8_collate(new->tag,original->tag);
1193 MpdData_real *n = (MpdData_real *) mpd_new_data_struct();
1194 n->type = MPD_DATA_TYPE_TAG;
1195 n->tag_type = new->tag_type;
1202 n->prev = original->prev;
1205 original->prev->next = n;
1207 n->first = original->first;
1209 if(n->prev == NULL){
1210 MpdData_real *fiter;
1211 for(fiter = n;fiter;fiter = fiter->next){
1216 self->_priv->data = mpd_data_get_first((MpdData *)original);
1218 path = gtk_tree_path_new();
1219 gtk_tree_path_append_index(path,i);
1220 iter.stamp = self->_priv->stamp;
1222 iter.user_data2 = GINT_TO_POINTER(i);
1224 gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
1225 gtk_tree_path_free(path);
1227 /* mark for insert */
1232 else if (compare > 0)
1234 MpdData_real *n = original->next;
1235 path = gtk_tree_path_new();
1236 gtk_tree_path_append_index(path,i);
1237 /* propegate change */
1240 gtk_tree_model_row_deleted(GTK_TREE_MODEL(self), path);
1241 gtk_tree_path_free(path);
1244 original = (MpdData_real *)mpd_data_delete_item((MpdData *)original);
1245 self->_priv->data = mpd_data_get_first((MpdData *)original);
1247 //original = original->next;
1248 has_next = (original )?1:0;
1252 original = original->next;
1253 has_next = (original )?1:0;
1259 /* add entries remaining in new */
1263 new_list = (MpdData_real *) self->_priv->data;
1264 while(new_list && new_list->next) new_list = new_list->next;
1266 new_list = (MpdData_real *)mpd_new_data_struct_append((MpdData*)new_list);
1267 new_list->type = MPD_DATA_TYPE_TAG;
1268 new_list->tag_type = new->tag_type;
1269 new_list->tag = new->tag;
1272 self->_priv->data = (MpdData *)new_list->first;
1273 if(self->_priv->data == NULL) g_warning("self->_priv->data == NULL");
1276 path = gtk_tree_path_new();
1277 gtk_tree_path_append_index(path,i);
1278 iter.stamp = self->_priv->stamp;
1279 iter.user_data = new_list;
1280 iter.user_data2 = GINT_TO_POINTER(i);
1282 gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
1283 gtk_tree_path_free(path);
1292 if(self->_priv->use_images){
1293 if(self->num_rows < old_list_size){
1294 for(i=self->num_rows;i< old_list_size; i++)
1297 if(self->_priv->images[i] != NULL) g_object_unref(self->_priv->images[i]);
1299 self->_priv->images = g_realloc(self->_priv->images, (self->num_rows)*sizeof(GdkPixbuf *));
1306 mpd_data_free(data2);
1308 g_assert(items == self->num_rows);
1312 signal last NONE (LONG)
1314 playtime_changed(self, gulong playtime)
1323 return self->_priv->playtime;
1327 get_pos( self, GtkTreeIter *iter)
1329 g_assert(iter->stamp == self->_priv->stamp);
1330 return GPOINTER_TO_INT(iter->user_data2);
1337 g_assert(self->_priv->data == NULL);
1338 self->_priv->use_images = FALSE;
1343 steal_mpd_data(self)
1347 int old_num_rows = self->num_rows;
1348 MpdData *data = self->_priv->data;
1349 while ( self->num_rows > 0 ) {
1350 path = gtk_tree_path_new();
1351 gtk_tree_path_append_index(path, self->num_rows - 1 );
1352 /* propegate change */
1353 gtk_tree_model_row_deleted(GTK_TREE_MODEL(self), path);
1354 gtk_tree_path_free(path);
1359 self->_priv->data = NULL;
1360 /* Free possible stored images */
1361 if(self->_priv->images && self->_priv->use_images)
1363 for(i=0;i< old_num_rows;i++)
1365 if(self->_priv->images[i])
1366 g_object_unref(self->_priv->images[i]);
1368 q_free(self->_priv->images);
1370 self->_priv->playtime = 0;
1371 self_playtime_changed(self, self->_priv->playtime);
1373 return mpd_data_get_first(data);
1376 /* Drag and stop handling */
1379 interface Gtk:Tree:Drag:Source
1381 gboolean row_draggable (Gtk:Tree:Drag:Source *drag_source, Gtk:Tree:Path *path)
1384 if(gtk_tree_model_get_iter(GTK_TREE_MODEL(drag_source), &iter, path))
1386 MpdData *data = iter.user_data;
1387 if(data->type == MPD_DATA_TYPE_SONG) {
1394 interface Gtk:Tree:Drag:Source
1396 gboolean drag_data_delete (Gtk:Tree:Drag:Source *drag_source, Gtk:Tree:Path *path)
1400 interface Gtk:Tree:Drag:Source
1402 gboolean drag_data_get (Gtk:Tree:Drag:Source *drag_source, Gtk:Tree:Path *path, Gtk:Selection:Data *selection_data)
1404 if(!gtk_tree_set_row_drag_data(selection_data,GTK_TREE_MODEL(drag_source), path)) {