Fix typo: Similar Artist -> Similar Artists
[gmpc.git] / src / gmpc-metaimage.gob
blob13771e99cc4e6dce39d12299b041efd4bbc27efe
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);
173             gtk_container_set_resize_mode(GTK_CONTAINER(self), GTK_RESIZE_IMMEDIATE);
174             gtk_widget_show_all(GTK_WIDGET(self));
176             g_signal_connect_swapped(G_OBJECT(self/*->_priv->image*/), "button-press-event", G_CALLBACK(self_menu_populate_callback), self);
177             self->_priv->meta_id = g_signal_connect(G_OBJECT(gmw), "data-changed", G_CALLBACK(self_meta_callback), self);
178             gtk_widget_add_events(GTK_WIDGET(self->_priv->image), GDK_POINTER_MOTION_MASK);
179             gtk_widget_set_no_show_all(GTK_WIDGET(self),TRUE);
181             win = gtk_window_new(GTK_WINDOW_POPUP);
182             gtk_window_set_resizable(GTK_WINDOW(win), FALSE);
183         //TODO
184 //          gtk_widget_modify_bg(win, GTK_STATE_NORMAL, &(GTK_WIDGET(self)->style->black));
185             gtk_container_set_border_width(GTK_CONTAINER(win), 6);
186             gtk_container_add(GTK_CONTAINER(win), GTK_WIDGET(self->_priv->tooltip_image));
188             gtk_widget_set_tooltip_window(GTK_WIDGET(self), GTK_WINDOW(win));
189             g_signal_connect(G_OBJECT(self), "query-tooltip", G_CALLBACK(self_tooltip_query), win);
190     }
191     private
192     gboolean tooltip_query(self, gint x, gint y,gboolean keyboard_mode, GtkTooltip *tip, GtkWidget *win)
193     {
194         if(self->_priv->state == STATE_IMAGE)
195         {
196                         gtk_widget_show(self->_priv->tooltip_image);
197                         return TRUE;
198                 }
199         return FALSE;
200     }
202         override (G:Object)
203                 void
204                 dispose(G:Object *obj)
205                 {
206                         Self *self = GMPC_METAIMAGE(obj);
207             if(self->_priv->meta_id)
208                         {
209                                 g_signal_handler_disconnect(G_OBJECT(gmw),self->_priv->meta_id);
210                                 self->_priv->meta_id =  0;
211                         }
212             if(self->_priv->metadata_force_reload)
213             {
214                 g_signal_handler_disconnect(G_OBJECT(gmw),self->_priv->metadata_force_reload);
215                 self->_priv->metadata_force_reload =  0;
216             }
217                         if(self->_priv->image_url) {
218                                 q_free(self->_priv->image_url);
219                         }
220                         if(self->_priv->status_signal){
221                                 g_signal_handler_disconnect(G_OBJECT(gmpcconn), self->_priv->status_signal);
222                                 self->_priv->status_signal =0;
223                         }
224                         if(self->_priv->connect_signal){
225                                 g_signal_handler_disconnect(G_OBJECT(gmpcconn), self->_priv->connect_signal);
226                                 self->_priv->connect_signal=0;
227                         }
228                         PARENT_HANDLER(obj);
229                 }
230     private
231         void
232         drag_data_recieved (self,
233                 GdkDragContext     *context,
234                 gint                x,
235                 gint                y,
236                 GtkSelectionData   *data,
237                 guint               info,
238                 guint               dnd_time,
239                 GtkWidget          *widget
240                 )
241         {
242             int found = FALSE;
243             if(self->_priv->song)
244             {
245                 if(info < num_drag_targets)
246                 {
247                     gchar **uri =  gtk_selection_data_get_uris(data);
248                     /* Only one uri is interresting */
249                     if(uri && uri[0])
250                     {
251                         gchar *path = g_filename_from_uri(uri[0], NULL, NULL);
253                         /* try to open it */
254                         if(path && gdk_pixbuf_get_file_info(path, NULL,NULL))
255                                                 {
256                                                         /* Create MetaData object */
257                                                         MetaData* met = meta_data_new();
258                                                         met->type = self->image_type;
259                                                         met->plugin_name = g_strdup("User set");
260                                                         met->content_type = META_DATA_CONTENT_URI;
261                                                         meta_data_set_uri(met, path);
263                                                         meta_data_set_entry(self->_priv->song, met);
264                                                         /* free Metadata object */
265                                                         meta_data_free(met);
266                                                 }
267                         g_free(path);
268                     }
269                     g_strfreev(uri);
270                 }
271             }
273             gtk_drag_finish(context, found, FALSE, dnd_time);
274         }
275         /* hack to make the "delayed" image load when it gets visible"
276          */
277         private
278         gboolean
279         expose_event(self, cairo_t *ct, gpointer data)
280         {
281             g_signal_handler_disconnect(G_OBJECT(self), self->_priv->expose_id);
282             self->_priv->expose_id = 0;
283             self_update_cover_from_song_delayed_real(self);
284             return FALSE;
285         }
287         public
288         Gmpc:Meta:Image *new_size(int type, int size)
289         {
290             Self *gmi =  GET_NEW;
291             gmi->image_type = type;
292             gmi->size = size;//pixbuf_cache_get_closest_size(size);
295             gtk_event_box_set_visible_window(GTK_EVENT_BOX(gmi), FALSE);
296             gtk_drag_dest_set(GTK_WIDGET(gmi),
297                     GTK_DEST_DEFAULT_ALL,
298                     data_image_drag_target, num_drag_targets,
299                     GDK_ACTION_COPY|GDK_ACTION_LINK|GDK_ACTION_DEFAULT|GDK_ACTION_MOVE);
301             g_signal_connect_swapped (G_OBJECT (gmi),"drag-data-received",
302                     G_CALLBACK (self_drag_data_recieved),gmi);
304             gmi->_priv->metadata_force_reload = g_signal_connect_swapped(G_OBJECT(gmw), "force-reload", G_CALLBACK(self_reload_image), gmi);
306             return gmi;
307         }
308         public
309                 GtkWidget * new (int type)
310                 {
311                         Self *gmi =  GET_NEW;
312                         gmi->image_type = type;
314             gtk_event_box_set_visible_window(GTK_EVENT_BOX(gmi), FALSE);
315             gtk_drag_dest_set(GTK_WIDGET(gmi),
316                     GTK_DEST_DEFAULT_ALL,
317                 data_image_drag_target, num_drag_targets,
318                 GDK_ACTION_COPY|GDK_ACTION_LINK|GDK_ACTION_DEFAULT|GDK_ACTION_MOVE);
319         g_signal_connect_swapped (G_OBJECT (gmi),"drag-data-received",
320                 G_CALLBACK (self_drag_data_recieved),gmi);
321             gmi->_priv->metadata_force_reload = g_signal_connect_swapped(G_OBJECT(gmw), "force-reload", G_CALLBACK(self_reload_image), gmi);
322             return (GtkWidget *)gmi;
323                 }
325         public
326                 void
327                 update_cover(self, MpdObj *mi, ChangedStatusType what, GmpcConnection *gc)
328                 {
330                         if(self->connection && what&(MPD_CST_STATE|MPD_CST_SONGID|MPD_CST_SONGPOS|MPD_CST_PLAYLIST) && self->is_visible)
331                         {
332                                 int state = mpd_player_get_state(self->connection);
333                                 if(state == MPD_PLAYER_STOP || state == MPD_PLAYER_UNKNOWN)
334                                 {
335                     self->_priv->refresh = TRUE;
336                                         self_set_cover_na(self);
337                                 }
338                                 else {
339                                         mpd_Song *song = mpd_playlist_get_current_song(self->connection);
340                                         if(song)
341                                         {
342                         if( self->_priv->refresh == FALSE &&
343                             self->_priv->song &&
344                             self->_priv->song->artist &&
345                             song->artist &&
346                             strcmp(song->artist, self->_priv->song->artist) == 0)
347                         {
348                             /* If we are artist iamge, don't update, same artist */
349                             if(self->image_type == META_ARTIST_ART ) return;
350                             /* If we are album, check if album matches too */
351                             else if(self->image_type == META_ALBUM_ART &&
352                                 self->_priv->song->album && song->album &&
353                                 strcmp(self->_priv->song->album, song->album) == 0){
354                                 return;
355                             }
356                         }
357                                                 self_update_cover_from_song(self, song);
358                                         }
359                                         else
360                                         {
361                                                 self_set_cover_na(self);
362                                         }
363                     self->_priv->refresh = FALSE;
364                                 }
366                         }
367                 }
368         private
369                 void
370                 connection_changed(self,MpdObj *mi, int connect, GmpcConnection *gmpconn)
371                 {
372                         if(!connect)
373                         {
374                                 self_set_cover_na(self);
375                         }
376             else
377             {
378                 if(self->connection)
379                 {
380                     int state = mpd_player_get_state(self->connection);
381                     if(state != MPD_PLAYER_STOP && state != MPD_PLAYER_UNKNOWN)
382                     {
383                         mpd_Song *song = mpd_playlist_get_current_song(self->connection);
384                         if(song){
385                             self_update_cover_from_song(self, song);
386                         }
387                     }
388                 }
390             }
392                 }
394         public
395                 void
396                 update_cover_from_song(self, const mpd_Song *song)
397                 {
398                         MetaDataResult ret;
399             MetaData *met= NULL;
402                         if(self->_priv->song)
403                                 mpd_freeSong(self->_priv->song);
404             self->_priv->song = NULL;
406                         if(!song)
407                         {
408                                 self_set_cover_na(self);
409                                 return;
410                         }
411             self->_priv->song = mpd_songDup(song);
413                         ret = meta_data_get_path(self->_priv->song, self->image_type, &met, NULL, NULL);
414                         if(ret == META_DATA_FETCHING)
415                         {
416                                 self_set_cover_fetching(self);
417                         }else if (ret == META_DATA_AVAILABLE) {
418                                 if (met->content_type == META_DATA_CONTENT_RAW) {
419                     self_set_cover_raw(self, met->content, met->size,met->md5sum);
420                 }else{
421                     self_set_cover_na(self);
422                 }
423                         } else if (ret == META_DATA_INVALID_QUERY) {
424                 self_set_cover_invalid_query(self);
425             }else {
426                                 self_set_cover_na(self);
427                         }
428                         if(met)
429                         {
430                             meta_data_free(met);
431                         }
432                 }
433         private
434         gboolean
435         update_cover_from_song_delayed_real(self)
436         {
437             MetaData *met= NULL;
438             int ret = meta_data_get_path(self->_priv->song, self->image_type, &met, NULL, NULL);
439             if(ret == META_DATA_FETCHING)
440             {
441                 self_set_cover_fetching(self);
442             }else if (ret == META_DATA_AVAILABLE) {
443                                 if (met->content_type == META_DATA_CONTENT_RAW) {
444                                         self_set_cover_raw(self, met->content, met->size,met->md5sum);
445                 } else {
446                     self_set_cover_na(self);
447                 }
448                         } else if (ret == META_DATA_INVALID_QUERY) {
449                 self_set_cover_invalid_query(self);
450             }else{
451                 self_set_cover_na(self);
452             }
453             if(met)
454             {
455                 meta_data_free(met);
456             }
457             return FALSE;
458         }
459         public
460                 void
461                 update_cover_from_song_delayed(self, mpd_Song *song)
462         {
463             if(self->_priv->song)
464                 mpd_freeSong(self->_priv->song);
466             gtk_widget_set_size_request(GTK_WIDGET(self), self->size, self->size);
467             self->_priv->song = mpd_songDup(song);
468             /* hack to make it load when it gets visible */
469             self->_priv->expose_id = g_signal_connect(G_OBJECT(self), "draw", G_CALLBACK(self_expose_event) , NULL);
470         }
472         private
473         void
474         meta_callback(GmpcMetaWatcher *gmv, mpd_Song *song, MetaDataType type, MetaDataResult ret, MetaData *met,gpointer data)
475     {
476         Self *self = data;
477         if(!song || !self || !self->_priv || !self->_priv->song)
478             return;
480         /**
481          * Check for fields
482          */
483         if(self->image_type != type)
484             return;
486         // Compare if callback is about 'our' song.
487         // TODO: optimize, by keeping checksum of current song around?
488         {
489             char *ck_a = mpd_song_checksum_type(self->_priv->song, self->image_type);
490             char *ck_b = mpd_song_checksum_type(song, self->image_type);
491             if(ck_a == NULL || ck_b == NULL || strcmp(ck_a, ck_b) != 0) {
492                 g_free(ck_a);
493                 g_free(ck_b);
494                 return;
495             }
496             g_free(ck_a);
497             g_free(ck_b);
498         }
500         /**
501          * If mpd is stopped before the result is back, set the cover to na, and ignore the result
502          */
503         if(self->connection && mpd_player_get_state(self->connection) == MPD_PLAYER_STOP)
504         {
505             self_set_cover_na(self);
506         }
507         else
508         {
509             if(ret == META_DATA_AVAILABLE) {
510                                 if (met->content_type == META_DATA_CONTENT_RAW) {
511                                         self_set_cover_raw(self, met->content, met->size,met->md5sum);
512                 }else{
513                     self_set_cover_na(self);
514                     g_log(LOG_DOMAIN, G_LOG_LEVEL_WARNING, "Method not implemented: %i", met->content_type);
515                 }
516             } else if (ret == META_DATA_FETCHING) {
517                 self_set_cover_fetching(self);
518             } else if (ret == META_DATA_INVALID_QUERY) {
519                 self_set_cover_invalid_query(self);
520             } else {
521                 self_set_cover_na(self);
522             }
523         }
524     }
527         public
528                 void
529                 set_cover_invalid_query(self)
530         {
531             int border = FALSE;
532             GError *error = NULL;
533             GdkPixbuf *pb2 = NULL;
536             if(self->_priv->state == STATE_INVALID_QUERY) {
537                 return;
538             }
539             self->_priv->state = STATE_INVALID_QUERY;
540             gmpc_meta_image_async_clear_now(GMPC_META_IMAGE_ASYNC(self->_priv->tooltip_image));
542             if(self->_priv->image_url)
543             {
544                 q_free(self->_priv->image_url);
545                 self->_priv->image_url = NULL;
546             }
547             if(self->hide_on_na)
548             {
549                 gtk_widget_hide(GTK_WIDGET(self));
550                 return;
551             }
553             if(cfg_get_single_value_as_int_with_default(config, "metaimage", "addcase",TRUE) && self->image_type == META_ALBUM_ART){
554                 pb2 = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "stylized-invalid-tags-cover", self->size, 0,&error);
555                 border = TRUE;
556                 if(error) {
557                     g_log(LOG_DOMAIN, G_LOG_LEVEL_WARNING, "Following error loading stylized-no-cover: %s", error->message);
558                     g_error_free(error);
559                     error = NULL;
560                 }
561             }
562             if(!pb2){
563                 pb2 = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), self->no_cover_icon, self->size, 0,&error);
564                 if(error) {
565                     g_log(LOG_DOMAIN, G_LOG_LEVEL_WARNING, "Following error loading stylized-no-cover: %s", error->message);
566                     g_error_free(error);
567                     error = NULL;
568                 }
569                 border = FALSE;
570             }
572             if(pb2) {
573                 if(self->squared) {
574                     gtk_widget_set_size_request(GTK_WIDGET(self), self->size, self->size);
575                 } else {
576                     //    gtk_widget_set_size_request(GTK_WIDGET(self), gdk_pixbuf_get_width(pb2), gdk_pixbuf_get_height(pb2));
577                 }
578                 gmpc_meta_image_async_set_pixbuf(GMPC_META_IMAGE_ASYNC(self->_priv->image), pb2);
579                 g_object_unref(pb2);
580             } else {
581                 /* If failed to load an image, clear it */
582                 gmpc_meta_image_async_clear_now(GMPC_META_IMAGE_ASYNC(self->_priv->image));
583             }
584         }
585         public
586                 void
587                 set_cover_na(self)
588         {
589             int border = FALSE;
590             GError *error = NULL;
591             GdkPixbuf *pb2 = NULL;
594             if(self->_priv->state == STATE_NA) {
595                 return;
596             }
597             self->_priv->state = STATE_NA;
598             gmpc_meta_image_async_clear_now(GMPC_META_IMAGE_ASYNC(self->_priv->tooltip_image));
600             if(self->_priv->image_url)
601             {
602                 q_free(self->_priv->image_url);
603                 self->_priv->image_url = NULL;
604             }
605             if(self->hide_on_na)
606             {
607                 gtk_widget_hide(GTK_WIDGET(self));
608                 return;
609             }
611             if(cfg_get_single_value_as_int_with_default(config, "metaimage", "addcase",TRUE) && self->image_type == META_ALBUM_ART){
612                 pb2 = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "stylized-no-cover", self->size, 0,&error);
613                 border = TRUE;
614                 if(error) {
615                     g_log(LOG_DOMAIN, G_LOG_LEVEL_WARNING, "Following error loading stylized-no-cover: %s", error->message);
616                     g_error_free(error);
617                     error = NULL;
618                 }
619             }
620             if(!pb2){
621                 pb2 = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), self->no_cover_icon, self->size, 0,&error);
622                 if(error) {
623                     g_log(LOG_DOMAIN, G_LOG_LEVEL_WARNING, "Following error loading stylized-no-cover: %s", error->message);
624                     g_error_free(error);
625                     error = NULL;
626                 }
627                 border = FALSE;
628             }
630             if(pb2) {
631                 if(self->squared) {
632                     gtk_widget_set_size_request(GTK_WIDGET(self), self->size, self->size);
633                 } else {
634                     //    gtk_widget_set_size_request(GTK_WIDGET(self), gdk_pixbuf_get_width(pb2), gdk_pixbuf_get_height(pb2));
635                 }
636                 gmpc_meta_image_async_set_pixbuf(GMPC_META_IMAGE_ASYNC(self->_priv->image), pb2);
637                 g_object_unref(pb2);
638             } else {
639                 /* If failed to load an image, clear it */
640                 gmpc_meta_image_async_clear_now(GMPC_META_IMAGE_ASYNC(self->_priv->image));
641             }
642         }
643         public
644                 void
645                 set_cover_fetching(self)
646         {
647             int border = FALSE;
648             GError *error = NULL;
649             GdkPixbuf *pb2 = NULL;
651             if(self->_priv->state == STATE_FETCHING) {
652                 return;
653             }
654             self->_priv->state = STATE_FETCHING;
655                         gmpc_meta_image_async_clear_now(GMPC_META_IMAGE_ASYNC(self->_priv->tooltip_image));
656                         if(self->_priv->image_url)
657             {
658                 q_free(self->_priv->image_url);
659                 self->_priv->image_url = NULL;
660             }
661             if(self->hide_on_na)
662             {
663                 gtk_widget_hide(GTK_WIDGET(self));
664             }
667             if(cfg_get_single_value_as_int_with_default(config, "metaimage", "addcase",TRUE) && self->image_type == META_ALBUM_ART){
668                 pb2 = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "stylized-fetching-cover", self->size, 0,&error);
669                 if(error) {
670                     g_log(LOG_DOMAIN, G_LOG_LEVEL_WARNING, "Following error loading stylized-no-cover: %s", error->message);
671                     g_error_free(error);
672                     error = NULL;
673                 }
674                 border=TRUE;
675             }
676             if(!pb2){
677                 pb2 = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), self->loading_cover_icon, self->size, 0,&error);
678                 if(error) {
679                     g_log(LOG_DOMAIN, G_LOG_LEVEL_WARNING, "Following error loading stylized-no-cover: %s", error->message);
680                     g_error_free(error);
681                     error = NULL;
682                 }
683                 border = FALSE;
685             }
687             if(self->squared) {
688                 gtk_widget_set_size_request(GTK_WIDGET(self), self->size, self->size);
689             } else {
690                 gmpc_meta_image_async_clear_now(GMPC_META_IMAGE_ASYNC(self->_priv->image));
691             }
693             if(pb2) {
694                 gmpc_meta_image_async_set_pixbuf(GMPC_META_IMAGE_ASYNC(self->_priv->image), pb2);
695                 g_object_unref(pb2);
696             } else {
697                 /* If failed to load an image, clear it */
698                 gmpc_meta_image_async_clear_now(GMPC_META_IMAGE_ASYNC(self->_priv->image));
699             }
700         }
702     public
703         void
704         set_cover_raw(self, unsigned char *data, unsigned int size, unsigned char *md5sum)
705                 {
706                         self->_priv->state = STATE_IMAGE;
707                         gmpc_meta_image_async_set_from_raw(GMPC_META_IMAGE_ASYNC(self->_priv->image),data, size, self->size,
708                                         ((cfg_get_single_value_as_int_with_default(config, "metaimage", "addcase",TRUE) && self->image_type == META_ALBUM_ART))?
709                                         GMPC_MODIFICATION_TYPE_CASING:GMPC_MODIFICATION_TYPE_BORDER,
710                                         md5sum
711                                         );
712                         if(self->squared) {
713                                 gtk_widget_set_size_request(GTK_WIDGET(self), self->size, self->size);
714                         }
715                         // Update tooltip.
716                         if(gtk_widget_get_has_tooltip(GTK_WIDGET(self)))
717                         {
718                                 gmpc_meta_image_async_set_from_raw(GMPC_META_IMAGE_ASYNC(self->_priv->tooltip_image),
719                                         data, size, 350,
720                                         0,
721                                         md5sum
722                                         );
723                         }
724                 }
726         public
727                 void
728                 set_is_visible(self, gboolean is_visible)
729                 {
730                         self->is_visible = is_visible;
731                         if(self->is_visible)
732                         {
733                                 if(self->connection)
734                                 {
735                                         self_update_cover(self,self->connection, MPD_CST_STATE, NULL);
736                                 }
737                         } else {
738                                 gtk_widget_hide(GTK_WIDGET(self));
739                         }
740                 }
742         public
743         void
744         query_refetch(self)
745         {
746                 if(self->_priv->song)
747                 {
748                         MetaDataResult ret;
749                         MetaData *met = NULL;
751                         ret = meta_data_get_path(self->_priv->song, self->image_type|META_QUERY_NO_CACHE, &met, NULL, NULL);
752                         if(ret == META_DATA_FETCHING)
753                         {
754                                 self_set_cover_fetching(self);
755                         }else if (ret == META_DATA_AVAILABLE) {
756                                 if (met->content_type == META_DATA_CONTENT_RAW) {
757                                         self_set_cover_raw(self, met->content, met->size,met->md5sum);
758                                 }else{
759                                         g_log(LOG_DOMAIN, G_LOG_LEVEL_WARNING, "Not implemented");
760                                         self_set_cover_na(self);
761                                 }
762             } else if (ret == META_DATA_INVALID_QUERY) {
763                 self_set_cover_invalid_query(self);
764                         } else {
765                                 self_set_cover_na(self);
766                         }
767                         if(met)
768                         {
769                                 meta_data_free(met);
770                         }
771                 }
772         }
774     signal last NONE (POINTER)
775     void menu_populate_client(self, GtkMenu *menu)
776     {
778     }
780         private
781         gboolean
782         menu_populate_callback(self,GdkEventButton *event,  gpointer data)
783         {
784                 if(self->_priv->song && event->button == 3 && self->_priv->state != STATE_INVALID_QUERY)
785                 {
786                         GtkWidget *menu = gtk_menu_new();
787                         GtkWidget *item = NULL;
789             self_menu_populate_client(self,GTK_MENU(menu));
791                         item = gtk_image_menu_item_new_with_label(_("Refetch"));
792                         gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), gtk_image_new_from_stock(GTK_STOCK_REFRESH, GTK_ICON_SIZE_MENU));
793                         g_signal_connect_swapped(G_OBJECT(item), "activate", G_CALLBACK(self_query_refetch),self);
794                         gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
796                         item = gtk_image_menu_item_new_with_label(_("Select file"));
797                         gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), gtk_image_new_from_stock(GTK_STOCK_OPEN, GTK_ICON_SIZE_MENU));
798                         g_signal_connect_swapped(G_OBJECT(item), "activate", G_CALLBACK(self_select_file),self);
799                         gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
801                         item = gtk_image_menu_item_new_from_stock(GTK_STOCK_CLEAR, NULL);
802                         g_signal_connect_swapped(G_OBJECT(item), "activate", G_CALLBACK(self_clear_entry),self);
803                         gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
805                         item = gtk_image_menu_item_new_with_label(_("Metadata selector"));
806                         gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), gtk_image_new_from_stock(GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU));
807                         g_signal_connect_swapped(G_OBJECT(item), "activate", G_CALLBACK(self_select_metadata_editor),self);
808                         gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
810                         gtk_widget_show_all(menu);
811                         gtk_menu_popup(GTK_MENU(menu), NULL, NULL,NULL, NULL, event->button, event->time);
812                         return TRUE;
813                 }
814         else if (self->_priv->state == STATE_INVALID_QUERY)
815         {
816             playlist3_show_error_message(_("Insufficient tags available to get image."), ERROR_WARNING);
817             return TRUE;
818         }
819                 return FALSE;
820         }
822         private
823         void
824         clear_entry(self)
825         {
826                 meta_data_clear_entry(self->_priv->song, self->image_type);
827         }
829     private void
830     select_metadata_editor(self)
831     {
832                 gmpc_meta_data_edit_window_new(self->_priv->song,self->image_type);
833     }
835         public
836         void
837         select_file(self)
838         {
839         gchar *p;
840                 mpd_Song *song = mpd_songDup(self->_priv->song);
841                 GtkFileFilter *gff = gtk_file_filter_new();
842                 GtkWidget *fcd = gtk_file_chooser_dialog_new(_("Select File"),NULL,
843                                          GTK_FILE_CHOOSER_ACTION_OPEN,
844                                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
845                                       GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
846                                       NULL);
847                 gtk_file_filter_set_name(gff, _("Images"));
848                 gtk_file_filter_add_pixbuf_formats(gff);
849                 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(fcd), gff);
851                 gff = gtk_file_filter_new();
852                 gtk_file_filter_set_name(gff, _("All"));
853                 gtk_file_filter_add_pattern(gff, "*");
854                 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(fcd), gff);
856                 gtk_widget_show_all(fcd);
858         p = cfg_get_single_value_as_string(config, "MetaData", "image-file-chooser");
859         if(p) {
860             gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(fcd), p);
861             g_free(p);
862         }
863         switch(gtk_dialog_run(GTK_DIALOG(fcd)))
864                 {
865                         case GTK_RESPONSE_ACCEPT:
866                                 {
867                                         gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fcd));
868                                         {
869                                                 /* Create MetaData object */
870                                                 MetaData* met = meta_data_new();
871                                                 met->type = self->image_type;
872                                                 met->plugin_name = g_strdup("User set");
873                                                 met->content_type = META_DATA_CONTENT_URI;
874                                                 meta_data_set_uri(met, filename);
876                                                 meta_data_set_entry(self->_priv->song, met);
877                                                 /* free Metadata object */
878                                                 meta_data_free(met);
880                                                 cfg_set_single_value_as_string(config, "MetaData", "image-file-chooser", filename);
881                                         }
883                                 }
884                         default:
885                                 break;
886                 }
887                 gtk_widget_destroy(fcd);
888                 mpd_freeSong(song);
889         }
891         public
892         void
893         reload_image(self)
894         {
895         gmpc_meta_image_async_clear_now(GMPC_META_IMAGE_ASYNC(self->_priv->image));
896         switch(self->_priv->state)
897                 {
898                         case STATE_NA:
899                     self->_priv->state = STATE_EMPTY;
900                                         self_set_cover_na(self);
901                                         break;
902                         case STATE_FETCHING:
903                     self->_priv->state = STATE_EMPTY;
904                                         self_set_cover_fetching(self);
905                                         break;
906                         case STATE_IMAGE:
907                     // Set dirty state.
908                     self_set_dirty(self);
909                     if(self->_priv->song != NULL) {
910                         // Steal song.
911                         mpd_Song *song = self->_priv->song;
912                         self->_priv->song = NULL;
913                         // self update.
914                         self_update_cover_from_song(self,song);
915                         // free.
916                         mpd_freeSong(song);
917                     } else {
918                         self_set_cover_na(self);
919                     }
920             /* fix bitching */
921             case STATE_EMPTY:
922             case NUM_STATES:
923                         default:
924                                         break;
925                 }
926         }
928         public void set_dirty(self)
929     {
930         self->_priv->state = STATE_EMPTY;
931     }