Update vala version.
[gmpc.git] / src / gmpc-mpddata-model.gob
blob1889c5757be05ad5792bbc40bf92878faa394cd6
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.
20 requires 2.0.0
22 %ht{
23 #include <config.h>
24 #include <gtk/gtk.h>
25 #include <libmpd/libmpd.h>
26 #include <libmpd/libmpd-internal.h>
29 %privateheader{
30 //#include "playlist3.h"
31 #include "config1.h"
32 #include "config-defaults.h"
33 #include "main.h"
34 #include "misc.h"
37 %h{
38 #define GMPC_MPD_DATA_MODEL(a) GMPC_MPDDATA_MODEL(a)
39         enum
40         {
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
73         } ;
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)};
94     /* sorting */
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
107         (nick = "Has Up",
108          blurb = "Show an 'up row",
109          default_value = FALSE,
110          export,
111          link);
113     init(self)
114     {
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);
150     }
151     class_init(klass);
153     public
154         Gmpc:MpdData:Model *new (void)
155         {
156             return GET_NEW;
157         }
159     override (G:Object)
160         void
161         finalize (G:Object *obj)
162         {
163             int i =0;
164             Self *self = GMPC_MPDDATA_MODEL(obj);
166             if(self->_priv->data)
167             {
168                 mpd_data_free(self->_priv->data);
169                 self->_priv->data = NULL;
170             }
171             if(self->_priv->images && self->_priv->use_images)
172             {
173                 for(i=0;i<self->num_rows;i++)
174                 {
175                     if(self->_priv->images[i]) {
176                         g_object_unref(self->_priv->images[i]);
177                         self->_priv->images[i] = NULL;
178                     }
179                 }
180                 q_free(self->_priv->images);
181                 /* q_free should do it, but force it */
182                 self->_priv->images = NULL;
183             }
184             PARENT_HANDLER(obj);
185         }
187     public void reset_rating(self)
188     {
189         MpdData_real *data = (MpdData_real *)self->_priv->data;
190         int i = 0;
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();
194                 GtkTreeIter iter;
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);
204             }
205             i++;
206         } 
207     }
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))
212         {
213             /* Here would be the implementation */
214             return (GtkTreeModelFlags)GTK_TREE_MODEL_LIST_ONLY;
215         }
218     interface Gtk:Tree:Model
219         public
220         gboolean iter_children(Gtk:Tree:Model *model, GtkTreeIter *iter, GtkTreeIter *parent)
221         {
222             Self *self  = GMPC_MPDDATA_MODEL(model);
223             if(parent)
224                 return FALSE;
225             if(self->num_rows == 0)
226                 return FALSE;
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 */
233             return TRUE;
234         }
235     /**
236      * Unused, not known in the model directly?
237      */
238     interface Gtk:Tree:Model
239         public
240         gint get_n_columns(Gtk:Tree:Model *model)
241         {
242             return MPDDATA_MODEL_N_COLUMNS;
243         }
245     interface Gtk:Tree:Model
246         private gboolean
247         get_iter(Gtk:Tree:Model *model (check null type),
248                 Gtk:Tree:Iter *iter (check null),
249                 Gtk:Tree:Path *path (check null))
250         {
251             Self *self = GMPC_MPDDATA_MODEL(model);
252             MpdData_real *data = NULL;
253             gint i=0;
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 */
258             /* No Children */
259             g_assert(depth == 1);
261             if (n >= self->num_rows || n < 0)
262                 return FALSE;
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){
267                 data = NULL;
268                 goto end;
269             }
270             data = (MpdData_real *)self->_priv->data;
272             if(self->has_up)
273                 i=1;
275             for(; i < (n);i++)
276             {
277                 data = data->next;
278             }
279 end:
280             iter->stamp = self->_priv->stamp;
281             iter->user_data = data;
282             iter->user_data2 = GINT_TO_POINTER(n);
283             return TRUE;
284         }
286     interface Gtk:Tree:Model
287         private gboolean
288         iter_next(Gtk:Tree:Model *model(check null type), GtkTreeIter *iter (check null))
289         {
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)
295             {
296                 return FALSE;
297             }
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
300              */
301             if(data == NULL &&  n == 0 && self->has_up == TRUE)
302             {
303                 iter->user_data = (MpdData *)self->_priv->data;
304                 iter->user_data2 = GINT_TO_POINTER(1);
305                 if(iter->user_data == NULL)
306                     return FALSE;
307                 return TRUE;
308             }
309             if(mpd_data_get_next_real(data,FALSE) == NULL)
310             {
311                 return FALSE;
312             }
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);
316             return TRUE;
317         }
319     interface Gtk:Tree:Model
320         private gboolean
321         iter_has_child(Gtk:Tree:Model *self (check null type), GtkTreeIter *iter)
322         {
323             return FALSE;
324         }
325     interface Gtk:Tree:Model
326         private gint
327         iter_n_children(Gtk:Tree:Model *model (check null type), GtkTreeIter *iter)
328         {
329             Self *list = GMPC_MPDDATA_MODEL(model);
330             if(iter)
331                 return 0;
332             return list->num_rows;
333         }
335     interface Gtk:Tree:Model
336         private gboolean
337         iter_parent(Gtk:Tree:Model *model (check null type), Gtk:Tree:Iter *iter, Gtk:Tree:Iter *child)
338         {
339             return FALSE;
340         }
341     private
342         void
343         cover_art_fetched(mpd_Song *song, MetaDataResult ret,MetaData *met,GtkTreeRowReference *ref)
344         {
346             if(ref)
347             {
348                 GtkTreePath *path =gtk_tree_row_reference_get_path(ref);
349                 GtkTreeModel *model = gtk_tree_row_reference_get_model(ref);
350                 if(path && model)
351                 {
352                                         int n;
353                     Self *self = GMPC_MPDDATA_MODEL(model);
354                     GtkTreeIter iter;
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);
364                     }
366                     if(ret == META_DATA_AVAILABLE)
367                     {
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),
375                                                                         met->md5sum);
377                         }else{
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);
380                             g_object_unref(pb);
381                         }
382                     }
383                     else   if(ret == META_DATA_UNAVAILABLE)
384                     {
385                         GdkPixbuf *pb2;
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);
388                         g_object_unref(pb2);
389                     }
390                     gtk_tree_model_row_changed(model,path, &iter);
391                     gtk_tree_path_free(path);
392                 }
394                 if(ret == META_DATA_AVAILABLE || ret == META_DATA_UNAVAILABLE)
395                 {
396                     if(ref)
397                     {
398                         gtk_tree_row_reference_free(ref);
399                     }
400                 }
401             }
402         }
404     interface Gtk:Tree:Model
405         public void
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))
407         {
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]);
417             /**
418              * Sanity checks
419              */
420              g_assert((n < self->num_rows || n == 0));
421             g_assert(!(data == NULL && n != 0));
423             /* handle row up */
424             if(data == NULL && n == 0)
425             {
426                 switch(column)
427                 {
428                     case MPDDATA_MODEL_COL_ICON_ID:
429                         g_value_set_string(value, GTK_STOCK_GO_UP);
430                         break;
431                     case MPDDATA_MODEL_ROW_TYPE:
432                         /**
433                          * Make define
434                          */
435                         g_value_set_int(value, -1);
436                         break;
437                     case MPDDATA_MODEL_COL_SONG_TITLE:
438                     case MPDDATA_MODEL_COL_MARKUP:
439                         {
440                             g_value_set_string(value,"..");
441                             break;
442                         }
443                     default:
444                         break;
445                 }
446                 return;
447             }
449             if(column == MPDDATA_MODEL_META_DATA)
450             {
451                 if(self->_priv->use_images && self->_priv->images)
452                 {
453                     if(self->_priv->images[n] == NULL)
454                     {
455                         GtkTreePath *path;
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) )
463                         {
464                             mpd_Song *song;
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);
467                             g_object_unref(pb);
468                             song = mpd_newSong();
469                             song->artist = g_strdup(data->tag);
470                             song->album = NULL;
471                             meta_data_get_path_callback(song, META_ARTIST_ART, (MetaDataCallback)self_cover_art_fetched, (gpointer)ref);
472                             mpd_freeSong(song);
473                         }
475                         else if(data->type == MPD_DATA_TYPE_TAG && data->tag_type == MPD_TAG_ITEM_ALBUM && self->_priv->req_artist)
476                         {
477                             mpd_Song *song;
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);
480                             g_object_unref(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);
486                             mpd_freeSong(song);
487                         }
488                         else if(data->type == MPD_DATA_TYPE_SONG&& data->song && data->song->album && (data->song->artist || self->_priv->req_artist))
489                         {
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)
491                             {
492                                 gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n],self->_priv->blank);
493                             }else{
494                                 mpd_Song *song;
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);
497                                 g_object_unref(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);
503                                 mpd_freeSong(song);
504                             }
505                         }
506                         else
507                         {
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);
510                             g_object_unref(pb);
511                         }
512                         gtk_tree_path_free(path);
513                     }
514                     g_value_set_object(value, gmpc_pixbuf_loader_async_get_pixbuf(self->_priv->images[n]));
515                 }else{
516                     g_value_set_object(value,NULL);
517                 }
518             }
519             if(column == MPDDATA_MODEL_USERDATA)
520             {
521                 g_value_set_pointer(value,data->userdata);
523             }
524             /* handle row type, this is independent of the row type */
525             if(column == MPDDATA_MODEL_ROW_TYPE)
526             {
527                 g_value_set_int(value, data->type);
528                 return;
529             }
530             if (data->type == MPD_DATA_TYPE_TAG) {
531                 switch(column)
532                 {
533                     case MPDDATA_MODEL_COL_ICON_ID:
534                         switch(data->tag_type)
535                         {
536                             case MPD_TAG_ITEM_ALBUM:
537                                 g_value_set_string(value, "media-album");
538                                 break;
539                             case MPD_TAG_ITEM_ARTIST:
540                                 g_value_set_string(value, "media-artist");
541                                 break;
542                             case MPD_TAG_ITEM_GENRE:
543                                 g_value_set_string(value, "media-genre");
544                                 break;
545                             case MPD_TAG_ITEM_TRACK:
546                                 g_value_set_string(value, "media-num-tracks");
547                                 break;
548                             default:
549                                 g_value_set_string(value, "media-tag");
550                         }
551                         break;
552                     case MPDDATA_MODEL_COL_SONG_TITLE:
553                     case MPDDATA_MODEL_COL_MARKUP:
554                         {
555                             g_value_set_string(value, data->tag);
556                             break;
557                         }
558                     case MPDDATA_MODEL_COL_PATH:
559                         g_value_set_string(value, data->tag);
560                         break;
561                     case MPDDATA_MODEL_TAG_TYPE:
562                         g_value_set_int(value, data->tag_type);
563                         break;
564                     default:
565                         break;
566                 }
567             } else if(data->type == MPD_DATA_TYPE_DIRECTORY) {
568                 switch(column)
569                 {
570                     case MPDDATA_MODEL_COL_ICON_ID:
571                         g_value_set_string(value, GTK_STOCK_OPEN);
572                         break;
573                     case MPDDATA_MODEL_COL_SONG_TITLE:
574                     case MPDDATA_MODEL_COL_SONG_TITLEFILE:
575                     case MPDDATA_MODEL_COL_MARKUP:
576                         {
577                             gchar *basename = g_path_get_basename(data->directory);
578                             g_value_set_string(value, basename);
579                             g_free(basename);
580                             break;
581                         }
582                     case MPDDATA_MODEL_COL_PATH:
583                         g_value_set_string(value, data->directory);
584                         break;
585                     default:
586                         break;
587                 }
588             }
589             else if(data->type == MPD_DATA_TYPE_PLAYLIST)
590             {
591                 switch(column)
592                 {
593                     case MPDDATA_MODEL_COL_ICON_ID:
594                         g_value_set_string(value, "media-playlist");
595                         break;
596                     case MPDDATA_MODEL_COL_SONG_TITLE:
597                     case MPDDATA_MODEL_COL_SONG_TITLEFILE:
598                     case MPDDATA_MODEL_COL_MARKUP:
599                         {
600                             gchar *basename = g_path_get_basename(data->playlist->path);
601                             g_value_set_string(value, basename);
602                             g_free(basename);
603                             break;
604                         }
605                     case MPDDATA_MODEL_COL_PATH:
606                         g_value_set_string(value, data->playlist->path);
607                         break;
608                     default:
609                         break;
610                 }
611             }
612             else if(data->type == MPD_DATA_TYPE_SONG)
613             {
614                 mpd_Song *song = data->song;
615                 switch (column) {
616                     case MPDDATA_MODEL_COL_MPDSONG:
617                         g_value_set_pointer(value, song);
618                         break;
619                     case MPDDATA_MODEL_COL_PLAYING:
620                         g_value_set_boolean(value, FALSE);
621                         break;
622                     case MPDDATA_MODEL_COL_PLAYING_FONT_WEIGHT:
623                         g_value_set_int(value, PANGO_WEIGHT_NORMAL);
624                         break;
625                     case MPDDATA_MODEL_COL_MARKUP:
626                         {
627                             /* we want to go cache this stuff */
628                             gchar buffer[1024];
629                             mpd_song_markup(buffer, 1024, GMPC_MPDDATA_MODEL(model)->_priv->markup, song);
630                             g_value_set_string(value, buffer);
631                             break;
632                         }
633                     case MPDDATA_MODEL_COL_PATH:
634                         g_value_set_string(value, song->file);
635                         break;
636                                                                                 case MPDDATA_MODEL_COL_PATH_EXTENSION:
637                                                                                                 {
638                                                     if(!song->file)
639                                                         break;
640                                                     int j = strlen(song->file);
641                                                                                                         for(;j>0&&song->file[j] != '.';j--);
642                                                                                                         g_value_set_string(value, &(song->file)[j+1]);
643                                                                                                         break;
644                                                                                                 }
645                                                                                 case MPDDATA_MODEL_COL_PATH_DIRECTORY:
646                                                                                                 {
647                                                     if(!song->file)
648                                                         break;
649                                                                                                         gchar *dir = g_path_get_dirname(song->file);
650                                                                                                         g_value_set_string(value, dir);
651                                                                                                         g_free(dir);
652                                                                                                         break;
653                                                                                                 }
654                     case MPDDATA_MODEL_COL_SONG_ARTIST:
655                         g_value_set_string(value, song->artist);
656                         break;
657                     case MPDDATA_MODEL_COL_SONG_ALBUM:
658                         g_value_set_string(value, song->album);
659                         break;
660                     case MPDDATA_MODEL_COL_SONG_TITLE:
661                         /* If there is a song available use that, else use the filename */
662                         if(song->title) {
663                             g_value_set_string(value, song->title);
664                         } else if (song->name) {
665                             g_value_set_string(value, song->name);
666                         }else {
667                             /* Use the markup stuff, this makes sure it gets processed equaly */
668                             gchar buffer[1024];
669                             mpd_song_markup(buffer, 1024, "%shortfile%", song);
670                             g_value_set_string(value, buffer);
671                         }
672                         break;
673                     case MPDDATA_MODEL_COL_SONG_TITLEFILE:
674                         if (song->file)
675                         {
676                             gchar *path = g_path_get_basename(song->file);
677                             g_value_set_string(value, path);
678                             g_free(path);
679                         }
680                         break;
681                     case MPDDATA_MODEL_COL_SONG_GENRE:
682                         g_value_set_string(value, song->genre);
683                         break;
684                     case MPDDATA_MODEL_COL_SONG_TRACK:
685                         g_value_set_string(value, song->track);
686                         break;
687                     case MPDDATA_MODEL_COL_SONG_NAME:
688                         g_value_set_string(value, song->name);
689                         break;
690                     case MPDDATA_MODEL_COL_SONG_COMPOSER:
691                         g_value_set_string(value, song->composer);
692                         break;
693                     case MPDDATA_MODEL_COL_SONG_PERFORMER:
694                         g_value_set_string(value, song->performer);
695                         break;
696                     case MPDDATA_MODEL_COL_SONG_DATE:
697                         g_value_set_string(value, song->date);
698                         break;
699                     case MPDDATA_MODEL_COL_SONG_LENGTH:
700                         g_value_set_int(value, song->time);
701                         break;
702                     case MPDDATA_MODEL_COL_SONG_RATING:
703                         if(song->rating < 0) {
704                             gchar *str = mpd_sticker_song_get(connection, song->file, "rating");
705                             if(str != NULL ) {
706                                 song->rating = atoi(str);
708                                 g_free(str);
709                             }
710                         }
711                         g_value_set_int(value,
712                                 (song->rating > 10)? 10: ((song->rating < 0)? 0: song->rating)
713                                 );
714                         break;
715                     case MPDDATA_MODEL_COL_SONG_LENGTH_FORMAT:
716                         {
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);
721                                 g_free(strdata);
722                             } else {
723                                 g_value_set_string(value, "n/a");
724                             }
725                         }
726                         break;
727                     case MPDDATA_MODEL_COL_SONG_DISC:
728                         g_value_set_string(value, song->disc);
729                         break;
730                     case MPDDATA_MODEL_COL_SONG_COMMENT:
731                         g_value_set_string(value, song->comment);
732                         break;
733                     case MPDDATA_MODEL_COL_SONG_POS:
734                         g_value_set_int(value, song->pos+1);
735                         break;
736                     case MPDDATA_MODEL_COL_SONG_ALBUMARTIST:
737                         g_value_set_string(value, song->albumartist);
738                         break;
739                     case MPDDATA_MODEL_COL_SONG_ID:
740                         g_value_set_int(value, song->id);
741                         break;
743                     case MPDDATA_MODEL_COL_ICON_ID:
744                         if (song->file && strstr(song->file, "://")) {
745                             g_value_set_string(value, "media-stream");
746                         } else {
747                             g_value_set_string(value, "media-audiofile");
748                         }
749                         break;
750                     case MPDDATA_MODEL_COL_SONG_PRIORITY:
751                         g_value_set_int(value, song->priority);
752                         break;
753                     default:
754                         break;
755                 }
756             }
757         }
759     interface Gtk:Tree:Model
760         private gboolean
761         iter_nth_child(Gtk:Tree:Model *model(check null type), GtkTreeIter *iter (check null), GtkTreeIter *parent, gint n (check >=0))
762         {
763             Self *self = GMPC_MPDDATA_MODEL(model);
764             int i;
765             MpdData_real *data = NULL;
766             if(parent)
767                 return FALSE;
768             if (n >= self->num_rows || n < 0)
769                 return FALSE;
771             data = (MpdData_real *)self->_priv->data;
772             i=0;
773             if(self->has_up)
774                 i=1;
775             for(; i < (n);i++)
776             {
777                 data = data->next;//mpd_data_get_next_real(data, FALSE);
778                 g_assert(data != NULL);
779             }
781             if(self->has_up && n == 0)
782                 data = NULL;
784             iter->stamp = self->_priv->stamp;
785             iter->user_data = data;
786             iter->user_data2 = GINT_TO_POINTER(n);
787             return TRUE;
788         }
790     interface Gtk:Tree:Model
791         private GtkTreePath *
792         get_path(Gtk:Tree:Model *self (check null type), GtkTreeIter *iter (check null))
793         {
794             GtkTreePath *path = NULL;
795             path = gtk_tree_path_new();
796             gtk_tree_path_append_index(path, GPOINTER_TO_INT(iter->user_data2));
797             return path;
798         }
800     interface Gtk:Tree:Model
801         private GType
802         get_column_type(Gtk:Tree:Model *model(check null type), gint ind (check >= 0))
803         {
804             Self *self = GMPC_MPDDATA_MODEL(model);
805             return self->types[ind];
806         }
807     public
808         void
809         set_request_artist(self, const char *artist)
810         {
811             int i;
812             GtkTreePath *path = NULL;
813             GtkTreeIter iter;
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)
819             {
820                 MpdData *data2 = mpd_data_get_first(self->_priv->data);
821                 for(i=0;i<self->num_rows;i++)
822                 {
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);
836                     }
837                     data2 = mpd_data_get_next_real(data2,FALSE);
838                 }
839             }
840         }
841         public
842         const char *
843         get_request_artist(self)
844         {
845             return self->_priv->req_artist;
846         }
847     public
848         long unsigned set_mpd_data(self, MpdData *data2)
849         {
850             int i;
851             long unsigned retval = 0;
852             GtkTreeIter iter;
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);
863                 self->num_rows--;
864             }
867             /**
868              * Free if there is a list and set it to NULL
869              */
870             if(self->_priv->data){
871                 mpd_data_free(mpd_data_get_first(self->_priv->data));
872             }
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);
877             self->num_rows =0;
878             /* Free possible stored images */
879             if(self->_priv->images && self->_priv->use_images)
880             {
881                 for(i=0;i< old_num_rows;i++)
882                 {
883                     if(self->_priv->images[i])
884                         g_object_unref(self->_priv->images[i]);
885                 }
886                 q_free(self->_priv->images);
887             }
888             if(data2 == NULL)
889             {
890                 self->_priv->playtime = 0;
891                 self_playtime_changed(self, self->_priv->playtime);
892                 return 0;
893             }
895             self->_priv->data = mpd_data_get_first(data2);
896             data2 = NULL;
897             if(self->_priv->sort_column == MPDDATA_MODEL_COL_ICON_ID)
898             {
899                 self->_priv->data = misc_sort_mpddata_by_album_disc_track(self->_priv->data);
900             }
901             else if ( self->_priv->sort_column != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID )
902             {
903                 self->_priv->data = misc_sort_mpddata(self->_priv->data,(GCompareDataFunc)self_sort_func,self );
904             }
906             if(self->has_up) {
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);
913                 self->num_rows++;
914                 /* propegate change */
915                 gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
916                 gtk_tree_path_free(path);
917             }
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);
925                 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)
930                 {
931                     if(data2->song && data2->song->time > 0)
932                         retval+= data2->song->time;
933                 }
934             }
935             if(self->_priv->use_images)
936             {
937                 self->_priv->images = g_malloc0((self->num_rows)*sizeof(*(self->_priv->images)));
938             }
940             self->_priv->playtime = retval;
941             self_playtime_changed(self, self->_priv->playtime);
942             return retval;
943         }
945     /* Sorting */
946     interface Gtk:Tree:Sortable
947         private
948         gboolean get_sort_column_id (Gtk:Tree:Sortable *model ,  gint *sort_column_id, GtkSortType *order)
949         {
950             Self *self = GMPC_MPDDATA_MODEL(model);
951             if(sort_column_id)
952                 *sort_column_id = self->_priv->sort_column;
953             if(order)
954                 *order = self->_priv->sort_order;
955             return TRUE;
956         }
958     interface Gtk:Tree:Sortable
959         private
960         void set_sort_column_id (Gtk:Tree:Sortable *model ,  gint sort_column_id, GtkSortType order)
961         {
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;
967             /* trigger signal */
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;
972         }
974     interface Gtk:Tree:Sortable
975         private
976         gboolean
977         has_default_sort_func(Gtk:Tree:Sortable *model)
978         {
979             return FALSE;
980         }
981       private
982         int sort_func(gpointer ppaa, gpointer ppbb, Self *self)
983         {
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 )
989             {
990                 int val = a->type - b->type;
991                 return val;
992             }
993             else if(a->type == b->type)
994             {
995                 int val=0;
996                 GtkTreeIter iter;
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 */
999                 iter.user_data = a;
1000                 iter.user_data2 = GPOINTER_TO_INT(0);
1001                 gtk_tree_model_get_value(GTK_TREE_MODEL(self), &iter, self->_priv->sort_column, &va);
1002                 iter.user_data = b;
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)
1006                 {
1007                     const char *ca,*cb;
1008                     ca = g_value_get_string(&va);
1009                     cb = g_value_get_string(&vb);
1010                     if(ca && cb) {
1011                         gchar *aa,*bb;
1012                         aa = g_utf8_strdown(ca, -1);
1013                         bb = g_utf8_strdown(cb, -1);
1014                         val = g_utf8_collate(aa,bb);
1015                         g_free(aa);
1016                         g_free(bb);
1017                     } else {
1018                         val = (ca == NULL)?((cb==NULL)?0:-1):1;
1019                     }
1020                     g_value_unset(&va);
1021                     g_value_unset(&vb);
1022                     /* return */
1023                     return val*fact;
1024                 }
1025                 if(self->_priv->sort_column == MPDDATA_MODEL_COL_SONG_TRACK)
1026                 {
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;
1031                     g_value_unset(&va);
1032                     g_value_unset(&vb);
1033                     return fact*(ca1-cb1);
1034                 }
1035                 else if(self->types[self->_priv->sort_column] == G_TYPE_INT)
1036                 {
1037                     val = g_value_get_int(&va) - g_value_get_int(&vb);
1038                 }
1039                 else if (self->types[self->_priv->sort_column] == G_TYPE_STRING)
1040                 {
1041                     const char *ca = g_value_get_string(&va);
1042                     const char *cb = g_value_get_string(&vb);
1043                     if(ca && cb) {
1044                         gchar *aa,*bb;
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);
1048                         g_free(aa);
1049                         g_free(bb);
1050                     } else {
1051                         val = (ca == NULL)?((cb==NULL)?0:-1):1;
1052                     }
1053                 }
1055                 if (val == 0 &&
1056                         a->type == MPD_DATA_TYPE_SONG &&
1057                         b->type == MPD_DATA_TYPE_SONG &&
1058                         a->song->file &&
1059                         b->song->file)
1060                 {
1061                     val = strcmp (a->song->file, b->song->file);
1062                 }
1064                 g_value_unset(&va);
1065                 g_value_unset(&vb);
1066                 return fact*val;
1067             }
1068             return 0;
1069         }
1071     interface Gtk:Tree:Sortable
1072         private
1073         void
1074         sort_column_changed(Gtk:Tree:Sortable *model)
1075         {
1076             Self *self = GMPC_MPDDATA_MODEL(model);
1077             if(!self->_priv->data || !(((MpdData_real *)self->_priv->data)->next))
1078                 return;
1079             if(self->_priv->sort_column == MPDDATA_MODEL_COL_ICON_ID)
1080             {
1081                 self->_priv->data = misc_sort_mpddata_by_album_disc_track(self->_priv->data);
1082             }
1083             else if(self->_priv->sort_column !=  GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID)
1084             {
1085                 self->_priv->data = misc_sort_mpddata(self->_priv->data,(GCompareDataFunc)self_sort_func,self );
1086             }
1087             self->_priv->old_sort_column = self->_priv->sort_column;
1089             /* tell the view that rows have changed  */
1090             {
1091                 int i=0;
1092                 GtkTreePath *path = NULL;
1093                 GtkTreeIter iter;
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))
1097                 {
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);
1107                     i++;
1108                 }
1109             }
1110         }
1112     public 
1113         int
1114         test_sort_func(gpointer ppaa, gpointer ppbb, GmpcMpdDataModel *self)
1115         {
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)
1119             {
1120                 if(a->tag_type != b->tag_type)
1121                     return a->tag_type - b->tag_type;
1122                 if(a->tag== NULL && b->tag != NULL)
1123                     return -1;
1124                 else if(b->tag == NULL && a->tag != NULL)
1125                     return 1;
1126                 else if (a->tag  && b->tag)
1127                 {
1128                     int val;
1129                     if(a->tag && b->tag) {
1130                         gchar *sa,*sb;
1131                         sa = g_utf8_strdown(a->tag, -1);
1132                         sb = g_utf8_strdown(b->tag, -1);
1133                         val = g_utf8_collate(sa,sb);
1134                         g_free(sa);
1135                         g_free(sb);
1136                     } else {
1137                         val = (a == NULL)?((b==NULL)?0:-1):1;
1138                     }
1139                     return val;
1140                 }
1141             }
1142             return a->type - b->type;
1143         }
1145         /**
1146          * For now this assumes tag list only.
1147          */
1148     public
1149         long unsigned set_mpd_data_slow (self, MpdData *data2)
1150         {
1151             MpdData_real *new_list = NULL;
1152             int i=0;
1153             int items=0;
1154             MpdData_real *original = (MpdData_real *)mpd_data_get_first(self->_priv->data);
1155             MpdData_real *new = (MpdData_real *) data2;
1156             GtkTreePath *path =NULL;
1157             GtkTreeIter iter;
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;
1164             if(new) {
1165                 MpdData_real *n = new->first;
1166                 while(n) {
1167                     items++;
1168                     n = n->next;
1169                 }
1170             }
1173             /* Free possible stored images */
1174             /**
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
1177              */
1178             /* Grow the list when needed */
1180             if(self->_priv->use_images)
1181             {
1182                 /* Clean them, they might not be in the right place anymore */
1183                 for(i=0;i< old_list_size; i++)
1184                 {
1185                     if(self->_priv->images[i] != NULL) g_object_unref(self->_priv->images[i]);
1186                     self->_priv->images[i] = NULL;
1187                 }
1188                 if(items > self->num_rows)
1189                 {
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++)
1193                     {
1194                         self->_priv->images[i] = NULL;
1195                     }
1196                 }
1197             }
1202             i=0;
1203             /* compare lists */
1204             if(self->_priv->data && original == NULL) g_warning("weirdness\n");
1205             if(self->_priv->data)
1206             {
1207                 int has_next;
1208                 do
1209                 {
1210                     has_next = 0;
1211                     if(new == NULL)
1212                     {
1213                         if(self->num_rows > items)
1214                         {
1215                             MpdData_real *n = original->next;
1216                             /* remove current row */
1217                             self->num_rows--;
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);
1229                             original=n;
1230                             has_next = (original )?1:0;
1231                         }
1232                     }
1233                     else
1234                     {
1235                         int compare;
1236                         compare = g_utf8_collate(new->tag,original->tag);
1237                         if(compare < 0)
1238                         {
1239                             /* add */
1240                             MpdData_real *n = (MpdData_real *) mpd_new_data_struct();
1241                             n->type = MPD_DATA_TYPE_TAG;
1242                             n->tag_type = new->tag_type;
1243                             n->tag = new->tag;
1244                             new->tag = NULL;
1246                             /* add */
1248                             self->num_rows++;
1249                             n->prev = original->prev;
1251                             if(original->prev)
1252                                 original->prev->next = n;
1253                             n->next = original;
1254                             n->first = original->first;
1255                             original->prev = n;
1256                             if(n->prev == NULL){
1257                                 MpdData_real *fiter;
1258                                 for(fiter = n;fiter;fiter = fiter->next){
1259                                     fiter->first = n;
1260                                 }
1261                             }
1263                             self->_priv->data = mpd_data_get_first((MpdData *)original);
1264                             {
1265                                 path = gtk_tree_path_new();
1266                                 gtk_tree_path_append_index(path,i);
1267                                 iter.stamp = self->_priv->stamp;
1268                                 iter.user_data = n;
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);
1273                             }
1274                             /* mark for insert */
1275                             new=new->next;
1276                             i++;
1277                             has_next = 1;
1278                         }
1279                         else if (compare > 0)
1280                         {
1281                             MpdData_real *n = original->next;
1282                             path = gtk_tree_path_new();
1283                             gtk_tree_path_append_index(path,i);
1284                             /* propegate change */
1286                             self->num_rows--;
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);
1293                             original = n;
1294                             //original = original->next;
1295                             has_next = (original )?1:0;
1297                         }else{
1298                             new = new->next;
1299                             original = original->next;
1300                             has_next = (original )?1:0;
1301                             i++;
1302                         }
1303                     }
1304                 }while(has_next);
1305             }
1306             /* add entries remaining in new */
1307             if(new)
1308             {
1310                 new_list = (MpdData_real *) self->_priv->data;
1311                 while(new_list && new_list->next) new_list = new_list->next;
1312                 do{
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;
1317                     new->tag = NULL;
1319                     self->_priv->data = (MpdData *)new_list->first;
1320                     if(self->_priv->data == NULL) g_warning("self->_priv->data == NULL");
1321                     self->num_rows++;
1322                     {
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);
1331                     }
1333                     new = new->next;
1334                     i++;
1335                 }while(new);
1336             }
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++)
1342                     {
1343                         /* clean it */
1344                         if(self->_priv->images[i] != NULL) g_object_unref(self->_priv->images[i]);
1345                     }
1346                     self->_priv->images = g_realloc(self->_priv->images, (self->num_rows)*sizeof(GdkPixbuf *));
1347                 }
1348             }
1352             if(data2)
1353                 mpd_data_free(data2);
1355             g_assert(items == self->num_rows);
1356             return 0;
1357         }
1359         signal last NONE (LONG)
1360         void
1361         playtime_changed(self, gulong playtime)
1362         {
1363             return;
1364         }
1366     public
1367     gulong
1368     get_playtime(self)
1369     {
1370         return self->_priv->playtime;
1371     }
1372     public
1373     gint
1374     get_pos( self, GtkTreeIter *iter)
1375     {
1376             g_assert(iter->stamp == self->_priv->stamp);
1377             return GPOINTER_TO_INT(iter->user_data2);
1378     }
1380     public
1381     void
1382     disable_image(self)
1383     {
1384         g_assert(self->_priv->data == NULL);
1385         self->_priv->use_images = FALSE;
1386     }
1388     public
1389     MpdData *
1390     steal_mpd_data(self)
1391     {
1392         GtkTreePath *path;
1393         int i;
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);
1402             self->num_rows--;
1403         }
1405         /* Clear */
1406         self->_priv->data = NULL;
1407         /* Free possible stored images */
1408         if(self->_priv->images && self->_priv->use_images)
1409         {
1410             for(i=0;i<  old_num_rows;i++)
1411             {
1412                 if(self->_priv->images[i])
1413                     g_object_unref(self->_priv->images[i]);
1414             }
1415             q_free(self->_priv->images);
1416         }
1417         self->_priv->playtime = 0;
1418         self_playtime_changed(self, self->_priv->playtime);
1420         return mpd_data_get_first(data);
1421     }
1423     /* Drag and stop handling */
1426                 interface Gtk:Tree:Drag:Source
1427                 private
1428                 gboolean row_draggable (Gtk:Tree:Drag:Source *drag_source, Gtk:Tree:Path *path)
1429                 {
1430             GtkTreeIter iter;
1431             if(gtk_tree_model_get_iter(GTK_TREE_MODEL(drag_source), &iter, path))
1432             {
1433                 MpdData *data = iter.user_data;
1434                 if(data->type == MPD_DATA_TYPE_SONG) {
1435                     return TRUE;
1436                 }
1437             }
1438             return FALSE;
1439         }
1441         interface Gtk:Tree:Drag:Source
1442         private
1443                 gboolean drag_data_delete (Gtk:Tree:Drag:Source *drag_source, Gtk:Tree:Path *path)
1444                 {
1445                         return TRUE;
1446                 }
1447                 interface Gtk:Tree:Drag:Source
1448                         private
1449                 gboolean drag_data_get (Gtk:Tree:Drag:Source *drag_source, Gtk:Tree:Path *path, Gtk:Selection:Data *selection_data)
1450                 {
1451             if(!gtk_tree_set_row_drag_data(selection_data,GTK_TREE_MODEL(drag_source), path)) {
1452                 return FALSE;
1453             }
1454                         return TRUE;
1455                 }