Re-add rating.
[gmpc.git] / src / gmpc-metaimage.gob
blob28c5ec27324da71bd4950f810c50e75a20e6af5f
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 %ph{
23 #include "main.h"
24 #include "misc.h"
25 #define LOG_DOMAIN "MetaImage"
28 %headertop{
29 #include <gtk/gtk.h>
30 #include <metadata.h>
31 #include <libmpd/libmpd.h>
32 #include "gmpc-extras.h"
36 static unsigned int num_drag_targets = 4; /* skip hyperlinks for now */
37 static GtkTargetEntry data_image_drag_target[] =
39     { (gchar *)"image/jpeg", 0, 0 },
40     { (gchar *)"image/png",  0, 1 },
41     { (gchar *)"image/gif",  0, 2 },
42         { (gchar *)"text/uri-list", 0, 3},
43     { (gchar *)"_NETSCAPE_URL", 0,4},
44     { (gchar *)"x-url/http", 0,5}
49 %ph{
50 /**
51  * Indicate the state
52  */
53 typedef enum _GmpcMetaImageState{
54         STATE_NA,
55         STATE_FETCHING,
56         STATE_IMAGE,
57     STATE_EMPTY,
58     STATE_INVALID_QUERY,
59         NUM_STATES
60 }GmpcMetaImageState;
68 class Gmpc:MetaImage from Gtk:Event:Box{
69         private GmpcMetaImageState state = {STATE_EMPTY};
70         private int refresh = FALSE;
71         private gulong status_signal = {0};
72         private gulong connect_signal = {0};
73         private gulong meta_id = {0};
75         private gulong metadata_force_reload = {0};
77         private gchar *image_url = {NULL};
79         private mpd_Song *song = {NULL} destroywith mpd_freeSong;
81         /** Public */
83         public int size = {48};
84         public MetaDataType image_type = {META_ALBUM_ART};
85         public MpdObj *connection= {NULL};
86         public gboolean hide_on_na = {FALSE};
87         public gboolean squared = {TRUE};
88         public gchar *no_cover_icon = {g_strdup("gmpc-no-cover")} destroywith g_free;
89         public gchar *loading_cover_icon = {g_strdup("gmpc-loading-cover")} destroywith g_free;
91         private gulong expose_id = {0};
92         public gboolean scale_up = FALSE;
93         /**
94          * I want fully set/get control,so I don't use property stuff,
95          * is there a way around this?
96          */
97         public gboolean is_visible = {TRUE};
99         property INT image_type
100                 (nick = "Image type",
101                 blurb = "Type of the image, like artist art",
102                 default_value = META_ALBUM_ART,
103                 export,
104                 link);
106         property INT size
107                 (nick = "Size",
108                 blurb = "Size of the image",
109                 default_value = 48,
110                 export,
111                 link);
113         property BOOLEAN hide_on_na
114                 (nick = "HideOnNA",
115                 blurb = "Hide the image when not available",
116                 default_value = FALSE,
117                 export,
118                 link);
120     property BOOLEAN scale_up
121         (nick = "Scale image up",
122         blurb = "Scale cover art up",
123         default_value = FALSE,
124         export,
125         link);
127         property BOOLEAN squared
128                 (nick = "Squared",
129                         blurb = "Keep the widget squared",
130                         default_value = TRUE,
131                         link);
132     public void set_no_cover_icon (self, const gchar *name)
133     {
134         if(self->no_cover_icon) {
135             q_free(self->no_cover_icon);
136         }
137         self->no_cover_icon = g_strdup(name);
138     }
139     public void set_loading_cover_icon (self, const gchar *name)
140     {
141         if(self->loading_cover_icon) {
142             q_free(self->loading_cover_icon);
143         }
144         self->loading_cover_icon = g_strdup(name);
145     }
146     public void set_squared (self, const gboolean item)
147     {
148         self->squared = item;
149     }
150     public void set_connection(self, MpdObj *conn)
151     {
152         self->connection = conn;
153         if(conn)
154         {
155             self->_priv->status_signal = g_signal_connect_swapped(G_OBJECT(gmpcconn),
156                     "status_changed",
157                     G_CALLBACK(self_update_cover),
158                     G_OBJECT(self));
160             self->_priv->connect_signal = g_signal_connect_swapped(G_OBJECT(gmpcconn),
161                     "connection_changed",
162                     G_CALLBACK(self_connection_changed),
163                     G_OBJECT(self));
164         }
165     }
166     private GtkWidget *image = { (GtkWidget *)gmpc_meta_image_async_new()} ;
167     private GtkWidget *tooltip_image = { (GtkWidget *)gmpc_meta_image_async_new()} ;
169     init (self)
170     {
171             GtkWidget *win = NULL;
172             gtk_container_add(GTK_CONTAINER(self), self->_priv->image);
174             gtk_widget_show_all(GTK_WIDGET(self));
176         g_signal_connect_swapped(G_OBJECT(self/*->_priv->image*/), "button-press-event",
177                 G_CALLBACK(self_menu_populate_callback), self);
178         self->_priv->meta_id = g_signal_connect(G_OBJECT(gmw), "data-changed", G_CALLBACK(self_meta_callback), self);
179             gtk_widget_add_events(GTK_WIDGET(self->_priv->image), GDK_POINTER_MOTION_MASK);
180             gtk_widget_set_no_show_all(GTK_WIDGET(self),TRUE);
182             win = gtk_window_new(GTK_WINDOW_POPUP);
183             gtk_window_set_resizable(GTK_WINDOW(win), FALSE);
184             gtk_container_set_border_width(GTK_CONTAINER(win), 6);
185             gtk_container_add(GTK_CONTAINER(win), GTK_WIDGET(self->_priv->tooltip_image));
187         gtk_widget_show(self->_priv->tooltip_image);
188         gtk_widget_set_name(win, "gtk-tooltip");
189             gtk_widget_set_tooltip_window(GTK_WIDGET(self), GTK_WINDOW(win));
190             g_signal_connect(G_OBJECT(self), "query-tooltip", G_CALLBACK(self_tooltip_query), win);
191     }
192     private
193     gboolean tooltip_query(self, gint x, gint y,gboolean keyboard_mode, GtkTooltip *tip, GtkWidget *win)
194     {
195         if(self->_priv->state == STATE_IMAGE)
196         {
197                         return TRUE;
198                 }
199         return FALSE;
200     }
202     override (Gtk:Widget)
203     GtkSizeRequestMode get_request_mode(self)
204     {
205         if(self->squared) {
206             return GTK_SIZE_REQUEST_CONSTANT_SIZE;
207         }
208         return gtk_widget_get_request_mode(self->_priv->image);
209     }
210     override (Gtk:Widget)
211     void get_preferred_height(self,int *min_height, int *nat_height)
212     {
213         if(self->squared) {
214             if(min_height != NULL) *min_height = self->size;
215             if(nat_height != NULL) *nat_height = self->size;
216         }else{
217             gtk_widget_get_preferred_height(self->_priv->image, min_height, nat_height);
218         }
219     }
221     override (Gtk:Widget)
222     void get_preferred_width(self,int *min_width, int *nat_width)
223     {
224         if(self->squared) {
225             if(min_width != NULL) *min_width = self->size;
226             if(nat_width != NULL) *nat_width = self->size;
227         }else{
228             gtk_widget_get_preferred_width(self->_priv->image, min_width, nat_width);
229         }
230     }
233         override (G:Object)
234                 void
235                 dispose(G:Object *obj)
236                 {
237                         Self *self = GMPC_METAIMAGE(obj);
238             if(self->_priv->meta_id)
239                         {
240                                 g_signal_handler_disconnect(G_OBJECT(gmw),self->_priv->meta_id);
241                                 self->_priv->meta_id =  0;
242                         }
243             if(self->_priv->metadata_force_reload)
244             {
245                 g_signal_handler_disconnect(G_OBJECT(gmw),self->_priv->metadata_force_reload);
246                 self->_priv->metadata_force_reload =  0;
247             }
248                         if(self->_priv->image_url) {
249                                 q_free(self->_priv->image_url);
250                         }
251                         if(self->_priv->status_signal){
252                                 g_signal_handler_disconnect(G_OBJECT(gmpcconn), self->_priv->status_signal);
253                                 self->_priv->status_signal =0;
254                         }
255                         if(self->_priv->connect_signal){
256                                 g_signal_handler_disconnect(G_OBJECT(gmpcconn), self->_priv->connect_signal);
257                                 self->_priv->connect_signal=0;
258                         }
259                         PARENT_HANDLER(obj);
260                 }
261     private
262         void
263         drag_data_recieved (self,
264                 GdkDragContext     *context,
265                 gint                x,
266                 gint                y,
267                 GtkSelectionData   *data,
268                 guint               info,
269                 guint               dnd_time,
270                 GtkWidget          *widget
271                 )
272         {
273             int found = FALSE;
274             if(self->_priv->song)
275             {
276                 if(info < num_drag_targets)
277                 {
278                     gchar **uri =  gtk_selection_data_get_uris(data);
279                     /* Only one uri is interresting */
280                     if(uri && uri[0])
281                     {
282                         gchar *path = g_filename_from_uri(uri[0], NULL, NULL);
284                         /* try to open it */
285                         if(path && gdk_pixbuf_get_file_info(path, NULL,NULL))
286                                                 {
287                                                         /* Create MetaData object */
288                                                         MetaData* met = meta_data_new();
289                                                         met->type = self->image_type;
290                                                         met->plugin_name = g_strdup("User set");
291                                                         met->content_type = META_DATA_CONTENT_URI;
292                                                         meta_data_set_uri(met, path);
294                                                         meta_data_set_entry(self->_priv->song, met);
295                                                         /* free Metadata object */
296                                                         meta_data_free(met);
297                                                 }
298                         g_free(path);
299                     }
300                     g_strfreev(uri);
301                 }
302             }
304             gtk_drag_finish(context, found, FALSE, dnd_time);
305         }
306         /* hack to make the "delayed" image load when it gets visible"
307          */
308         private
309         gboolean
310         expose_event(self, cairo_t *ct, gpointer data)
311         {
312             g_signal_handler_disconnect(G_OBJECT(self), self->_priv->expose_id);
313             self->_priv->expose_id = 0;
314             self_update_cover_from_song_delayed_real(self);
315             return FALSE;
316         }
318         public
319         Gmpc:Meta:Image *new_size(int type, int size)
320         {
321             Self *gmi =  GET_NEW;
322             gmi->image_type = type;
323             gmi->size = size;//pixbuf_cache_get_closest_size(size);
326             gtk_event_box_set_visible_window(GTK_EVENT_BOX(gmi), FALSE);
327             gtk_drag_dest_set(GTK_WIDGET(gmi),
328                     GTK_DEST_DEFAULT_ALL,
329                     data_image_drag_target, num_drag_targets,
330                     GDK_ACTION_COPY|GDK_ACTION_LINK|GDK_ACTION_DEFAULT|GDK_ACTION_MOVE);
332             g_signal_connect_swapped (G_OBJECT (gmi),"drag-data-received",
333                     G_CALLBACK (self_drag_data_recieved),gmi);
335             gmi->_priv->metadata_force_reload = g_signal_connect_swapped(G_OBJECT(gmw), "force-reload", G_CALLBACK(self_reload_image), gmi);
337             return gmi;
338         }
339         public
340                 GtkWidget * new (int type)
341                 {
342                         Self *gmi =  GET_NEW;
343                         gmi->image_type = type;
345             gtk_event_box_set_visible_window(GTK_EVENT_BOX(gmi), FALSE);
346             gtk_drag_dest_set(GTK_WIDGET(gmi),
347                     GTK_DEST_DEFAULT_ALL,
348                 data_image_drag_target, num_drag_targets,
349                 GDK_ACTION_COPY|GDK_ACTION_LINK|GDK_ACTION_DEFAULT|GDK_ACTION_MOVE);
350         g_signal_connect_swapped (G_OBJECT (gmi),"drag-data-received",
351                 G_CALLBACK (self_drag_data_recieved),gmi);
352             gmi->_priv->metadata_force_reload = g_signal_connect_swapped(G_OBJECT(gmw), "force-reload", G_CALLBACK(self_reload_image), gmi);
353             return (GtkWidget *)gmi;
354                 }
356         public
357                 void
358                 update_cover(self, MpdObj *mi, ChangedStatusType what, GmpcConnection *gc)
359                 {
361                         if(self->connection && what&(MPD_CST_STATE|MPD_CST_SONGID|MPD_CST_SONGPOS|MPD_CST_PLAYLIST) && self->is_visible)
362                         {
363                                 int state = mpd_player_get_state(self->connection);
364                                 if(state == MPD_PLAYER_STOP || state == MPD_PLAYER_UNKNOWN)
365                                 {
366                     self->_priv->refresh = TRUE;
367                                         self_set_cover_na(self);
368                                 }
369                                 else {
370                                         mpd_Song *song = mpd_playlist_get_current_song(self->connection);
371                                         if(song)
372                                         {
373                         if( self->_priv->refresh == FALSE &&
374                             self->_priv->song &&
375                             self->_priv->song->artist &&
376                             song->artist &&
377                             strcmp(song->artist, self->_priv->song->artist) == 0)
378                         {
379                             /* If we are artist iamge, don't update, same artist */
380                             if(self->image_type == META_ARTIST_ART ) return;
381                             /* If we are album, check if album matches too */
382                             else if(self->image_type == META_ALBUM_ART &&
383                                 self->_priv->song->album && song->album &&
384                                 strcmp(self->_priv->song->album, song->album) == 0){
385                                 return;
386                             }
387                         }
388                                                 self_update_cover_from_song(self, song);
389                                         }
390                                         else
391                                         {
392                                                 self_set_cover_na(self);
393                                         }
394                     self->_priv->refresh = FALSE;
395                                 }
397                         }
398                 }
399         private
400                 void
401                 connection_changed(self,MpdObj *mi, int connect, GmpcConnection *gmpconn)
402                 {
403                         if(!connect)
404                         {
405                                 self_set_cover_na(self);
406                         }
407             else
408             {
409                 if(self->connection)
410                 {
411                     int state = mpd_player_get_state(self->connection);
412                     if(state != MPD_PLAYER_STOP && state != MPD_PLAYER_UNKNOWN)
413                     {
414                         mpd_Song *song = mpd_playlist_get_current_song(self->connection);
415                         if(song){
416                             self_update_cover_from_song(self, song);
417                         }
418                     }
419                 }
421             }
423                 }
425         public
426                 void
427                 update_cover_from_song(self, const mpd_Song *song)
428                 {
429                         MetaDataResult ret;
430             MetaData *met= NULL;
433                         if(self->_priv->song)
434                                 mpd_freeSong(self->_priv->song);
435             self->_priv->song = NULL;
437                         if(!song)
438                         {
439                                 self_set_cover_na(self);
440                                 return;
441                         }
442             self->_priv->song = mpd_songDup(song);
444                         ret = meta_data_get_path(self->_priv->song, self->image_type, &met, NULL, NULL);
445                         if(ret == META_DATA_FETCHING)
446                         {
447                                 self_set_cover_fetching(self);
448                         }else if (ret == META_DATA_AVAILABLE) {
449                                 if (met->content_type == META_DATA_CONTENT_RAW) {
450                     self_set_cover_raw(self, met->content, met->size,met->md5sum);
451                 }else{
452                     self_set_cover_na(self);
453                 }
454                         } else if (ret == META_DATA_INVALID_QUERY) {
455                 self_set_cover_invalid_query(self);
456             }else {
457                                 self_set_cover_na(self);
458                         }
459                         if(met)
460                         {
461                             meta_data_free(met);
462                         }
463                 }
464         private
465         gboolean
466         update_cover_from_song_delayed_real(self)
467         {
468             MetaData *met= NULL;
469             int ret = meta_data_get_path(self->_priv->song, self->image_type, &met, NULL, NULL);
470             if(ret == META_DATA_FETCHING)
471             {
472                 self_set_cover_fetching(self);
473             }else if (ret == META_DATA_AVAILABLE) {
474                                 if (met->content_type == META_DATA_CONTENT_RAW) {
475                                         self_set_cover_raw(self, met->content, met->size,met->md5sum);
476                 } else {
477                     self_set_cover_na(self);
478                 }
479                         } else if (ret == META_DATA_INVALID_QUERY) {
480                 self_set_cover_invalid_query(self);
481             }else{
482                 self_set_cover_na(self);
483             }
484             if(met)
485             {
486                 meta_data_free(met);
487             }
488             return FALSE;
489         }
490         public
491                 void
492                 update_cover_from_song_delayed(self, mpd_Song *song)
493         {
494             if(self->_priv->song)
495                 mpd_freeSong(self->_priv->song);
497             self->_priv->song = mpd_songDup(song);
498             /* hack to make it load when it gets visible */
499             self->_priv->expose_id = g_signal_connect(G_OBJECT(self), "draw", G_CALLBACK(self_expose_event) , NULL);
500         }
502         private
503         void
504         meta_callback(GmpcMetaWatcher *gmv, mpd_Song *song, MetaDataType type, MetaDataResult ret, MetaData *met,gpointer data)
505     {
506         Self *self = data;
507         if(!song || !self || !self->_priv || !self->_priv->song)
508             return;
510         /**
511          * Check for fields
512          */
513         if(self->image_type != type)
514             return;
516         // Compare if callback is about 'our' song.
517         // TODO: optimize, by keeping checksum of current song around?
518         {
519             char *ck_a = mpd_song_checksum_type(self->_priv->song, self->image_type);
520             char *ck_b = mpd_song_checksum_type(song, self->image_type);
521             if(ck_a == NULL || ck_b == NULL || strcmp(ck_a, ck_b) != 0) {
522                 g_free(ck_a);
523                 g_free(ck_b);
524                 return;
525             }
526             g_free(ck_a);
527             g_free(ck_b);
528         }
530         /**
531          * If mpd is stopped before the result is back, set the cover to na, and ignore the result
532          */
533         if(self->connection && mpd_player_get_state(self->connection) == MPD_PLAYER_STOP)
534         {
535             self_set_cover_na(self);
536         }
537         else
538         {
539             if(ret == META_DATA_AVAILABLE) {
540                                 if (met->content_type == META_DATA_CONTENT_RAW) {
541                                         self_set_cover_raw(self, met->content, met->size,met->md5sum);
542                 }else{
543                     self_set_cover_na(self);
544                     g_log(LOG_DOMAIN, G_LOG_LEVEL_WARNING, "Method not implemented: %i", met->content_type);
545                 }
546             } else if (ret == META_DATA_FETCHING) {
547                 self_set_cover_fetching(self);
548             } else if (ret == META_DATA_INVALID_QUERY) {
549                 self_set_cover_invalid_query(self);
550             } else {
551                 self_set_cover_na(self);
552             }
553         }
554     }
557         public
558                 void
559                 set_cover_invalid_query(self)
560         {
561             int border = FALSE;
562             GError *error = NULL;
563             GdkPixbuf *pb2 = NULL;
566             if(self->_priv->state == STATE_INVALID_QUERY) {
567                 return;
568             }
569             self->_priv->state = STATE_INVALID_QUERY;
570             gmpc_meta_image_async_clear_now(GMPC_META_IMAGE_ASYNC(self->_priv->tooltip_image));
572             if(self->_priv->image_url)
573             {
574                 q_free(self->_priv->image_url);
575                 self->_priv->image_url = NULL;
576             }
577             if(self->hide_on_na)
578             {
579                 gtk_widget_hide(GTK_WIDGET(self));
580                 return;
581             }
583             if(cfg_get_single_value_as_int_with_default(config, "metaimage", "addcase",TRUE) && self->image_type == META_ALBUM_ART){
584                 pb2 = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "stylized-invalid-tags-cover", self->size, 0,&error);
585                 border = TRUE;
586                 if(error) {
587                     g_log(LOG_DOMAIN, G_LOG_LEVEL_WARNING, "Following error loading stylized-no-cover: %s", error->message);
588                     g_error_free(error);
589                     error = NULL;
590                 }
591             }
592             if(!pb2){
593                 pb2 = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), self->no_cover_icon, self->size, 0,&error);
594                 if(error) {
595                     g_log(LOG_DOMAIN, G_LOG_LEVEL_WARNING, "Following error loading stylized-no-cover: %s", error->message);
596                     g_error_free(error);
597                     error = NULL;
598                 }
599                 border = FALSE;
600             }
602             if(pb2) {
603                 gmpc_meta_image_async_set_pixbuf(GMPC_META_IMAGE_ASYNC(self->_priv->image), pb2);
604                 g_object_unref(pb2);
605             } else {
606                 /* If failed to load an image, clear it */
607                 gmpc_meta_image_async_clear_now(GMPC_META_IMAGE_ASYNC(self->_priv->image));
608             }
609         }
610         public
611                 void
612                 set_cover_na(self)
613         {
614             int border = FALSE;
615             GError *error = NULL;
616             GdkPixbuf *pb2 = NULL;
619             if(self->_priv->state == STATE_NA) {
620                 return;
621             }
622             self->_priv->state = STATE_NA;
623             gmpc_meta_image_async_clear_now(GMPC_META_IMAGE_ASYNC(self->_priv->tooltip_image));
625             if(self->_priv->image_url)
626             {
627                 q_free(self->_priv->image_url);
628                 self->_priv->image_url = NULL;
629             }
630             if(self->hide_on_na)
631             {
632                 gtk_widget_hide(GTK_WIDGET(self));
633                 return;
634             }
636             if(cfg_get_single_value_as_int_with_default(config, "metaimage", "addcase",TRUE) && self->image_type == META_ALBUM_ART){
637                 pb2 = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "stylized-no-cover", self->size, 0,&error);
638                 border = TRUE;
639                 if(error) {
640                     g_log(LOG_DOMAIN, G_LOG_LEVEL_WARNING, "Following error loading stylized-no-cover: %s", error->message);
641                     g_error_free(error);
642                     error = NULL;
643                 }
644             }
645             if(!pb2){
646                 pb2 = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), self->no_cover_icon, self->size, 0,&error);
647                 if(error) {
648                     g_log(LOG_DOMAIN, G_LOG_LEVEL_WARNING, "Following error loading stylized-no-cover: %s", error->message);
649                     g_error_free(error);
650                     error = NULL;
651                 }
652                 border = FALSE;
653             }
655             if(pb2) {
656                 gmpc_meta_image_async_set_pixbuf(GMPC_META_IMAGE_ASYNC(self->_priv->image), pb2);
657                 g_object_unref(pb2);
658             } else {
659                 /* If failed to load an image, clear it */
660                 gmpc_meta_image_async_clear_now(GMPC_META_IMAGE_ASYNC(self->_priv->image));
661             }
662         }
663         public
664                 void
665                 set_cover_fetching(self)
666         {
667             int border = FALSE;
668             GError *error = NULL;
669             GdkPixbuf *pb2 = NULL;
671             if(self->_priv->state == STATE_FETCHING) {
672                 return;
673             }
674             self->_priv->state = STATE_FETCHING;
675                         gmpc_meta_image_async_clear_now(GMPC_META_IMAGE_ASYNC(self->_priv->tooltip_image));
676                         if(self->_priv->image_url)
677             {
678                 q_free(self->_priv->image_url);
679                 self->_priv->image_url = NULL;
680             }
681             if(self->hide_on_na)
682             {
683                 gtk_widget_hide(GTK_WIDGET(self));
684             }
687             if(cfg_get_single_value_as_int_with_default(config, "metaimage", "addcase",TRUE) && self->image_type == META_ALBUM_ART){
688                 pb2 = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "stylized-fetching-cover", self->size, 0,&error);
689                 if(error) {
690                     g_log(LOG_DOMAIN, G_LOG_LEVEL_WARNING, "Following error loading stylized-no-cover: %s", error->message);
691                     g_error_free(error);
692                     error = NULL;
693                 }
694                 border=TRUE;
695             }
696             if(!pb2){
697                 pb2 = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), self->loading_cover_icon, self->size, 0,&error);
698                 if(error) {
699                     g_log(LOG_DOMAIN, G_LOG_LEVEL_WARNING, "Following error loading stylized-no-cover: %s", error->message);
700                     g_error_free(error);
701                     error = NULL;
702                 }
703                 border = FALSE;
705             }
707             if(!self->squared) {
708                 gmpc_meta_image_async_clear_now(GMPC_META_IMAGE_ASYNC(self->_priv->image));
709             }
711             if(pb2) {
712                 gmpc_meta_image_async_set_pixbuf(GMPC_META_IMAGE_ASYNC(self->_priv->image), pb2);
713                 g_object_unref(pb2);
714             } else {
715                 /* If failed to load an image, clear it */
716                 gmpc_meta_image_async_clear_now(GMPC_META_IMAGE_ASYNC(self->_priv->image));
717             }
718         }
720     public
721         void
722         set_cover_raw(self, unsigned char *data, unsigned int size, unsigned char *md5sum)
723                 {
724                         self->_priv->state = STATE_IMAGE;
725                         gmpc_meta_image_async_set_from_raw(GMPC_META_IMAGE_ASYNC(self->_priv->image),data, size, self->size,
726                                         ((cfg_get_single_value_as_int_with_default(config, "metaimage", "addcase",TRUE) && self->image_type == META_ALBUM_ART))?
727                                         GMPC_MODIFICATION_TYPE_CASING:GMPC_MODIFICATION_TYPE_BORDER,
728                                         md5sum
729                                         );
730                         // Update tooltip.
731                         if(gtk_widget_get_has_tooltip(GTK_WIDGET(self)))
732                         {
733                                 gmpc_meta_image_async_set_from_raw(GMPC_META_IMAGE_ASYNC(self->_priv->tooltip_image),
734                                         data, size, 350,
735                                         0,
736                                         md5sum
737                                         );
738                         }
739                 }
741         public
742                 void
743                 set_is_visible(self, gboolean is_visible)
744                 {
745                         self->is_visible = is_visible;
746                         if(self->is_visible)
747                         {
748                                 if(self->connection)
749                                 {
750                                         self_update_cover(self,self->connection, MPD_CST_STATE, NULL);
751                                 }
752                         } else {
753                                 gtk_widget_hide(GTK_WIDGET(self));
754                         }
755                 }
757         public
758         void
759         query_refetch(self)
760         {
761                 if(self->_priv->song)
762                 {
763                         MetaDataResult ret;
764                         MetaData *met = NULL;
766                         ret = meta_data_get_path(self->_priv->song, self->image_type|META_QUERY_NO_CACHE, &met, NULL, NULL);
767                         if(ret == META_DATA_FETCHING)
768                         {
769                                 self_set_cover_fetching(self);
770                         }else if (ret == META_DATA_AVAILABLE) {
771                                 if (met->content_type == META_DATA_CONTENT_RAW) {
772                                         self_set_cover_raw(self, met->content, met->size,met->md5sum);
773                                 }else{
774                                         g_log(LOG_DOMAIN, G_LOG_LEVEL_WARNING, "Not implemented");
775                                         self_set_cover_na(self);
776                                 }
777             } else if (ret == META_DATA_INVALID_QUERY) {
778                 self_set_cover_invalid_query(self);
779                         } else {
780                                 self_set_cover_na(self);
781                         }
782                         if(met)
783                         {
784                                 meta_data_free(met);
785                         }
786                 }
787         }
789     signal last NONE (POINTER)
790     void menu_populate_client(self, GtkMenu *menu)
791     {
793     }
795         private
796         gboolean
797         menu_populate_callback(self,GdkEventButton *event,  gpointer data)
798         {
799                 if(self->_priv->song && event->button == 3 && self->_priv->state != STATE_INVALID_QUERY)
800                 {
801                         GtkWidget *menu = gtk_menu_new();
802                         GtkWidget *item = NULL;
804             self_menu_populate_client(self,GTK_MENU(menu));
806                         item = gtk_image_menu_item_new_with_label(_("Refetch"));
807                         gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), gtk_image_new_from_stock(GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU));
808                         g_signal_connect_swapped(G_OBJECT(item), "activate", G_CALLBACK(self_query_refetch),self);
809                         gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
811                         item = gtk_image_menu_item_new_with_label(_("Select file"));
812                         gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), gtk_image_new_from_stock(GTK_STOCK_OPEN, GTK_ICON_SIZE_MENU));
813                         g_signal_connect_swapped(G_OBJECT(item), "activate", G_CALLBACK(self_select_file),self);
814                         gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
816                         item = gtk_image_menu_item_new_from_stock(GTK_STOCK_CLEAR, NULL);
817                         g_signal_connect_swapped(G_OBJECT(item), "activate", G_CALLBACK(self_clear_entry),self);
818                         gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
820                         item = gtk_image_menu_item_new_with_label(_("Metadata selector"));
821                         gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), gtk_image_new_from_stock(GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU));
822                         g_signal_connect_swapped(G_OBJECT(item), "activate", G_CALLBACK(self_select_metadata_editor),self);
823                         gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
825                         gtk_widget_show_all(menu);
826                         gtk_menu_popup(GTK_MENU(menu), NULL, NULL,NULL, NULL, event->button, event->time);
827                         return TRUE;
828                 }
829         else if (self->_priv->state == STATE_INVALID_QUERY)
830         {
831             playlist3_show_error_message(_("Insufficient tags available to get image."), ERROR_WARNING);
832             return TRUE;
833         }
834                 return FALSE;
835         }
837         private
838         void
839         clear_entry(self)
840         {
841                 meta_data_clear_entry(self->_priv->song, self->image_type);
842         }
844     private void
845     select_metadata_editor(self)
846     {
847                 gmpc_meta_data_edit_window_new(self->_priv->song,self->image_type);
848     }
850         public
851         void
852         select_file(self)
853         {
854         gchar *p;
855                 mpd_Song *song = mpd_songDup(self->_priv->song);
856                 GtkFileFilter *gff = gtk_file_filter_new();
857                 GtkWidget *fcd = gtk_file_chooser_dialog_new(_("Select File"),NULL,
858                                          GTK_FILE_CHOOSER_ACTION_OPEN,
859                                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
860                                       GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
861                                       NULL);
862                 gtk_file_filter_set_name(gff, _("Images"));
863                 gtk_file_filter_add_pixbuf_formats(gff);
864                 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(fcd), gff);
866                 gff = gtk_file_filter_new();
867                 gtk_file_filter_set_name(gff, _("All"));
868                 gtk_file_filter_add_pattern(gff, "*");
869                 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(fcd), gff);
871                 gtk_widget_show_all(fcd);
873         p = cfg_get_single_value_as_string(config, "MetaData", "image-file-chooser");
874         if(p) {
875             gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(fcd), p);
876             g_free(p);
877         }
878         switch(gtk_dialog_run(GTK_DIALOG(fcd)))
879                 {
880                         case GTK_RESPONSE_ACCEPT:
881                                 {
882                                         gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fcd));
883                                         {
884                                                 /* Create MetaData object */
885                                                 MetaData* met = meta_data_new();
886                                                 met->type = self->image_type;
887                                                 met->plugin_name = g_strdup("User set");
888                                                 met->content_type = META_DATA_CONTENT_URI;
889                                                 meta_data_set_uri(met, filename);
891                                                 meta_data_set_entry(self->_priv->song, met);
892                                                 /* free Metadata object */
893                                                 meta_data_free(met);
895                                                 cfg_set_single_value_as_string(config, "MetaData", "image-file-chooser", filename);
896                                         }
898                                 }
899                         default:
900                                 break;
901                 }
902                 gtk_widget_destroy(fcd);
903                 mpd_freeSong(song);
904         }
906         public
907         void
908         reload_image(self)
909         {
910         gmpc_meta_image_async_clear_now(GMPC_META_IMAGE_ASYNC(self->_priv->image));
911         switch(self->_priv->state)
912                 {
913                         case STATE_NA:
914                     self->_priv->state = STATE_EMPTY;
915                                         self_set_cover_na(self);
916                                         break;
917                         case STATE_FETCHING:
918                     self->_priv->state = STATE_EMPTY;
919                                         self_set_cover_fetching(self);
920                                         break;
921                         case STATE_IMAGE:
922                     // Set dirty state.
923                     self_set_dirty(self);
924                     if(self->_priv->song != NULL) {
925                         // Steal song.
926                         mpd_Song *song = self->_priv->song;
927                         self->_priv->song = NULL;
928                         // self update.
929                         self_update_cover_from_song(self,song);
930                         // free.
931                         mpd_freeSong(song);
932                     } else {
933                         self_set_cover_na(self);
934                     }
935             /* fix bitching */
936             case STATE_EMPTY:
937             case NUM_STATES:
938                         default:
939                                         break;
940                 }
941         }
942     
944         public void set_dirty(self)
945     {
946         self->_priv->state = STATE_EMPTY;
947     }