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_COL_SONG_RATING, /* rating */
67 MPDDATA_MODEL_TAG_TYPE, /* tag type */
68 MPDDATA_MODEL_ROW_TYPE, /* type of the row */
69 MPDDATA_MODEL_META_DATA, /* metadata */
70 MPDDATA_MODEL_USERDATA,
71 MPDDATA_MODEL_COL_SONG_PRIORITY, /* priority */
72 MPDDATA_MODEL_N_COLUMNS
77 #define LOG_DOMAIN "MpdData.Model"
80 class Gmpc:MpdData:Model from G:Object
81 (interface Gtk:Tree:Sortable)
82 (interface Gtk:Tree:Drag:Source)
83 (interface Gtk:Tree:Model)
85 private gint stamp = {g_random_int()};
86 public GType types[MPDDATA_MODEL_N_COLUMNS];
87 private MpdData *data = NULL;
88 public gint num_rows = 0;
89 private GmpcPixbufLoaderAsync **images = NULL;
90 private gchar *req_artist = {NULL} destroywith g_free;
91 private GdkPixbuf *blank = {NULL} destroywith g_object_unref;
92 public gint icon_size = {cfg_get_single_value_as_int_with_default(config, "gmpc-mpddata-model", "icon-size", 32)};
95 private GtkSortType sort_order = GTK_SORT_ASCENDING;
96 private int sort_column = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID;
97 private int old_sort_column = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID;
98 public gboolean has_up = FALSE;
99 private gchar *markup = {cfg_get_single_value_as_string_with_default(config, "playlist", "browser_markup",DEFAULT_MARKUP_BROWSER)} destroywith g_free;
101 private gulong playtime = {0};
104 private gboolean use_images = TRUE;
106 property BOOLEAN has_up
108 blurb = "Show an 'up row",
109 default_value = FALSE,
115 self->types[MPDDATA_MODEL_COL_MPDSONG] = G_TYPE_POINTER;
116 self->types[MPDDATA_MODEL_COL_MARKUP] = G_TYPE_STRING;
117 self->types[MPDDATA_MODEL_COL_PLAYING] = G_TYPE_BOOLEAN;
118 self->types[MPDDATA_MODEL_COL_PLAYING_FONT_WEIGHT] = G_TYPE_INT;
119 self->types[MPDDATA_MODEL_COL_PATH] = G_TYPE_STRING;
120 self->types[MPDDATA_MODEL_COL_SONG_ARTIST] = G_TYPE_STRING;
121 self->types[MPDDATA_MODEL_COL_SONG_ALBUM] = G_TYPE_STRING;
122 self->types[MPDDATA_MODEL_COL_SONG_TITLE] = G_TYPE_STRING;
123 self->types[MPDDATA_MODEL_COL_SONG_TITLEFILE] = G_TYPE_STRING;
124 self->types[MPDDATA_MODEL_COL_SONG_TRACK] = G_TYPE_STRING;
125 self->types[MPDDATA_MODEL_COL_SONG_GENRE] = G_TYPE_STRING;
126 self->types[MPDDATA_MODEL_COL_SONG_NAME] = G_TYPE_STRING;
127 self->types[MPDDATA_MODEL_COL_SONG_COMPOSER] = G_TYPE_STRING;
128 self->types[MPDDATA_MODEL_COL_SONG_PERFORMER]= G_TYPE_STRING;
129 self->types[MPDDATA_MODEL_COL_SONG_DATE] = G_TYPE_STRING;
130 self->types[MPDDATA_MODEL_COL_SONG_LENGTH] = G_TYPE_INT;
131 self->types[MPDDATA_MODEL_COL_SONG_RATING] = G_TYPE_INT;
132 self->types[MPDDATA_MODEL_COL_SONG_LENGTH_FORMAT] = G_TYPE_STRING;
133 self->types[MPDDATA_MODEL_COL_SONG_DISC] = G_TYPE_STRING;
134 self->types[MPDDATA_MODEL_COL_SONG_COMMENT] = G_TYPE_STRING;
135 self->types[MPDDATA_MODEL_COL_SONG_POS] = G_TYPE_INT;
136 self->types[MPDDATA_MODEL_COL_SONG_ALBUMARTIST] = G_TYPE_STRING;
137 self->types[MPDDATA_MODEL_COL_PATH_EXTENSION] = G_TYPE_STRING;
138 self->types[MPDDATA_MODEL_COL_PATH_DIRECTORY] = G_TYPE_STRING;
139 self->types[MPDDATA_MODEL_COL_SONG_ID] = G_TYPE_INT;
140 self->types[MPDDATA_MODEL_COL_ICON_ID] = G_TYPE_STRING;
141 self->types[MPDDATA_MODEL_TAG_TYPE] = G_TYPE_INT;
142 self->types[MPDDATA_MODEL_ROW_TYPE] = G_TYPE_INT;
143 self->types[MPDDATA_MODEL_USERDATA] = G_TYPE_POINTER;
144 self->types[MPDDATA_MODEL_META_DATA] = GDK_TYPE_PIXBUF;
145 self->types[MPDDATA_MODEL_COL_SONG_PRIORITY] = G_TYPE_INT;
148 self->_priv->blank = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, self->icon_size, self->icon_size);
149 gdk_pixbuf_fill(self->_priv->blank, 0x00000000);
154 Gmpc:MpdData:Model *new (void)
161 finalize (G:Object *obj)
164 Self *self = GMPC_MPDDATA_MODEL(obj);
166 if(self->_priv->data)
168 mpd_data_free(self->_priv->data);
169 self->_priv->data = NULL;
171 if(self->_priv->images && self->_priv->use_images)
173 for(i=0;i<self->num_rows;i++)
175 if(self->_priv->images[i]) {
176 g_object_unref(self->_priv->images[i]);
177 self->_priv->images[i] = NULL;
180 q_free(self->_priv->images);
181 /* q_free should do it, but force it */
182 self->_priv->images = NULL;
187 public void reset_rating(self)
189 MpdData_real *data = (MpdData_real *)self->_priv->data;
191 for(data = data->first; data != NULL ; data= data->next) {
192 if(data->type == MPD_DATA_TYPE_SONG && data->song != NULL) {
193 GtkTreePath *path = gtk_tree_path_new();
195 data->song->rating = -1;
196 /* Update the view */
197 gtk_tree_path_append_index(path,i);
198 iter.stamp = self->_priv->stamp;
199 iter.user_data = data;
200 iter.user_data2 = GINT_TO_POINTER(i);
201 /* propegate change */
202 gtk_tree_model_row_changed(GTK_TREE_MODEL(self), path, &iter);
203 gtk_tree_path_free(path);
208 /* function implemented for the Gtk:Tree:Model interface */
209 interface Gtk:Tree:Model
210 private GtkTreeModelFlags
211 get_flags (Gtk:Tree:Model *self (check null type))
213 /* Here would be the implementation */
214 return (GtkTreeModelFlags)GTK_TREE_MODEL_LIST_ONLY;
218 interface Gtk:Tree:Model
220 gboolean iter_children(Gtk:Tree:Model *model, GtkTreeIter *iter, GtkTreeIter *parent)
222 Self *self = GMPC_MPDDATA_MODEL(model);
225 if(self->num_rows == 0)
227 /* Set iter to first item in list */
228 iter->stamp = self->_priv->stamp;
229 iter->user_data = self->_priv->data;
230 iter->user_data2 = GINT_TO_POINTER(0);
231 iter->user_data3 = NULL; /* unused */
236 * Unused, not known in the model directly?
238 interface Gtk:Tree:Model
240 gint get_n_columns(Gtk:Tree:Model *model)
242 return MPDDATA_MODEL_N_COLUMNS;
245 interface Gtk:Tree:Model
247 get_iter(Gtk:Tree:Model *model (check null type),
248 Gtk:Tree:Iter *iter (check null),
249 Gtk:Tree:Path *path (check null))
251 Self *self = GMPC_MPDDATA_MODEL(model);
252 MpdData_real *data = NULL;
254 const gint *indices = gtk_tree_path_get_indices(path);
255 gint depth = gtk_tree_path_get_depth(path);
256 gint n = indices[0]; /* the n-th top level row */
259 g_assert(depth == 1);
261 if (n >= self->num_rows || n < 0)
264 g_assert(n<self->num_rows);
265 /* If it is the first item in a has_up list, do nothing */
266 if(self->has_up && n == 0){
270 data = (MpdData_real *)self->_priv->data;
280 iter->stamp = self->_priv->stamp;
281 iter->user_data = data;
282 iter->user_data2 = GINT_TO_POINTER(n);
286 interface Gtk:Tree:Model
288 iter_next(Gtk:Tree:Model *model(check null type), GtkTreeIter *iter (check null))
290 Self *self = GMPC_MPDDATA_MODEL(model);
291 MpdData *data = iter->user_data;
292 int n = GPOINTER_TO_INT(iter->user_data2) ;
293 /* if the row is the last row in the list or bigger then the last row, return FALSE */
294 if(n >= self->num_rows)
298 /* if the row is the first row in the list, and it has a "go-up" entry,
299 * it can have a next entry
301 if(data == NULL && n == 0 && self->has_up == TRUE)
303 iter->user_data = (MpdData *)self->_priv->data;
304 iter->user_data2 = GINT_TO_POINTER(1);
305 if(iter->user_data == NULL)
309 if(mpd_data_get_next_real(data,FALSE) == NULL)
313 iter->user_data = (MpdData *)mpd_data_get_next_real(data,FALSE);
314 iter->user_data2 = GINT_TO_POINTER(n+1);
315 g_assert(iter->user_data != NULL);
319 interface Gtk:Tree:Model
321 iter_has_child(Gtk:Tree:Model *self (check null type), GtkTreeIter *iter)
325 interface Gtk:Tree:Model
327 iter_n_children(Gtk:Tree:Model *model (check null type), GtkTreeIter *iter)
329 Self *list = GMPC_MPDDATA_MODEL(model);
332 return list->num_rows;
335 interface Gtk:Tree:Model
337 iter_parent(Gtk:Tree:Model *model (check null type), Gtk:Tree:Iter *iter, Gtk:Tree:Iter *child)
343 cover_art_fetched(mpd_Song *song, MetaDataResult ret,MetaData *met,GtkTreeRowReference *ref)
348 GtkTreePath *path =gtk_tree_row_reference_get_path(ref);
349 GtkTreeModel *model = gtk_tree_row_reference_get_model(ref);
353 Self *self = GMPC_MPDDATA_MODEL(model);
356 g_assert(self->_priv->use_images == TRUE);
358 gtk_tree_model_get_iter(model, &iter, path);
359 n = GPOINTER_TO_INT(iter.user_data2) ;
361 if(self->_priv->images[n] == NULL){
362 self->_priv->images[n]= gmpc_pixbuf_loader_async_new();
363 gmpc_pixbuf_loader_async_set_rref(GMPC_PIXBUF_LOADER_ASYNC(self->_priv->images[n]), ref);
366 if(ret == META_DATA_AVAILABLE)
369 GdkPixbuf *pb = NULL;
370 if (met->content_type == META_DATA_CONTENT_RAW) {
371 MpdData_real *data = iter.user_data;
372 gmpc_pixbuf_loader_async_set_from_raw(GMPC_PIXBUF_LOADER_ASYNC(self->_priv->images[n]),
373 met->content, met->size, self->icon_size, self->icon_size,
374 ((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),
378 pb = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-no-cover", self->icon_size, 0,NULL);
379 gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n], pb);
383 else if(ret == META_DATA_UNAVAILABLE)
386 pb2 = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-no-cover", self->icon_size, 0,NULL);
387 gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n], pb2);
390 gtk_tree_model_row_changed(model,path, &iter);
391 gtk_tree_path_free(path);
394 if(ret == META_DATA_AVAILABLE || ret == META_DATA_UNAVAILABLE)
398 gtk_tree_row_reference_free(ref);
404 interface Gtk:Tree:Model
406 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))
408 Self *self = GMPC_MPDDATA_MODEL(model);
409 MpdData_real *data = iter->user_data;
410 int n = GPOINTER_TO_INT(iter->user_data2);
412 if(self->_priv->data == NULL)
413 g_warning("self->_priv->data == NULL");
414 /* set value to the correct type */
415 g_value_init(value, self->types[column]);
420 g_assert((n < self->num_rows || n == 0));
421 g_assert(!(data == NULL && n != 0));
424 if(data == NULL && n == 0)
428 case MPDDATA_MODEL_COL_ICON_ID:
429 g_value_set_string(value, GTK_STOCK_GO_UP);
431 case MPDDATA_MODEL_ROW_TYPE:
435 g_value_set_int(value, -1);
437 case MPDDATA_MODEL_COL_SONG_TITLE:
438 case MPDDATA_MODEL_COL_MARKUP:
440 g_value_set_string(value,"..");
449 if(column == MPDDATA_MODEL_META_DATA)
451 if(self->_priv->use_images && self->_priv->images)
453 if(self->_priv->images[n] == NULL)
456 GtkTreeRowReference *ref;
457 path = gtk_tree_model_get_path(GTK_TREE_MODEL(self), iter);
458 ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(self),path);
460 self->_priv->images[n]= gmpc_pixbuf_loader_async_new();
461 gmpc_pixbuf_loader_async_set_rref(GMPC_PIXBUF_LOADER_ASYNC(self->_priv->images[n]), ref);
462 if(data->type == MPD_DATA_TYPE_TAG && (data->tag_type == MPD_TAG_ITEM_ARTIST || data->tag_type == MPD_TAG_ITEM_ALBUM_ARTIST) )
465 GdkPixbuf *pb = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-loading-cover", self->icon_size, 0,NULL);
466 gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n], pb);
468 song = mpd_newSong();
469 song->artist = g_strdup(data->tag);
471 meta_data_get_path_callback(song, META_ARTIST_ART, (MetaDataCallback)self_cover_art_fetched, (gpointer)ref);
475 else if(data->type == MPD_DATA_TYPE_TAG && data->tag_type == MPD_TAG_ITEM_ALBUM && self->_priv->req_artist)
478 GdkPixbuf *pb = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-loading-cover", self->icon_size, 0,NULL);
479 gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n],pb);
481 song = mpd_newSong();
482 song->artist = g_strdup(self->_priv->req_artist);
483 song->albumartist = g_strdup(self->_priv->req_artist);
484 song->album = g_strdup(data->tag);
485 meta_data_get_path_callback(song, META_ALBUM_ART, (MetaDataCallback)self_cover_art_fetched, ref);
488 else if(data->type == MPD_DATA_TYPE_SONG&& data->song && data->song->album && (data->song->artist || self->_priv->req_artist))
490 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)
492 gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n],self->_priv->blank);
495 GdkPixbuf *pb = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-loading-cover", self->icon_size, 0,NULL);
496 gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n],pb);
498 song = mpd_newSong();
499 song->artist = g_strdup((data->song->artist)?data->song->artist:self->_priv->req_artist);
500 song->albumartist = g_strdup((data->song->albumartist)?data->song->albumartist:self->_priv->req_artist);
501 song->album = g_strdup(data->song->album);
502 meta_data_get_path_callback(song, META_ALBUM_ART, (MetaDataCallback)self_cover_art_fetched, ref);
508 GdkPixbuf *pb = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-no-cover", self->icon_size, 0,NULL);
509 gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n],pb);
512 gtk_tree_path_free(path);
514 g_value_set_object(value, gmpc_pixbuf_loader_async_get_pixbuf(self->_priv->images[n]));
516 g_value_set_object(value,NULL);
519 if(column == MPDDATA_MODEL_USERDATA)
521 g_value_set_pointer(value,data->userdata);
524 /* handle row type, this is independent of the row type */
525 if(column == MPDDATA_MODEL_ROW_TYPE)
527 g_value_set_int(value, data->type);
530 if (data->type == MPD_DATA_TYPE_TAG) {
533 case MPDDATA_MODEL_COL_ICON_ID:
534 switch(data->tag_type)
536 case MPD_TAG_ITEM_ALBUM:
537 g_value_set_string(value, "media-album");
539 case MPD_TAG_ITEM_ARTIST:
540 g_value_set_string(value, "media-artist");
542 case MPD_TAG_ITEM_GENRE:
543 g_value_set_string(value, "media-genre");
545 case MPD_TAG_ITEM_TRACK:
546 g_value_set_string(value, "media-num-tracks");
549 g_value_set_string(value, "media-tag");
552 case MPDDATA_MODEL_COL_SONG_TITLE:
553 case MPDDATA_MODEL_COL_MARKUP:
555 g_value_set_string(value, data->tag);
558 case MPDDATA_MODEL_COL_PATH:
559 g_value_set_string(value, data->tag);
561 case MPDDATA_MODEL_TAG_TYPE:
562 g_value_set_int(value, data->tag_type);
567 } else if(data->type == MPD_DATA_TYPE_DIRECTORY) {
570 case MPDDATA_MODEL_COL_ICON_ID:
571 g_value_set_string(value, GTK_STOCK_OPEN);
573 case MPDDATA_MODEL_COL_SONG_TITLE:
574 case MPDDATA_MODEL_COL_SONG_TITLEFILE:
575 case MPDDATA_MODEL_COL_MARKUP:
577 gchar *basename = g_path_get_basename(data->directory);
578 g_value_set_string(value, basename);
582 case MPDDATA_MODEL_COL_PATH:
583 g_value_set_string(value, data->directory);
589 else if(data->type == MPD_DATA_TYPE_PLAYLIST)
593 case MPDDATA_MODEL_COL_ICON_ID:
594 g_value_set_string(value, "media-playlist");
596 case MPDDATA_MODEL_COL_SONG_TITLE:
597 case MPDDATA_MODEL_COL_SONG_TITLEFILE:
598 case MPDDATA_MODEL_COL_MARKUP:
600 gchar *basename = g_path_get_basename(data->playlist->path);
601 g_value_set_string(value, basename);
605 case MPDDATA_MODEL_COL_PATH:
606 g_value_set_string(value, data->playlist->path);
612 else if(data->type == MPD_DATA_TYPE_SONG)
614 mpd_Song *song = data->song;
616 case MPDDATA_MODEL_COL_MPDSONG:
617 g_value_set_pointer(value, song);
619 case MPDDATA_MODEL_COL_PLAYING:
620 g_value_set_boolean(value, FALSE);
622 case MPDDATA_MODEL_COL_PLAYING_FONT_WEIGHT:
623 g_value_set_int(value, PANGO_WEIGHT_NORMAL);
625 case MPDDATA_MODEL_COL_MARKUP:
627 /* we want to go cache this stuff */
629 mpd_song_markup(buffer, 1024, GMPC_MPDDATA_MODEL(model)->_priv->markup, song);
630 g_value_set_string(value, buffer);
633 case MPDDATA_MODEL_COL_PATH:
634 g_value_set_string(value, song->file);
636 case MPDDATA_MODEL_COL_PATH_EXTENSION:
640 int j = strlen(song->file);
641 for(;j>0&&song->file[j] != '.';j--);
642 g_value_set_string(value, &(song->file)[j+1]);
645 case MPDDATA_MODEL_COL_PATH_DIRECTORY:
649 gchar *dir = g_path_get_dirname(song->file);
650 g_value_set_string(value, dir);
654 case MPDDATA_MODEL_COL_SONG_ARTIST:
655 g_value_set_string(value, song->artist);
657 case MPDDATA_MODEL_COL_SONG_ALBUM:
658 g_value_set_string(value, song->album);
660 case MPDDATA_MODEL_COL_SONG_TITLE:
661 /* If there is a song available use that, else use the filename */
663 g_value_set_string(value, song->title);
664 } else if (song->name) {
665 g_value_set_string(value, song->name);
667 /* Use the markup stuff, this makes sure it gets processed equaly */
669 mpd_song_markup(buffer, 1024, "%shortfile%", song);
670 g_value_set_string(value, buffer);
673 case MPDDATA_MODEL_COL_SONG_TITLEFILE:
676 gchar *path = g_path_get_basename(song->file);
677 g_value_set_string(value, path);
681 case MPDDATA_MODEL_COL_SONG_GENRE:
682 g_value_set_string(value, song->genre);
684 case MPDDATA_MODEL_COL_SONG_TRACK:
685 g_value_set_string(value, song->track);
687 case MPDDATA_MODEL_COL_SONG_NAME:
688 g_value_set_string(value, song->name);
690 case MPDDATA_MODEL_COL_SONG_COMPOSER:
691 g_value_set_string(value, song->composer);
693 case MPDDATA_MODEL_COL_SONG_PERFORMER:
694 g_value_set_string(value, song->performer);
696 case MPDDATA_MODEL_COL_SONG_DATE:
697 g_value_set_string(value, song->date);
699 case MPDDATA_MODEL_COL_SONG_LENGTH:
700 g_value_set_int(value, song->time);
702 case MPDDATA_MODEL_COL_SONG_RATING:
703 if(song->rating < 0) {
704 gchar *str = mpd_sticker_song_get(connection, song->file, "rating");
706 song->rating = atoi(str);
711 g_value_set_int(value,
712 (song->rating > 10)? 10: ((song->rating < 0)? 0: song->rating)
715 case MPDDATA_MODEL_COL_SONG_LENGTH_FORMAT:
717 if(song->time >= 0) {
718 gchar *strdata = g_strdup_printf("%02i:%02i",
719 song->time/60, song->time%60);
720 g_value_set_string(value, strdata);
723 g_value_set_string(value, "n/a");
727 case MPDDATA_MODEL_COL_SONG_DISC:
728 g_value_set_string(value, song->disc);
730 case MPDDATA_MODEL_COL_SONG_COMMENT:
731 g_value_set_string(value, song->comment);
733 case MPDDATA_MODEL_COL_SONG_POS:
734 g_value_set_int(value, song->pos+1);
736 case MPDDATA_MODEL_COL_SONG_ALBUMARTIST:
737 g_value_set_string(value, song->albumartist);
739 case MPDDATA_MODEL_COL_SONG_ID:
740 g_value_set_int(value, song->id);
743 case MPDDATA_MODEL_COL_ICON_ID:
744 if (song->file && strstr(song->file, "://")) {
745 g_value_set_string(value, "media-stream");
747 g_value_set_string(value, "media-audiofile");
750 case MPDDATA_MODEL_COL_SONG_PRIORITY:
751 g_value_set_int(value, song->priority);
759 interface Gtk:Tree:Model
761 iter_nth_child(Gtk:Tree:Model *model(check null type), GtkTreeIter *iter (check null), GtkTreeIter *parent, gint n (check >=0))
763 Self *self = GMPC_MPDDATA_MODEL(model);
765 MpdData_real *data = NULL;
768 if (n >= self->num_rows || n < 0)
771 data = (MpdData_real *)self->_priv->data;
777 data = data->next;//mpd_data_get_next_real(data, FALSE);
778 g_assert(data != NULL);
781 if(self->has_up && n == 0)
784 iter->stamp = self->_priv->stamp;
785 iter->user_data = data;
786 iter->user_data2 = GINT_TO_POINTER(n);
790 interface Gtk:Tree:Model
791 private GtkTreePath *
792 get_path(Gtk:Tree:Model *self (check null type), GtkTreeIter *iter (check null))
794 GtkTreePath *path = NULL;
795 path = gtk_tree_path_new();
796 gtk_tree_path_append_index(path, GPOINTER_TO_INT(iter->user_data2));
800 interface Gtk:Tree:Model
802 get_column_type(Gtk:Tree:Model *model(check null type), gint ind (check >= 0))
804 Self *self = GMPC_MPDDATA_MODEL(model);
805 return self->types[ind];
809 set_request_artist(self, const char *artist)
812 GtkTreePath *path = NULL;
814 if(self->_priv->req_artist)
815 q_free(self->_priv->req_artist);
816 self->_priv->req_artist = (artist != NULL && artist[0] != '\0')?g_strdup(artist):NULL;
817 /* Free possible stored images */
818 if(self->_priv->images && self->_priv->use_images)
820 MpdData *data2 = mpd_data_get_first(self->_priv->data);
821 for(i=0;i<self->num_rows;i++)
823 if(self->_priv->images[i]){
824 g_object_unref(self->_priv->images[i]);
825 self->_priv->images[i] = NULL;
827 /* Update the view */
828 path = gtk_tree_path_new();
829 gtk_tree_path_append_index(path,i);
830 iter.stamp = self->_priv->stamp;
831 iter.user_data = data2;
832 iter.user_data2 = GINT_TO_POINTER(i);
833 /* propegate change */
834 gtk_tree_model_row_changed(GTK_TREE_MODEL(self), path, &iter);
835 gtk_tree_path_free(path);
837 data2 = mpd_data_get_next_real(data2,FALSE);
843 get_request_artist(self)
845 return self->_priv->req_artist;
848 long unsigned set_mpd_data(self, MpdData *data2)
851 long unsigned retval = 0;
853 GtkTreePath *path = NULL;
854 int old_num_rows = self->num_rows;
855 /* Do some cleanup, like removing rows, and so */
856 /* loop and remove */
857 while ( self->num_rows > 0 ) {
858 path = gtk_tree_path_new();
859 gtk_tree_path_append_index(path, self->num_rows - 1 );
860 /* propegate change */
861 gtk_tree_model_row_deleted(GTK_TREE_MODEL(self), path);
862 gtk_tree_path_free(path);
868 * Free if there is a list and set it to NULL
870 if(self->_priv->data){
871 mpd_data_free(mpd_data_get_first(self->_priv->data));
873 self->_priv->data = NULL;
875 if(self->num_rows != 0)
876 g_log(LOG_DOMAIN, G_LOG_LEVEL_WARNING,"not every row cleared %i\n",self->num_rows);
878 /* Free possible stored images */
879 if(self->_priv->images && self->_priv->use_images)
881 for(i=0;i< old_num_rows;i++)
883 if(self->_priv->images[i])
884 g_object_unref(self->_priv->images[i]);
886 q_free(self->_priv->images);
890 self->_priv->playtime = 0;
891 self_playtime_changed(self, self->_priv->playtime);
895 self->_priv->data = mpd_data_get_first(data2);
897 if(self->_priv->sort_column == MPDDATA_MODEL_COL_ICON_ID)
899 self->_priv->data = misc_sort_mpddata_by_album_disc_track(self->_priv->data);
901 else if ( self->_priv->sort_column != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID )
903 self->_priv->data = misc_sort_mpddata(self->_priv->data,(GCompareDataFunc)self_sort_func,self );
907 path = gtk_tree_path_new();
908 gtk_tree_path_append_index(path, self->num_rows);
909 iter.stamp = self->_priv->stamp;
910 iter.user_data = NULL;
911 iter.user_data2 = GINT_TO_POINTER(self->num_rows);
914 /* propegate change */
915 gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
916 gtk_tree_path_free(path);
918 for(data2 = mpd_data_get_first(self->_priv->data);data2; data2 = mpd_data_get_next_real(data2,FALSE)) {
919 path = gtk_tree_path_new();
920 gtk_tree_path_append_index(path,self->num_rows);
921 iter.stamp = self->_priv->stamp;
922 iter.user_data = data2;
923 iter.user_data2 = GINT_TO_POINTER(self->num_rows);
926 /* propegate change */
927 gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
928 gtk_tree_path_free(path);
929 if(data2->type == MPD_DATA_TYPE_SONG)
931 if(data2->song && data2->song->time > 0)
932 retval+= data2->song->time;
935 if(self->_priv->use_images)
937 self->_priv->images = g_malloc0((self->num_rows)*sizeof(*(self->_priv->images)));
940 self->_priv->playtime = retval;
941 self_playtime_changed(self, self->_priv->playtime);
946 interface Gtk:Tree:Sortable
948 gboolean get_sort_column_id (Gtk:Tree:Sortable *model , gint *sort_column_id, GtkSortType *order)
950 Self *self = GMPC_MPDDATA_MODEL(model);
952 *sort_column_id = self->_priv->sort_column;
954 *order = self->_priv->sort_order;
958 interface Gtk:Tree:Sortable
960 void set_sort_column_id (Gtk:Tree:Sortable *model , gint sort_column_id, GtkSortType order)
962 Self *self = GMPC_MPDDATA_MODEL(model);
963 gint old_col = self->_priv->sort_column;
964 GtkSortType old_ord = self->_priv->sort_order;
965 self->_priv->sort_column = sort_column_id;
966 self->_priv->sort_order = order;
968 if(old_col != sort_column_id || old_ord != order)
969 gtk_tree_sortable_sort_column_changed(model);
970 self->_priv->old_sort_column = self->_priv->sort_column;
974 interface Gtk:Tree:Sortable
977 has_default_sort_func(Gtk:Tree:Sortable *model)
982 int sort_func(gpointer ppaa, gpointer ppbb, Self *self)
984 MpdData_real *a = *(MpdData_real **)ppaa;
985 MpdData_real *b = *(MpdData_real **)ppbb;
986 int fact = (self->_priv->sort_order == GTK_SORT_ASCENDING)?-1:1;
988 if(a->type != b->type )
990 int val = a->type - b->type;
993 else if(a->type == b->type)
997 GValue va = {0,},vb = {0,};
998 /* Get values from tree view, so we don't have to replicate code to sort the right entries */
1000 iter.user_data2 = GPOINTER_TO_INT(0);
1001 gtk_tree_model_get_value(GTK_TREE_MODEL(self), &iter, self->_priv->sort_column, &va);
1003 gtk_tree_model_get_value(GTK_TREE_MODEL(self), &iter, self->_priv->sort_column, &vb);
1004 /* if the type is a directory or a tag, always sort on the title column */
1005 if(a->type == MPD_DATA_TYPE_DIRECTORY || b->type == MPD_DATA_TYPE_TAG)
1008 ca = g_value_get_string(&va);
1009 cb = g_value_get_string(&vb);
1012 aa = g_utf8_strdown(ca, -1);
1013 bb = g_utf8_strdown(cb, -1);
1014 val = g_utf8_collate(aa,bb);
1018 val = (ca == NULL)?((cb==NULL)?0:-1):1;
1025 if(self->_priv->sort_column == MPDDATA_MODEL_COL_SONG_TRACK)
1027 const char *ca = g_value_get_string(&va);
1028 const char *cb = g_value_get_string(&vb);
1029 gint ca1 = (ca)?atoi(ca):0;
1030 gint cb1 = (cb)?atoi(cb):0;
1033 return fact*(ca1-cb1);
1035 else if(self->types[self->_priv->sort_column] == G_TYPE_INT)
1037 val = g_value_get_int(&va) - g_value_get_int(&vb);
1039 else if (self->types[self->_priv->sort_column] == G_TYPE_STRING)
1041 const char *ca = g_value_get_string(&va);
1042 const char *cb = g_value_get_string(&vb);
1045 aa = g_utf8_normalize(ca, -1,G_NORMALIZE_ALL);
1046 bb = g_utf8_normalize(cb, -1,G_NORMALIZE_ALL);
1047 val = g_utf8_collate(aa,bb);
1051 val = (ca == NULL)?((cb==NULL)?0:-1):1;
1056 a->type == MPD_DATA_TYPE_SONG &&
1057 b->type == MPD_DATA_TYPE_SONG &&
1061 val = strcmp (a->song->file, b->song->file);
1071 interface Gtk:Tree:Sortable
1074 sort_column_changed(Gtk:Tree:Sortable *model)
1076 Self *self = GMPC_MPDDATA_MODEL(model);
1077 if(!self->_priv->data || !(((MpdData_real *)self->_priv->data)->next))
1079 if(self->_priv->sort_column == MPDDATA_MODEL_COL_ICON_ID)
1081 self->_priv->data = misc_sort_mpddata_by_album_disc_track(self->_priv->data);
1083 else if(self->_priv->sort_column != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID)
1085 self->_priv->data = misc_sort_mpddata(self->_priv->data,(GCompareDataFunc)self_sort_func,self );
1087 self->_priv->old_sort_column = self->_priv->sort_column;
1089 /* tell the view that rows have changed */
1092 GtkTreePath *path = NULL;
1094 MpdData *data2 = mpd_data_get_first(self->_priv->data);
1095 if(self->has_up) i = 1;
1096 for(;data2; data2 = mpd_data_get_next_real(data2,FALSE))
1098 path = gtk_tree_path_new();
1099 gtk_tree_path_append_index(path,i);
1100 iter.stamp = self->_priv->stamp;
1101 iter.user_data = data2;
1102 iter.user_data2 = GINT_TO_POINTER(i);
1104 /* propegate change */
1105 gtk_tree_model_row_changed(GTK_TREE_MODEL(self), path, &iter);
1106 gtk_tree_path_free(path);
1114 test_sort_func(gpointer ppaa, gpointer ppbb, GmpcMpdDataModel *self)
1116 MpdData_real *a = *(MpdData_real **)ppaa;
1117 MpdData_real *b = *(MpdData_real **)ppbb;
1118 if(a->type == MPD_DATA_TYPE_TAG && b->type == MPD_DATA_TYPE_TAG)
1120 if(a->tag_type != b->tag_type)
1121 return a->tag_type - b->tag_type;
1122 if(a->tag== NULL && b->tag != NULL)
1124 else if(b->tag == NULL && a->tag != NULL)
1126 else if (a->tag && b->tag)
1129 if(a->tag && b->tag) {
1131 sa = g_utf8_strdown(a->tag, -1);
1132 sb = g_utf8_strdown(b->tag, -1);
1133 val = g_utf8_collate(sa,sb);
1137 val = (a == NULL)?((b==NULL)?0:-1):1;
1142 return a->type - b->type;
1146 * For now this assumes tag list only.
1149 long unsigned set_mpd_data_slow (self, MpdData *data2)
1151 MpdData_real *new_list = NULL;
1154 MpdData_real *original = (MpdData_real *)mpd_data_get_first(self->_priv->data);
1155 MpdData_real *new = (MpdData_real *) data2;
1156 GtkTreePath *path =NULL;
1158 int old_list_size = self->num_rows;
1160 /* sort it identical */
1161 data2 = misc_sort_mpddata(data2,(GCompareDataFunc)self_test_sort_func,self );
1162 new = (MpdData_real *)data2;
1165 MpdData_real *n = new->first;
1173 /* Free possible stored images */
1175 * TODO: Don't free the list, but grow it to MAX(items, self->num_rows);
1176 * Then after updating, resize it to new size
1178 /* Grow the list when needed */
1180 if(self->_priv->use_images)
1182 /* Clean them, they might not be in the right place anymore */
1183 for(i=0;i< old_list_size; i++)
1185 if(self->_priv->images[i] != NULL) g_object_unref(self->_priv->images[i]);
1186 self->_priv->images[i] = NULL;
1188 if(items > self->num_rows)
1190 self->_priv->images = g_realloc(self->_priv->images, items*sizeof(GdkPixbuf *));
1191 /* set the new ones to NUll */
1192 for(i=self->num_rows;i<items;i++)
1194 self->_priv->images[i] = NULL;
1204 if(self->_priv->data && original == NULL) g_warning("weirdness\n");
1205 if(self->_priv->data)
1213 if(self->num_rows > items)
1215 MpdData_real *n = original->next;
1216 /* remove current row */
1219 path = gtk_tree_path_new();
1220 gtk_tree_path_append_index(path,i);
1222 /* propegate change */
1223 gtk_tree_model_row_deleted(GTK_TREE_MODEL(self), path);
1224 gtk_tree_path_free(path);
1226 original =(MpdData_real *) mpd_data_delete_item((MpdData *)original);
1227 self->_priv->data = mpd_data_get_first((MpdData *)original);
1230 has_next = (original )?1:0;
1236 compare = g_utf8_collate(new->tag,original->tag);
1240 MpdData_real *n = (MpdData_real *) mpd_new_data_struct();
1241 n->type = MPD_DATA_TYPE_TAG;
1242 n->tag_type = new->tag_type;
1249 n->prev = original->prev;
1252 original->prev->next = n;
1254 n->first = original->first;
1256 if(n->prev == NULL){
1257 MpdData_real *fiter;
1258 for(fiter = n;fiter;fiter = fiter->next){
1263 self->_priv->data = mpd_data_get_first((MpdData *)original);
1265 path = gtk_tree_path_new();
1266 gtk_tree_path_append_index(path,i);
1267 iter.stamp = self->_priv->stamp;
1269 iter.user_data2 = GINT_TO_POINTER(i);
1271 gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
1272 gtk_tree_path_free(path);
1274 /* mark for insert */
1279 else if (compare > 0)
1281 MpdData_real *n = original->next;
1282 path = gtk_tree_path_new();
1283 gtk_tree_path_append_index(path,i);
1284 /* propegate change */
1287 gtk_tree_model_row_deleted(GTK_TREE_MODEL(self), path);
1288 gtk_tree_path_free(path);
1291 original = (MpdData_real *)mpd_data_delete_item((MpdData *)original);
1292 self->_priv->data = mpd_data_get_first((MpdData *)original);
1294 //original = original->next;
1295 has_next = (original )?1:0;
1299 original = original->next;
1300 has_next = (original )?1:0;
1306 /* add entries remaining in new */
1310 new_list = (MpdData_real *) self->_priv->data;
1311 while(new_list && new_list->next) new_list = new_list->next;
1313 new_list = (MpdData_real *)mpd_new_data_struct_append((MpdData*)new_list);
1314 new_list->type = MPD_DATA_TYPE_TAG;
1315 new_list->tag_type = new->tag_type;
1316 new_list->tag = new->tag;
1319 self->_priv->data = (MpdData *)new_list->first;
1320 if(self->_priv->data == NULL) g_warning("self->_priv->data == NULL");
1323 path = gtk_tree_path_new();
1324 gtk_tree_path_append_index(path,i);
1325 iter.stamp = self->_priv->stamp;
1326 iter.user_data = new_list;
1327 iter.user_data2 = GINT_TO_POINTER(i);
1329 gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
1330 gtk_tree_path_free(path);
1339 if(self->_priv->use_images){
1340 if(self->num_rows < old_list_size){
1341 for(i=self->num_rows;i< old_list_size; i++)
1344 if(self->_priv->images[i] != NULL) g_object_unref(self->_priv->images[i]);
1346 self->_priv->images = g_realloc(self->_priv->images, (self->num_rows)*sizeof(GdkPixbuf *));
1353 mpd_data_free(data2);
1355 g_assert(items == self->num_rows);
1359 signal last NONE (LONG)
1361 playtime_changed(self, gulong playtime)
1370 return self->_priv->playtime;
1374 get_pos( self, GtkTreeIter *iter)
1376 g_assert(iter->stamp == self->_priv->stamp);
1377 return GPOINTER_TO_INT(iter->user_data2);
1384 g_assert(self->_priv->data == NULL);
1385 self->_priv->use_images = FALSE;
1390 steal_mpd_data(self)
1394 int old_num_rows = self->num_rows;
1395 MpdData *data = self->_priv->data;
1396 while ( self->num_rows > 0 ) {
1397 path = gtk_tree_path_new();
1398 gtk_tree_path_append_index(path, self->num_rows - 1 );
1399 /* propegate change */
1400 gtk_tree_model_row_deleted(GTK_TREE_MODEL(self), path);
1401 gtk_tree_path_free(path);
1406 self->_priv->data = NULL;
1407 /* Free possible stored images */
1408 if(self->_priv->images && self->_priv->use_images)
1410 for(i=0;i< old_num_rows;i++)
1412 if(self->_priv->images[i])
1413 g_object_unref(self->_priv->images[i]);
1415 q_free(self->_priv->images);
1417 self->_priv->playtime = 0;
1418 self_playtime_changed(self, self->_priv->playtime);
1420 return mpd_data_get_first(data);
1423 /* Drag and stop handling */
1426 interface Gtk:Tree:Drag:Source
1428 gboolean row_draggable (Gtk:Tree:Drag:Source *drag_source, Gtk:Tree:Path *path)
1431 if(gtk_tree_model_get_iter(GTK_TREE_MODEL(drag_source), &iter, path))
1433 MpdData *data = iter.user_data;
1434 if(data->type == MPD_DATA_TYPE_SONG) {
1441 interface Gtk:Tree:Drag:Source
1443 gboolean drag_data_delete (Gtk:Tree:Drag:Source *drag_source, Gtk:Tree:Path *path)
1447 interface Gtk:Tree:Drag:Source
1449 gboolean drag_data_get (Gtk:Tree:Drag:Source *drag_source, Gtk:Tree:Path *path, Gtk:Selection:Data *selection_data)
1451 if(!gtk_tree_set_row_drag_data(selection_data,GTK_TREE_MODEL(drag_source), path)) {