Merge branch 'master', remote-tracking branch 'home/master'
[gmpc.git] / src / gmpc-mpddata-model.gob
blob2c6b450ca9182b9401fab9bf985cfd8346160ff2
1 /* Gnome Music Player Client (GMPC)
2  * Copyright (C) 2004-2011 Qball Cow <qball@gmpclient.org>
3  * Project homepage: http://gmpclient.org/
4  
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_TAG_TYPE,                                   /* tag type */
67                 MPDDATA_MODEL_ROW_TYPE,                                         /* type of the row */
68                 MPDDATA_MODEL_META_DATA,                        /* metadata */
69                 MPDDATA_MODEL_USERDATA,
70                 MPDDATA_MODEL_N_COLUMNS
71         } ;
75 #define LOG_DOMAIN "MpdData.Model"
78  class Gmpc:MpdData:Model from G:Object
79           (interface Gtk:Tree:Sortable)
80             (interface Gtk:Tree:Drag:Source)
81             (interface Gtk:Tree:Model)
83     private gint stamp          = {g_random_int()};
84     public      GType types[MPDDATA_MODEL_N_COLUMNS]; 
85     private MpdData *data       = NULL;
86     public gint num_rows = 0;
87     private GmpcPixbufLoaderAsync **images = NULL;
88     private gchar *req_artist = {NULL} destroywith g_free;
89     private GdkPixbuf *blank = {NULL} destroywith g_object_unref;
90     public gint icon_size = {cfg_get_single_value_as_int_with_default(config, "gmpc-mpddata-model", "icon-size", 32)};
92     /* sorting */
93     private GtkSortType sort_order = GTK_SORT_ASCENDING;
94     private int sort_column = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID;
95     private int old_sort_column = GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID;
96     public gboolean has_up       = FALSE;
97     private gchar *markup        = {cfg_get_single_value_as_string_with_default(config, "playlist", "browser_markup",DEFAULT_MARKUP_BROWSER)} destroywith g_free;
99     private gulong playtime = {0};
102     private gboolean use_images = TRUE;
104     property BOOLEAN has_up
105         (nick = "Has Up",
106          blurb = "Show an 'up row",
107          default_value = FALSE,
108          export,
109          link);
111     init(self)
112     {
113         self->types[MPDDATA_MODEL_COL_MPDSONG] = G_TYPE_POINTER;
114         self->types[MPDDATA_MODEL_COL_MARKUP] = G_TYPE_STRING;
115         self->types[MPDDATA_MODEL_COL_PLAYING] = G_TYPE_BOOLEAN;
116         self->types[MPDDATA_MODEL_COL_PLAYING_FONT_WEIGHT] = G_TYPE_INT;
117         self->types[MPDDATA_MODEL_COL_PATH] = G_TYPE_STRING;
118         self->types[MPDDATA_MODEL_COL_SONG_ARTIST] = G_TYPE_STRING;
119         self->types[MPDDATA_MODEL_COL_SONG_ALBUM] = G_TYPE_STRING;
120         self->types[MPDDATA_MODEL_COL_SONG_TITLE] = G_TYPE_STRING;
121         self->types[MPDDATA_MODEL_COL_SONG_TITLEFILE] = G_TYPE_STRING;
122         self->types[MPDDATA_MODEL_COL_SONG_TRACK] = G_TYPE_STRING;
123         self->types[MPDDATA_MODEL_COL_SONG_GENRE] = G_TYPE_STRING;
124         self->types[MPDDATA_MODEL_COL_SONG_NAME] = G_TYPE_STRING;
125         self->types[MPDDATA_MODEL_COL_SONG_COMPOSER] = G_TYPE_STRING;
126         self->types[MPDDATA_MODEL_COL_SONG_PERFORMER]= G_TYPE_STRING;
127         self->types[MPDDATA_MODEL_COL_SONG_DATE] = G_TYPE_STRING;
128         self->types[MPDDATA_MODEL_COL_SONG_LENGTH] = G_TYPE_INT;
129         self->types[MPDDATA_MODEL_COL_SONG_LENGTH_FORMAT] = G_TYPE_STRING;
130         self->types[MPDDATA_MODEL_COL_SONG_DISC] = G_TYPE_STRING;
131         self->types[MPDDATA_MODEL_COL_SONG_COMMENT] = G_TYPE_STRING;
132         self->types[MPDDATA_MODEL_COL_SONG_POS] = G_TYPE_INT;
133         self->types[MPDDATA_MODEL_COL_SONG_ALBUMARTIST] = G_TYPE_STRING;
134         self->types[MPDDATA_MODEL_COL_PATH_EXTENSION] = G_TYPE_STRING;
135         self->types[MPDDATA_MODEL_COL_PATH_DIRECTORY] = G_TYPE_STRING;
136         self->types[MPDDATA_MODEL_COL_SONG_ID] = G_TYPE_INT;
137         self->types[MPDDATA_MODEL_COL_ICON_ID] = G_TYPE_STRING;
138         self->types[MPDDATA_MODEL_TAG_TYPE] = G_TYPE_INT;
139         self->types[MPDDATA_MODEL_ROW_TYPE] = G_TYPE_INT;
140         self->types[MPDDATA_MODEL_USERDATA] = G_TYPE_POINTER;
141         self->types[MPDDATA_MODEL_META_DATA] = GDK_TYPE_PIXBUF;
144         self->_priv->blank = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, self->icon_size, self->icon_size);
145         gdk_pixbuf_fill(self->_priv->blank, 0x00000000);
146     }
147     class_init(klass);
149     public
150         Gmpc:MpdData:Model *new (void)
151         {
152             return GET_NEW;
153         }
155     override (G:Object)
156         void 
157         finalize (G:Object *obj)
158         {
159             int i =0;
160             Self *self = GMPC_MPDDATA_MODEL(obj); 
162             if(self->_priv->data)
163             {
164                 mpd_data_free(self->_priv->data);
165                 self->_priv->data = NULL;
166             }
167             if(self->_priv->images && self->_priv->use_images)
168             {
169                 for(i=0;i<self->num_rows;i++)
170                 {
171                     if(self->_priv->images[i]) {
172                         g_object_unref(self->_priv->images[i]);
173                         self->_priv->images[i] = NULL;
174                     }
175                 }
176                 q_free(self->_priv->images);
177                 /* q_free should do it, but force it */
178                 self->_priv->images = NULL;
179             }
180             PARENT_HANDLER(obj);
181         }
182     /* function implemented for the Gtk:Tree:Model interface */
183     interface Gtk:Tree:Model
184         private GtkTreeModelFlags
185         get_flags (Gtk:Tree:Model *self (check null type))
186         {
187             /* Here would be the implementation */
188             return (GtkTreeModelFlags)GTK_TREE_MODEL_LIST_ONLY;
189         }
192     interface Gtk:Tree:Model
193         public
194         gboolean iter_children(Gtk:Tree:Model *model, GtkTreeIter *iter, GtkTreeIter *parent)
195         {
196             Self *self  = GMPC_MPDDATA_MODEL(model);
197             if(parent)
198                 return FALSE;
199             if(self->num_rows == 0)
200                 return FALSE;
201             /* Set iter to first item in list */
202             iter->stamp = self->_priv->stamp; 
203             iter->user_data = self->_priv->data;
204             iter->user_data2 =  GINT_TO_POINTER(0);
205             iter->user_data3 = NULL;    /* unused */
207             return TRUE;
208         }
209     /**
210      * Unused, not known in the model directly? 
211      */
212     interface Gtk:Tree:Model    
213         public
214         gint get_n_columns(Gtk:Tree:Model *model)
215         {
216             return MPDDATA_MODEL_N_COLUMNS;
217         }
219     interface Gtk:Tree:Model
220         private gboolean
221         get_iter(Gtk:Tree:Model *model (check null type),
222                 Gtk:Tree:Iter *iter (check null),
223                 Gtk:Tree:Path *path (check null))
224         {
225             Self *self = GMPC_MPDDATA_MODEL(model);
226             MpdData_real *data = NULL;
227             gint i=0;
228             const gint *indices = gtk_tree_path_get_indices(path);
229             gint depth = gtk_tree_path_get_depth(path);
230             gint n = indices[0];        /* the n-th top level row */
232             /* No Children */
233             g_assert(depth == 1); 
235             if (n >= self->num_rows || n < 0)
236                 return FALSE; 
238             g_assert(n<self->num_rows);
239             /* If it is the first item in a has_up list, do nothing */
240             if(self->has_up && n == 0){
241                 data = NULL;
242                 goto end;
243             }
244             data = (MpdData_real *)self->_priv->data;
246             if(self->has_up)
247                 i=1;
249             for(; i < (n);i++)
250             {
251                 data = data->next; 
252             }
253 end:
254             iter->stamp = self->_priv->stamp;
255             iter->user_data = data; 
256             iter->user_data2 = GINT_TO_POINTER(n);
257             return TRUE;
258         }
260     interface Gtk:Tree:Model
261         private gboolean
262         iter_next(Gtk:Tree:Model *model(check null type), GtkTreeIter *iter (check null))
263         {
264             Self *self = GMPC_MPDDATA_MODEL(model);
265             MpdData *data = iter->user_data;
266             int n = GPOINTER_TO_INT(iter->user_data2) ;
267             /* if the row is the last row in the list or bigger then the last row, return FALSE */
268             if(n >= self->num_rows)
269             {
270                 return FALSE;
271             }
272             /* if the row is the first row in the list, and it has a "go-up" entry, 
273              * it can have a next entry
274              */
275             if(data == NULL &&  n == 0 && self->has_up == TRUE)
276             {
277                 iter->user_data = (MpdData *)self->_priv->data; 
278                 iter->user_data2 = GINT_TO_POINTER(1);
279                 if(iter->user_data == NULL)
280                     return FALSE;
281                 return TRUE;
282             }
283             if(mpd_data_get_next_real(data,FALSE) == NULL)
284             {
285                 return FALSE;
286             }
287             iter->user_data = (MpdData *)mpd_data_get_next_real(data,FALSE); 
288             iter->user_data2 = GINT_TO_POINTER(n+1);
289             g_assert(iter->user_data != NULL);
290             return TRUE;
291         }
293     interface Gtk:Tree:Model
294         private gboolean
295         iter_has_child(Gtk:Tree:Model *self (check null type), GtkTreeIter *iter)
296         {
297             return FALSE;
298         }
299     interface Gtk:Tree:Model
300         private gint
301         iter_n_children(Gtk:Tree:Model *model (check null type), GtkTreeIter *iter)
302         { 
303             Self *list = GMPC_MPDDATA_MODEL(model);
304             if(iter)
305                 return 0;
306             return list->num_rows;
307         }
309     interface Gtk:Tree:Model
310         private gboolean
311         iter_parent(Gtk:Tree:Model *model (check null type), Gtk:Tree:Iter *iter, Gtk:Tree:Iter *child)
312         {
313             return FALSE;
314         }
315     private 
316         void
317         cover_art_fetched(mpd_Song *song, MetaDataResult ret,MetaData *met,GtkTreeRowReference *ref)
318         {
320             if(ref)
321             {
322                 GtkTreePath *path =gtk_tree_row_reference_get_path(ref);
323                 GtkTreeModel *model = gtk_tree_row_reference_get_model(ref);
324                 if(path && model)
325                 {
326                                         int n;  
327                     Self *self = GMPC_MPDDATA_MODEL(model);
328                     GtkTreeIter iter;
330                     g_assert(self->_priv->use_images == TRUE);
331                     
332                     gtk_tree_model_get_iter(model, &iter, path);
333                     n = GPOINTER_TO_INT(iter.user_data2) ;
335                     if(self->_priv->images[n] == NULL){
336                         self->_priv->images[n]= gmpc_pixbuf_loader_async_new();
337                         gmpc_pixbuf_loader_async_set_rref(GMPC_PIXBUF_LOADER_ASYNC(self->_priv->images[n]), ref);
338                     }
340                     if(ret == META_DATA_AVAILABLE)
341                     {
342                         
343                         GdkPixbuf *pb = NULL;
344                         if(met->content_type == META_DATA_CONTENT_URI)
345                         {
346                             const gchar *coverpath = meta_data_get_uri(met);
347                             MpdData_real *data = iter.user_data;        
348                             gmpc_pixbuf_loader_async_set_from_file(GMPC_PIXBUF_LOADER_ASYNC(self->_priv->images[n]),
349                                     coverpath, self->icon_size, self->icon_size,
350                                     ((data->type == MPD_DATA_TYPE_TAG && data->tag_type == MPD_TAG_ITEM_ALBUM) || (data->type == MPD_DATA_TYPE_SONG)?GMPC_MODIFICATION_TYPE_CASING: GMPC_MODIFICATION_TYPE_NONE));
351                                                 } else if (met->content_type == META_DATA_CONTENT_RAW) {
352                             MpdData_real *data = iter.user_data;        
353                             gmpc_pixbuf_loader_async_set_from_raw(GMPC_PIXBUF_LOADER_ASYNC(self->_priv->images[n]),
354                                     met->content, met->size, self->icon_size, self->icon_size,
355                                     ((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));
357                         }else{
358                             pb = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-no-cover", self->icon_size, 0,NULL);
359                             gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n], pb);
360                             g_object_unref(pb);
361                         }
362                     }
363                     else   if(ret == META_DATA_UNAVAILABLE)
364                     {
365                         GdkPixbuf *pb2;
366                         pb2 = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-no-cover", self->icon_size, 0,NULL);
367                         gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n], pb2);
368                         g_object_unref(pb2);
369                     }
370                     gtk_tree_model_row_changed(model,path, &iter);
371                     gtk_tree_path_free(path);
372                 }
374                 if(ret == META_DATA_AVAILABLE || ret == META_DATA_UNAVAILABLE)
375                 {
376                     if(ref)
377                     {
378                         gtk_tree_row_reference_free(ref);
379                     }
380                 }
381             }
382         }
384     interface Gtk:Tree:Model
385         public void
386         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))
387         {
388             Self *self = GMPC_MPDDATA_MODEL(model);
389             MpdData_real *data = iter->user_data;       
390             int n = GPOINTER_TO_INT(iter->user_data2);
392             if(self->_priv->data == NULL)
393                 g_warning("self->_priv->data == NULL");
394             /* set value to the correct type */
395             g_value_init(value, self->types[column]);
397             /** 
398              * Sanity checks 
399              */
400              g_assert((n < self->num_rows || n == 0));
401             g_assert(!(data == NULL && n != 0));
403             /* handle row up */
404             if(data == NULL && n == 0)
405             {
406                 switch(column)
407                 {
408                     case MPDDATA_MODEL_COL_ICON_ID:
409                         g_value_set_string(value, GTK_STOCK_GO_UP);
410                         break;
411                     case MPDDATA_MODEL_ROW_TYPE:
412                         /**
413                          * Make define 
414                          */
415                         g_value_set_int(value, -1);
416                         break;
417                     case MPDDATA_MODEL_COL_SONG_TITLE:
418                     case MPDDATA_MODEL_COL_MARKUP:
419                         {
420                             g_value_set_string(value,"..");
421                             break;
422                         }
423                     default:
424                         break;
425                 }
426                 return;
427             }
429             if(column == MPDDATA_MODEL_META_DATA)
430             {
431                 if(self->_priv->use_images && self->_priv->images)
432                 {
433                     if(self->_priv->images[n] == NULL)
434                     {
435                         GtkTreePath *path;
436                         GtkTreeRowReference *ref;
437                         path = gtk_tree_model_get_path(GTK_TREE_MODEL(self), iter);
438                         ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(self),path);
440                         self->_priv->images[n]= gmpc_pixbuf_loader_async_new();
441                         gmpc_pixbuf_loader_async_set_rref(GMPC_PIXBUF_LOADER_ASYNC(self->_priv->images[n]), ref);
442                         if(data->type == MPD_DATA_TYPE_TAG && (data->tag_type == MPD_TAG_ITEM_ARTIST || data->tag_type == MPD_TAG_ITEM_ALBUM_ARTIST) )
443                         {
444                             mpd_Song *song;
445                             GdkPixbuf *pb = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-loading-cover", self->icon_size, 0,NULL);
446                             gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n], pb);
447                             g_object_unref(pb);
448                             song = mpd_newSong();
449                             song->artist = g_strdup(data->tag);
450                             song->album = NULL; 
451                             gmpc_meta_watcher_get_meta_path_callback(gmw,song, META_ARTIST_ART, (MetaDataCallback)self_cover_art_fetched, (gpointer)ref);
452                             mpd_freeSong(song); 
453                         }
455                         else if(data->type == MPD_DATA_TYPE_TAG && data->tag_type == MPD_TAG_ITEM_ALBUM && self->_priv->req_artist)
456                         {
457                             mpd_Song *song;
458                             GdkPixbuf *pb = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-loading-cover", self->icon_size, 0,NULL);
459                             gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n],pb); 
460                             g_object_unref(pb);
461                             song = mpd_newSong();
462                             song->artist = g_strdup(self->_priv->req_artist); 
463                             song->albumartist = g_strdup(self->_priv->req_artist); 
464                             song->album = g_strdup(data->tag);
465                             gmpc_meta_watcher_get_meta_path_callback(gmw,song, META_ALBUM_ART, (MetaDataCallback)self_cover_art_fetched, ref);
466                             mpd_freeSong(song); 
467                         }
468                         else if(data->type == MPD_DATA_TYPE_SONG&& data->song && data->song->album && (data->song->artist || self->_priv->req_artist)) 
469                         {
470                             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)
471                             {
472                                 gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n],self->_priv->blank);
473                             }else{
474                                 mpd_Song *song;
475                                 GdkPixbuf *pb = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-loading-cover", self->icon_size, 0,NULL);
476                                 gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n],pb);
477                                 g_object_unref(pb);
478                                 song = mpd_newSong();
479                                 song->artist = g_strdup((data->song->artist)?data->song->artist:self->_priv->req_artist); 
480                                 song->albumartist = g_strdup((data->song->albumartist)?data->song->albumartist:self->_priv->req_artist); 
481                                 song->album = g_strdup(data->song->album);
482                                 gmpc_meta_watcher_get_meta_path_callback(gmw,song, META_ALBUM_ART, (MetaDataCallback)self_cover_art_fetched, ref);
483                                 mpd_freeSong(song);     
484                             }
485                         }
486                         else 
487                         {
488                             GdkPixbuf *pb = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-no-cover", self->icon_size, 0,NULL);
489                             gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n],pb);
490                             g_object_unref(pb);
491                         }
492                         gtk_tree_path_free(path);
493                     }
494                     g_value_set_object(value, gmpc_pixbuf_loader_async_get_pixbuf(self->_priv->images[n]));
495                 }else{
496                     g_value_set_object(value,NULL); 
497                 }
498             }
499             if(column == MPDDATA_MODEL_USERDATA)
500             {
501                 g_value_set_pointer(value,data->userdata);
503             }
504             /* handle row type, this is independent of the row type */
505             if(column == MPDDATA_MODEL_ROW_TYPE)
506             {
507                 g_value_set_int(value, data->type);
508                 return;
509             } 
510             if (data->type == MPD_DATA_TYPE_TAG) {
511                 switch(column)
512                 {
513                     case MPDDATA_MODEL_COL_ICON_ID:
514                         switch(data->tag_type) 
515                         {
516                             case MPD_TAG_ITEM_ALBUM:
517                                 g_value_set_string(value, "media-album");
518                                 break;
519                             case MPD_TAG_ITEM_ARTIST:
520                                 g_value_set_string(value, "media-artist");
521                                 break;
522                             case MPD_TAG_ITEM_GENRE:
523                                 g_value_set_string(value, "media-genre");
524                                 break;
525                             case MPD_TAG_ITEM_TRACK:
526                                 g_value_set_string(value, "media-num-tracks");
527                                 break;
528                             default:
529                                 g_value_set_string(value, "media-tag");
530                         }
531                         break;
532                     case MPDDATA_MODEL_COL_SONG_TITLE:
533                     case MPDDATA_MODEL_COL_MARKUP:
534                         {
535                             g_value_set_string(value, data->tag);
536                             break;
537                         }
538                     case MPDDATA_MODEL_COL_PATH:
539                         g_value_set_string(value, data->tag);
540                         break;                                                  
541                     case MPDDATA_MODEL_TAG_TYPE:
542                         g_value_set_int(value, data->tag_type);
543                         break;                                                  
544                     default:
545                         break;
546                 }
547             } else if(data->type == MPD_DATA_TYPE_DIRECTORY) {
548                 switch(column)
549                 {
550                     case MPDDATA_MODEL_COL_ICON_ID:
551                         g_value_set_string(value, GTK_STOCK_OPEN);
552                         break;
553                     case MPDDATA_MODEL_COL_SONG_TITLE:
554                     case MPDDATA_MODEL_COL_SONG_TITLEFILE:
555                     case MPDDATA_MODEL_COL_MARKUP:
556                         {
557                             gchar *basename = g_path_get_basename(data->directory);
558                             g_value_set_string(value, basename);
559                             g_free(basename);
560                             break;
561                         }
562                     case MPDDATA_MODEL_COL_PATH:
563                         g_value_set_string(value, data->directory);
564                         break;                                                  
565                     default:
566                         break;
567                 }
568             }
569             else if(data->type == MPD_DATA_TYPE_PLAYLIST)
570             {
571                 switch(column)
572                 {
573                     case MPDDATA_MODEL_COL_ICON_ID:
574                         g_value_set_string(value, "media-playlist");
575                         break;
576                     case MPDDATA_MODEL_COL_SONG_TITLE:
577                     case MPDDATA_MODEL_COL_SONG_TITLEFILE:
578                     case MPDDATA_MODEL_COL_MARKUP:
579                         {
580                             gchar *basename = g_path_get_basename(data->playlist->path);
581                             g_value_set_string(value, basename);
582                             g_free(basename);
583                             break;
584                         }
585                     case MPDDATA_MODEL_COL_PATH:
586                         g_value_set_string(value, data->playlist->path);
587                         break;                                                  
588                     default:
589                         break;
590                 }
591             }
592             else if(data->type == MPD_DATA_TYPE_SONG)
593             {
594                 mpd_Song *song = data->song;
595                 switch (column) {
596                     case MPDDATA_MODEL_COL_MPDSONG:
597                         g_value_set_pointer(value, song);
598                         break;
599                     case MPDDATA_MODEL_COL_PLAYING:
600                         g_value_set_boolean(value, FALSE);
601                         break;
602                     case MPDDATA_MODEL_COL_PLAYING_FONT_WEIGHT:
603                         g_value_set_int(value, PANGO_WEIGHT_NORMAL);
604                         break;
605                     case MPDDATA_MODEL_COL_MARKUP:
606                         {
607                             /* we want to go cache this stuff */
608                             gchar buffer[1024];
609                             mpd_song_markup(buffer, 1024, GMPC_MPDDATA_MODEL(model)->_priv->markup, song);
610                             g_value_set_string(value, buffer);
611                             break;
612                         }
613                     case MPDDATA_MODEL_COL_PATH:
614                         g_value_set_string(value, song->file);
615                         break;
616                                                                                 case MPDDATA_MODEL_COL_PATH_EXTENSION:
617                                                                                                 {
618                                                                                                         int j = strlen(song->file);
619                                                                                                         for(;j>0&&song->file[j] != '.';j--);
620                                                                                                         g_value_set_string(value, &(song->file)[j+1]);
621                                                                                                         break;
622                                                                                                 }
623                                                                                 case MPDDATA_MODEL_COL_PATH_DIRECTORY:
624                                                                                                 {
625                                                                                                         gchar *dir = g_path_get_dirname(song->file);
626                                                                                                         g_value_set_string(value, dir);
627                                                                                                         g_free(dir);
628                                                                                                         break;
629                                                                                                 }
630                     case MPDDATA_MODEL_COL_SONG_ARTIST:
631                         g_value_set_string(value, song->artist);
632                         break;
633                     case MPDDATA_MODEL_COL_SONG_ALBUM:
634                         g_value_set_string(value, song->album);
635                         break;
636                     case MPDDATA_MODEL_COL_SONG_TITLE:
637                         /* If there is a song available use that, else use the filename */
638                         if(song->title) {
639                             g_value_set_string(value, song->title);
640                         } else if (song->name) {
641                             g_value_set_string(value, song->name);
642                         }else {
643                             /* Use the markup stuff, this makes sure it gets processed equaly */
644                             gchar buffer[1024];
645                             mpd_song_markup(buffer, 1024, "%shortfile%", song);
646                             g_value_set_string(value, buffer);
647                         }
648                         break;
649                     case MPDDATA_MODEL_COL_SONG_TITLEFILE:
650                         {
651                             gchar *path = g_path_get_basename(song->file);
652                             g_value_set_string(value, path);
653                             g_free(path);
654                         }
655                         break;                                                          
656                     case MPDDATA_MODEL_COL_SONG_GENRE:
657                         g_value_set_string(value, song->genre);
658                         break;
659                     case MPDDATA_MODEL_COL_SONG_TRACK:
660                         g_value_set_string(value, song->track);
661                         break;
662                     case MPDDATA_MODEL_COL_SONG_NAME:
663                         g_value_set_string(value, song->name);
664                         break;
665                     case MPDDATA_MODEL_COL_SONG_COMPOSER:
666                         g_value_set_string(value, song->composer);
667                         break;
668                     case MPDDATA_MODEL_COL_SONG_PERFORMER:
669                         g_value_set_string(value, song->performer);
670                         break;
671                     case MPDDATA_MODEL_COL_SONG_DATE:
672                         g_value_set_string(value, song->date);
673                         break;
674                     case MPDDATA_MODEL_COL_SONG_LENGTH:
675                         g_value_set_int(value, song->time);
676                         break;
677                     case MPDDATA_MODEL_COL_SONG_LENGTH_FORMAT:
678                         {
679                             if(song->time >= 0) {
680                                 gchar *strdata = g_strdup_printf("%02i:%02i",
681                                         song->time/60, song->time%60);
682                                 g_value_set_string(value, strdata);
683                                 g_free(strdata);
684                             } else {
685                                 g_value_set_string(value, "n/a");
686                             }
687                         }
688                         break;                                                  
689                     case MPDDATA_MODEL_COL_SONG_DISC:
690                         g_value_set_string(value, song->disc);
691                         break;
692                     case MPDDATA_MODEL_COL_SONG_COMMENT:
693                         g_value_set_string(value, song->comment);
694                         break;                                                  
695                     case MPDDATA_MODEL_COL_SONG_POS:
696                         g_value_set_int(value, song->pos+1);
697                         break;
698                     case MPDDATA_MODEL_COL_SONG_ALBUMARTIST:
699                         g_value_set_string(value, song->albumartist);
700                         break;
701                     case MPDDATA_MODEL_COL_SONG_ID:
702                         g_value_set_int(value, song->id);
703                         break;
705                     case MPDDATA_MODEL_COL_ICON_ID:
706                         if (strstr(song->file, "://")) {
707                             g_value_set_string(value, "media-stream");
708                         } else {
709                             g_value_set_string(value, "media-audiofile");
710                         }
711                         break;
712                     default:
713                         break;
714                 }
715             }
716         }
718     interface Gtk:Tree:Model
719         private gboolean
720         iter_nth_child(Gtk:Tree:Model *model(check null type), GtkTreeIter *iter (check null), GtkTreeIter *parent, gint n (check >=0)) 
721         {
722             Self *self = GMPC_MPDDATA_MODEL(model);
723             int i;
724             MpdData_real *data = NULL;
725             if(parent)
726                 return FALSE;
727             if (n >= self->num_rows || n < 0)
728                 return FALSE; 
730             data = (MpdData_real *)self->_priv->data;
731             i=0;
732             if(self->has_up)
733                 i=1;
734             for(; i < (n);i++)
735             {
736                 data = data->next;//mpd_data_get_next_real(data, FALSE);
737                 g_assert(data != NULL);
738             }
740             if(self->has_up && n == 0)
741                 data = NULL;
743             iter->stamp = self->_priv->stamp;
744             iter->user_data = data; 
745             iter->user_data2 = GINT_TO_POINTER(n);
746             return TRUE;
747         }
749     interface Gtk:Tree:Model
750         private GtkTreePath *
751         get_path(Gtk:Tree:Model *self (check null type), GtkTreeIter *iter (check null))
752         {
753             GtkTreePath *path = NULL;
754             path = gtk_tree_path_new();
755             gtk_tree_path_append_index(path, GPOINTER_TO_INT(iter->user_data2));
756             return path;
757         }
759     interface Gtk:Tree:Model
760         private GType
761         get_column_type(Gtk:Tree:Model *model(check null type), gint ind (check >= 0))
762         {
763             Self *self = GMPC_MPDDATA_MODEL(model);
764             return self->types[ind];
765         } 
766     public 
767         void 
768         set_request_artist(self, const char *artist)
769         {
770             int i;
771             GtkTreePath *path = NULL;
772             GtkTreeIter iter;
773             if(self->_priv->req_artist) 
774                 q_free(self->_priv->req_artist);
775             self->_priv->req_artist = (artist != NULL && artist[0] != '\0')?g_strdup(artist):NULL;
776             /* Free possible stored images */
777             if(self->_priv->images && self->_priv->use_images)
778             {
779                 MpdData *data2 = mpd_data_get_first(self->_priv->data); 
780                 for(i=0;i<self->num_rows;i++)
781                 {
782                     if(self->_priv->images[i]){
783                         g_object_unref(self->_priv->images[i]);
784                         self->_priv->images[i] = NULL;
785                     
786                         /* Update the view */
787                         path = gtk_tree_path_new();
788                         gtk_tree_path_append_index(path,i);
789                         iter.stamp = self->_priv->stamp;
790                         iter.user_data = data2;
791                         iter.user_data2 =  GINT_TO_POINTER(i);
792                         /* propegate change */
793                         gtk_tree_model_row_changed(GTK_TREE_MODEL(self), path, &iter);
794                         gtk_tree_path_free(path);
795                     }
796                     data2 = mpd_data_get_next_real(data2,FALSE);
797                 }
798             }
799         }
800         public
801         const char *
802         get_request_artist(self)
803         {
804             return self->_priv->req_artist;
805         }
806     public 
807         long unsigned set_mpd_data(self, MpdData *data2)
808         {
809             int i;
810             long unsigned retval = 0;
811             GtkTreeIter iter;
812             GtkTreePath *path = NULL;
813             int old_num_rows = self->num_rows;
814             /* Do some cleanup, like removing rows, and so */
815             /* loop and remove */
816             while ( self->num_rows > 0 ) {
817                 path = gtk_tree_path_new();
818                 gtk_tree_path_append_index(path, self->num_rows - 1 );
819                 /* propegate change */
820                 gtk_tree_model_row_deleted(GTK_TREE_MODEL(self), path);
821                 gtk_tree_path_free(path);
822                 self->num_rows--;
823             }
826             /**
827              * Free if there is a list and set it to NULL 
828              */
829             if(self->_priv->data){
830                 mpd_data_free(mpd_data_get_first(self->_priv->data));
831             }
832             self->_priv->data = NULL;
834             if(self->num_rows != 0)
835                 g_log(LOG_DOMAIN, G_LOG_LEVEL_WARNING,"not every row cleared %i\n",self->num_rows);
836             self->num_rows =0;
837             /* Free possible stored images */
838             if(self->_priv->images && self->_priv->use_images)
839             {
840                 for(i=0;i< old_num_rows;i++)
841                 {
842                     if(self->_priv->images[i])
843                         g_object_unref(self->_priv->images[i]);
844                 }
845                 q_free(self->_priv->images);
846             }
847             if(data2 == NULL)
848             {
849                 self->_priv->playtime = 0;
850                 self_playtime_changed(self, self->_priv->playtime);
851                 return 0;
852             }
854             self->_priv->data = mpd_data_get_first(data2);
855             data2 = NULL;
856             if(self->_priv->sort_column == MPDDATA_MODEL_COL_ICON_ID)
857             {
858                 self->_priv->data = misc_sort_mpddata_by_album_disc_track(self->_priv->data);
859             }
860             else if ( self->_priv->sort_column != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID )
861             {
862                 self->_priv->data = misc_sort_mpddata(self->_priv->data,(GCompareDataFunc)self_sort_func,self ); 
863             }
865             if(self->has_up) {
866                 path = gtk_tree_path_new();
867                 gtk_tree_path_append_index(path, self->num_rows);
868                 iter.stamp = self->_priv->stamp;
869                 iter.user_data = NULL; 
870                 iter.user_data2 =  GINT_TO_POINTER(self->num_rows);
872                 self->num_rows++;
873                 /* propegate change */
874                 gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
875                 gtk_tree_path_free(path);
876             }
877             for(data2 = mpd_data_get_first(self->_priv->data);data2; data2 = mpd_data_get_next_real(data2,FALSE)) {
878                 path = gtk_tree_path_new();
879                 gtk_tree_path_append_index(path,self->num_rows);
880                 iter.stamp = self->_priv->stamp;
881                 iter.user_data = data2;
882                 iter.user_data2 =  GINT_TO_POINTER(self->num_rows);
884                 self->num_rows++;
885                 /* propegate change */
886                 gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
887                 gtk_tree_path_free(path);
888                 if(data2->type == MPD_DATA_TYPE_SONG)
889                 {
890                     if(data2->song && data2->song->time > 0)
891                         retval+= data2->song->time;
892                 }
893             }
894             if(self->_priv->use_images)
895             {
896                 self->_priv->images = g_malloc0((self->num_rows)*sizeof(*(self->_priv->images)));
897             }
899             self->_priv->playtime = retval;
900             self_playtime_changed(self, self->_priv->playtime);
901             return retval;
902         }
904     /* Sorting */
905     interface Gtk:Tree:Sortable
906         private 
907         gboolean get_sort_column_id (Gtk:Tree:Sortable *model ,  gint *sort_column_id, GtkSortType *order)
908         {
909             Self *self = GMPC_MPDDATA_MODEL(model);
910             if(sort_column_id) 
911                 *sort_column_id = self->_priv->sort_column;
912             if(order)
913                 *order = self->_priv->sort_order;
914             return TRUE;
915         }
917     interface Gtk:Tree:Sortable
918         private 
919         void set_sort_column_id (Gtk:Tree:Sortable *model ,  gint sort_column_id, GtkSortType order)
920         {
921             Self *self = GMPC_MPDDATA_MODEL(model);
922             gint old_col = self->_priv->sort_column;
923             GtkSortType old_ord = self->_priv->sort_order;
924             self->_priv->sort_column = sort_column_id;
925             self->_priv->sort_order = order;
926             /* trigger signal */
927             if(old_col != sort_column_id || old_ord != order)
928                 gtk_tree_sortable_sort_column_changed(model);
929             self->_priv->old_sort_column = self->_priv->sort_column;
931         }
933     interface Gtk:Tree:Sortable
934         private
935         gboolean
936         has_default_sort_func(Gtk:Tree:Sortable *model)
937         {
938             return FALSE;
939         }
940       private 
941         int sort_func(gpointer ppaa, gpointer ppbb, Self *self)
942         {
943             MpdData_real *a = *(MpdData_real **)ppaa;
944             MpdData_real *b = *(MpdData_real **)ppbb;
945             int fact = (self->_priv->sort_order == GTK_SORT_ASCENDING)?-1:1;
947             if(a->type != b->type )
948             {
949                 int val = a->type - b->type; 
950                 return val; 
951             }
952             else if(a->type == b->type)
953             {
954                 int val=0;
955                 GtkTreeIter iter;
956                 GValue va = {0,},vb = {0,};
957                 /* Get values from tree view, so we don't have to replicate code to sort the right entries */
958                 iter.user_data = a;
959                 iter.user_data2 = GPOINTER_TO_INT(0);
960                 gtk_tree_model_get_value(GTK_TREE_MODEL(self), &iter, self->_priv->sort_column, &va);
961                 iter.user_data = b;
962                 gtk_tree_model_get_value(GTK_TREE_MODEL(self), &iter, self->_priv->sort_column, &vb);
963                 /* if the type is a directory or a tag, always sort on the title column */
964                 if(a->type == MPD_DATA_TYPE_DIRECTORY || b->type == MPD_DATA_TYPE_TAG)
965                 {
966                     const char *ca,*cb;
967                     ca = g_value_get_string(&va);
968                     cb = g_value_get_string(&vb);
969                     if(ca && cb) {
970                         gchar *aa,*bb;
971                         aa = g_utf8_strdown(ca, -1);
972                         bb = g_utf8_strdown(cb, -1);
973                         val = g_utf8_collate(aa,bb);
974                         g_free(aa);
975                         g_free(bb);
976                     } else {
977                         val = (ca == NULL)?((cb==NULL)?0:-1):1;
978                     }
979                     g_value_unset(&va);
980                     g_value_unset(&vb);
981                     /* return */
982                     return val*fact;
983                 }
984                 if(self->_priv->sort_column == MPDDATA_MODEL_COL_SONG_TRACK)
985                 {
986                     const char *ca = g_value_get_string(&va);
987                     const char *cb = g_value_get_string(&vb);
988                     gint ca1 = (ca)?atoi(ca):0;
989                     gint cb1 = (cb)?atoi(cb):0;
990                     g_value_unset(&va);
991                     g_value_unset(&vb);
992                     return fact*(ca1-cb1);
993                 }
994                 else if(self->types[self->_priv->sort_column] == G_TYPE_INT)
995                 {
996                     val = g_value_get_int(&va) - g_value_get_int(&vb);
997                 }
998                 else if (self->types[self->_priv->sort_column] == G_TYPE_STRING)
999                 {
1000                     const char *ca = g_value_get_string(&va);
1001                     const char *cb = g_value_get_string(&vb);
1002                     if(ca && cb) {
1003                         gchar *aa,*bb;
1004                         aa = g_utf8_normalize(ca, -1,G_NORMALIZE_ALL);
1005                         bb = g_utf8_normalize(cb, -1,G_NORMALIZE_ALL);
1006                         val = g_utf8_collate(aa,bb);
1007                         g_free(aa);
1008                         g_free(bb);
1009                     } else {
1010                         val = (ca == NULL)?((cb==NULL)?0:-1):1;
1011                     }
1012                 }
1014                 if (val == 0 &&
1015                         a->type == MPD_DATA_TYPE_SONG &&
1016                         b->type == MPD_DATA_TYPE_SONG && 
1017                         a->song->file &&
1018                         b->song->file)
1019                 {
1020                     val = strcmp (a->song->file, b->song->file);
1021                 }
1023                 g_value_unset(&va);
1024                 g_value_unset(&vb);
1025                 return fact*val; 
1026             }
1027             return 0;
1028         }
1030     interface Gtk:Tree:Sortable
1031         private
1032         void 
1033         sort_column_changed(Gtk:Tree:Sortable *model)
1034         {
1035             Self *self = GMPC_MPDDATA_MODEL(model);
1036             if(!self->_priv->data || !(((MpdData_real *)self->_priv->data)->next))
1037                 return; 
1038             if(self->_priv->sort_column == MPDDATA_MODEL_COL_ICON_ID)
1039             {
1040                 self->_priv->data = misc_sort_mpddata_by_album_disc_track(self->_priv->data); 
1041             }
1042             else if(self->_priv->sort_column !=  GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID)
1043             {
1044                 self->_priv->data = misc_sort_mpddata(self->_priv->data,(GCompareDataFunc)self_sort_func,self ); 
1045             }
1046             self->_priv->old_sort_column = self->_priv->sort_column;
1048             /* tell the view that rows have changed  */    
1049             {
1050                 int i=0;
1051                 GtkTreePath *path = NULL;
1052                 GtkTreeIter iter;
1053                 MpdData *data2 = mpd_data_get_first(self->_priv->data); 
1054                 if(self->has_up) i = 1;
1055                 for(;data2; data2 = mpd_data_get_next_real(data2,FALSE)) 
1056                 {
1057                     path = gtk_tree_path_new();
1058                     gtk_tree_path_append_index(path,i);
1059                     iter.stamp = self->_priv->stamp;
1060                     iter.user_data = data2;
1061                     iter.user_data2 =  GINT_TO_POINTER(i);
1063                     /* propegate change */
1064                     gtk_tree_model_row_changed(GTK_TREE_MODEL(self), path, &iter);
1065                     gtk_tree_path_free(path);
1066                     i++;
1067                 }
1068             }
1069         }
1071         private
1072         int
1073         test_sort_func(gpointer ppaa, gpointer ppbb, Self *self)
1074         {
1075             MpdData_real *a = *(MpdData_real **)ppaa;
1076             MpdData_real *b = *(MpdData_real **)ppbb;
1077             if(a->type == MPD_DATA_TYPE_TAG && b->type == MPD_DATA_TYPE_TAG)
1078             {
1079                 if(a->tag_type != b->tag_type)
1080                     return a->tag_type - b->tag_type;
1081                 if(a->tag== NULL && b->tag != NULL)
1082                     return -1;
1083                 else if(b->tag == NULL && a->tag != NULL)
1084                     return 1;
1085                 else if (a->tag  && b->tag)
1086                 {
1087                     int val;
1088                     if(a->tag && b->tag) {
1089                         gchar *sa,*sb;
1090                         sa = g_utf8_strdown(a->tag, -1);
1091                         sb = g_utf8_strdown(b->tag, -1);
1092                         val = g_utf8_collate(sa,sb);
1093                         g_free(sa);
1094                         g_free(sb);
1095                     } else {
1096                         val = (a == NULL)?((b==NULL)?0:-1):1;
1097                     }
1098                     return val;
1099                 }
1100             }
1101             return a->type - b->type;
1102         }
1103         
1104         /**
1105          * For now this assumes tag list only.
1106          */
1107     public 
1108         long unsigned set_mpd_data_slow (self, MpdData *data2)
1109         {                                               
1110             MpdData_real *new_list = NULL;
1111             int i=0;
1112             int items=0;
1113             MpdData_real *original = (MpdData_real *)mpd_data_get_first(self->_priv->data);
1114             MpdData_real *new = (MpdData_real *) data2;
1115             GtkTreePath *path =NULL;
1116             GtkTreeIter iter;
1117             int old_list_size = self->num_rows;
1119             /* sort it identical */
1120             data2 = misc_sort_mpddata(data2,(GCompareDataFunc)self_test_sort_func,self ); 
1121             new = (MpdData_real *)data2;
1123             if(new) {
1124                 MpdData_real *n = new->first;
1125                 while(n) {
1126                     items++;
1127                     n = n->next;
1128                 }
1129             }
1132             /* Free possible stored images */
1133             /**
1134              * TODO: Don't free the list, but grow it to MAX(items, self->num_rows);
1135              * Then after updating, resize it to new size 
1136              */
1137             /* Grow the list when needed */
1139             if(self->_priv->use_images)
1140             {
1141                 /* Clean them, they might not be in the right place anymore */
1142                 for(i=0;i< old_list_size; i++)
1143                 {
1144                     if(self->_priv->images[i] != NULL) g_object_unref(self->_priv->images[i]);
1145                     self->_priv->images[i] = NULL;
1146                 }
1147                 if(items > self->num_rows)
1148                 {
1149                     self->_priv->images = g_realloc(self->_priv->images, items*sizeof(GdkPixbuf *));
1150                     /* set the new ones to NUll */
1151                     for(i=self->num_rows;i<items;i++)
1152                     {
1153                         self->_priv->images[i] = NULL;
1154                     }
1155                 }
1156             }
1161             i=0;
1162             /* compare lists */
1163             if(self->_priv->data && original == NULL) g_warning("weirdness\n");
1164             if(self->_priv->data)
1165             {
1166                 int has_next;
1167                 do
1168                 {
1169                     has_next = 0;
1170                     if(new == NULL)
1171                     {
1172                         if(self->num_rows > items)
1173                         {
1174                             MpdData_real *n = original->next;
1175                             /* remove current row */
1176                             self->num_rows--;
1178                             path = gtk_tree_path_new();
1179                             gtk_tree_path_append_index(path,i);
1181                             /* propegate change */
1182                             gtk_tree_model_row_deleted(GTK_TREE_MODEL(self), path);
1183                             gtk_tree_path_free(path);
1184                            
1185                             original =(MpdData_real *) mpd_data_delete_item((MpdData *)original); 
1186                             self->_priv->data = mpd_data_get_first((MpdData *)original);
1188                             original=n;
1189                             has_next = (original )?1:0;
1190                         }
1191                     }
1192                     else
1193                     {
1194                         int compare;
1195                         gchar *aa,*bb;
1196                         compare = g_utf8_collate(new->tag,original->tag);
1197                         if(compare < 0)
1198                         {
1199                             /* add */
1200                             MpdData_real *n = (MpdData_real *) mpd_new_data_struct();
1201                             n->type = MPD_DATA_TYPE_TAG;
1202                             n->tag_type = new->tag_type;
1203                             n->tag = new->tag;
1204                             new->tag = NULL;
1206                             /* add */
1208                             self->num_rows++;
1209                             n->prev = original->prev;
1211                             if(original->prev)
1212                                 original->prev->next = n;
1213                             n->next = original;
1214                             n->first = original->first;
1215                             original->prev = n;
1216                             if(n->prev == NULL){
1217                                 MpdData_real *fiter;
1218                                 for(fiter = n;fiter;fiter = fiter->next){
1219                                     fiter->first = n;
1220                                 }
1221                             }
1223                             self->_priv->data = mpd_data_get_first((MpdData *)original);
1224                             {
1225                                 path = gtk_tree_path_new();
1226                                 gtk_tree_path_append_index(path,i);
1227                                 iter.stamp = self->_priv->stamp;
1228                                 iter.user_data = n;
1229                                 iter.user_data2 =  GINT_TO_POINTER(i);
1231                                 gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
1232                                 gtk_tree_path_free(path);
1233                             }
1234                             /* mark for insert */
1235                             new=new->next;
1236                             i++;
1237                             has_next = 1;
1238                         }
1239                         else if (compare > 0)
1240                         {
1241                             MpdData_real *n = original->next;
1242                             path = gtk_tree_path_new();
1243                             gtk_tree_path_append_index(path,i);
1244                             /* propegate change */
1246                             self->num_rows--;
1247                             gtk_tree_model_row_deleted(GTK_TREE_MODEL(self), path);
1248                             gtk_tree_path_free(path);
1251                             original = (MpdData_real *)mpd_data_delete_item((MpdData *)original); 
1252                             self->_priv->data = mpd_data_get_first((MpdData *)original);
1253                             original = n;
1254                             //original = original->next; 
1255                             has_next = (original )?1:0;
1257                         }else{
1258                             new = new->next;
1259                             original = original->next;
1260                             has_next = (original )?1:0;
1261                             i++;
1262                         }
1263                     }            
1264                 }while(has_next);
1265             }
1266             /* add entries remaining in new */
1267             if(new)
1268             {
1269                 
1270                 new_list = (MpdData_real *) self->_priv->data;
1271                 while(new_list && new_list->next) new_list = new_list->next;
1272                 do{
1273                     new_list = (MpdData_real *)mpd_new_data_struct_append((MpdData*)new_list); 
1274                     new_list->type = MPD_DATA_TYPE_TAG;
1275                     new_list->tag_type = new->tag_type;
1276                     new_list->tag = new->tag;
1277                     new->tag = NULL;
1279                     self->_priv->data = (MpdData *)new_list->first;
1280                     if(self->_priv->data == NULL) g_warning("self->_priv->data == NULL");
1281                     self->num_rows++;
1282                     {
1283                         path = gtk_tree_path_new();
1284                         gtk_tree_path_append_index(path,i);
1285                         iter.stamp = self->_priv->stamp;
1286                         iter.user_data = new_list;
1287                         iter.user_data2 =  GINT_TO_POINTER(i);
1289                         gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
1290                         gtk_tree_path_free(path);
1291                     }
1293                     new = new->next;
1294                     i++;
1295                 }while(new);
1296             }
1299             if(self->_priv->use_images){
1300                 if(self->num_rows < old_list_size){
1301                     for(i=self->num_rows;i< old_list_size; i++)
1302                     {
1303                         /* clean it */
1304                         if(self->_priv->images[i] != NULL) g_object_unref(self->_priv->images[i]);
1305                     }
1306                     self->_priv->images = g_realloc(self->_priv->images, (self->num_rows)*sizeof(GdkPixbuf *));
1307                 }
1308             }
1310    
1312             if(data2)
1313                 mpd_data_free(data2);
1315             g_assert(items == self->num_rows);
1316             return 0;                    
1317         }
1319         signal last NONE (LONG)
1320         void
1321         playtime_changed(self, gulong playtime)
1322         {
1323             return;
1324         }
1326     public
1327     gulong
1328     get_playtime(self)
1329     {
1330         return self->_priv->playtime;
1331     }
1332     public
1333     gint
1334     get_pos( self, GtkTreeIter *iter)
1335     {
1336             g_assert(iter->stamp == self->_priv->stamp);
1337             return GPOINTER_TO_INT(iter->user_data2);
1338     }
1340     public
1341     void
1342     disable_image(self)
1343     {
1344         g_assert(self->_priv->data == NULL);
1345         self->_priv->use_images = FALSE;
1346     }
1348     public
1349     MpdData * 
1350     steal_mpd_data(self)
1351     {
1352         GtkTreePath *path;
1353         int i;
1354         int old_num_rows = self->num_rows;
1355         MpdData *data = self->_priv->data; 
1356         while ( self->num_rows > 0 ) {
1357             path = gtk_tree_path_new();
1358             gtk_tree_path_append_index(path, self->num_rows - 1 );
1359             /* propegate change */
1360             gtk_tree_model_row_deleted(GTK_TREE_MODEL(self), path);
1361             gtk_tree_path_free(path);
1362             self->num_rows--;
1363         }
1365         /* Clear */
1366         self->_priv->data = NULL;
1367         /* Free possible stored images */
1368         if(self->_priv->images && self->_priv->use_images)
1369         {
1370             for(i=0;i<  old_num_rows;i++)
1371             {
1372                 if(self->_priv->images[i])
1373                     g_object_unref(self->_priv->images[i]);
1374             }
1375             q_free(self->_priv->images);
1376         }
1377         self->_priv->playtime = 0;
1378         self_playtime_changed(self, self->_priv->playtime);
1380         return mpd_data_get_first(data);
1381     }
1383     /* Drag and stop handling */
1386                 interface Gtk:Tree:Drag:Source
1387                 private 
1388                 gboolean row_draggable (Gtk:Tree:Drag:Source *drag_source, Gtk:Tree:Path *path)
1389                 {
1390             GtkTreeIter iter;
1391             if(gtk_tree_model_get_iter(GTK_TREE_MODEL(drag_source), &iter, path))
1392             {
1393                 MpdData *data = iter.user_data; 
1394                 if(data->type == MPD_DATA_TYPE_SONG) {
1395                     return TRUE;
1396                 }
1397             }
1398             return FALSE;
1399         }
1401         interface Gtk:Tree:Drag:Source
1402         private 
1403                 gboolean drag_data_delete (Gtk:Tree:Drag:Source *drag_source, Gtk:Tree:Path *path) 
1404                 {
1405                         return TRUE;
1406                 }
1407                 interface Gtk:Tree:Drag:Source
1408                         private 
1409                 gboolean drag_data_get (Gtk:Tree:Drag:Source *drag_source, Gtk:Tree:Path *path, Gtk:Selection:Data *selection_data)
1410                 {
1411             if(!gtk_tree_set_row_drag_data(selection_data,GTK_TREE_MODEL(drag_source), path)) {
1412                 return FALSE;
1413             }
1414                         return TRUE;
1415                 }