Tag browser:
[gmpc.git] / src / gob / gmpc-mpddata-model.gob
blob061147e9abe52534868503355267cf01cff76da6
1 requires 2.0.0
3 %ht{
4 #include <gtk/gtk.h>
5 #include <libmpd/libmpd.h>
6 #include <libmpd/libmpd-internal.h>
7 %}
9 %privateheader{
10 #include "playlist3.h"
11 #include "config1.h"
12 #include "config-defaults.h"
13 #include "main.h"
14 #include "misc.h"
18 %h{
19         enum
20         {
21                 MPDDATA_MODEL_COL_MPDSONG = 0,                /* get the mpd_Song */
22                 MPDDATA_MODEL_COL_PLAYING,                                    /* Shows if this song is the current song */
23                 MPDDATA_MODEL_COL_PLAYING_FONT_WEIGHT,  /* Shows if this song is the current song */
24                 MPDDATA_MODEL_COL_PATH,                                         /* Path to song/file/directory */ 
25                 MPDDATA_MODEL_COL_MARKUP,                                     /* a string that has FALSEmarkup */
26                 MPDDATA_MODEL_COL_SONG_ARTIST,                      /* artist name */
27                 MPDDATA_MODEL_COL_SONG_ALBUM,                         /* album name */
28                 MPDDATA_MODEL_COL_SONG_TITLE,                         /* song title */
29                 MPDDATA_MODEL_COL_SONG_TITLEFILE,                   /* song title */
30                 MPDDATA_MODEL_COL_SONG_GENRE,                         /* song genre */
31                 MPDDATA_MODEL_COL_SONG_TRACK,                         /* song track */
32                 MPDDATA_MODEL_COL_SONG_NAME,                          /* stream name */
33                 MPDDATA_MODEL_COL_SONG_COMPOSER,                    /* composer name */
34                 MPDDATA_MODEL_COL_SONG_PERFORMER,                   /* performer */
35                 MPDDATA_MODEL_COL_SONG_DATE,                          /* date */
36                 MPDDATA_MODEL_COL_SONG_LENGTH_FORMAT,     /* length formatted */
37                 MPDDATA_MODEL_COL_SONG_DISC,                          /* disc */
38                 MPDDATA_MODEL_COL_SONG_COMMENT,                     /* comment */
39                 MPDDATA_MODEL_COL_SONG_POS,                                   /* position */
40                 MPDDATA_MODEL_COL_PATH_EXTENSION,                               /* Extention */
41                 MPDDATA_MODEL_COL_PATH_DIRECTORY,                               /* Directory */
42                 MPDDATA_MODEL_COL_SONG_ID,                                    /* col id */
43                 MPDDATA_MODEL_COL_ICON_ID,                                    /* icon id */
44                 MPDDATA_MODEL_COL_SONG_LENGTH,                      /* length */
45                 MPDDATA_MODEL_TAG_TYPE,                                   /* tag type */
46                 MPDDATA_MODEL_ROW_TYPE,                                         /* type of the row */
47                 MPDDATA_MODEL_META_DATA,                        /* metadata */
48                 MPDDATA_MODEL_USERDATA,
49                 MPDDATA_MODEL_N_COLUMNS
50         } ;
53  class Gmpc:MpdData:Model from G:Object
54           (interface Gtk:Tree:Sortable)
55           (interface Gtk:Tree:Model)
57     private gint stamp          = {g_random_int()};
58     public      GType types[MPDDATA_MODEL_N_COLUMNS]; 
59     private MpdData *data       = NULL;
60     public gint num_rows = 0;
61     private GdkPixbuf **images = NULL;
62     private gchar *req_artist = {NULL} destroywith g_free;
64     /* sorting */
65     private GtkSortType sort_order = GTK_SORT_ASCENDING;
66     private int sort_column = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID;
67     private int old_sort_column = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID;
68     public gboolean has_up       = FALSE;
69     private gchar *markup        = {gmpc_signals_get_browser_markup(gmpc_signals)} destroywith g_free;
70     private gulong markup_signal = 0; 
72     private gulong playtime = {0};
74     property BOOLEAN has_up
75         (nick = "Has Up",
76          blurb = "Show an 'up row",
77          default_value = FALSE,
78          export,
79          link);
81     private
82     void
83     markup_changed(self, const char *markup, gpointer data)
84     {
85         int i=0;
86         GtkTreePath *path = NULL;
87         GtkTreeIter iter;
88         MpdData *data2 = mpd_data_get_first(self->_priv->data); 
90         /* set the new markup */
91         if(self->_priv->markup )
92             g_free(self->_priv->markup);
93         self->_priv->markup  = g_strdup(markup);
95         if(self->has_up) i = 1;
96         for(;data2; data2 = mpd_data_get_next_real(data2,FALSE)) 
97         {
98             path = gtk_tree_path_new();
99             gtk_tree_path_append_index(path,i);
100             iter.stamp = self->_priv->stamp;
101             iter.user_data = data2;
102             iter.user_data2 =  GINT_TO_POINTER(i);
104             /* propegate change */
105             gtk_tree_model_row_changed(GTK_TREE_MODEL(self), path, &iter);
106             i++;
107         }
108     }
110     init(self)
111     {
112         self->types[MPDDATA_MODEL_COL_MPDSONG] = G_TYPE_POINTER;
113         self->types[MPDDATA_MODEL_COL_MARKUP] = G_TYPE_STRING;
114         self->types[MPDDATA_MODEL_COL_PLAYING] = G_TYPE_BOOLEAN;
115         self->types[MPDDATA_MODEL_COL_PLAYING_FONT_WEIGHT] = G_TYPE_INT;
116         self->types[MPDDATA_MODEL_COL_PATH] = G_TYPE_STRING;
117         self->types[MPDDATA_MODEL_COL_SONG_ARTIST] = G_TYPE_STRING;
118         self->types[MPDDATA_MODEL_COL_SONG_ALBUM] = G_TYPE_STRING;
119         self->types[MPDDATA_MODEL_COL_SONG_TITLE] = G_TYPE_STRING;
120         self->types[MPDDATA_MODEL_COL_SONG_TITLEFILE] = G_TYPE_STRING;
121         self->types[MPDDATA_MODEL_COL_SONG_TRACK] = G_TYPE_STRING;
122         self->types[MPDDATA_MODEL_COL_SONG_GENRE] = G_TYPE_STRING;
123         self->types[MPDDATA_MODEL_COL_SONG_NAME] = G_TYPE_STRING;
124         self->types[MPDDATA_MODEL_COL_SONG_COMPOSER] = G_TYPE_STRING;
125         self->types[MPDDATA_MODEL_COL_SONG_PERFORMER]= G_TYPE_STRING;
126         self->types[MPDDATA_MODEL_COL_SONG_DATE] = G_TYPE_STRING;
127         self->types[MPDDATA_MODEL_COL_SONG_LENGTH] = G_TYPE_INT;
128         self->types[MPDDATA_MODEL_COL_SONG_LENGTH_FORMAT] = G_TYPE_STRING;
129         self->types[MPDDATA_MODEL_COL_SONG_DISC] = G_TYPE_STRING;
130         self->types[MPDDATA_MODEL_COL_SONG_COMMENT] = G_TYPE_STRING;
131         self->types[MPDDATA_MODEL_COL_SONG_POS] = G_TYPE_INT;
132         self->types[MPDDATA_MODEL_COL_PATH_EXTENSION] = G_TYPE_STRING;
133         self->types[MPDDATA_MODEL_COL_PATH_DIRECTORY] = G_TYPE_STRING;
134         self->types[MPDDATA_MODEL_COL_SONG_ID] = G_TYPE_INT;
135         self->types[MPDDATA_MODEL_COL_ICON_ID] = G_TYPE_STRING;
136         self->types[MPDDATA_MODEL_TAG_TYPE] = G_TYPE_INT;
137         self->types[MPDDATA_MODEL_ROW_TYPE] = G_TYPE_INT;
138         self->types[MPDDATA_MODEL_USERDATA] = G_TYPE_POINTER;
139         self->types[MPDDATA_MODEL_META_DATA] = G_TYPE_OBJECT;
141         self->_priv->markup_signal = g_signal_connect_swapped(G_OBJECT(gmpc_signals), "browser-markup-changed", G_CALLBACK(self_markup_changed), self);
142     }
143     class_init(klass);
145     public
146         Gmpc:MpdData:Model *new (void)
147         {
148             return GET_NEW;
149         }
151     override (G:Object)
152         void 
153         finalize (G:Object *obj)
154         {
155             int i =0;
156             Self *self = GMPC_MPDDATA_MODEL(obj); 
157             if(self->_priv->markup_signal)
158             {
159                 g_signal_handler_disconnect(G_OBJECT(gmpc_signals),self->_priv->markup_signal);
160             }
161             self->_priv->markup_signal = 0;
163             if(self->_priv->data)
164             {
165                 mpd_data_free(self->_priv->data);
166                 self->_priv->data = NULL;
167             }
168             if(self->_priv->images)
169             {
170                 for(i=0;i<self->num_rows;i++)
171                 {
172                     if(self->_priv->images[i]) {
173                         g_object_unref(self->_priv->images[i]);
174                         self->_priv->images[i] = NULL;
175                     }
176                 }
177                 q_free(self->_priv->images);
178                 /* q_free should do it, but force it */
179                 self->_priv->images = NULL;
180             }
181             PARENT_HANDLER(obj);
182         }
183     /* function implemented for the Gtk:Tree:Model interface */
184     interface Gtk:Tree:Model
185         private GtkTreeModelFlags
186         get_flags (Gtk:Tree:Model *self (check null type))
187         {
188             /* Here would be the implementation */
189             return (GtkTreeModelFlags)GTK_TREE_MODEL_LIST_ONLY;
190         }
193     interface Gtk:Tree:Model
194         public
195         gboolean iter_children(Gtk:Tree:Model *model, GtkTreeIter *iter, GtkTreeIter *parent)
196         {
197             Self *self  = GMPC_MPDDATA_MODEL(model);
198             if(parent)
199                 return FALSE;
200             if(self->num_rows == 0)
201                 return FALSE;
202             /* Set iter to first item in list */
203             iter->stamp = self->_priv->stamp; 
204             iter->user_data = self->_priv->data;
205             iter->user_data2 =  GINT_TO_POINTER(0);
206             iter->user_data3 = NULL;    /* unused */
208             return TRUE;
209         }
210     /**
211      * Unused, not known in the model directly? 
212      */
213     interface Gtk:Tree:Model    
214         public
215         gint get_n_columns(Gtk:Tree:Model *model)
216         {
217             return MPDDATA_MODEL_N_COLUMNS;
218         }
220     interface Gtk:Tree:Model
221         private gboolean
222         get_iter(Gtk:Tree:Model *model (check null type),
223                 Gtk:Tree:Iter *iter (check null),
224                 Gtk:Tree:Path *path (check null))
225         {
226             MpdData *data = NULL;
227             Self *self = GMPC_MPDDATA_MODEL(model);
228             gint *indices, n,depth,i;
229             indices = gtk_tree_path_get_indices(path);
230             depth = gtk_tree_path_get_depth(path);
232             /* No Children */
233             g_assert(depth == 1); 
235             n = indices[0];             /* the n-th top level row */
236             if (n >= self->num_rows || n < 0)
237                 return FALSE; 
239             data = (MpdData*)self->_priv->data;
240             i=0;
241             if(self->has_up)
242             {
243                 i=1;
244             }
245             for(; i < (n);i++)
246             {
247                 data = mpd_data_get_next_real(data,FALSE);
248             }
249             g_assert(n<self->num_rows);
250             if(self->has_up && n == 0)
251                 data = NULL;
253             iter->stamp = self->_priv->stamp;
254             iter->user_data = data; 
255             iter->user_data2 = GINT_TO_POINTER(n);
256             return TRUE;
257         }
259     interface Gtk:Tree:Model
260         private gboolean
261         iter_next(Gtk:Tree:Model *model(check null type), GtkTreeIter *iter (check null))
262         {
263             Self *self = GMPC_MPDDATA_MODEL(model);
264             MpdData *data = iter->user_data;
265             int n = GPOINTER_TO_INT(iter->user_data2) ;
266             /* if the row is the last row in the list or bigger then the last row, return FALSE */
267             if(n >= self->num_rows)
268             {
269                 return FALSE;
270             }
271             /* if the row is the first row in the list, and it has a "go-up" entry, 
272              * it can have a next entry
273              */
274             if(data == NULL &&  n == 0 && self->has_up == TRUE)
275             {
276                 iter->user_data = (MpdData *)self->_priv->data; 
277                 iter->user_data2 = GINT_TO_POINTER(1);
278                 if(iter->user_data == NULL)
279                     return FALSE;
280                 return TRUE;
281             }
282             if(mpd_data_get_next_real(data,FALSE) == NULL)
283             {
284                 return FALSE;
285             }
286             iter->user_data = (MpdData *)mpd_data_get_next_real(data,FALSE); 
287             iter->user_data2 = GINT_TO_POINTER(n+1);
288             g_assert(iter->user_data != NULL);
289             return TRUE;
290         }
292     interface Gtk:Tree:Model
293         private gboolean
294         iter_has_child(Gtk:Tree:Model *self (check null type), GtkTreeIter *iter)
295         {
296             return FALSE;
297         }
298     interface Gtk:Tree:Model
299         private gint
300         iter_n_children(Gtk:Tree:Model *model (check null type), GtkTreeIter *iter)
301         { 
302             Self *list = GMPC_MPDDATA_MODEL(model);
303             if(iter)
304                 return 0;
305             return list->num_rows;
306         }
308     interface Gtk:Tree:Model
309         private gboolean
310         iter_parent(Gtk:Tree:Model *model (check null type), Gtk:Tree:Iter *iter, Gtk:Tree:Iter *child)
311         {
312             return FALSE;
313         }
314     private 
315         void
316         cover_art_fetched(mpd_Song *song, MetaDataResult ret, char *coverpath,GtkTreeRowReference *ref)
317         {
319             if(ref && ref)
320             {
321                 GtkTreePath *path =gtk_tree_row_reference_get_path(ref);
322                 GtkTreeModel *model = gtk_tree_row_reference_get_model(ref);
323                 if(path && model)
324                 {
325                                         int n;  
326                     int size = cfg_get_single_value_as_int_with_default(config, "gmpc-mpddata-model", "icon-size", 64);
327                     Self *self = GMPC_MPDDATA_MODEL(model);
328                     GtkTreeIter iter;
329                     gtk_tree_model_get_iter(model, &iter, path);
330                     n = GPOINTER_TO_INT(iter.user_data2) ;
331                     if(ret == META_DATA_AVAILABLE)
332                     {
333                         GdkPixbuf *pb = gdk_pixbuf_new_from_file_at_size(coverpath,size,size,NULL);
334                         screenshot_add_border(&pb);
335                         if(self->_priv->images[n]) g_object_unref(self->_priv->images[n]); 
336                         self->_priv->images[n] = pb;
337                     }
338                     else   if(ret == META_DATA_UNAVAILABLE)
339                     {
340                         GdkPixbuf *pb2;
341                         pb2 = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-no-cover", size, 0,NULL);
342                         if(self->_priv->images[n]) g_object_unref(self->_priv->images[n]); 
343                         self->_priv->images[n] = pb2;
344                     }
345                     gtk_tree_model_row_changed(model,path, &iter);
346                 }
348                 if(ret == META_DATA_AVAILABLE || ret == META_DATA_UNAVAILABLE)
349                 {
350                     if(ref)
351                     {
352                         gtk_tree_row_reference_free(ref);
353                     }
354                 }
355             }
356         }
358     interface Gtk:Tree:Model
359         public void
360         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))
361         {
362             Self *self = GMPC_MPDDATA_MODEL(model);
363             MpdData_real *data = iter->user_data;       
364             int n = GPOINTER_TO_INT(iter->user_data2);
366             /* set value to the correct type */
367             g_value_init(value, self->types[column]);
369             /** 
370              * Sanity checks 
371              */
372              g_assert((n < self->num_rows || n == 0));
373             g_assert(!(data == NULL && n != 0));
375             /* handle row up */
376             if(data == NULL && n == 0)
377             {
378                 switch(column)
379                 {
380                     case MPDDATA_MODEL_COL_ICON_ID:
381                         g_value_set_string(value, GTK_STOCK_GO_UP);
382                         break;
383                     case MPDDATA_MODEL_ROW_TYPE:
384                         /**
385                          * Make define 
386                          */
387                         g_value_set_int(value, -1);
388                         break;
389                     case MPDDATA_MODEL_COL_SONG_TITLE:
390                     case MPDDATA_MODEL_COL_MARKUP:
391                         {
392                             g_value_set_string(value,"..");
393                             break;
394                         }
395                     default:
396                         break;
397                 }
398                 return;
399             }
401             if(column == MPDDATA_MODEL_META_DATA)
402             {
403                 if(self->_priv->images[n] == NULL)
404                 {
405                     int size = cfg_get_single_value_as_int_with_default(config, "gmpc-mpddata-model", "icon-size", 64);
406                     if(data->type == MPD_DATA_TYPE_TAG && data->tag_type == MPD_TAG_ITEM_ARTIST)
407                     {
408                         mpd_Song *song;
409                         GtkTreePath *path;
410                         GtkTreeRowReference *ref;
411                         self->_priv->images[n] = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-loading-cover", size, 0,NULL);
412                         song = mpd_newSong();
413                         song->artist = g_strdup(data->tag);
414                         song->album = NULL; 
415                         path = gtk_tree_model_get_path(GTK_TREE_MODEL(self), iter);
416                         ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(self),path);
417                         gmpc_meta_watcher_get_meta_path_callback(gmw,song, META_ARTIST_ART, (MetaDataCallback)self_cover_art_fetched, (gpointer)ref);
418                         mpd_freeSong(song);     
419                         gtk_tree_path_free(path);
420                     }
422                     else if(data->type == MPD_DATA_TYPE_TAG && data->tag_type == MPD_TAG_ITEM_ALBUM && self->_priv->req_artist)
423                     {
424                         mpd_Song *song;
425                         GtkTreePath *path;
426                         GtkTreeRowReference *ref;
427                         self->_priv->images[n] = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-loading-cover", size, 0,NULL);
428                         song = mpd_newSong();
429                         song->artist = g_strdup(self->_priv->req_artist); 
430                         song->album = g_strdup(data->tag);
431                         path = gtk_tree_model_get_path(GTK_TREE_MODEL(self), iter);
432                         ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(self),path);
433                         gmpc_meta_watcher_get_meta_path_callback(gmw,song, META_ALBUM_ART, (MetaDataCallback)self_cover_art_fetched, ref);
434                         mpd_freeSong(song);     
435                         gtk_tree_path_free(path);
436                     }
437                     else 
438                     {
439                         self->_priv->images[n] = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-no-cover", size, 0,NULL);
440                     }
441                 }
442                 g_value_set_object(value, self->_priv->images[n]);
443             }
444             if(column == MPDDATA_MODEL_USERDATA)
445             {
446                 g_value_set_pointer(value,data->userdata);
448             }
449             /* handle row type, this is independent of the row type */
450             if(column == MPDDATA_MODEL_ROW_TYPE)
451             {
452                 g_value_set_int(value, data->type);
453                 return;
454             } 
455             if (data->type == MPD_DATA_TYPE_TAG) {
456                 switch(column)
457                 {
458                     case MPDDATA_MODEL_COL_ICON_ID:
459                         switch(data->tag_type) 
460                         {
461                             case MPD_TAG_ITEM_ALBUM:
462                                 g_value_set_string(value, "media-album");
463                                 break;
464                             case MPD_TAG_ITEM_ARTIST:
465                                 g_value_set_string(value, "media-artist");
466                                 break;
467                             default:
468                                 g_value_set_string(value, "media-tag");
469                         }
470                         break;
471                     case MPDDATA_MODEL_COL_SONG_TITLE:
472                     case MPDDATA_MODEL_COL_MARKUP:
473                         {
474                             g_value_set_string(value, data->tag);
475                             break;
476                         }
477                     case MPDDATA_MODEL_COL_PATH:
478                         g_value_set_string(value, data->tag);
479                         break;                                                  
480                     case MPDDATA_MODEL_TAG_TYPE:
481                         g_value_set_int(value, data->tag_type);
482                         break;                                                  
483                     default:
484                         break;
485                 }
486             } else if(data->type == MPD_DATA_TYPE_DIRECTORY) {
487                 switch(column)
488                 {
489                     case MPDDATA_MODEL_COL_ICON_ID:
490                         g_value_set_string(value, GTK_STOCK_OPEN);
491                         break;
492                     case MPDDATA_MODEL_COL_SONG_TITLE:
493                     case MPDDATA_MODEL_COL_SONG_TITLEFILE:
494                     case MPDDATA_MODEL_COL_MARKUP:
495                         {
496                             gchar *basename = g_path_get_basename(data->directory);
497                             g_value_set_string(value, basename);
498                             g_free(basename);
499                             break;
500                         }
501                     case MPDDATA_MODEL_COL_PATH:
502                         g_value_set_string(value, data->directory);
503                         break;                                                  
504                     default:
505                         break;
506                 }
507             }
508             else if(data->type == MPD_DATA_TYPE_PLAYLIST)
509             {
510                 switch(column)
511                 {
512                     case MPDDATA_MODEL_COL_ICON_ID:
513                         g_value_set_string(value, "media-playlist");
514                         break;
515                     case MPDDATA_MODEL_COL_SONG_TITLE:
516                     case MPDDATA_MODEL_COL_SONG_TITLEFILE:
517                     case MPDDATA_MODEL_COL_MARKUP:
518                         {
519                             gchar *basename = g_path_get_basename(data->playlist);
520                             g_value_set_string(value, basename);
521                             g_free(basename);
522                             break;
523                         }
524                     case MPDDATA_MODEL_COL_PATH:
525                         g_value_set_string(value, data->playlist);
526                         break;                                                  
527                     default:
528                         break;
529                 }
530             }
531             else if(data->type == MPD_DATA_TYPE_SONG)
532             {
533                 mpd_Song *song = data->song;
534                 switch (column) {
535                     case MPDDATA_MODEL_COL_MPDSONG:
536                         g_value_set_pointer(value, song);
537                         break;
538                     case MPDDATA_MODEL_COL_PLAYING:
539                         g_value_set_boolean(value, FALSE);
540                         break;
541                     case MPDDATA_MODEL_COL_PLAYING_FONT_WEIGHT:
542                         g_value_set_int(value, PANGO_WEIGHT_NORMAL);
543                         break;
544                     case MPDDATA_MODEL_COL_MARKUP:
545                         {
546                             /* we want to go cache this stuff */
547                             gchar buffer[1024];
548                             mpd_song_markup(buffer, 1024, GMPC_MPDDATA_MODEL(model)->_priv->markup, song);
549                             g_value_set_string(value, buffer);
550                             break;
551                         }
552                     case MPDDATA_MODEL_COL_PATH:
553                         g_value_set_string(value, song->file);
554                         break;
555                                                                                 case MPDDATA_MODEL_COL_PATH_EXTENSION:
556                                                                                                 {
557                                                                                                         int j = strlen(song->file);
558                                                                                                         for(;j>0&&song->file[j] != '.';j--);
559                                                                                                         g_value_set_string(value, &(song->file)[j+1]);
560                                                                                                         break;
561                                                                                                 }
562                                                                                 case MPDDATA_MODEL_COL_PATH_DIRECTORY:
563                                                                                                 {
564                                                                                                         gchar *dir = g_path_get_dirname(song->file);
565                                                                                                         g_value_set_string(value, dir);
566                                                                                                         g_free(dir);
567                                                                                                         break;
568                                                                                                 }
569                     case MPDDATA_MODEL_COL_SONG_ARTIST:
570                         g_value_set_string(value, song->artist);
571                         break;
572                     case MPDDATA_MODEL_COL_SONG_ALBUM:
573                         g_value_set_string(value, song->album);
574                         break;
575                     case MPDDATA_MODEL_COL_SONG_TITLE:
576                         /* If there is a song available use that, else use the filename */
577                         if(song->title) {
578                             g_value_set_string(value, song->title);
579                         } else {
580                             /* Use the markup stuff, this makes sure it gets processed equaly */
581                             gchar buffer[1024];
582                             mpd_song_markup(buffer, 1024, "%shortfile%", song);
583                             g_value_set_string(value, buffer);
584                         }
585                         break;
586                     case MPDDATA_MODEL_COL_SONG_TITLEFILE:
587                         {
588                             gchar *path = g_path_get_basename(song->file);
589                             g_value_set_string(value, path);
590                             g_free(path);
591                         }
592                         break;                                                          
593                     case MPDDATA_MODEL_COL_SONG_GENRE:
594                         g_value_set_string(value, song->genre);
595                         break;
596                     case MPDDATA_MODEL_COL_SONG_TRACK:
597                         g_value_set_string(value, song->track);
598                         break;
599                     case MPDDATA_MODEL_COL_SONG_NAME:
600                         g_value_set_string(value, song->name);
601                         break;
602                     case MPDDATA_MODEL_COL_SONG_COMPOSER:
603                         g_value_set_string(value, song->composer);
604                         break;
605                     case MPDDATA_MODEL_COL_SONG_PERFORMER:
606                         g_value_set_string(value, song->performer);
607                         break;
608                     case MPDDATA_MODEL_COL_SONG_DATE:
609                         g_value_set_string(value, song->date);
610                         break;
611                     case MPDDATA_MODEL_COL_SONG_LENGTH:
612                         g_value_set_int(value, song->time);
613                         break;
614                     case MPDDATA_MODEL_COL_SONG_LENGTH_FORMAT:
615                         {
616                             if(song->time >= 0) {
617                                 gchar *strdata = g_strdup_printf("%02i:%02i",
618                                         song->time/60, song->time%60);
619                                 g_value_set_string(value, strdata);
620                                 g_free(strdata);
621                             } else {
622                                 g_value_set_string(value, "n/a");
623                             }
624                         }
625                         break;                                                  
626                     case MPDDATA_MODEL_COL_SONG_DISC:
627                         g_value_set_string(value, song->disc);
628                         break;
629                     case MPDDATA_MODEL_COL_SONG_COMMENT:
630                         g_value_set_string(value, song->comment);
631                         break;                                                  
632                     case MPDDATA_MODEL_COL_SONG_POS:
633                         g_value_set_int(value, song->pos+1);
634                         break;
635                     case MPDDATA_MODEL_COL_SONG_ID:
636                         g_value_set_int(value, song->id);
637                         break;
639                     case MPDDATA_MODEL_COL_ICON_ID:
640                         if (strstr(song->file, "://")) {
641                             g_value_set_string(value, "media-stream");
642                         } else {
643                             g_value_set_string(value, "media-audiofile");
644                         }
645                         break;
646                 }
647             }
648         }
650     interface Gtk:Tree:Model
651         private gboolean
652         iter_nth_child(Gtk:Tree:Model *model(check null type), GtkTreeIter *iter (check null), GtkTreeIter *parent, gint n (check >=0)) 
653         {
654             Self *self = GMPC_MPDDATA_MODEL(model);
655             int i;
656             MpdData *data = NULL;
657             if(parent)
658                 return FALSE;
659             if (n >= self->num_rows || n < 0)
660                 return FALSE; 
662             data = (MpdData *)self->_priv->data;
663             i=0;
664             if(self->has_up)
665                 i=1;
666             for(; i < (n);i++)
667             {
668                 data = mpd_data_get_next_real(data, FALSE);
669             }
671             if(self->has_up && n == 0)
672                 data = NULL;
674             iter->stamp = self->_priv->stamp;
675             iter->user_data = data; 
676             iter->user_data2 = GINT_TO_POINTER(n);
677             return TRUE;
678         }
680     interface Gtk:Tree:Model
681         private GtkTreePath *
682         get_path(Gtk:Tree:Model *self (check null type), GtkTreeIter *iter (check null))
683         {
684             GtkTreePath *path = NULL;
685             path = gtk_tree_path_new();
686             gtk_tree_path_append_index(path, GPOINTER_TO_INT(iter->user_data2));
687             return path;
688         }
690     interface Gtk:Tree:Model
691         private GType
692         get_column_type(Gtk:Tree:Model *model(check null type), gint ind (check >= 0))
693         {
694             Self *self = GMPC_MPDDATA_MODEL(model);
695             return self->types[ind];
696         } 
697     public 
698         void 
699         set_request_artist(self, const char *artist)
700         {
701             if(self->_priv->req_artist) 
702                 q_free(self->_priv->req_artist);
703             self->_priv->req_artist = (artist != NULL)?g_strdup(artist):NULL;
704         }
705         public
706         const char *
707         get_request_artist(self)
708         {
709             return self->_priv->req_artist;
710         }
711     public 
712         long unsigned set_mpd_data(self, MpdData *data2)
713         {
714             int i;
715             long unsigned retval = 0;
716             GtkTreeIter iter;
717             GtkTreePath *path = NULL;
718             /* Do some cleanup, like removing rows, and so */
719             /* loop and remove */
720             while ( self->num_rows > 0 ) {
721                 path = gtk_tree_path_new();
722                 gtk_tree_path_append_index(path, self->num_rows - 1 );
723                 /* propegate change */
724                 gtk_tree_model_row_deleted(GTK_TREE_MODEL(self), path);
725                 gtk_tree_path_free(path);
726                 self->num_rows--;
727             }
730             /**
731              * Free if there is a list and set it to NULL 
732              */
733             if(self->_priv->data)
734                 mpd_data_free(self->_priv->data);
735             self->_priv->data = NULL;
737             if(self->num_rows != 0)
738                 debug_printf(DEBUG_ERROR,"not every row cleared %i\n",self->num_rows);
739             self->num_rows =0;
741             /* Free possible stored images */
742             if(self->_priv->images)
743             {
744                 for(i=0;i<self->num_rows;i++)
745                 {
746                     if(self->_priv->images[i])
747                         g_object_unref(self->_priv->images[i]);
748                 }
749                 q_free(self->_priv->images);
750             }
751             if(data2 == NULL)
752             {
753                 self->_priv->playtime = 0;
754                 self_playtime_changed(self, self->_priv->playtime);
755                 return 0;
756             }
758             self->_priv->data = mpd_data_get_first(data2);
759             data2 = NULL;
760             if(self->_priv->sort_column == MPDDATA_MODEL_COL_ICON_ID)
761             {
762                 self->_priv->data = misc_sort_mpddata_by_album_disc_track(self->_priv->data);
763             }
764             else if ( self->_priv->sort_column != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID )
765             {
766                 self->_priv->data = misc_sort_mpddata(self->_priv->data,(GCompareDataFunc)self_sort_func,self ); 
767             }
769             if(self->has_up) {
770                 path = gtk_tree_path_new();
771                 gtk_tree_path_append_index(path, self->num_rows);
772                 iter.stamp = self->_priv->stamp;
773                 iter.user_data = NULL; 
774                 iter.user_data2 =  GINT_TO_POINTER(self->num_rows);
776                 /* propegate change */
777                 gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
778                 gtk_tree_path_free(path);
779                 self->num_rows++;
780             }
781             for(data2 = mpd_data_get_first(self->_priv->data);data2; data2 = mpd_data_get_next_real(data2,FALSE)) {
782                 path = gtk_tree_path_new();
783                 gtk_tree_path_append_index(path,self->num_rows);
784                 iter.stamp = self->_priv->stamp;
785                 iter.user_data = data2;
786                 iter.user_data2 =  GINT_TO_POINTER(self->num_rows);
788                 /* propegate change */
789                 gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
790                 gtk_tree_path_free(path);
791                 self->num_rows++;
792                 if(data2->type == MPD_DATA_TYPE_SONG)
793                 {
794                     if(data2->song && data2->song->time > 0)
795                         retval+= data2->song->time;
796                 }
797             }
798             self->_priv->images = g_malloc0((self->num_rows)*sizeof(*(self->_priv->images)));
800             self->_priv->playtime = retval;
801             self_playtime_changed(self, self->_priv->playtime);
802             return retval;
803         }
805     /* Sorting */
806     interface Gtk:Tree:Sortable
807         private 
808         gboolean get_sort_column_id (Gtk:Tree:Sortable *model ,  gint *sort_column_id, GtkSortType *order)
809         {
810             Self *self = GMPC_MPDDATA_MODEL(model);
811             if(sort_column_id) 
812                 *sort_column_id = self->_priv->sort_column;
813             if(order)
814                 *order = self->_priv->sort_order;
815             return TRUE;
816         }
818     interface Gtk:Tree:Sortable
819         private 
820         void set_sort_column_id (Gtk:Tree:Sortable *model ,  gint sort_column_id, GtkSortType order)
821         {
822             Self *self = GMPC_MPDDATA_MODEL(model);
823             int old_col = self->_priv->sort_column, old_ord = self->_priv->sort_order;
824 /*            if(sort_column_id == -1)
825                 return;
826   */          self->_priv->sort_column = sort_column_id;
827             self->_priv->sort_order = order;
828             /* trigger signal */
829             if(old_col != sort_column_id || old_ord != order)
830                 gtk_tree_sortable_sort_column_changed(model);
831             self->_priv->old_sort_column = self->_priv->sort_column;
833         }
835     interface Gtk:Tree:Sortable
836         private
837         gboolean
838         has_default_sort_func(Gtk:Tree:Sortable *model)
839         {
840             return FALSE;
841         }
842       private 
843         int sort_func(gpointer ppaa, gpointer ppbb, Self *self)
844         {
845             MpdData_real *a = *(MpdData_real **)ppaa;
846             MpdData_real *b = *(MpdData_real **)ppbb;
847             int fact = (self->_priv->sort_order == GTK_SORT_ASCENDING)?-1:1;
849             if(a->type != b->type )
850             {
851                 int val = a->type - b->type; 
852                 return val; 
853             }
854             else if(a->type == b->type)
855             {
856                 int val=0;
857                 GtkTreeIter iter;
858                 GValue va = {0,},vb = {0,};
859                 /* if the type is a directory or a tag, always sort on the title column */
860                 if(a->type == MPD_DATA_TYPE_DIRECTORY || b->type == MPD_DATA_TYPE_TAG)
861                 {
862                     const char *ca,*cb;
863                     iter.user_data = a;
864                     iter.user_data2 = GPOINTER_TO_INT(0);
865                     gtk_tree_model_get_value(GTK_TREE_MODEL(self), &iter, MPDDATA_MODEL_COL_SONG_TITLE, &va);
866                     iter.user_data = b;
867                     gtk_tree_model_get_value(GTK_TREE_MODEL(self), &iter, MPDDATA_MODEL_COL_SONG_TITLE, &vb);
868                     ca = g_value_get_string(&va);
869                     cb = g_value_get_string(&vb);
870                     if(ca && cb) {
871                         gchar *aa,*bb;
872                         aa = g_utf8_strdown(ca, -1);
873                         bb = g_utf8_strdown(cb, -1);
874                         val = g_utf8_collate(aa,bb);
875                         g_free(aa);
876                         g_free(bb);
877                     } else {
878                         val = (ca == NULL)?((cb==NULL)?0:-1):1;
879                     }
880                     g_value_unset(&va);
881                     g_value_unset(&vb);
882                     /* return */
883                     return val*fact;
884                 }
885                 /* Get values from tree view, so we don't have to replicate code to sort the right entries */
886                 iter.user_data = a;
887                 iter.user_data2 = GPOINTER_TO_INT(0);
888                 gtk_tree_model_get_value(GTK_TREE_MODEL(self), &iter, self->_priv->sort_column, &va);
889                 iter.user_data = b;
890                 gtk_tree_model_get_value(GTK_TREE_MODEL(self), &iter, self->_priv->sort_column, &vb);
891                 if(self->_priv->sort_column == MPDDATA_MODEL_COL_SONG_TRACK)
892                 {
893                     const char *ca = g_value_get_string(&va);
894                     const char *cb = g_value_get_string(&vb);
895                     gint ca1 = (ca)?atoi(ca):0;
896                     gint cb1 = (cb)?atoi(cb):0;
897                     g_value_unset(&va);
898                     g_value_unset(&vb);
899                     return fact*(ca1-cb1);
900                 }
901                 else if(self->types[self->_priv->sort_column] == G_TYPE_INT)
902                 {
903                     val = g_value_get_int(&va) - g_value_get_int(&vb);
904                 }
905                 else if (self->types[self->_priv->sort_column] == G_TYPE_STRING)
906                 {
907                     const char *ca = g_value_get_string(&va);
908                     const char *cb = g_value_get_string(&vb);
909                     if(ca && cb) {
910                         gchar *aa,*bb;
911                         aa = g_utf8_strdown(ca, -1);
912                         bb = g_utf8_strdown(cb, -1);
913                         val = g_utf8_collate(aa,bb);
914                         g_free(aa);
915                         g_free(bb);
916                     } else {
917                         val = (ca == NULL)?((cb==NULL)?0:-1):1;
918                     }
919                 }
920                 g_value_unset(&va);
921                 g_value_unset(&vb);
922                 return fact*val; 
923             }
924             return 0;
925         }
927     interface Gtk:Tree:Sortable
928         private
929         void 
930         sort_column_changed(Gtk:Tree:Sortable *model)
931         {
932             Self *self = GMPC_MPDDATA_MODEL(model);
933             if(!self->_priv->data || !(((MpdData_real *)self->_priv->data)->next))
934                 return; 
935             if(self->_priv->sort_column == MPDDATA_MODEL_COL_ICON_ID)
936             {
937                 self->_priv->data = misc_sort_mpddata_by_album_disc_track(self->_priv->data); 
938             }
939             else if(self->_priv->sort_column !=  GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID)
940             {
941                 self->_priv->data = misc_sort_mpddata(self->_priv->data,(GCompareDataFunc)self_sort_func,self ); 
942             }
943             self->_priv->old_sort_column = self->_priv->sort_column;
945             /* tell the view that rows have changed  */    
946             {
947                 int i=0;
948                 GtkTreePath *path = NULL;
949                 GtkTreeIter iter;
950                 MpdData *data2 = mpd_data_get_first(self->_priv->data); 
951                 if(self->has_up) i = 1;
952                 for(;data2; data2 = mpd_data_get_next_real(data2,FALSE)) 
953                 {
954                     path = gtk_tree_path_new();
955                     gtk_tree_path_append_index(path,i);
956                     iter.stamp = self->_priv->stamp;
957                     iter.user_data = data2;
958                     iter.user_data2 =  GINT_TO_POINTER(i);
960                     /* propegate change */
961                     gtk_tree_model_row_changed(GTK_TREE_MODEL(self), path, &iter);
962                     i++;
963                 }
964             }
965         }
967         private
968         int
969         test_sort_func(gpointer ppaa, gpointer ppbb, Self *self)
970         {
971             MpdData_real *a = *(MpdData_real **)ppaa;
972             MpdData_real *b = *(MpdData_real **)ppbb;
973             return strcmp(a->tag, b->tag); 
974         }
976         /**
977          * For now this assumes tag list only.
978          */
979     public 
980         long unsigned set_mpd_data_slow (self, MpdData *data2)
981         {                                               
982             MpdData_real *new_list = NULL;
983             int i=0;
984             int items=0;
985             MpdData_real *original = (MpdData_real *)self->_priv->data;
986             MpdData_real *new = (MpdData_real *) data2;
987             GtkTreePath *path =NULL;
988             GtkTreeIter iter;
990             /* Free possible stored images */
991             if(self->_priv->images)
992             {
993                 for(i=0;i<self->num_rows;i++)
994                 {
995                     if(self->_priv->images[i])
996                         g_object_unref(self->_priv->images[i]);
997                 }
998                 q_free(self->_priv->images);
999             }
1003             /* sort it identical */
1004             new = (MpdData_real *)misc_sort_mpddata(data2,(GCompareDataFunc)self_test_sort_func,self ); 
1007             if(new) {
1008                 MpdData_real *n = new->first;
1009                 while(n) {
1010                     items++;
1011                     n = n->next;
1012                 }
1013             }
1014             i=0;
1015             /* compare lists */
1016             if(original)
1017             {
1019                 int has_next;
1020                 do
1021                 {
1022                     has_next = 0;
1023                     if(new == NULL)
1024                     {
1025                         if(self->num_rows > items)
1026                         {
1027                             /* remove current row */
1028                             self->num_rows--;
1030                             path = gtk_tree_path_new();
1031                             gtk_tree_path_append_index(path,i);
1033                             /* propegate change */
1034                             gtk_tree_model_row_deleted(GTK_TREE_MODEL(self), path);
1035                             gtk_tree_path_free(path);
1037                             original = original->next;
1038                             has_next = (original )?1:0;
1039                         }
1040                     }
1041                     else
1042                     {
1043                         int compare = strcmp(new->tag, original->tag);
1044                         if(compare < 0)
1045                         {
1046                             /* add */
1047                             MpdData_real *n = (MpdData_real *) mpd_new_data_struct_append((MpdData *)new_list); 
1048                             n->type = MPD_DATA_TYPE_TAG;
1049                             n->tag_type = new->tag_type;
1050                             n->tag = new->tag;
1051                             new->tag = NULL;
1052                             new_list = n;
1054                             /* add */
1055                             path = gtk_tree_path_new();
1056                             gtk_tree_path_append_index(path,i);
1057                             iter.stamp = self->_priv->stamp;
1058                             iter.user_data = n;
1059                             iter.user_data2 =  GINT_TO_POINTER(i);
1061                             /* propegate change */
1062                             gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
1063                             gtk_tree_path_free(path);
1064                             self->num_rows++;
1066                             new=new->next;
1067                             i++;
1068                             has_next = 1;
1069                         }
1070                         else if (compare > 0)
1071                         {
1072                             path = gtk_tree_path_new();
1073                             gtk_tree_path_append_index(path,i);
1074                             /* propegate change */
1075                             gtk_tree_model_row_deleted(GTK_TREE_MODEL(self), path);
1076                             gtk_tree_path_free(path);
1077                             self->num_rows--;
1080                             original = original->next; 
1081                             has_next = (original )?1:0;
1083                         }else{
1084                             new_list = (MpdData_real *)mpd_new_data_struct_append((MpdData*)new_list); 
1085                             new_list->type = MPD_DATA_TYPE_TAG;
1086                             new_list->tag_type = new->tag_type;
1087                             new_list->tag = new->tag;
1088                             new->tag = NULL;
1090                             new = new->next;
1091                             original = original->next;
1092                             has_next = (original )?1:0;
1093                             i++;
1094                         }
1095                     }            
1096                 }while(has_next);
1097             }
1098             /* add entries remaining in new */
1099             if(new)
1100             {
1101                 do{
1102                     new_list = (MpdData_real *)mpd_new_data_struct_append((MpdData*)new_list); 
1103                     new_list->type = MPD_DATA_TYPE_TAG;
1104                     new_list->tag_type = new->tag_type;
1105                     new_list->tag = new->tag;
1106                     new->tag = NULL;
1108                     /* add */
1109                     path = gtk_tree_path_new();
1110                     gtk_tree_path_append_index(path,self->num_rows);
1111                     iter.stamp = self->_priv->stamp;
1112                     iter.user_data = new_list;
1113                     iter.user_data2 =  GINT_TO_POINTER(self->num_rows);
1115                     /* propegate change */
1116                     gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
1117                     gtk_tree_path_free(path);
1118                     self->num_rows++;
1120                     new = new->next;
1121                     i++;
1122                 }while(new);
1123             }
1124             if(self->_priv->data)
1125                 mpd_data_free(self->_priv->data);
1127             self->_priv->data = mpd_data_get_first((MpdData*)new_list);
1128             if(data2)
1129                 g_assert(self->_priv->data != NULL);
1131             self->_priv->images = g_malloc0((self->num_rows)*sizeof(*(self->_priv->images)));
1132             if(data2)
1133                 mpd_data_free(data2);
1134             g_assert(items == self->num_rows);
1135             return 0;                    
1136         }
1138         signal last NONE (LONG)
1139         void
1140         playtime_changed(self, gulong playtime)
1141         {
1142             return;
1143         }
1145     public
1146     gulong
1147     get_playtime(self)
1148     {
1149         return self->_priv->playtime;
1150     }
1151     public
1152     gint
1153     get_pos( self, GtkTreeIter *iter)
1154     {
1155             g_assert(iter->stamp == self->_priv->stamp);
1156             return GPOINTER_TO_INT(iter->user_data2);
1157     }