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_COL_SONG_PRIORITY, /* priority */
71 MPDDATA_MODEL_N_COLUMNS
76 #define LOG_DOMAIN "MpdData.Model"
79 class Gmpc:MpdData:Model from G:Object
80 (interface Gtk:Tree:Sortable)
81 (interface Gtk:Tree:Drag:Source)
82 (interface Gtk:Tree:Model)
84 private gint stamp = {g_random_int()};
85 public GType types[MPDDATA_MODEL_N_COLUMNS];
86 private MpdData *data = NULL;
87 public gint num_rows = 0;
88 private GmpcPixbufLoaderAsync **images = NULL;
89 private gchar *req_artist = {NULL} destroywith g_free;
90 private GdkPixbuf *blank = {NULL} destroywith g_object_unref;
91 public gint icon_size = {cfg_get_single_value_as_int_with_default(config, "gmpc-mpddata-model", "icon-size", 32)};
94 private GtkSortType sort_order = GTK_SORT_ASCENDING;
95 private int sort_column = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID;
96 private int old_sort_column = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID;
97 public gboolean has_up = FALSE;
98 private gchar *markup = {cfg_get_single_value_as_string_with_default(config, "playlist", "browser_markup",DEFAULT_MARKUP_BROWSER)} destroywith g_free;
100 private gulong playtime = {0};
103 private gboolean use_images = TRUE;
105 property BOOLEAN has_up
107 blurb = "Show an 'up row",
108 default_value = FALSE,
114 self->types[MPDDATA_MODEL_COL_MPDSONG] = G_TYPE_POINTER;
115 self->types[MPDDATA_MODEL_COL_MARKUP] = G_TYPE_STRING;
116 self->types[MPDDATA_MODEL_COL_PLAYING] = G_TYPE_BOOLEAN;
117 self->types[MPDDATA_MODEL_COL_PLAYING_FONT_WEIGHT] = G_TYPE_INT;
118 self->types[MPDDATA_MODEL_COL_PATH] = G_TYPE_STRING;
119 self->types[MPDDATA_MODEL_COL_SONG_ARTIST] = G_TYPE_STRING;
120 self->types[MPDDATA_MODEL_COL_SONG_ALBUM] = G_TYPE_STRING;
121 self->types[MPDDATA_MODEL_COL_SONG_TITLE] = G_TYPE_STRING;
122 self->types[MPDDATA_MODEL_COL_SONG_TITLEFILE] = G_TYPE_STRING;
123 self->types[MPDDATA_MODEL_COL_SONG_TRACK] = G_TYPE_STRING;
124 self->types[MPDDATA_MODEL_COL_SONG_GENRE] = G_TYPE_STRING;
125 self->types[MPDDATA_MODEL_COL_SONG_NAME] = G_TYPE_STRING;
126 self->types[MPDDATA_MODEL_COL_SONG_COMPOSER] = G_TYPE_STRING;
127 self->types[MPDDATA_MODEL_COL_SONG_PERFORMER]= G_TYPE_STRING;
128 self->types[MPDDATA_MODEL_COL_SONG_DATE] = G_TYPE_STRING;
129 self->types[MPDDATA_MODEL_COL_SONG_LENGTH] = G_TYPE_INT;
130 self->types[MPDDATA_MODEL_COL_SONG_LENGTH_FORMAT] = G_TYPE_STRING;
131 self->types[MPDDATA_MODEL_COL_SONG_DISC] = G_TYPE_STRING;
132 self->types[MPDDATA_MODEL_COL_SONG_COMMENT] = G_TYPE_STRING;
133 self->types[MPDDATA_MODEL_COL_SONG_POS] = G_TYPE_INT;
134 self->types[MPDDATA_MODEL_COL_SONG_ALBUMARTIST] = G_TYPE_STRING;
135 self->types[MPDDATA_MODEL_COL_PATH_EXTENSION] = G_TYPE_STRING;
136 self->types[MPDDATA_MODEL_COL_PATH_DIRECTORY] = G_TYPE_STRING;
137 self->types[MPDDATA_MODEL_COL_SONG_ID] = G_TYPE_INT;
138 self->types[MPDDATA_MODEL_COL_ICON_ID] = G_TYPE_STRING;
139 self->types[MPDDATA_MODEL_TAG_TYPE] = G_TYPE_INT;
140 self->types[MPDDATA_MODEL_ROW_TYPE] = G_TYPE_INT;
141 self->types[MPDDATA_MODEL_USERDATA] = G_TYPE_POINTER;
142 self->types[MPDDATA_MODEL_META_DATA] = GDK_TYPE_PIXBUF;
143 self->types[MPDDATA_MODEL_COL_SONG_PRIORITY] = G_TYPE_INT;
146 self->_priv->blank = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, self->icon_size, self->icon_size);
147 gdk_pixbuf_fill(self->_priv->blank, 0x00000000);
152 Gmpc:MpdData:Model *new (void)
159 finalize (G:Object *obj)
162 Self *self = GMPC_MPDDATA_MODEL(obj);
164 if(self->_priv->data)
166 mpd_data_free(self->_priv->data);
167 self->_priv->data = NULL;
169 if(self->_priv->images && self->_priv->use_images)
171 for(i=0;i<self->num_rows;i++)
173 if(self->_priv->images[i]) {
174 g_object_unref(self->_priv->images[i]);
175 self->_priv->images[i] = NULL;
178 q_free(self->_priv->images);
179 /* q_free should do it, but force it */
180 self->_priv->images = NULL;
184 /* function implemented for the Gtk:Tree:Model interface */
185 interface Gtk:Tree:Model
186 private GtkTreeModelFlags
187 get_flags (Gtk:Tree:Model *self (check null type))
189 /* Here would be the implementation */
190 return (GtkTreeModelFlags)GTK_TREE_MODEL_LIST_ONLY;
194 interface Gtk:Tree:Model
196 gboolean iter_children(Gtk:Tree:Model *model, GtkTreeIter *iter, GtkTreeIter *parent)
198 Self *self = GMPC_MPDDATA_MODEL(model);
201 if(self->num_rows == 0)
203 /* Set iter to first item in list */
204 iter->stamp = self->_priv->stamp;
205 iter->user_data = self->_priv->data;
206 iter->user_data2 = GINT_TO_POINTER(0);
207 iter->user_data3 = NULL; /* unused */
212 * Unused, not known in the model directly?
214 interface Gtk:Tree:Model
216 gint get_n_columns(Gtk:Tree:Model *model)
218 return MPDDATA_MODEL_N_COLUMNS;
221 interface Gtk:Tree:Model
223 get_iter(Gtk:Tree:Model *model (check null type),
224 Gtk:Tree:Iter *iter (check null),
225 Gtk:Tree:Path *path (check null))
227 Self *self = GMPC_MPDDATA_MODEL(model);
228 MpdData_real *data = NULL;
230 const gint *indices = gtk_tree_path_get_indices(path);
231 gint depth = gtk_tree_path_get_depth(path);
232 gint n = indices[0]; /* the n-th top level row */
235 g_assert(depth == 1);
237 if (n >= self->num_rows || n < 0)
240 g_assert(n<self->num_rows);
241 /* If it is the first item in a has_up list, do nothing */
242 if(self->has_up && n == 0){
246 data = (MpdData_real *)self->_priv->data;
256 iter->stamp = self->_priv->stamp;
257 iter->user_data = data;
258 iter->user_data2 = GINT_TO_POINTER(n);
262 interface Gtk:Tree:Model
264 iter_next(Gtk:Tree:Model *model(check null type), GtkTreeIter *iter (check null))
266 Self *self = GMPC_MPDDATA_MODEL(model);
267 MpdData *data = iter->user_data;
268 int n = GPOINTER_TO_INT(iter->user_data2) ;
269 /* if the row is the last row in the list or bigger then the last row, return FALSE */
270 if(n >= self->num_rows)
274 /* if the row is the first row in the list, and it has a "go-up" entry,
275 * it can have a next entry
277 if(data == NULL && n == 0 && self->has_up == TRUE)
279 iter->user_data = (MpdData *)self->_priv->data;
280 iter->user_data2 = GINT_TO_POINTER(1);
281 if(iter->user_data == NULL)
285 if(mpd_data_get_next_real(data,FALSE) == NULL)
289 iter->user_data = (MpdData *)mpd_data_get_next_real(data,FALSE);
290 iter->user_data2 = GINT_TO_POINTER(n+1);
291 g_assert(iter->user_data != NULL);
295 interface Gtk:Tree:Model
297 iter_has_child(Gtk:Tree:Model *self (check null type), GtkTreeIter *iter)
301 interface Gtk:Tree:Model
303 iter_n_children(Gtk:Tree:Model *model (check null type), GtkTreeIter *iter)
305 Self *list = GMPC_MPDDATA_MODEL(model);
308 return list->num_rows;
311 interface Gtk:Tree:Model
313 iter_parent(Gtk:Tree:Model *model (check null type), Gtk:Tree:Iter *iter, Gtk:Tree:Iter *child)
319 cover_art_fetched(mpd_Song *song, MetaDataResult ret,MetaData *met,GtkTreeRowReference *ref)
324 GtkTreePath *path =gtk_tree_row_reference_get_path(ref);
325 GtkTreeModel *model = gtk_tree_row_reference_get_model(ref);
329 Self *self = GMPC_MPDDATA_MODEL(model);
332 g_assert(self->_priv->use_images == TRUE);
334 gtk_tree_model_get_iter(model, &iter, path);
335 n = GPOINTER_TO_INT(iter.user_data2) ;
337 if(self->_priv->images[n] == NULL){
338 self->_priv->images[n]= gmpc_pixbuf_loader_async_new();
339 gmpc_pixbuf_loader_async_set_rref(GMPC_PIXBUF_LOADER_ASYNC(self->_priv->images[n]), ref);
342 if(ret == META_DATA_AVAILABLE)
345 GdkPixbuf *pb = NULL;
346 if (met->content_type == META_DATA_CONTENT_RAW) {
347 MpdData_real *data = iter.user_data;
348 gmpc_pixbuf_loader_async_set_from_raw(GMPC_PIXBUF_LOADER_ASYNC(self->_priv->images[n]),
349 met->content, met->size, 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),
354 pb = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-no-cover", self->icon_size, 0,NULL);
355 gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n], pb);
359 else if(ret == META_DATA_UNAVAILABLE)
362 pb2 = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-no-cover", self->icon_size, 0,NULL);
363 gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n], pb2);
366 gtk_tree_model_row_changed(model,path, &iter);
367 gtk_tree_path_free(path);
370 if(ret == META_DATA_AVAILABLE || ret == META_DATA_UNAVAILABLE)
374 gtk_tree_row_reference_free(ref);
380 interface Gtk:Tree:Model
382 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))
384 Self *self = GMPC_MPDDATA_MODEL(model);
385 MpdData_real *data = iter->user_data;
386 int n = GPOINTER_TO_INT(iter->user_data2);
388 if(self->_priv->data == NULL)
389 g_warning("self->_priv->data == NULL");
390 /* set value to the correct type */
391 g_value_init(value, self->types[column]);
396 g_assert((n < self->num_rows || n == 0));
397 g_assert(!(data == NULL && n != 0));
400 if(data == NULL && n == 0)
404 case MPDDATA_MODEL_COL_ICON_ID:
405 g_value_set_string(value, GTK_STOCK_GO_UP);
407 case MPDDATA_MODEL_ROW_TYPE:
411 g_value_set_int(value, -1);
413 case MPDDATA_MODEL_COL_SONG_TITLE:
414 case MPDDATA_MODEL_COL_MARKUP:
416 g_value_set_string(value,"..");
425 if(column == MPDDATA_MODEL_META_DATA)
427 if(self->_priv->use_images && self->_priv->images)
429 if(self->_priv->images[n] == NULL)
432 GtkTreeRowReference *ref;
433 path = gtk_tree_model_get_path(GTK_TREE_MODEL(self), iter);
434 ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(self),path);
436 self->_priv->images[n]= gmpc_pixbuf_loader_async_new();
437 gmpc_pixbuf_loader_async_set_rref(GMPC_PIXBUF_LOADER_ASYNC(self->_priv->images[n]), ref);
438 if(data->type == MPD_DATA_TYPE_TAG && (data->tag_type == MPD_TAG_ITEM_ARTIST || data->tag_type == MPD_TAG_ITEM_ALBUM_ARTIST) )
441 GdkPixbuf *pb = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-loading-cover", self->icon_size, 0,NULL);
442 gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n], pb);
444 song = mpd_newSong();
445 song->artist = g_strdup(data->tag);
447 meta_data_get_path_callback(song, META_ARTIST_ART, (MetaDataCallback)self_cover_art_fetched, (gpointer)ref);
451 else if(data->type == MPD_DATA_TYPE_TAG && data->tag_type == MPD_TAG_ITEM_ALBUM && self->_priv->req_artist)
454 GdkPixbuf *pb = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-loading-cover", self->icon_size, 0,NULL);
455 gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n],pb);
457 song = mpd_newSong();
458 song->artist = g_strdup(self->_priv->req_artist);
459 song->albumartist = g_strdup(self->_priv->req_artist);
460 song->album = g_strdup(data->tag);
461 meta_data_get_path_callback(song, META_ALBUM_ART, (MetaDataCallback)self_cover_art_fetched, ref);
464 else if(data->type == MPD_DATA_TYPE_SONG&& data->song && data->song->album && (data->song->artist || self->_priv->req_artist))
466 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)
468 gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n],self->_priv->blank);
471 GdkPixbuf *pb = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-loading-cover", self->icon_size, 0,NULL);
472 gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n],pb);
474 song = mpd_newSong();
475 song->artist = g_strdup((data->song->artist)?data->song->artist:self->_priv->req_artist);
476 song->albumartist = g_strdup((data->song->albumartist)?data->song->albumartist:self->_priv->req_artist);
477 song->album = g_strdup(data->song->album);
478 meta_data_get_path_callback(song, META_ALBUM_ART, (MetaDataCallback)self_cover_art_fetched, ref);
484 GdkPixbuf *pb = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-no-cover", self->icon_size, 0,NULL);
485 gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n],pb);
488 gtk_tree_path_free(path);
490 g_value_set_object(value, gmpc_pixbuf_loader_async_get_pixbuf(self->_priv->images[n]));
492 g_value_set_object(value,NULL);
495 if(column == MPDDATA_MODEL_USERDATA)
497 g_value_set_pointer(value,data->userdata);
500 /* handle row type, this is independent of the row type */
501 if(column == MPDDATA_MODEL_ROW_TYPE)
503 g_value_set_int(value, data->type);
506 if (data->type == MPD_DATA_TYPE_TAG) {
509 case MPDDATA_MODEL_COL_ICON_ID:
510 switch(data->tag_type)
512 case MPD_TAG_ITEM_ALBUM:
513 g_value_set_string(value, "media-album");
515 case MPD_TAG_ITEM_ARTIST:
516 g_value_set_string(value, "media-artist");
518 case MPD_TAG_ITEM_GENRE:
519 g_value_set_string(value, "media-genre");
521 case MPD_TAG_ITEM_TRACK:
522 g_value_set_string(value, "media-num-tracks");
525 g_value_set_string(value, "media-tag");
528 case MPDDATA_MODEL_COL_SONG_TITLE:
529 case MPDDATA_MODEL_COL_MARKUP:
531 g_value_set_string(value, data->tag);
534 case MPDDATA_MODEL_COL_PATH:
535 g_value_set_string(value, data->tag);
537 case MPDDATA_MODEL_TAG_TYPE:
538 g_value_set_int(value, data->tag_type);
543 } else if(data->type == MPD_DATA_TYPE_DIRECTORY) {
546 case MPDDATA_MODEL_COL_ICON_ID:
547 g_value_set_string(value, GTK_STOCK_OPEN);
549 case MPDDATA_MODEL_COL_SONG_TITLE:
550 case MPDDATA_MODEL_COL_SONG_TITLEFILE:
551 case MPDDATA_MODEL_COL_MARKUP:
553 gchar *basename = g_path_get_basename(data->directory);
554 g_value_set_string(value, basename);
558 case MPDDATA_MODEL_COL_PATH:
559 g_value_set_string(value, data->directory);
565 else if(data->type == MPD_DATA_TYPE_PLAYLIST)
569 case MPDDATA_MODEL_COL_ICON_ID:
570 g_value_set_string(value, "media-playlist");
572 case MPDDATA_MODEL_COL_SONG_TITLE:
573 case MPDDATA_MODEL_COL_SONG_TITLEFILE:
574 case MPDDATA_MODEL_COL_MARKUP:
576 gchar *basename = g_path_get_basename(data->playlist->path);
577 g_value_set_string(value, basename);
581 case MPDDATA_MODEL_COL_PATH:
582 g_value_set_string(value, data->playlist->path);
588 else if(data->type == MPD_DATA_TYPE_SONG)
590 mpd_Song *song = data->song;
592 case MPDDATA_MODEL_COL_MPDSONG:
593 g_value_set_pointer(value, song);
595 case MPDDATA_MODEL_COL_PLAYING:
596 g_value_set_boolean(value, FALSE);
598 case MPDDATA_MODEL_COL_PLAYING_FONT_WEIGHT:
599 g_value_set_int(value, PANGO_WEIGHT_NORMAL);
601 case MPDDATA_MODEL_COL_MARKUP:
603 /* we want to go cache this stuff */
605 mpd_song_markup(buffer, 1024, GMPC_MPDDATA_MODEL(model)->_priv->markup, song);
606 g_value_set_string(value, buffer);
609 case MPDDATA_MODEL_COL_PATH:
610 g_value_set_string(value, song->file);
612 case MPDDATA_MODEL_COL_PATH_EXTENSION:
614 int j = strlen(song->file);
615 for(;j>0&&song->file[j] != '.';j--);
616 g_value_set_string(value, &(song->file)[j+1]);
619 case MPDDATA_MODEL_COL_PATH_DIRECTORY:
621 gchar *dir = g_path_get_dirname(song->file);
622 g_value_set_string(value, dir);
626 case MPDDATA_MODEL_COL_SONG_ARTIST:
627 g_value_set_string(value, song->artist);
629 case MPDDATA_MODEL_COL_SONG_ALBUM:
630 g_value_set_string(value, song->album);
632 case MPDDATA_MODEL_COL_SONG_TITLE:
633 /* If there is a song available use that, else use the filename */
635 g_value_set_string(value, song->title);
636 } else if (song->name) {
637 g_value_set_string(value, song->name);
639 /* Use the markup stuff, this makes sure it gets processed equaly */
641 mpd_song_markup(buffer, 1024, "%shortfile%", song);
642 g_value_set_string(value, buffer);
645 case MPDDATA_MODEL_COL_SONG_TITLEFILE:
647 gchar *path = g_path_get_basename(song->file);
648 g_value_set_string(value, path);
652 case MPDDATA_MODEL_COL_SONG_GENRE:
653 g_value_set_string(value, song->genre);
655 case MPDDATA_MODEL_COL_SONG_TRACK:
656 g_value_set_string(value, song->track);
658 case MPDDATA_MODEL_COL_SONG_NAME:
659 g_value_set_string(value, song->name);
661 case MPDDATA_MODEL_COL_SONG_COMPOSER:
662 g_value_set_string(value, song->composer);
664 case MPDDATA_MODEL_COL_SONG_PERFORMER:
665 g_value_set_string(value, song->performer);
667 case MPDDATA_MODEL_COL_SONG_DATE:
668 g_value_set_string(value, song->date);
670 case MPDDATA_MODEL_COL_SONG_LENGTH:
671 g_value_set_int(value, song->time);
673 case MPDDATA_MODEL_COL_SONG_LENGTH_FORMAT:
675 if(song->time >= 0) {
676 gchar *strdata = g_strdup_printf("%02i:%02i",
677 song->time/60, song->time%60);
678 g_value_set_string(value, strdata);
681 g_value_set_string(value, "n/a");
685 case MPDDATA_MODEL_COL_SONG_DISC:
686 g_value_set_string(value, song->disc);
688 case MPDDATA_MODEL_COL_SONG_COMMENT:
689 g_value_set_string(value, song->comment);
691 case MPDDATA_MODEL_COL_SONG_POS:
692 g_value_set_int(value, song->pos+1);
694 case MPDDATA_MODEL_COL_SONG_ALBUMARTIST:
695 g_value_set_string(value, song->albumartist);
697 case MPDDATA_MODEL_COL_SONG_ID:
698 g_value_set_int(value, song->id);
701 case MPDDATA_MODEL_COL_ICON_ID:
702 if (strstr(song->file, "://")) {
703 g_value_set_string(value, "media-stream");
705 g_value_set_string(value, "media-audiofile");
708 case MPDDATA_MODEL_COL_SONG_PRIORITY:
709 g_value_set_int(value, song->priority);
717 interface Gtk:Tree:Model
719 iter_nth_child(Gtk:Tree:Model *model(check null type), GtkTreeIter *iter (check null), GtkTreeIter *parent, gint n (check >=0))
721 Self *self = GMPC_MPDDATA_MODEL(model);
723 MpdData_real *data = NULL;
726 if (n >= self->num_rows || n < 0)
729 data = (MpdData_real *)self->_priv->data;
735 data = data->next;//mpd_data_get_next_real(data, FALSE);
736 g_assert(data != NULL);
739 if(self->has_up && n == 0)
742 iter->stamp = self->_priv->stamp;
743 iter->user_data = data;
744 iter->user_data2 = GINT_TO_POINTER(n);
748 interface Gtk:Tree:Model
749 private GtkTreePath *
750 get_path(Gtk:Tree:Model *self (check null type), GtkTreeIter *iter (check null))
752 GtkTreePath *path = NULL;
753 path = gtk_tree_path_new();
754 gtk_tree_path_append_index(path, GPOINTER_TO_INT(iter->user_data2));
758 interface Gtk:Tree:Model
760 get_column_type(Gtk:Tree:Model *model(check null type), gint ind (check >= 0))
762 Self *self = GMPC_MPDDATA_MODEL(model);
763 return self->types[ind];
767 set_request_artist(self, const char *artist)
770 GtkTreePath *path = NULL;
772 if(self->_priv->req_artist)
773 q_free(self->_priv->req_artist);
774 self->_priv->req_artist = (artist != NULL && artist[0] != '\0')?g_strdup(artist):NULL;
775 /* Free possible stored images */
776 if(self->_priv->images && self->_priv->use_images)
778 MpdData *data2 = mpd_data_get_first(self->_priv->data);
779 for(i=0;i<self->num_rows;i++)
781 if(self->_priv->images[i]){
782 g_object_unref(self->_priv->images[i]);
783 self->_priv->images[i] = NULL;
785 /* Update the view */
786 path = gtk_tree_path_new();
787 gtk_tree_path_append_index(path,i);
788 iter.stamp = self->_priv->stamp;
789 iter.user_data = data2;
790 iter.user_data2 = GINT_TO_POINTER(i);
791 /* propegate change */
792 gtk_tree_model_row_changed(GTK_TREE_MODEL(self), path, &iter);
793 gtk_tree_path_free(path);
795 data2 = mpd_data_get_next_real(data2,FALSE);
801 get_request_artist(self)
803 return self->_priv->req_artist;
806 long unsigned set_mpd_data(self, MpdData *data2)
809 long unsigned retval = 0;
811 GtkTreePath *path = NULL;
812 int old_num_rows = self->num_rows;
813 /* Do some cleanup, like removing rows, and so */
814 /* loop and remove */
815 while ( self->num_rows > 0 ) {
816 path = gtk_tree_path_new();
817 gtk_tree_path_append_index(path, self->num_rows - 1 );
818 /* propegate change */
819 gtk_tree_model_row_deleted(GTK_TREE_MODEL(self), path);
820 gtk_tree_path_free(path);
826 * Free if there is a list and set it to NULL
828 if(self->_priv->data){
829 mpd_data_free(mpd_data_get_first(self->_priv->data));
831 self->_priv->data = NULL;
833 if(self->num_rows != 0)
834 g_log(LOG_DOMAIN, G_LOG_LEVEL_WARNING,"not every row cleared %i\n",self->num_rows);
836 /* Free possible stored images */
837 if(self->_priv->images && self->_priv->use_images)
839 for(i=0;i< old_num_rows;i++)
841 if(self->_priv->images[i])
842 g_object_unref(self->_priv->images[i]);
844 q_free(self->_priv->images);
848 self->_priv->playtime = 0;
849 self_playtime_changed(self, self->_priv->playtime);
853 self->_priv->data = mpd_data_get_first(data2);
855 if(self->_priv->sort_column == MPDDATA_MODEL_COL_ICON_ID)
857 self->_priv->data = misc_sort_mpddata_by_album_disc_track(self->_priv->data);
859 else if ( self->_priv->sort_column != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID )
861 self->_priv->data = misc_sort_mpddata(self->_priv->data,(GCompareDataFunc)self_sort_func,self );
865 path = gtk_tree_path_new();
866 gtk_tree_path_append_index(path, self->num_rows);
867 iter.stamp = self->_priv->stamp;
868 iter.user_data = NULL;
869 iter.user_data2 = GINT_TO_POINTER(self->num_rows);
872 /* propegate change */
873 gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
874 gtk_tree_path_free(path);
876 for(data2 = mpd_data_get_first(self->_priv->data);data2; data2 = mpd_data_get_next_real(data2,FALSE)) {
877 path = gtk_tree_path_new();
878 gtk_tree_path_append_index(path,self->num_rows);
879 iter.stamp = self->_priv->stamp;
880 iter.user_data = data2;
881 iter.user_data2 = GINT_TO_POINTER(self->num_rows);
884 /* propegate change */
885 gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
886 gtk_tree_path_free(path);
887 if(data2->type == MPD_DATA_TYPE_SONG)
889 if(data2->song && data2->song->time > 0)
890 retval+= data2->song->time;
893 if(self->_priv->use_images)
895 self->_priv->images = g_malloc0((self->num_rows)*sizeof(*(self->_priv->images)));
898 self->_priv->playtime = retval;
899 self_playtime_changed(self, self->_priv->playtime);
904 interface Gtk:Tree:Sortable
906 gboolean get_sort_column_id (Gtk:Tree:Sortable *model , gint *sort_column_id, GtkSortType *order)
908 Self *self = GMPC_MPDDATA_MODEL(model);
910 *sort_column_id = self->_priv->sort_column;
912 *order = self->_priv->sort_order;
916 interface Gtk:Tree:Sortable
918 void set_sort_column_id (Gtk:Tree:Sortable *model , gint sort_column_id, GtkSortType order)
920 Self *self = GMPC_MPDDATA_MODEL(model);
921 gint old_col = self->_priv->sort_column;
922 GtkSortType old_ord = self->_priv->sort_order;
923 self->_priv->sort_column = sort_column_id;
924 self->_priv->sort_order = order;
926 if(old_col != sort_column_id || old_ord != order)
927 gtk_tree_sortable_sort_column_changed(model);
928 self->_priv->old_sort_column = self->_priv->sort_column;
932 interface Gtk:Tree:Sortable
935 has_default_sort_func(Gtk:Tree:Sortable *model)
940 int sort_func(gpointer ppaa, gpointer ppbb, Self *self)
942 MpdData_real *a = *(MpdData_real **)ppaa;
943 MpdData_real *b = *(MpdData_real **)ppbb;
944 int fact = (self->_priv->sort_order == GTK_SORT_ASCENDING)?-1:1;
946 if(a->type != b->type )
948 int val = a->type - b->type;
951 else if(a->type == b->type)
955 GValue va = {0,},vb = {0,};
956 /* Get values from tree view, so we don't have to replicate code to sort the right entries */
958 iter.user_data2 = GPOINTER_TO_INT(0);
959 gtk_tree_model_get_value(GTK_TREE_MODEL(self), &iter, self->_priv->sort_column, &va);
961 gtk_tree_model_get_value(GTK_TREE_MODEL(self), &iter, self->_priv->sort_column, &vb);
962 /* if the type is a directory or a tag, always sort on the title column */
963 if(a->type == MPD_DATA_TYPE_DIRECTORY || b->type == MPD_DATA_TYPE_TAG)
966 ca = g_value_get_string(&va);
967 cb = g_value_get_string(&vb);
970 aa = g_utf8_strdown(ca, -1);
971 bb = g_utf8_strdown(cb, -1);
972 val = g_utf8_collate(aa,bb);
976 val = (ca == NULL)?((cb==NULL)?0:-1):1;
983 if(self->_priv->sort_column == MPDDATA_MODEL_COL_SONG_TRACK)
985 const char *ca = g_value_get_string(&va);
986 const char *cb = g_value_get_string(&vb);
987 gint ca1 = (ca)?atoi(ca):0;
988 gint cb1 = (cb)?atoi(cb):0;
991 return fact*(ca1-cb1);
993 else if(self->types[self->_priv->sort_column] == G_TYPE_INT)
995 val = g_value_get_int(&va) - g_value_get_int(&vb);
997 else if (self->types[self->_priv->sort_column] == G_TYPE_STRING)
999 const char *ca = g_value_get_string(&va);
1000 const char *cb = g_value_get_string(&vb);
1003 aa = g_utf8_normalize(ca, -1,G_NORMALIZE_ALL);
1004 bb = g_utf8_normalize(cb, -1,G_NORMALIZE_ALL);
1005 val = g_utf8_collate(aa,bb);
1009 val = (ca == NULL)?((cb==NULL)?0:-1):1;
1014 a->type == MPD_DATA_TYPE_SONG &&
1015 b->type == MPD_DATA_TYPE_SONG &&
1019 val = strcmp (a->song->file, b->song->file);
1029 interface Gtk:Tree:Sortable
1032 sort_column_changed(Gtk:Tree:Sortable *model)
1034 Self *self = GMPC_MPDDATA_MODEL(model);
1035 if(!self->_priv->data || !(((MpdData_real *)self->_priv->data)->next))
1037 if(self->_priv->sort_column == MPDDATA_MODEL_COL_ICON_ID)
1039 self->_priv->data = misc_sort_mpddata_by_album_disc_track(self->_priv->data);
1041 else if(self->_priv->sort_column != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID)
1043 self->_priv->data = misc_sort_mpddata(self->_priv->data,(GCompareDataFunc)self_sort_func,self );
1045 self->_priv->old_sort_column = self->_priv->sort_column;
1047 /* tell the view that rows have changed */
1050 GtkTreePath *path = NULL;
1052 MpdData *data2 = mpd_data_get_first(self->_priv->data);
1053 if(self->has_up) i = 1;
1054 for(;data2; data2 = mpd_data_get_next_real(data2,FALSE))
1056 path = gtk_tree_path_new();
1057 gtk_tree_path_append_index(path,i);
1058 iter.stamp = self->_priv->stamp;
1059 iter.user_data = data2;
1060 iter.user_data2 = GINT_TO_POINTER(i);
1062 /* propegate change */
1063 gtk_tree_model_row_changed(GTK_TREE_MODEL(self), path, &iter);
1064 gtk_tree_path_free(path);
1072 test_sort_func(gpointer ppaa, gpointer ppbb, GmpcMpdDataModel *self)
1074 MpdData_real *a = *(MpdData_real **)ppaa;
1075 MpdData_real *b = *(MpdData_real **)ppbb;
1076 if(a->type == MPD_DATA_TYPE_TAG && b->type == MPD_DATA_TYPE_TAG)
1078 if(a->tag_type != b->tag_type)
1079 return a->tag_type - b->tag_type;
1080 if(a->tag== NULL && b->tag != NULL)
1082 else if(b->tag == NULL && a->tag != NULL)
1084 else if (a->tag && b->tag)
1087 if(a->tag && b->tag) {
1089 sa = g_utf8_strdown(a->tag, -1);
1090 sb = g_utf8_strdown(b->tag, -1);
1091 val = g_utf8_collate(sa,sb);
1095 val = (a == NULL)?((b==NULL)?0:-1):1;
1100 return a->type - b->type;
1104 * For now this assumes tag list only.
1107 long unsigned set_mpd_data_slow (self, MpdData *data2)
1109 MpdData_real *new_list = NULL;
1112 MpdData_real *original = (MpdData_real *)mpd_data_get_first(self->_priv->data);
1113 MpdData_real *new = (MpdData_real *) data2;
1114 GtkTreePath *path =NULL;
1116 int old_list_size = self->num_rows;
1118 /* sort it identical */
1119 data2 = misc_sort_mpddata(data2,(GCompareDataFunc)self_test_sort_func,self );
1120 new = (MpdData_real *)data2;
1123 MpdData_real *n = new->first;
1131 /* Free possible stored images */
1133 * TODO: Don't free the list, but grow it to MAX(items, self->num_rows);
1134 * Then after updating, resize it to new size
1136 /* Grow the list when needed */
1138 if(self->_priv->use_images)
1140 /* Clean them, they might not be in the right place anymore */
1141 for(i=0;i< old_list_size; i++)
1143 if(self->_priv->images[i] != NULL) g_object_unref(self->_priv->images[i]);
1144 self->_priv->images[i] = NULL;
1146 if(items > self->num_rows)
1148 self->_priv->images = g_realloc(self->_priv->images, items*sizeof(GdkPixbuf *));
1149 /* set the new ones to NUll */
1150 for(i=self->num_rows;i<items;i++)
1152 self->_priv->images[i] = NULL;
1162 if(self->_priv->data && original == NULL) g_warning("weirdness\n");
1163 if(self->_priv->data)
1171 if(self->num_rows > items)
1173 MpdData_real *n = original->next;
1174 /* remove current row */
1177 path = gtk_tree_path_new();
1178 gtk_tree_path_append_index(path,i);
1180 /* propegate change */
1181 gtk_tree_model_row_deleted(GTK_TREE_MODEL(self), path);
1182 gtk_tree_path_free(path);
1184 original =(MpdData_real *) mpd_data_delete_item((MpdData *)original);
1185 self->_priv->data = mpd_data_get_first((MpdData *)original);
1188 has_next = (original )?1:0;
1194 compare = g_utf8_collate(new->tag,original->tag);
1198 MpdData_real *n = (MpdData_real *) mpd_new_data_struct();
1199 n->type = MPD_DATA_TYPE_TAG;
1200 n->tag_type = new->tag_type;
1207 n->prev = original->prev;
1210 original->prev->next = n;
1212 n->first = original->first;
1214 if(n->prev == NULL){
1215 MpdData_real *fiter;
1216 for(fiter = n;fiter;fiter = fiter->next){
1221 self->_priv->data = mpd_data_get_first((MpdData *)original);
1223 path = gtk_tree_path_new();
1224 gtk_tree_path_append_index(path,i);
1225 iter.stamp = self->_priv->stamp;
1227 iter.user_data2 = GINT_TO_POINTER(i);
1229 gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
1230 gtk_tree_path_free(path);
1232 /* mark for insert */
1237 else if (compare > 0)
1239 MpdData_real *n = original->next;
1240 path = gtk_tree_path_new();
1241 gtk_tree_path_append_index(path,i);
1242 /* propegate change */
1245 gtk_tree_model_row_deleted(GTK_TREE_MODEL(self), path);
1246 gtk_tree_path_free(path);
1249 original = (MpdData_real *)mpd_data_delete_item((MpdData *)original);
1250 self->_priv->data = mpd_data_get_first((MpdData *)original);
1252 //original = original->next;
1253 has_next = (original )?1:0;
1257 original = original->next;
1258 has_next = (original )?1:0;
1264 /* add entries remaining in new */
1268 new_list = (MpdData_real *) self->_priv->data;
1269 while(new_list && new_list->next) new_list = new_list->next;
1271 new_list = (MpdData_real *)mpd_new_data_struct_append((MpdData*)new_list);
1272 new_list->type = MPD_DATA_TYPE_TAG;
1273 new_list->tag_type = new->tag_type;
1274 new_list->tag = new->tag;
1277 self->_priv->data = (MpdData *)new_list->first;
1278 if(self->_priv->data == NULL) g_warning("self->_priv->data == NULL");
1281 path = gtk_tree_path_new();
1282 gtk_tree_path_append_index(path,i);
1283 iter.stamp = self->_priv->stamp;
1284 iter.user_data = new_list;
1285 iter.user_data2 = GINT_TO_POINTER(i);
1287 gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
1288 gtk_tree_path_free(path);
1297 if(self->_priv->use_images){
1298 if(self->num_rows < old_list_size){
1299 for(i=self->num_rows;i< old_list_size; i++)
1302 if(self->_priv->images[i] != NULL) g_object_unref(self->_priv->images[i]);
1304 self->_priv->images = g_realloc(self->_priv->images, (self->num_rows)*sizeof(GdkPixbuf *));
1311 mpd_data_free(data2);
1313 g_assert(items == self->num_rows);
1317 signal last NONE (LONG)
1319 playtime_changed(self, gulong playtime)
1328 return self->_priv->playtime;
1332 get_pos( self, GtkTreeIter *iter)
1334 g_assert(iter->stamp == self->_priv->stamp);
1335 return GPOINTER_TO_INT(iter->user_data2);
1342 g_assert(self->_priv->data == NULL);
1343 self->_priv->use_images = FALSE;
1348 steal_mpd_data(self)
1352 int old_num_rows = self->num_rows;
1353 MpdData *data = self->_priv->data;
1354 while ( self->num_rows > 0 ) {
1355 path = gtk_tree_path_new();
1356 gtk_tree_path_append_index(path, self->num_rows - 1 );
1357 /* propegate change */
1358 gtk_tree_model_row_deleted(GTK_TREE_MODEL(self), path);
1359 gtk_tree_path_free(path);
1364 self->_priv->data = NULL;
1365 /* Free possible stored images */
1366 if(self->_priv->images && self->_priv->use_images)
1368 for(i=0;i< old_num_rows;i++)
1370 if(self->_priv->images[i])
1371 g_object_unref(self->_priv->images[i]);
1373 q_free(self->_priv->images);
1375 self->_priv->playtime = 0;
1376 self_playtime_changed(self, self->_priv->playtime);
1378 return mpd_data_get_first(data);
1381 /* Drag and stop handling */
1384 interface Gtk:Tree:Drag:Source
1386 gboolean row_draggable (Gtk:Tree:Drag:Source *drag_source, Gtk:Tree:Path *path)
1389 if(gtk_tree_model_get_iter(GTK_TREE_MODEL(drag_source), &iter, path))
1391 MpdData *data = iter.user_data;
1392 if(data->type == MPD_DATA_TYPE_SONG) {
1399 interface Gtk:Tree:Drag:Source
1401 gboolean drag_data_delete (Gtk:Tree:Drag:Source *drag_source, Gtk:Tree:Path *path)
1405 interface Gtk:Tree:Drag:Source
1407 gboolean drag_data_get (Gtk:Tree:Drag:Source *drag_source, Gtk:Tree:Path *path, Gtk:Selection:Data *selection_data)
1409 if(!gtk_tree_set_row_drag_data(selection_data,GTK_TREE_MODEL(drag_source), path)) {