Cleanups
[gmpc.git] / src / gmpc-mpddata-model.gob
blobc4fde795c2defb50704ffe2f235b04e66b475475
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_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);
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                     {
343                         GdkPixbuf *pb = NULL;
344                                                 if (met->content_type == META_DATA_CONTENT_RAW) {
345                             MpdData_real *data = iter.user_data;
346                             gmpc_pixbuf_loader_async_set_from_raw(GMPC_PIXBUF_LOADER_ASYNC(self->_priv->images[n]),
347                                     met->content, met->size, self->icon_size, self->icon_size,
348                                     ((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),
349                                                                         met->md5sum);
351                         }else{
352                             pb = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-no-cover", self->icon_size, 0,NULL);
353                             gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n], pb);
354                             g_object_unref(pb);
355                         }
356                     }
357                     else   if(ret == META_DATA_UNAVAILABLE)
358                     {
359                         GdkPixbuf *pb2;
360                         pb2 = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-no-cover", self->icon_size, 0,NULL);
361                         gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n], pb2);
362                         g_object_unref(pb2);
363                     }
364                     gtk_tree_model_row_changed(model,path, &iter);
365                     gtk_tree_path_free(path);
366                 }
368                 if(ret == META_DATA_AVAILABLE || ret == META_DATA_UNAVAILABLE)
369                 {
370                     if(ref)
371                     {
372                         gtk_tree_row_reference_free(ref);
373                     }
374                 }
375             }
376         }
378     interface Gtk:Tree:Model
379         public void
380         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))
381         {
382             Self *self = GMPC_MPDDATA_MODEL(model);
383             MpdData_real *data = iter->user_data;
384             int n = GPOINTER_TO_INT(iter->user_data2);
386             if(self->_priv->data == NULL)
387                 g_warning("self->_priv->data == NULL");
388             /* set value to the correct type */
389             g_value_init(value, self->types[column]);
391             /**
392              * Sanity checks
393              */
394              g_assert((n < self->num_rows || n == 0));
395             g_assert(!(data == NULL && n != 0));
397             /* handle row up */
398             if(data == NULL && n == 0)
399             {
400                 switch(column)
401                 {
402                     case MPDDATA_MODEL_COL_ICON_ID:
403                         g_value_set_string(value, GTK_STOCK_GO_UP);
404                         break;
405                     case MPDDATA_MODEL_ROW_TYPE:
406                         /**
407                          * Make define
408                          */
409                         g_value_set_int(value, -1);
410                         break;
411                     case MPDDATA_MODEL_COL_SONG_TITLE:
412                     case MPDDATA_MODEL_COL_MARKUP:
413                         {
414                             g_value_set_string(value,"..");
415                             break;
416                         }
417                     default:
418                         break;
419                 }
420                 return;
421             }
423             if(column == MPDDATA_MODEL_META_DATA)
424             {
425                 if(self->_priv->use_images && self->_priv->images)
426                 {
427                     if(self->_priv->images[n] == NULL)
428                     {
429                         GtkTreePath *path;
430                         GtkTreeRowReference *ref;
431                         path = gtk_tree_model_get_path(GTK_TREE_MODEL(self), iter);
432                         ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(self),path);
434                         self->_priv->images[n]= gmpc_pixbuf_loader_async_new();
435                         gmpc_pixbuf_loader_async_set_rref(GMPC_PIXBUF_LOADER_ASYNC(self->_priv->images[n]), ref);
436                         if(data->type == MPD_DATA_TYPE_TAG && (data->tag_type == MPD_TAG_ITEM_ARTIST || data->tag_type == MPD_TAG_ITEM_ALBUM_ARTIST) )
437                         {
438                             mpd_Song *song;
439                             GdkPixbuf *pb = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-loading-cover", self->icon_size, 0,NULL);
440                             gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n], pb);
441                             g_object_unref(pb);
442                             song = mpd_newSong();
443                             song->artist = g_strdup(data->tag);
444                             song->album = NULL;
445                             meta_data_get_path_callback(song, META_ARTIST_ART, (MetaDataCallback)self_cover_art_fetched, (gpointer)ref);
446                             mpd_freeSong(song);
447                         }
449                         else if(data->type == MPD_DATA_TYPE_TAG && data->tag_type == MPD_TAG_ITEM_ALBUM && self->_priv->req_artist)
450                         {
451                             mpd_Song *song;
452                             GdkPixbuf *pb = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-loading-cover", self->icon_size, 0,NULL);
453                             gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n],pb);
454                             g_object_unref(pb);
455                             song = mpd_newSong();
456                             song->artist = g_strdup(self->_priv->req_artist);
457                             song->albumartist = g_strdup(self->_priv->req_artist);
458                             song->album = g_strdup(data->tag);
459                             meta_data_get_path_callback(song, META_ALBUM_ART, (MetaDataCallback)self_cover_art_fetched, ref);
460                             mpd_freeSong(song);
461                         }
462                         else if(data->type == MPD_DATA_TYPE_SONG&& data->song && data->song->album && (data->song->artist || self->_priv->req_artist))
463                         {
464                             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)
465                             {
466                                 gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n],self->_priv->blank);
467                             }else{
468                                 mpd_Song *song;
469                                 GdkPixbuf *pb = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-loading-cover", self->icon_size, 0,NULL);
470                                 gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n],pb);
471                                 g_object_unref(pb);
472                                 song = mpd_newSong();
473                                 song->artist = g_strdup((data->song->artist)?data->song->artist:self->_priv->req_artist);
474                                 song->albumartist = g_strdup((data->song->albumartist)?data->song->albumartist:self->_priv->req_artist);
475                                 song->album = g_strdup(data->song->album);
476                                 meta_data_get_path_callback(song, META_ALBUM_ART, (MetaDataCallback)self_cover_art_fetched, ref);
477                                 mpd_freeSong(song);
478                             }
479                         }
480                         else
481                         {
482                             GdkPixbuf *pb = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "gmpc-no-cover", self->icon_size, 0,NULL);
483                             gmpc_pixbuf_loader_async_set_pixbuf(self->_priv->images[n],pb);
484                             g_object_unref(pb);
485                         }
486                         gtk_tree_path_free(path);
487                     }
488                     g_value_set_object(value, gmpc_pixbuf_loader_async_get_pixbuf(self->_priv->images[n]));
489                 }else{
490                     g_value_set_object(value,NULL);
491                 }
492             }
493             if(column == MPDDATA_MODEL_USERDATA)
494             {
495                 g_value_set_pointer(value,data->userdata);
497             }
498             /* handle row type, this is independent of the row type */
499             if(column == MPDDATA_MODEL_ROW_TYPE)
500             {
501                 g_value_set_int(value, data->type);
502                 return;
503             }
504             if (data->type == MPD_DATA_TYPE_TAG) {
505                 switch(column)
506                 {
507                     case MPDDATA_MODEL_COL_ICON_ID:
508                         switch(data->tag_type)
509                         {
510                             case MPD_TAG_ITEM_ALBUM:
511                                 g_value_set_string(value, "media-album");
512                                 break;
513                             case MPD_TAG_ITEM_ARTIST:
514                                 g_value_set_string(value, "media-artist");
515                                 break;
516                             case MPD_TAG_ITEM_GENRE:
517                                 g_value_set_string(value, "media-genre");
518                                 break;
519                             case MPD_TAG_ITEM_TRACK:
520                                 g_value_set_string(value, "media-num-tracks");
521                                 break;
522                             default:
523                                 g_value_set_string(value, "media-tag");
524                         }
525                         break;
526                     case MPDDATA_MODEL_COL_SONG_TITLE:
527                     case MPDDATA_MODEL_COL_MARKUP:
528                         {
529                             g_value_set_string(value, data->tag);
530                             break;
531                         }
532                     case MPDDATA_MODEL_COL_PATH:
533                         g_value_set_string(value, data->tag);
534                         break;
535                     case MPDDATA_MODEL_TAG_TYPE:
536                         g_value_set_int(value, data->tag_type);
537                         break;
538                     default:
539                         break;
540                 }
541             } else if(data->type == MPD_DATA_TYPE_DIRECTORY) {
542                 switch(column)
543                 {
544                     case MPDDATA_MODEL_COL_ICON_ID:
545                         g_value_set_string(value, GTK_STOCK_OPEN);
546                         break;
547                     case MPDDATA_MODEL_COL_SONG_TITLE:
548                     case MPDDATA_MODEL_COL_SONG_TITLEFILE:
549                     case MPDDATA_MODEL_COL_MARKUP:
550                         {
551                             gchar *basename = g_path_get_basename(data->directory);
552                             g_value_set_string(value, basename);
553                             g_free(basename);
554                             break;
555                         }
556                     case MPDDATA_MODEL_COL_PATH:
557                         g_value_set_string(value, data->directory);
558                         break;
559                     default:
560                         break;
561                 }
562             }
563             else if(data->type == MPD_DATA_TYPE_PLAYLIST)
564             {
565                 switch(column)
566                 {
567                     case MPDDATA_MODEL_COL_ICON_ID:
568                         g_value_set_string(value, "media-playlist");
569                         break;
570                     case MPDDATA_MODEL_COL_SONG_TITLE:
571                     case MPDDATA_MODEL_COL_SONG_TITLEFILE:
572                     case MPDDATA_MODEL_COL_MARKUP:
573                         {
574                             gchar *basename = g_path_get_basename(data->playlist->path);
575                             g_value_set_string(value, basename);
576                             g_free(basename);
577                             break;
578                         }
579                     case MPDDATA_MODEL_COL_PATH:
580                         g_value_set_string(value, data->playlist->path);
581                         break;
582                     default:
583                         break;
584                 }
585             }
586             else if(data->type == MPD_DATA_TYPE_SONG)
587             {
588                 mpd_Song *song = data->song;
589                 switch (column) {
590                     case MPDDATA_MODEL_COL_MPDSONG:
591                         g_value_set_pointer(value, song);
592                         break;
593                     case MPDDATA_MODEL_COL_PLAYING:
594                         g_value_set_boolean(value, FALSE);
595                         break;
596                     case MPDDATA_MODEL_COL_PLAYING_FONT_WEIGHT:
597                         g_value_set_int(value, PANGO_WEIGHT_NORMAL);
598                         break;
599                     case MPDDATA_MODEL_COL_MARKUP:
600                         {
601                             /* we want to go cache this stuff */
602                             gchar buffer[1024];
603                             mpd_song_markup(buffer, 1024, GMPC_MPDDATA_MODEL(model)->_priv->markup, song);
604                             g_value_set_string(value, buffer);
605                             break;
606                         }
607                     case MPDDATA_MODEL_COL_PATH:
608                         g_value_set_string(value, song->file);
609                         break;
610                                                                                 case MPDDATA_MODEL_COL_PATH_EXTENSION:
611                                                                                                 {
612                                                                                                         int j = strlen(song->file);
613                                                                                                         for(;j>0&&song->file[j] != '.';j--);
614                                                                                                         g_value_set_string(value, &(song->file)[j+1]);
615                                                                                                         break;
616                                                                                                 }
617                                                                                 case MPDDATA_MODEL_COL_PATH_DIRECTORY:
618                                                                                                 {
619                                                                                                         gchar *dir = g_path_get_dirname(song->file);
620                                                                                                         g_value_set_string(value, dir);
621                                                                                                         g_free(dir);
622                                                                                                         break;
623                                                                                                 }
624                     case MPDDATA_MODEL_COL_SONG_ARTIST:
625                         g_value_set_string(value, song->artist);
626                         break;
627                     case MPDDATA_MODEL_COL_SONG_ALBUM:
628                         g_value_set_string(value, song->album);
629                         break;
630                     case MPDDATA_MODEL_COL_SONG_TITLE:
631                         /* If there is a song available use that, else use the filename */
632                         if(song->title) {
633                             g_value_set_string(value, song->title);
634                         } else if (song->name) {
635                             g_value_set_string(value, song->name);
636                         }else {
637                             /* Use the markup stuff, this makes sure it gets processed equaly */
638                             gchar buffer[1024];
639                             mpd_song_markup(buffer, 1024, "%shortfile%", song);
640                             g_value_set_string(value, buffer);
641                         }
642                         break;
643                     case MPDDATA_MODEL_COL_SONG_TITLEFILE:
644                         {
645                             gchar *path = g_path_get_basename(song->file);
646                             g_value_set_string(value, path);
647                             g_free(path);
648                         }
649                         break;
650                     case MPDDATA_MODEL_COL_SONG_GENRE:
651                         g_value_set_string(value, song->genre);
652                         break;
653                     case MPDDATA_MODEL_COL_SONG_TRACK:
654                         g_value_set_string(value, song->track);
655                         break;
656                     case MPDDATA_MODEL_COL_SONG_NAME:
657                         g_value_set_string(value, song->name);
658                         break;
659                     case MPDDATA_MODEL_COL_SONG_COMPOSER:
660                         g_value_set_string(value, song->composer);
661                         break;
662                     case MPDDATA_MODEL_COL_SONG_PERFORMER:
663                         g_value_set_string(value, song->performer);
664                         break;
665                     case MPDDATA_MODEL_COL_SONG_DATE:
666                         g_value_set_string(value, song->date);
667                         break;
668                     case MPDDATA_MODEL_COL_SONG_LENGTH:
669                         g_value_set_int(value, song->time);
670                         break;
671                     case MPDDATA_MODEL_COL_SONG_LENGTH_FORMAT:
672                         {
673                             if(song->time >= 0) {
674                                 gchar *strdata = g_strdup_printf("%02i:%02i",
675                                         song->time/60, song->time%60);
676                                 g_value_set_string(value, strdata);
677                                 g_free(strdata);
678                             } else {
679                                 g_value_set_string(value, "n/a");
680                             }
681                         }
682                         break;
683                     case MPDDATA_MODEL_COL_SONG_DISC:
684                         g_value_set_string(value, song->disc);
685                         break;
686                     case MPDDATA_MODEL_COL_SONG_COMMENT:
687                         g_value_set_string(value, song->comment);
688                         break;
689                     case MPDDATA_MODEL_COL_SONG_POS:
690                         g_value_set_int(value, song->pos+1);
691                         break;
692                     case MPDDATA_MODEL_COL_SONG_ALBUMARTIST:
693                         g_value_set_string(value, song->albumartist);
694                         break;
695                     case MPDDATA_MODEL_COL_SONG_ID:
696                         g_value_set_int(value, song->id);
697                         break;
699                     case MPDDATA_MODEL_COL_ICON_ID:
700                         if (strstr(song->file, "://")) {
701                             g_value_set_string(value, "media-stream");
702                         } else {
703                             g_value_set_string(value, "media-audiofile");
704                         }
705                         break;
706                     default:
707                         break;
708                 }
709             }
710         }
712     interface Gtk:Tree:Model
713         private gboolean
714         iter_nth_child(Gtk:Tree:Model *model(check null type), GtkTreeIter *iter (check null), GtkTreeIter *parent, gint n (check >=0))
715         {
716             Self *self = GMPC_MPDDATA_MODEL(model);
717             int i;
718             MpdData_real *data = NULL;
719             if(parent)
720                 return FALSE;
721             if (n >= self->num_rows || n < 0)
722                 return FALSE;
724             data = (MpdData_real *)self->_priv->data;
725             i=0;
726             if(self->has_up)
727                 i=1;
728             for(; i < (n);i++)
729             {
730                 data = data->next;//mpd_data_get_next_real(data, FALSE);
731                 g_assert(data != NULL);
732             }
734             if(self->has_up && n == 0)
735                 data = NULL;
737             iter->stamp = self->_priv->stamp;
738             iter->user_data = data;
739             iter->user_data2 = GINT_TO_POINTER(n);
740             return TRUE;
741         }
743     interface Gtk:Tree:Model
744         private GtkTreePath *
745         get_path(Gtk:Tree:Model *self (check null type), GtkTreeIter *iter (check null))
746         {
747             GtkTreePath *path = NULL;
748             path = gtk_tree_path_new();
749             gtk_tree_path_append_index(path, GPOINTER_TO_INT(iter->user_data2));
750             return path;
751         }
753     interface Gtk:Tree:Model
754         private GType
755         get_column_type(Gtk:Tree:Model *model(check null type), gint ind (check >= 0))
756         {
757             Self *self = GMPC_MPDDATA_MODEL(model);
758             return self->types[ind];
759         }
760     public
761         void
762         set_request_artist(self, const char *artist)
763         {
764             int i;
765             GtkTreePath *path = NULL;
766             GtkTreeIter iter;
767             if(self->_priv->req_artist)
768                 q_free(self->_priv->req_artist);
769             self->_priv->req_artist = (artist != NULL && artist[0] != '\0')?g_strdup(artist):NULL;
770             /* Free possible stored images */
771             if(self->_priv->images && self->_priv->use_images)
772             {
773                 MpdData *data2 = mpd_data_get_first(self->_priv->data);
774                 for(i=0;i<self->num_rows;i++)
775                 {
776                     if(self->_priv->images[i]){
777                         g_object_unref(self->_priv->images[i]);
778                         self->_priv->images[i] = NULL;
780                         /* Update the view */
781                         path = gtk_tree_path_new();
782                         gtk_tree_path_append_index(path,i);
783                         iter.stamp = self->_priv->stamp;
784                         iter.user_data = data2;
785                         iter.user_data2 =  GINT_TO_POINTER(i);
786                         /* propegate change */
787                         gtk_tree_model_row_changed(GTK_TREE_MODEL(self), path, &iter);
788                         gtk_tree_path_free(path);
789                     }
790                     data2 = mpd_data_get_next_real(data2,FALSE);
791                 }
792             }
793         }
794         public
795         const char *
796         get_request_artist(self)
797         {
798             return self->_priv->req_artist;
799         }
800     public
801         long unsigned set_mpd_data(self, MpdData *data2)
802         {
803             int i;
804             long unsigned retval = 0;
805             GtkTreeIter iter;
806             GtkTreePath *path = NULL;
807             int old_num_rows = self->num_rows;
808             /* Do some cleanup, like removing rows, and so */
809             /* loop and remove */
810             while ( self->num_rows > 0 ) {
811                 path = gtk_tree_path_new();
812                 gtk_tree_path_append_index(path, self->num_rows - 1 );
813                 /* propegate change */
814                 gtk_tree_model_row_deleted(GTK_TREE_MODEL(self), path);
815                 gtk_tree_path_free(path);
816                 self->num_rows--;
817             }
820             /**
821              * Free if there is a list and set it to NULL
822              */
823             if(self->_priv->data){
824                 mpd_data_free(mpd_data_get_first(self->_priv->data));
825             }
826             self->_priv->data = NULL;
828             if(self->num_rows != 0)
829                 g_log(LOG_DOMAIN, G_LOG_LEVEL_WARNING,"not every row cleared %i\n",self->num_rows);
830             self->num_rows =0;
831             /* Free possible stored images */
832             if(self->_priv->images && self->_priv->use_images)
833             {
834                 for(i=0;i< old_num_rows;i++)
835                 {
836                     if(self->_priv->images[i])
837                         g_object_unref(self->_priv->images[i]);
838                 }
839                 q_free(self->_priv->images);
840             }
841             if(data2 == NULL)
842             {
843                 self->_priv->playtime = 0;
844                 self_playtime_changed(self, self->_priv->playtime);
845                 return 0;
846             }
848             self->_priv->data = mpd_data_get_first(data2);
849             data2 = NULL;
850             if(self->_priv->sort_column == MPDDATA_MODEL_COL_ICON_ID)
851             {
852                 self->_priv->data = misc_sort_mpddata_by_album_disc_track(self->_priv->data);
853             }
854             else if ( self->_priv->sort_column != GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID )
855             {
856                 self->_priv->data = misc_sort_mpddata(self->_priv->data,(GCompareDataFunc)self_sort_func,self );
857             }
859             if(self->has_up) {
860                 path = gtk_tree_path_new();
861                 gtk_tree_path_append_index(path, self->num_rows);
862                 iter.stamp = self->_priv->stamp;
863                 iter.user_data = NULL;
864                 iter.user_data2 =  GINT_TO_POINTER(self->num_rows);
866                 self->num_rows++;
867                 /* propegate change */
868                 gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
869                 gtk_tree_path_free(path);
870             }
871             for(data2 = mpd_data_get_first(self->_priv->data);data2; data2 = mpd_data_get_next_real(data2,FALSE)) {
872                 path = gtk_tree_path_new();
873                 gtk_tree_path_append_index(path,self->num_rows);
874                 iter.stamp = self->_priv->stamp;
875                 iter.user_data = data2;
876                 iter.user_data2 =  GINT_TO_POINTER(self->num_rows);
878                 self->num_rows++;
879                 /* propegate change */
880                 gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
881                 gtk_tree_path_free(path);
882                 if(data2->type == MPD_DATA_TYPE_SONG)
883                 {
884                     if(data2->song && data2->song->time > 0)
885                         retval+= data2->song->time;
886                 }
887             }
888             if(self->_priv->use_images)
889             {
890                 self->_priv->images = g_malloc0((self->num_rows)*sizeof(*(self->_priv->images)));
891             }
893             self->_priv->playtime = retval;
894             self_playtime_changed(self, self->_priv->playtime);
895             return retval;
896         }
898     /* Sorting */
899     interface Gtk:Tree:Sortable
900         private
901         gboolean get_sort_column_id (Gtk:Tree:Sortable *model ,  gint *sort_column_id, GtkSortType *order)
902         {
903             Self *self = GMPC_MPDDATA_MODEL(model);
904             if(sort_column_id)
905                 *sort_column_id = self->_priv->sort_column;
906             if(order)
907                 *order = self->_priv->sort_order;
908             return TRUE;
909         }
911     interface Gtk:Tree:Sortable
912         private
913         void set_sort_column_id (Gtk:Tree:Sortable *model ,  gint sort_column_id, GtkSortType order)
914         {
915             Self *self = GMPC_MPDDATA_MODEL(model);
916             gint old_col = self->_priv->sort_column;
917             GtkSortType old_ord = self->_priv->sort_order;
918             self->_priv->sort_column = sort_column_id;
919             self->_priv->sort_order = order;
920             /* trigger signal */
921             if(old_col != sort_column_id || old_ord != order)
922                 gtk_tree_sortable_sort_column_changed(model);
923             self->_priv->old_sort_column = self->_priv->sort_column;
925         }
927     interface Gtk:Tree:Sortable
928         private
929         gboolean
930         has_default_sort_func(Gtk:Tree:Sortable *model)
931         {
932             return FALSE;
933         }
934       private
935         int sort_func(gpointer ppaa, gpointer ppbb, Self *self)
936         {
937             MpdData_real *a = *(MpdData_real **)ppaa;
938             MpdData_real *b = *(MpdData_real **)ppbb;
939             int fact = (self->_priv->sort_order == GTK_SORT_ASCENDING)?-1:1;
941             if(a->type != b->type )
942             {
943                 int val = a->type - b->type;
944                 return val;
945             }
946             else if(a->type == b->type)
947             {
948                 int val=0;
949                 GtkTreeIter iter;
950                 GValue va = {0,},vb = {0,};
951                 /* Get values from tree view, so we don't have to replicate code to sort the right entries */
952                 iter.user_data = a;
953                 iter.user_data2 = GPOINTER_TO_INT(0);
954                 gtk_tree_model_get_value(GTK_TREE_MODEL(self), &iter, self->_priv->sort_column, &va);
955                 iter.user_data = b;
956                 gtk_tree_model_get_value(GTK_TREE_MODEL(self), &iter, self->_priv->sort_column, &vb);
957                 /* if the type is a directory or a tag, always sort on the title column */
958                 if(a->type == MPD_DATA_TYPE_DIRECTORY || b->type == MPD_DATA_TYPE_TAG)
959                 {
960                     const char *ca,*cb;
961                     ca = g_value_get_string(&va);
962                     cb = g_value_get_string(&vb);
963                     if(ca && cb) {
964                         gchar *aa,*bb;
965                         aa = g_utf8_strdown(ca, -1);
966                         bb = g_utf8_strdown(cb, -1);
967                         val = g_utf8_collate(aa,bb);
968                         g_free(aa);
969                         g_free(bb);
970                     } else {
971                         val = (ca == NULL)?((cb==NULL)?0:-1):1;
972                     }
973                     g_value_unset(&va);
974                     g_value_unset(&vb);
975                     /* return */
976                     return val*fact;
977                 }
978                 if(self->_priv->sort_column == MPDDATA_MODEL_COL_SONG_TRACK)
979                 {
980                     const char *ca = g_value_get_string(&va);
981                     const char *cb = g_value_get_string(&vb);
982                     gint ca1 = (ca)?atoi(ca):0;
983                     gint cb1 = (cb)?atoi(cb):0;
984                     g_value_unset(&va);
985                     g_value_unset(&vb);
986                     return fact*(ca1-cb1);
987                 }
988                 else if(self->types[self->_priv->sort_column] == G_TYPE_INT)
989                 {
990                     val = g_value_get_int(&va) - g_value_get_int(&vb);
991                 }
992                 else if (self->types[self->_priv->sort_column] == G_TYPE_STRING)
993                 {
994                     const char *ca = g_value_get_string(&va);
995                     const char *cb = g_value_get_string(&vb);
996                     if(ca && cb) {
997                         gchar *aa,*bb;
998                         aa = g_utf8_normalize(ca, -1,G_NORMALIZE_ALL);
999                         bb = g_utf8_normalize(cb, -1,G_NORMALIZE_ALL);
1000                         val = g_utf8_collate(aa,bb);
1001                         g_free(aa);
1002                         g_free(bb);
1003                     } else {
1004                         val = (ca == NULL)?((cb==NULL)?0:-1):1;
1005                     }
1006                 }
1008                 if (val == 0 &&
1009                         a->type == MPD_DATA_TYPE_SONG &&
1010                         b->type == MPD_DATA_TYPE_SONG &&
1011                         a->song->file &&
1012                         b->song->file)
1013                 {
1014                     val = strcmp (a->song->file, b->song->file);
1015                 }
1017                 g_value_unset(&va);
1018                 g_value_unset(&vb);
1019                 return fact*val;
1020             }
1021             return 0;
1022         }
1024     interface Gtk:Tree:Sortable
1025         private
1026         void
1027         sort_column_changed(Gtk:Tree:Sortable *model)
1028         {
1029             Self *self = GMPC_MPDDATA_MODEL(model);
1030             if(!self->_priv->data || !(((MpdData_real *)self->_priv->data)->next))
1031                 return;
1032             if(self->_priv->sort_column == MPDDATA_MODEL_COL_ICON_ID)
1033             {
1034                 self->_priv->data = misc_sort_mpddata_by_album_disc_track(self->_priv->data);
1035             }
1036             else if(self->_priv->sort_column !=  GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID)
1037             {
1038                 self->_priv->data = misc_sort_mpddata(self->_priv->data,(GCompareDataFunc)self_sort_func,self );
1039             }
1040             self->_priv->old_sort_column = self->_priv->sort_column;
1042             /* tell the view that rows have changed  */
1043             {
1044                 int i=0;
1045                 GtkTreePath *path = NULL;
1046                 GtkTreeIter iter;
1047                 MpdData *data2 = mpd_data_get_first(self->_priv->data);
1048                 if(self->has_up) i = 1;
1049                 for(;data2; data2 = mpd_data_get_next_real(data2,FALSE))
1050                 {
1051                     path = gtk_tree_path_new();
1052                     gtk_tree_path_append_index(path,i);
1053                     iter.stamp = self->_priv->stamp;
1054                     iter.user_data = data2;
1055                     iter.user_data2 =  GINT_TO_POINTER(i);
1057                     /* propegate change */
1058                     gtk_tree_model_row_changed(GTK_TREE_MODEL(self), path, &iter);
1059                     gtk_tree_path_free(path);
1060                     i++;
1061                 }
1062             }
1063         }
1065         private
1066         int
1067         test_sort_func(gpointer ppaa, gpointer ppbb, Self *self)
1068         {
1069             MpdData_real *a = *(MpdData_real **)ppaa;
1070             MpdData_real *b = *(MpdData_real **)ppbb;
1071             if(a->type == MPD_DATA_TYPE_TAG && b->type == MPD_DATA_TYPE_TAG)
1072             {
1073                 if(a->tag_type != b->tag_type)
1074                     return a->tag_type - b->tag_type;
1075                 if(a->tag== NULL && b->tag != NULL)
1076                     return -1;
1077                 else if(b->tag == NULL && a->tag != NULL)
1078                     return 1;
1079                 else if (a->tag  && b->tag)
1080                 {
1081                     int val;
1082                     if(a->tag && b->tag) {
1083                         gchar *sa,*sb;
1084                         sa = g_utf8_strdown(a->tag, -1);
1085                         sb = g_utf8_strdown(b->tag, -1);
1086                         val = g_utf8_collate(sa,sb);
1087                         g_free(sa);
1088                         g_free(sb);
1089                     } else {
1090                         val = (a == NULL)?((b==NULL)?0:-1):1;
1091                     }
1092                     return val;
1093                 }
1094             }
1095             return a->type - b->type;
1096         }
1098         /**
1099          * For now this assumes tag list only.
1100          */
1101     public
1102         long unsigned set_mpd_data_slow (self, MpdData *data2)
1103         {
1104             MpdData_real *new_list = NULL;
1105             int i=0;
1106             int items=0;
1107             MpdData_real *original = (MpdData_real *)mpd_data_get_first(self->_priv->data);
1108             MpdData_real *new = (MpdData_real *) data2;
1109             GtkTreePath *path =NULL;
1110             GtkTreeIter iter;
1111             int old_list_size = self->num_rows;
1113             /* sort it identical */
1114             data2 = misc_sort_mpddata(data2,(GCompareDataFunc)self_test_sort_func,self );
1115             new = (MpdData_real *)data2;
1117             if(new) {
1118                 MpdData_real *n = new->first;
1119                 while(n) {
1120                     items++;
1121                     n = n->next;
1122                 }
1123             }
1126             /* Free possible stored images */
1127             /**
1128              * TODO: Don't free the list, but grow it to MAX(items, self->num_rows);
1129              * Then after updating, resize it to new size
1130              */
1131             /* Grow the list when needed */
1133             if(self->_priv->use_images)
1134             {
1135                 /* Clean them, they might not be in the right place anymore */
1136                 for(i=0;i< old_list_size; i++)
1137                 {
1138                     if(self->_priv->images[i] != NULL) g_object_unref(self->_priv->images[i]);
1139                     self->_priv->images[i] = NULL;
1140                 }
1141                 if(items > self->num_rows)
1142                 {
1143                     self->_priv->images = g_realloc(self->_priv->images, items*sizeof(GdkPixbuf *));
1144                     /* set the new ones to NUll */
1145                     for(i=self->num_rows;i<items;i++)
1146                     {
1147                         self->_priv->images[i] = NULL;
1148                     }
1149                 }
1150             }
1155             i=0;
1156             /* compare lists */
1157             if(self->_priv->data && original == NULL) g_warning("weirdness\n");
1158             if(self->_priv->data)
1159             {
1160                 int has_next;
1161                 do
1162                 {
1163                     has_next = 0;
1164                     if(new == NULL)
1165                     {
1166                         if(self->num_rows > items)
1167                         {
1168                             MpdData_real *n = original->next;
1169                             /* remove current row */
1170                             self->num_rows--;
1172                             path = gtk_tree_path_new();
1173                             gtk_tree_path_append_index(path,i);
1175                             /* propegate change */
1176                             gtk_tree_model_row_deleted(GTK_TREE_MODEL(self), path);
1177                             gtk_tree_path_free(path);
1179                             original =(MpdData_real *) mpd_data_delete_item((MpdData *)original);
1180                             self->_priv->data = mpd_data_get_first((MpdData *)original);
1182                             original=n;
1183                             has_next = (original )?1:0;
1184                         }
1185                     }
1186                     else
1187                     {
1188                         int compare;
1189                         compare = g_utf8_collate(new->tag,original->tag);
1190                         if(compare < 0)
1191                         {
1192                             /* add */
1193                             MpdData_real *n = (MpdData_real *) mpd_new_data_struct();
1194                             n->type = MPD_DATA_TYPE_TAG;
1195                             n->tag_type = new->tag_type;
1196                             n->tag = new->tag;
1197                             new->tag = NULL;
1199                             /* add */
1201                             self->num_rows++;
1202                             n->prev = original->prev;
1204                             if(original->prev)
1205                                 original->prev->next = n;
1206                             n->next = original;
1207                             n->first = original->first;
1208                             original->prev = n;
1209                             if(n->prev == NULL){
1210                                 MpdData_real *fiter;
1211                                 for(fiter = n;fiter;fiter = fiter->next){
1212                                     fiter->first = n;
1213                                 }
1214                             }
1216                             self->_priv->data = mpd_data_get_first((MpdData *)original);
1217                             {
1218                                 path = gtk_tree_path_new();
1219                                 gtk_tree_path_append_index(path,i);
1220                                 iter.stamp = self->_priv->stamp;
1221                                 iter.user_data = n;
1222                                 iter.user_data2 =  GINT_TO_POINTER(i);
1224                                 gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
1225                                 gtk_tree_path_free(path);
1226                             }
1227                             /* mark for insert */
1228                             new=new->next;
1229                             i++;
1230                             has_next = 1;
1231                         }
1232                         else if (compare > 0)
1233                         {
1234                             MpdData_real *n = original->next;
1235                             path = gtk_tree_path_new();
1236                             gtk_tree_path_append_index(path,i);
1237                             /* propegate change */
1239                             self->num_rows--;
1240                             gtk_tree_model_row_deleted(GTK_TREE_MODEL(self), path);
1241                             gtk_tree_path_free(path);
1244                             original = (MpdData_real *)mpd_data_delete_item((MpdData *)original);
1245                             self->_priv->data = mpd_data_get_first((MpdData *)original);
1246                             original = n;
1247                             //original = original->next;
1248                             has_next = (original )?1:0;
1250                         }else{
1251                             new = new->next;
1252                             original = original->next;
1253                             has_next = (original )?1:0;
1254                             i++;
1255                         }
1256                     }
1257                 }while(has_next);
1258             }
1259             /* add entries remaining in new */
1260             if(new)
1261             {
1263                 new_list = (MpdData_real *) self->_priv->data;
1264                 while(new_list && new_list->next) new_list = new_list->next;
1265                 do{
1266                     new_list = (MpdData_real *)mpd_new_data_struct_append((MpdData*)new_list);
1267                     new_list->type = MPD_DATA_TYPE_TAG;
1268                     new_list->tag_type = new->tag_type;
1269                     new_list->tag = new->tag;
1270                     new->tag = NULL;
1272                     self->_priv->data = (MpdData *)new_list->first;
1273                     if(self->_priv->data == NULL) g_warning("self->_priv->data == NULL");
1274                     self->num_rows++;
1275                     {
1276                         path = gtk_tree_path_new();
1277                         gtk_tree_path_append_index(path,i);
1278                         iter.stamp = self->_priv->stamp;
1279                         iter.user_data = new_list;
1280                         iter.user_data2 =  GINT_TO_POINTER(i);
1282                         gtk_tree_model_row_inserted(GTK_TREE_MODEL(self), path, &iter);
1283                         gtk_tree_path_free(path);
1284                     }
1286                     new = new->next;
1287                     i++;
1288                 }while(new);
1289             }
1292             if(self->_priv->use_images){
1293                 if(self->num_rows < old_list_size){
1294                     for(i=self->num_rows;i< old_list_size; i++)
1295                     {
1296                         /* clean it */
1297                         if(self->_priv->images[i] != NULL) g_object_unref(self->_priv->images[i]);
1298                     }
1299                     self->_priv->images = g_realloc(self->_priv->images, (self->num_rows)*sizeof(GdkPixbuf *));
1300                 }
1301             }
1305             if(data2)
1306                 mpd_data_free(data2);
1308             g_assert(items == self->num_rows);
1309             return 0;
1310         }
1312         signal last NONE (LONG)
1313         void
1314         playtime_changed(self, gulong playtime)
1315         {
1316             return;
1317         }
1319     public
1320     gulong
1321     get_playtime(self)
1322     {
1323         return self->_priv->playtime;
1324     }
1325     public
1326     gint
1327     get_pos( self, GtkTreeIter *iter)
1328     {
1329             g_assert(iter->stamp == self->_priv->stamp);
1330             return GPOINTER_TO_INT(iter->user_data2);
1331     }
1333     public
1334     void
1335     disable_image(self)
1336     {
1337         g_assert(self->_priv->data == NULL);
1338         self->_priv->use_images = FALSE;
1339     }
1341     public
1342     MpdData *
1343     steal_mpd_data(self)
1344     {
1345         GtkTreePath *path;
1346         int i;
1347         int old_num_rows = self->num_rows;
1348         MpdData *data = self->_priv->data;
1349         while ( self->num_rows > 0 ) {
1350             path = gtk_tree_path_new();
1351             gtk_tree_path_append_index(path, self->num_rows - 1 );
1352             /* propegate change */
1353             gtk_tree_model_row_deleted(GTK_TREE_MODEL(self), path);
1354             gtk_tree_path_free(path);
1355             self->num_rows--;
1356         }
1358         /* Clear */
1359         self->_priv->data = NULL;
1360         /* Free possible stored images */
1361         if(self->_priv->images && self->_priv->use_images)
1362         {
1363             for(i=0;i<  old_num_rows;i++)
1364             {
1365                 if(self->_priv->images[i])
1366                     g_object_unref(self->_priv->images[i]);
1367             }
1368             q_free(self->_priv->images);
1369         }
1370         self->_priv->playtime = 0;
1371         self_playtime_changed(self, self->_priv->playtime);
1373         return mpd_data_get_first(data);
1374     }
1376     /* Drag and stop handling */
1379                 interface Gtk:Tree:Drag:Source
1380                 private
1381                 gboolean row_draggable (Gtk:Tree:Drag:Source *drag_source, Gtk:Tree:Path *path)
1382                 {
1383             GtkTreeIter iter;
1384             if(gtk_tree_model_get_iter(GTK_TREE_MODEL(drag_source), &iter, path))
1385             {
1386                 MpdData *data = iter.user_data;
1387                 if(data->type == MPD_DATA_TYPE_SONG) {
1388                     return TRUE;
1389                 }
1390             }
1391             return FALSE;
1392         }
1394         interface Gtk:Tree:Drag:Source
1395         private
1396                 gboolean drag_data_delete (Gtk:Tree:Drag:Source *drag_source, Gtk:Tree:Path *path)
1397                 {
1398                         return TRUE;
1399                 }
1400                 interface Gtk:Tree:Drag:Source
1401                         private
1402                 gboolean drag_data_get (Gtk:Tree:Drag:Source *drag_source, Gtk:Tree:Path *path, Gtk:Selection:Data *selection_data)
1403                 {
1404             if(!gtk_tree_set_row_drag_data(selection_data,GTK_TREE_MODEL(drag_source), path)) {
1405                 return FALSE;
1406             }
1407                         return TRUE;
1408                 }