1 /*
2 |  Copyright (C) 2007 P.G. Richardson <phantom_sf at users.sourceforge.net>
3 |  Part of the gtkpod project.
4 |
5 |  URL: http://www.gtkpod.org/
6 |  URL: http://gtkpod.sourceforge.net/
7 |
8 |  This program is free software; you can redistribute it and/or modify
9 |  it under the terms of the GNU General Public License as published by
10 |  the Free Software Foundation; either version 2 of the License, or
11 |  (at your option) any later version.
12 |
13 |  This program is distributed in the hope that it will be useful,
14 |  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 |  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 |  GNU General Public License for more details.
17 |
18 |  You should have received a copy of the GNU General Public License
19 |  along with this program; if not, write to the Free Software
20 |  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 |
22 |  iTunes and iPod are trademarks of Apple
23 |
24 |  This product is not supported/written/published by Apple!
25 |
26 |  $Id$
27  */
28 
29 #ifdef HAVE_CONFIG_H
30 #include <config.h>
31 #endif
32 
33 #include "display_private.h"
34 #include "display.h"
35 #include "itdb.h"
36 #include "prefs.h"
37 #include "display_coverart.h"
38 #include "context_menus.h"
39 #include "details.h"
40 #include "fileselection.h"
41 #include "fetchcover.h"
42 #include "info.h"
43 #include "gdk/gdk.h"
44 #include <pango/pangocairo.h>
45 #include <math.h>
46 
47 #define DEBUG 0
48 
49 /* Declarations */
50 static void free_album (Album_Item *album);
51 static void free_CDWidget ();
52 static gint compare_album_keys (gchar *a, gchar *b);
53 static void set_display_window_dimensions ();
54 static void set_highlight (Cover_Item *cover, gint index, cairo_t *cr);
55 static void set_shadow_reflection (Cover_Item *cover, cairo_t *cr);
56 static void remove_track_from_album (Album_Item *album, Track *track, gchar *key, gint index, GList *keylistitem);
57 static GdkPixbuf *coverart_get_default_track_thumb (gint default_img_size);
58 static GdkPixbuf *coverart_get_track_thumb (Track *track, Itdb_Device *device, gint default_img_size);
59 
60 /* callback declarations */
61 static gboolean on_gtkpod_window_configure (GtkWidget *widget, GdkEventConfigure *event, gpointer data);
62 static void on_cover_display_button_clicked (GtkWidget *widget, gpointer data);
63 static gboolean on_contentpanel_scroll_wheel_turned (GtkWidget *widget, GdkEventScroll *event, gpointer user_data);
64 static gint on_main_cover_image_clicked (GtkWidget *widget, GdkEvent *event, gpointer data);
65 static void on_cover_display_slider_value_changed (GtkRange *range, gpointer user_data);
66 
67 /* dnd declarations */
68 static gboolean dnd_coverart_drag_drop(GtkWidget *widget, GdkDragContext *drag_context, gint x, gint y, guint time, gpointer user_data);
69 static void dnd_coverart_drag_data_received(GtkWidget *widget, GdkDragContext *dc, gint x, gint y, GtkSelectionData *data, guint info, guint time, gpointer user_data);
70 static gboolean dnd_coverart_drag_motion (GtkWidget *widget, GdkDragContext *dc, gint x, gint y, guint time, gpointer user_data);
71 
72 static void set_cover_dimensions (Cover_Item *cover, int cover_index, gdouble img_width, gdouble img_height);
73 static void coverart_sort_images (GtkSortType order);
74 static void set_slider_range (gint index);
75 static GdkColor *convert_hexstring_to_gdk_color (gchar *hexstring);
76 static gboolean on_drawing_area_exposed (GtkWidget *draw_area, GdkEventExpose *event);
77 static void draw (cairo_t *cairo_context);
78 static void redraw (gboolean force_pixbuf_update);
79 
80 /* Prefs keys */
81 const gchar *KEY_DISPLAY_COVERART="display_coverart";
82 
83 /* The structure that holds values used throughout all the functions */
84 static CD_Widget *cdwidget = NULL;
85 /* The backing hash for the albums and its associated key list */
86 static GHashTable *album_hash;
87 static GList *album_key_list;
88 /* Dimensions used for the canvas */
89 static gint WIDTH;
90 static gint HEIGHT;
91 static gint DEFAULT_WIDTH;
92 static gint DEFAULT_HEIGHT;
93 /* Path of the png file used for albums without cd covers */
94 static gchar *DEFAULT_FILE;
95 /* Flag set to force an update of covers if a modification has been made */
96 static gboolean force_pixbuf_covers = FALSE;
97 /* signal handler id for the components */
98 static gulong slide_signal_id;
99 static gulong rbutton_signal_id;
100 static gulong lbutton_signal_id;
101 static gulong window_signal_id;
102 static gulong contentpanel_signal_id;
103 
104 static GtkTargetEntry coverart_drop_types [] =
105 {
106   /* Konqueror supported flavours */
107   { "image/jpeg", 0, DND_IMAGE_JPEG },
108 
109   /* Fallback flavours */
110   { "text/plain", 0, DND_TEXT_PLAIN },
111   { "STRING", 0, DND_TEXT_PLAIN }
112 };
113 
114 #if 0
115 static void debug_albums ()
116 {
117   gint i;
118   Cover_Item *cover;
119   Album_Item *album;
120   gchar *key;
121 
122   printf("Album list\n");
123   for(i = 0; i < g_list_length(album_key_list); ++i)
124   {
125     key = g_list_nth_data(album_key_list, i);
126     if (key == NULL)
127       printf("Album key is null\n");
128     else
129     {
130       album  = g_hash_table_lookup (album_hash, key);
131       printf("Index = %d -> Album Details: Artist = %s, Album = %s, No. Tracks = %d\n", i, album->artist, album->albumname, g_list_length (album->tracks));
132     }
133   }
134 
135   printf("Cover List\n");
136   for(i = 0; i < IMG_TOTAL; ++i)
137   {
138     cover = g_ptr_array_index(cdwidget->cdcovers, i);
139     if (cover->album == NULL)
140       printf("Cover album is null\n");
141     else
142       printf("Cover Details: Artist = %s, Album = %s\n", cover->album->artist, cover->album->albumname);
143   }
144 }
145 #endif
146 
147 /**
148  * coverart_init:
149  *
150  * Initialises the image file used if an album has no cover. This
151  * needs to be loaded early as it uses the path of the binary
152  * to determine where to load the file from, in the same way as
153  * main() determines where to load the glade file from.
154  *
155  * Currently called from gtkpod_init. Should not need to be called
156  * subsequent to this.
157  *
158  * @progpath: path of the gtkpod binary being loaded.
159  *
160  */
coverart_init(gchar * progpath)161 void coverart_init (gchar *progpath)
162 {
163   gchar *progname;
164 
165   progname = g_find_program_in_path (progpath);
166   static const gchar *SEPsrcSEPgtkpod = G_DIR_SEPARATOR_S "src" G_DIR_SEPARATOR_S "gtkpod";
167 
168   if (!g_path_is_absolute (progname))
169   {
170     gchar *cur_dir = g_get_current_dir ();
171     gchar *prog_absolute;
172 
173     if (g_str_has_prefix (progname, "." G_DIR_SEPARATOR_S))
174       prog_absolute = g_build_filename (cur_dir, progname+2, NULL);
175     else
176       prog_absolute = g_build_filename (cur_dir, progname, NULL);
177 
178     g_free (progname);
179     g_free (cur_dir);
180     progname = prog_absolute;
181   }
182 
183   if (g_str_has_suffix (progname, SEPsrcSEPgtkpod))
184   {
185     gchar *suffix = g_strrstr (progname, SEPsrcSEPgtkpod);
186 
187     if (suffix)
188     {
189       *suffix = 0;
190       DEFAULT_FILE = g_build_filename (progname, "data", "default-cover.png", NULL);
191     }
192   }
193 
194   g_free (progname);
195   if (DEFAULT_FILE && !g_file_test (DEFAULT_FILE, G_FILE_TEST_EXISTS))
196   {
197     g_free (DEFAULT_FILE);
198     DEFAULT_FILE = NULL;
199   }
200 
201   if (!DEFAULT_FILE)
202   {
203     DEFAULT_FILE = g_build_filename (PACKAGE_DATA_DIR, PACKAGE, "data", "default-cover.png", NULL);
204   }
205 }
206 
207 /**
208  * coverart_init_display:
209  *
210  * Initialise the boxes and canvases of the coverart_display.
211  *
212  */
coverart_init_display()213 void coverart_init_display ()
214 {
215   /* Alway initialise these buttons whether the coverart is displayed or not as these are the
216    * up/down buttons on the display window and should be properly initialised
217    */
218   GtkWidget *upbutton = gtkpod_xml_get_widget (main_window_xml, "cover_up_button");
219   GtkWidget *downbutton = gtkpod_xml_get_widget (main_window_xml, "cover_down_button");
220   GtkWidget *lbutton = gtkpod_xml_get_widget (main_window_xml, "cover_display_leftbutton");
221   GtkWidget *rbutton = gtkpod_xml_get_widget (main_window_xml, "cover_display_rightbutton");
222   GtkWidget *slider = gtkpod_xml_get_widget (main_window_xml, "cover_display_scaler");
223 
224   /* show/hide coverart display -- default to show */
225   if (prefs_get_int (KEY_DISPLAY_COVERART))
226   {
227     gtk_widget_hide (upbutton);
228     gtk_widget_show (downbutton);
229     gtk_widget_show (lbutton);
230     gtk_widget_show (rbutton);
231     gtk_widget_show (slider);
232     if (cdwidget != NULL)
233       gtk_widget_show_all (cdwidget->contentpanel);
234   }
235   else
236   {
237     gtk_widget_show (upbutton);
238     gtk_widget_hide (downbutton);
239     gtk_widget_hide (lbutton);
240     gtk_widget_hide (rbutton);
241     gtk_widget_hide (slider);
242     if (cdwidget != NULL)
243       gtk_widget_hide_all (cdwidget->contentpanel);
244     return;
245   }
246 
247   cdwidget = g_new0(CD_Widget, 1);
248 
249   cdwidget->canvasbox = gtkpod_xml_get_widget (main_window_xml, "cover_display_canvasbox");
250   cdwidget->contentpanel = gtkpod_xml_get_widget (main_window_xml, "cover_display_window");
251   cdwidget->controlbox = gtkpod_xml_get_widget (main_window_xml, "cover_display_controlbox");
252   cdwidget->leftbutton = GTK_BUTTON (gtkpod_xml_get_widget (main_window_xml, "cover_display_leftbutton"));
253   cdwidget->rightbutton = GTK_BUTTON (gtkpod_xml_get_widget (main_window_xml, "cover_display_rightbutton"));
254   cdwidget->cdslider = GTK_HSCALE (gtkpod_xml_get_widget (main_window_xml, "cover_display_scaler"));
255   /* create a new drawing area */
256   cdwidget->draw_area = gtk_drawing_area_new();
257   cdwidget->cdcovers = g_ptr_array_sized_new (IMG_TOTAL);
258 
259   g_return_if_fail (cdwidget->contentpanel);
260   g_return_if_fail (cdwidget->canvasbox);
261   g_return_if_fail (cdwidget->controlbox);
262   g_return_if_fail (cdwidget->leftbutton);
263   g_return_if_fail (cdwidget->rightbutton);
264   g_return_if_fail (cdwidget->cdslider);
265   g_return_if_fail (cdwidget->draw_area);
266 
267   /* Initialise the album hash backing store */
268   album_hash = g_hash_table_new_full ( g_str_hash, g_str_equal,
269       (GDestroyNotify) g_free,
270       (GDestroyNotify) free_album);
271   album_key_list = NULL;
272   set_display_window_dimensions ();
273 
274   gint i;
275   Cover_Item *cover;
276   for(i = 0; i < IMG_TOTAL; ++i)
277   {
278     cover = g_new0(Cover_Item, 1);
279     g_ptr_array_add(cdwidget->cdcovers, cover);
280     cover = NULL;
281   }
282   gtk_box_pack_start_defaults (GTK_BOX(cdwidget->canvasbox), GTK_WIDGET(cdwidget->draw_area));
283 
284   /* create the expose event for the drawing area */
285   g_signal_connect (G_OBJECT (cdwidget->draw_area), "expose_event",  G_CALLBACK (on_drawing_area_exposed), NULL);
286   gtk_widget_add_events (cdwidget->draw_area, GDK_BUTTON_PRESS_MASK);
287   /* set up some callback events on the main scaled image */
288   g_signal_connect(GTK_OBJECT(cdwidget->draw_area), "button-press-event", GTK_SIGNAL_FUNC(on_main_cover_image_clicked), NULL);
289 
290   /* Dnd destinaton for foreign image files */
291   gtk_drag_dest_set (
292       cdwidget->canvasbox,
293       0,
294       coverart_drop_types,
295       TGNR (coverart_drop_types),
296       GDK_ACTION_COPY|GDK_ACTION_MOVE);
297 
298   g_signal_connect ((gpointer) cdwidget->canvasbox, "drag-drop",
299       G_CALLBACK (dnd_coverart_drag_drop),
300       NULL);
301 
302   g_signal_connect ((gpointer) cdwidget->canvasbox, "drag-data-received",
303       G_CALLBACK (dnd_coverart_drag_data_received),
304       NULL);
305 
306   g_signal_connect ((gpointer) cdwidget->canvasbox, "drag-motion",
307       G_CALLBACK (dnd_coverart_drag_motion),
308       NULL);
309 
310   contentpanel_signal_id = g_signal_connect (G_OBJECT(cdwidget->contentpanel), "scroll-event",
311       G_CALLBACK(on_contentpanel_scroll_wheel_turned), NULL);
312 
313   lbutton_signal_id = g_signal_connect (G_OBJECT(cdwidget->leftbutton), "clicked",
314       G_CALLBACK(on_cover_display_button_clicked), NULL);
315 
316   rbutton_signal_id = g_signal_connect (G_OBJECT(cdwidget->rightbutton), "clicked",
317       G_CALLBACK(on_cover_display_button_clicked), NULL);
318 
319   slide_signal_id = g_signal_connect (G_OBJECT(cdwidget->cdslider), "value-changed",
320       G_CALLBACK(on_cover_display_slider_value_changed), NULL);
321 
322   window_signal_id = g_signal_connect (gtkpod_window, "configure_event",
323       G_CALLBACK (on_gtkpod_window_configure), NULL);
324 
325   gtk_widget_show_all (cdwidget->contentpanel);
326 
327   coverart_block_change (FALSE);
328 }
329 
330 /**
331  * set_display_window_dimensions:
332  *
333  * Initialises the display component width and height.
334  * Sets the podpane's paned position value too.
335  */
set_display_window_dimensions()336 static void set_display_window_dimensions ()
337 {
338   GtkWidget *podpane;
339 
340   g_object_get (gtkpod_window,
341       "default_height", &HEIGHT,
342       NULL);
343 
344   HEIGHT = HEIGHT / 2.5;
345   DEFAULT_HEIGHT = HEIGHT;
346 
347   podpane = gtkpod_xml_get_widget (main_window_xml, "paned0");
348   g_return_if_fail (podpane);
349 
350   WIDTH = HEIGHT;
351   gtk_paned_set_position (GTK_PANED(podpane), WIDTH);
352   DEFAULT_WIDTH = WIDTH;
353 
354   gtk_widget_set_size_request(GTK_WIDGET(cdwidget->canvasbox), WIDTH, HEIGHT);
355   /* set the size of the drawing area */
356   gtk_widget_set_size_request (GTK_WIDGET(cdwidget->draw_area), WIDTH, HEIGHT);
357 }
358 
359 /**
360  * coverart_block_change:
361  *
362  * Select covers events can be switched off when automatic
363  * selections of tracks are taking place.
364  *
365  * @val: indicating whether to block or unblock select cover events
366  *
367  */
coverart_block_change(gboolean val)368 void coverart_block_change (gboolean val)
369 {
370   if (GTK_WIDGET_REALIZED(gtkpod_window))
371   {
372     if (val)
373     {
374       GdkCursor *cursor = gdk_cursor_new (GDK_WATCH);
375       gdk_window_set_cursor (gtkpod_window->window, cursor);
376       gdk_cursor_unref (cursor);
377     }
378     else
379       gdk_window_set_cursor (gtkpod_window->window, NULL);
380   }
381 
382   if (cdwidget != NULL)
383     cdwidget->block_display_change = val;
384 }
385 
386 /**
387  * redraw:
388  *
389  * Draw the artist and album text strings.
390  *
391  * @cairo_context: the context of the artwork display
392  * @text: the text to be added to the artwork display
393  * @x: the x coordinate of its location
394  * @y: the y coordinate of its location
395  *
396  */
draw_string(cairo_t * cairo_context,const gchar * text,gdouble x,gdouble y)397 static void draw_string (cairo_t *cairo_context,
398     const gchar *text,
399     gdouble x,
400     gdouble y)
401 {
402   static PangoFontDescription *desc = NULL;
403   GdkColor *color = coverart_get_foreground_display_color ();
404   PangoLayout *layout;
405   PangoRectangle extents;
406 
407   gdk_cairo_set_source_color (cairo_context, color);
408   g_free (color);
409 
410   if(!desc)
411   {
412     desc = pango_font_description_from_string ("Sans Bold 9");
413   }
414 
415   layout = pango_cairo_create_layout (cairo_context);
416   pango_layout_set_text (layout, text, -1);
417   pango_layout_set_font_description (layout, desc);
418   pango_layout_get_pixel_extents (layout, NULL, &extents);
419 
420   cairo_move_to (cairo_context,
421       x + extents.x - (extents.width / 2),
422       y + extents.y - (extents.height / 2));
423 
424   pango_cairo_show_layout (cairo_context, layout);
425 
426   g_object_unref (layout);
427 }
428 
429 /**
430  * redraw:
431  *
432  * Utility function for set all the x, y, width and height
433  * dimensions applicable to a single cover widget
434  *
435  * @force_pixbuf_update: flag indicating whether to force an update of the pixbuf covers
436  */
redraw(gboolean force_pixbuf_update)437 static void redraw (gboolean force_pixbuf_update)
438 {
439   force_pixbuf_covers = force_pixbuf_update;
440   GdkRegion *region = gdk_drawable_get_clip_region (cdwidget->draw_area->window);
441   /* redraw the cairo canvas completely by exposing it */
442   gdk_window_invalidate_region (cdwidget->draw_area->window, region, TRUE);
443   gdk_window_process_updates (cdwidget->draw_area->window, TRUE);
444   gdk_region_destroy (region);
445 
446   if (g_list_length (album_key_list) <= 1)
447   {
448     gtk_widget_set_sensitive (GTK_WIDGET(cdwidget->cdslider), FALSE);
449     gtk_widget_set_sensitive (GTK_WIDGET(cdwidget->leftbutton), FALSE);
450     gtk_widget_set_sensitive (GTK_WIDGET(cdwidget->rightbutton), FALSE);
451   }
452   else
453   {
454     gtk_widget_set_sensitive (GTK_WIDGET(cdwidget->cdslider), TRUE);
455     gtk_widget_set_sensitive (GTK_WIDGET(cdwidget->leftbutton), TRUE);
456     gtk_widget_set_sensitive (GTK_WIDGET(cdwidget->rightbutton), TRUE);
457   }
458 }
459 
460 /**
461  * draw:
462  *
463  * Paint the coverart display using cairo.
464  *
465  * @cairo_context: the coverart display context
466  */
draw(cairo_t * cairo_context)467 static void draw (cairo_t *cairo_context)
468 {
469   gint cover_index[] = {0, 8, 1, 7, 2, 6, 3, 5, 4};
470   /* Draw the background */
471   GdkColor *color = coverart_get_background_display_color ();
472 
473   cairo_save (cairo_context);
474   gdk_cairo_set_source_color (cairo_context, color);
475   cairo_set_operator (cairo_context, CAIRO_OPERATOR_SOURCE);
476   cairo_paint (cairo_context);
477   cairo_restore (cairo_context);
478   g_free (color);
479 
480   Album_Item *album;
481   gint i, album_index;
482   Cover_Item *cover;
483   gchar *key;
484 
485   for(i = 0; i < IMG_TOTAL; ++i)
486   {
487     cover = g_ptr_array_index(cdwidget->cdcovers, cover_index[i]);
488     album_index = cdwidget->first_imgindex + cover_index[i];
489 
490     /* Get the key from the key list appropriate to the index
491      * provided by the first image index property
492      */
493     key = g_list_nth_data (album_key_list, album_index);
494 
495     if (key == NULL)
496       continue;
497 
498     /* Find the Album Item appropriate to the key */
499     album  = g_hash_table_lookup (album_hash, key);
500     cover->album = album;
501 
502     if (force_pixbuf_covers)
503     {
504       g_object_unref (album->albumart);
505       album->albumart = NULL;
506       if (album->scaled_art != NULL)
507       {
508         g_object_unref (album->scaled_art);
509         album->scaled_art = NULL;
510       }
511     }
512 
513     Track *track;
514     if (album->albumart == NULL)
515     {
516       track = g_list_nth_data (album->tracks, 0);
517       album->albumart = coverart_get_track_thumb (track, track->itdb->device, DEFAULT_IMG_SIZE);
518     }
519 
520     /* Set the x, y, height and width of the CD cover */
521     set_cover_dimensions (
522         cover,
523         cover_index[i],
524         gdk_pixbuf_get_width (album->albumart),
525         gdk_pixbuf_get_height (album->albumart));
526 
527     /* Set the Cover */
528     GdkPixbuf *scaled;
529     if (album->scaled_art == NULL)
530       scaled = gdk_pixbuf_scale_simple (
531           album->albumart,
532           cover->img_width,
533           cover->img_height,
534           GDK_INTERP_BILINEAR);
535     else
536       scaled = album->scaled_art;
537 
538     gdk_cairo_set_source_pixbuf (
539         cairo_context,
540         scaled,
541         cover->img_x,
542         cover->img_y);
543     cairo_paint (cairo_context);
544 
545     /* Draw a black line around the cd cover */
546     cairo_set_line_width (cairo_context, 1);
547     cairo_set_source_rgb (cairo_context, 0, 0, 0);
548 
549     cairo_rectangle (
550         cairo_context,
551         cover->img_x,
552         cover->img_y,
553         cover->img_width,
554         cover->img_height);
555 
556     cairo_stroke (cairo_context);
557 
558     /* Display the highlight */
559     set_highlight (cover, cover_index[i], cairo_context);
560 
561     /* flip image vertically to create reflection */
562     GdkPixbuf *reflection;
563     reflection = gdk_pixbuf_flip (scaled, FALSE);
564 
565     gdk_cairo_set_source_pixbuf (
566         cairo_context,
567         reflection,
568         cover->img_x,
569         cover->img_y + cover->img_height + 2);
570     cairo_paint (cairo_context);
571 
572     g_object_unref (reflection);
573     g_object_unref (scaled);
574 
575     /* Set the reflection shadow */
576     set_shadow_reflection (cover, cairo_context);
577 
578     cairo_save(cairo_context);
579 
580     /* Set the text if the index is the central image cover */
581     if (cover_index[i] == IMG_MAIN)
582     {
583       draw_string (cairo_context, album->artist, WIDTH / 2,
584           cover->img_y + cover->img_height + 15);
585 
586       draw_string (cairo_context, album->albumname, WIDTH / 2,
587           cover->img_y + cover->img_height + 30);
588     }
589     cairo_restore(cairo_context);
590   }
591 
592   force_pixbuf_covers = FALSE;
593 
594   /* free the scaled pixbufs from the non visible covers either side of the current display.
595    * Experimental feature that should save on memory.
596    */
597   key = g_list_nth_data (album_key_list, cdwidget->first_imgindex - 1);
598   if (key != NULL)
599   {
600     album  = g_hash_table_lookup (album_hash, key);
601     if (album->scaled_art)
602     {
603       g_object_unref (album->scaled_art);
604       album->scaled_art = NULL;
605     }
606   }
607 
608   key = g_list_nth_data (album_key_list, cdwidget->first_imgindex + IMG_TOTAL + 1);
609   if (key != NULL)
610   {
611     album  = g_hash_table_lookup (album_hash, key);
612     if (album->scaled_art)
613     {
614       g_object_unref (album->scaled_art);
615       album->scaled_art = NULL;
616     }
617   }
618 }
619 
620 /**
621  * coverart_display_update:
622  *
623  * Refreshes the coverart display depending on the playlist selection. Using the
624  * clear_track_list, the refresh can be quicker is set to FALSE. However, the track
625  * list is not updated in this case. Using TRUE, the display is completely cleared and
626  * redrawn.
627  *
628  * @clear_track_list: flag indicating whether to clear the displaytracks list or not
629  *
630  */
coverart_display_update(gboolean clear_track_list)631 void coverart_display_update (gboolean clear_track_list)
632 {
633   gint i, sort;
634   GList *tracks;
635   Track *track;
636   Album_Item *album;
637   Playlist *playlist;
638 
639   if ( ! prefs_get_int (KEY_DISPLAY_COVERART))
640     return;
641 
642   /* initialize display if not already done */
643   if (!cdwidget)
644     coverart_init_display ();
645 
646   /* Ensure that the setting of images hasnt been turned off
647    * due to being in the middle of a selection operation
648    */
649   if (cdwidget->block_display_change)
650     return;
651 
652   if (clear_track_list)
653   {
654     /* Free up the hash table and the key list */
655     g_hash_table_foreach_remove(album_hash, (GHRFunc) gtk_true, NULL);
656     g_list_free (album_key_list);
657     album_key_list = NULL;
658 
659     /* Find the selected playlist */
660     playlist = pm_get_selected_playlist ();
661 
662     if (playlist)
663       tracks = playlist->members;
664 
665     if (! playlist || ! tracks)
666     {
667       redraw (FALSE);
668       return;
669     }
670 
671     while (tracks)
672     {
673       gchar *album_key;
674       track = tracks->data;
675 
676       album_key = g_strconcat (track->artist, "_", track->album, NULL);
677       /* Check whether an album item has already been created in connection
678        * with the track's artist and album
679        */
680       album = g_hash_table_lookup (album_hash, album_key);
681       if (album == NULL)
682       {
683         /* Album item not found so create a new one and populate */
684         album = g_new0 (Album_Item, 1);
685         album->albumart = NULL;
686         album->scaled_art = NULL;
687         album->albumname = g_strdup (track->album);
688         album->artist = g_strdup (track->artist);
689         album->tracks = NULL;
690         album->tracks = g_list_prepend (album->tracks, track);
691 
692         /* Insert the new Album Item into the hash */
693         g_hash_table_insert (album_hash, album_key, album);
694         /* Add the key to the list for sorting and other functions */
695         album_key_list = g_list_prepend (album_key_list, album_key);
696       }
697       else
698       {
699         /* Album Item found in the album hash so
700          * append the track to the end of the
701          * track list */
702         g_free (album_key);
703         album->tracks = g_list_prepend (album->tracks, track);
704       }
705 
706       tracks = tracks->next;
707     }
708 
709     cdwidget->first_imgindex = 0;
710   }
711 
712   /* Remove all null tracks before any sorting should take place */
713   album_key_list = g_list_remove_all (album_key_list, NULL);
714 
715   /* Sort the tracks to the order set in the preference */
716   sort = prefs_get_int("st_sort");
717   if (sort != SORT_NONE)
718   {
719       coverart_sort_images (sort);
720   }
721   else
722   {
723       /* restore original order) */
724       album_key_list = g_list_reverse (album_key_list);
725   }
726 
727   /* Add 4 null tracks to the end of the track list for padding */
728   for (i = 0; i < IMG_MAIN; ++i)
729     album_key_list = g_list_append (album_key_list, NULL);
730 
731   /* Add 4 null tracks to the start of the track list for padding */
732   for (i = 0; i < IMG_MAIN; ++i)
733     album_key_list = g_list_prepend (album_key_list, NULL);
734 
735   redraw (FALSE);
736 
737   set_slider_range (cdwidget->first_imgindex);
738 
739   /*
740       printf("######### ORIGINAL LINE UP ########\n");
741       debug_albums ();
742       printf("######### END OF ORIGINAL LINE UP #######\n");
743    */
744 }
745 
746 /**
747  * set_cover_dimensions:
748  *
749  * Utility function for set all the x, y, width and height
750  * dimensions applicable to a single cover widget
751  *
752  * @cover: cover widget for which dimensions are to be set
753  * @cover_index: index of the widget. Used to determine whether
754  * 												cover is the main cover or not
755  */
set_cover_dimensions(Cover_Item * cover,int cover_index,gdouble img_width,gdouble img_height)756 static void set_cover_dimensions (Cover_Item *cover, int cover_index, gdouble img_width, gdouble img_height)
757 {
758   gdouble x = 0, y = 0;
759   gdouble small_img_width, small_img_height;
760   gdouble display_width = 0, display_height = 0;
761   gdouble display_diff = 0, display_ratio = 0;
762   gint temp_index = 0;
763 
764   if (img_width > (WIDTH / 2))
765   {
766     display_ratio = img_width / img_height;
767     img_width = (WIDTH / 2);
768     img_height = img_width / display_ratio;
769   }
770 
771   small_img_width = img_width * 0.75;
772   small_img_height = img_height * 0.75;
773 
774   /* WIDTH is the width of the display_coverart window
775    * BORDER is the 10 pixel frame around the images
776    */
777   display_width = (WIDTH / 2) - (BORDER * 2);
778 
779   display_diff = display_width - small_img_width;
780 
781   /* Set the x location of the cover image */
782   switch(cover_index) {
783   case 0:
784   case 1:
785   case 2:
786   case 3:
787     display_ratio = ((gdouble) cover_index) / 4;
788     x = BORDER + (display_ratio * display_diff);
789     break;
790   case IMG_MAIN:
791     /* The Main Image CD Cover Image */
792     x = (WIDTH - img_width) / 2;
793     break;
794   case 5:
795   case 6:
796   case 7:
797   case 8:
798     temp_index = cover_index - 8;
799     if (temp_index < 0)
800       temp_index = temp_index * -1;
801 
802     display_ratio = ((gdouble) temp_index) / 4;
803     x = WIDTH - (BORDER + small_img_width + (display_ratio * display_diff));
804     break;
805   }
806 
807   /* Set the y location of the cover image. The y location must be determined by
808    * height of the cover image so that the hightlight and shadow fit in correctly.
809    */
810   display_height = HEIGHT - (BORDER * 2);
811 
812   switch(cover_index) {
813   case 0:
814   case 8:
815     y = display_height - (small_img_height + (BORDER * 15));
816     break;
817   case 1:
818   case 7:
819     y = display_height - (small_img_height + (BORDER * 12));
820     break;
821   case 2:
822   case 6:
823     y = display_height - (small_img_height + (BORDER * 9));
824     break;
825   case 3:
826   case 5:
827     y = display_height - (small_img_height + (BORDER * 6));
828     break;
829   case IMG_MAIN:
830     /* The Main Image CD Cover Image */
831     y = HEIGHT - (img_height + (BORDER * 4));
832   }
833 
834   cover->img_x = x;
835   cover->img_y = y;
836 
837   if (cover_index == IMG_MAIN)
838   {
839     cover->img_height = img_height;
840     cover->img_width = img_width;
841   }
842   else
843   {
844     cover->img_height = small_img_height;
845     cover->img_width = small_img_width;
846   }
847 }
848 
849 /**
850  * set_highlight:
851  *
852  * Sets the highlighted image to the cover to give shine
853  * and a reflection.
854  *
855  * @cover: A Cover_Item object which the higlighted is added to.
856  *
857  */
set_highlight(Cover_Item * cover,gint index,cairo_t * cr)858 static void set_highlight (Cover_Item *cover, gint index, cairo_t * cr)
859 {
860   if(index == IMG_MAIN)
861     return;
862 
863   cairo_save(cr);
864   cairo_pattern_t *pat;
865   pat = cairo_pattern_create_linear (
866       cover->img_x,
867       cover->img_y,
868       cover->img_x,
869       cover->img_y + (((gdouble) cover->img_height) / 2.5));
870   cairo_pattern_add_color_stop_rgba (pat, 0.0, 1, 1, 1, 0);
871   cairo_pattern_add_color_stop_rgba (pat, 0.4, 1, 1, 1, 0.6);
872   cairo_pattern_add_color_stop_rgba (pat, 0.9, 1, 1, 1, 0);
873   cairo_pattern_set_extend (pat, CAIRO_EXTEND_REPEAT);
874 
875   cairo_rectangle (
876       cr,
877       cover->img_x,
878       cover->img_y,
879       cover->img_width,
880       cover->img_height);
881   cairo_rotate (cr, M_PI / 4);
882   cairo_set_source (cr, pat);
883   cairo_fill (cr);
884   cairo_pattern_destroy (pat);
885   cairo_restore(cr);
886 }
887 
888 /**
889  * set_shadow_reflection:
890  *
891  * Sets the shadow reflection to the same as the
892  * background of the display.
893  *
894  * @cover: A Cover_Item object which the higlighted is added to.
895  *
896  */
set_shadow_reflection(Cover_Item * cover,cairo_t * cr)897 static void set_shadow_reflection (Cover_Item *cover, cairo_t *cr)
898 {
899   GdkColor *color = coverart_get_background_display_color();
900   gdouble r = ((gdouble) (color->red >> 8)) / 255;
901   gdouble g = ((gdouble) (color->green >>8)) / 255;
902   gdouble b = ((gdouble) (color->blue >> 8)) / 255;
903   g_free (color);
904 
905   cairo_pattern_t *pat;
906   pat = cairo_pattern_create_linear (
907       cover->img_x,
908       cover->img_y + cover->img_height + 2,
909       cover->img_x,
910       cover->img_y + cover->img_height + 2 + cover->img_height);
911   cairo_pattern_add_color_stop_rgba (pat, 0, r, g, b, 0.3);
912   cairo_pattern_add_color_stop_rgba (pat, 0.5, r, g, b, 1);
913   cairo_rectangle (
914       cr,
915       cover->img_x,
916       cover->img_y + cover->img_height + 2,
917       cover->img_width + 10,
918       cover->img_height);
919   cairo_set_source (cr, pat);
920   cairo_fill (cr);
921   cairo_pattern_destroy (pat);
922 }
923 
924 /**
925  *
926  * Function to cause a refresh on the given track.
927  * The signal will be one of:
928  *
929  *    COVERART_REMOVE_SIGNAL - track deleted
930  *    COVERART_CREATE_SIGNAL - track created
931  *    COVERART_CHANGE_SIGNAL - track modified
932  *
933  * If the track was in the current display of artwork then the
934  * artwork will be updated. If it was not then a refresh is unnecessary
935  * and the function will return accordingly.
936  *
937  * @track: affected track
938  * @signal: flag indicating the type of track change that has occurred.
939  */
coverart_track_changed(Track * track,gint signal)940 void coverart_track_changed (Track *track, gint signal)
941 {
942   GList *keypos;
943   gchar *trk_key;
944   Album_Item *album;
945   gint index;
946   gboolean findremove;
947   /*
948    * Scenarios:
949    * a) A track is being deleted that is not in the display
950    * b) A track is being deleted that is in the display
951    * c) A track has changed is some way so maybe the coverart
952    * d) A track has been created and its artist and album are already in the displaylist
953    * e) A track has been created and its artist and album are not in the displaylist
954    */
955 
956   /* If coverart not displayed then ignore */
957   if (! prefs_get_int (KEY_DISPLAY_COVERART))
958     return;
959 
960   trk_key = g_strconcat (track->artist, "_", track->album, NULL);
961   /* Determine the index of the found album */
962   keypos = g_list_find_custom (album_key_list, trk_key, (GCompareFunc) compare_album_keys);
963 
964   switch (signal)
965   {
966   case COVERART_REMOVE_SIGNAL:
967     g_return_if_fail (keypos);
968     g_free (trk_key);
969 
970     /* Reassign trk_key to the key from the list */
971     trk_key = keypos->data;
972     index = g_list_position (album_key_list, keypos);
973 
974     album = g_hash_table_lookup (album_hash, trk_key);
975 
976     /* Remove the track from the album item */
977     remove_track_from_album (album, track, trk_key, index, keypos);
978 
979     /* Check if album is being displayed by checking the index */
980     if (index >= cdwidget->first_imgindex && index <= (cdwidget->first_imgindex + IMG_TOTAL))
981     {
982       /* reset the covers and should reset to original position but without the index */
983       redraw (FALSE);
984     }
985 
986     /* Size of key list may have changed so reset the slider
987      * to appropriate range and index.
988      */
989     set_slider_range (index - IMG_MAIN);
990     break;
991   case COVERART_CREATE_SIGNAL:
992     /* Check whether an album item has already been created in connection
993      * with the track's artist and album
994      */
995     album = g_hash_table_lookup (album_hash, trk_key);
996     if (album == NULL)
997     {
998       /* Album item not found so create a new one and populate */
999       album = g_new0 (Album_Item, 1);
1000       album->albumart = NULL;
1001       album->scaled_art = NULL;
1002       album->albumname = g_strdup (track->album);
1003       album->artist = g_strdup (track->artist);
1004       album->tracks = NULL;
1005       album->tracks = g_list_append (album->tracks, track);
1006 
1007       /* Insert the new Album Item into the hash */
1008       g_hash_table_insert (album_hash, trk_key, album);
1009 
1010       /* Add the key to the list for sorting and other functions */
1011       /* But first ... */
1012       /* Remove all null tracks before any sorting should take place */
1013       album_key_list = g_list_remove_all (album_key_list, NULL);
1014 
1015       if (prefs_get_int("st_sort") == SORT_ASCENDING)
1016       {
1017         album_key_list = g_list_insert_sorted (album_key_list, trk_key, (GCompareFunc) compare_album_keys);
1018       }
1019       else if (prefs_get_int("st_sort") == SORT_DESCENDING)
1020       {
1021         /* Already in descending order so reverse into ascending order */
1022         album_key_list = g_list_reverse (album_key_list);
1023         /* Insert the track */
1024         album_key_list = g_list_insert_sorted (album_key_list, trk_key, (GCompareFunc) compare_album_keys);
1025         /* Reverse again */
1026         album_key_list = g_list_reverse (album_key_list);
1027       }
1028       else
1029       {
1030         /* NO SORT */
1031         album_key_list = g_list_append (album_key_list, trk_key);
1032       }
1033 
1034       /* Readd in the null tracks */
1035       /* Add 4 null tracks to the end of the track list for padding */
1036       gint i;
1037       for (i = 0; i < IMG_MAIN; ++i)
1038         album_key_list = g_list_append (album_key_list, NULL);
1039 
1040       /* Add 4 null tracks to the start of the track list for padding */
1041       for (i = 0; i < IMG_MAIN; ++i)
1042         album_key_list = g_list_prepend (album_key_list, NULL);
1043 
1044       redraw (FALSE);
1045     }
1046     else
1047     {
1048       /* Album Item found in the album hash so append the track to
1049        * the end of the track list
1050        */
1051       album->tracks = g_list_append (album->tracks, track);
1052     }
1053 
1054     /* Set the slider to the newly inserted track.
1055      * In fact sets image_index to 4 albums previous
1056      * to newly inserted album to ensure this album is
1057      * the main middle one.
1058      */
1059     keypos = g_list_find_custom (album_key_list, trk_key, (GCompareFunc) compare_album_keys);
1060     index = g_list_position (album_key_list, keypos);
1061     set_slider_range (index - IMG_MAIN);
1062 
1063     break;
1064   case COVERART_CHANGE_SIGNAL:
1065     /* A track is declaring itself as changed so what to do? */
1066     findremove = FALSE;
1067     if (keypos == NULL)
1068     {
1069       /* The track could not be found according to the key!
1070        * The ONLY way this could happen is if the user changed the
1071        * artist or album of the track. Well it should be rare but the only
1072        * way to remove it from its "old" album item is to search each one
1073        */
1074       findremove = TRUE;
1075     }
1076     else
1077     {
1078       /* Track has a valid key so can get the album back.
1079        * Either has happened:
1080        * a) Artist/Album key has been changed so the track is being moved to another existing album
1081        * b) Some other change has occurred that is irrelevant to this code.
1082        */
1083 
1084       /* To determine if a) is the case need to determine whether track exists in the
1085        * album items track list. If it does then b) is true and nothing more is required.
1086        */
1087       album = g_hash_table_lookup (album_hash, trk_key);
1088       g_return_if_fail (album);
1089 
1090       index = g_list_index (album->tracks, track);
1091       if (index != -1)
1092       {
1093         /* Track exists in the album list so ignore the change and return */
1094         ExtraTrackData *etd;
1095         etd = track->userdata;
1096         if (etd->tartwork_changed == TRUE)
1097         {
1098           etd->tartwork_changed = FALSE;
1099           redraw(TRUE);
1100         }
1101 
1102         return;
1103       }
1104       else
1105       {
1106         /* Track does not exist in the album list so the artist/album key has definitely changed */
1107         findremove = TRUE;
1108       }
1109     }
1110 
1111     if (findremove)
1112     {
1113       /* It has been determined that the track has had its key changed
1114        * and thus a search must be performed to find the "original" album
1115        * that the track belonged to, remove it then add the track to the new
1116        * album.
1117        */
1118       GList *klist;
1119       gchar *key;
1120       klist = g_list_first (album_key_list);
1121       while (klist != NULL)
1122       {
1123         key = (gchar *) klist->data;
1124         index = g_list_index (album_key_list, key);
1125         if (key != NULL)
1126         {
1127           album = g_hash_table_lookup (album_hash, key);
1128 
1129           gint album_trk_index;
1130           album_trk_index = g_list_index (album->tracks, track);
1131           if (album_trk_index != -1)
1132           {
1133             /* The track is in this album so remove it in preparation for readding
1134              * under the new album key
1135              */
1136             remove_track_from_album (album, track, key, index, klist);
1137             redraw(FALSE);
1138             /* Found the album and removed so no need to continue the loop */
1139             break;
1140           }
1141         }
1142         klist = klist->next;
1143       }
1144 
1145       /* Create a new album item or find existing album to house the "brand new" track */
1146       coverart_track_changed (track, COVERART_CREATE_SIGNAL);
1147     }
1148   }
1149 }
1150 
1151 /**
1152  * on_drawing_area_exposed:
1153  *
1154  * Callback for the drawing area. When the drwaing area is covered,
1155  * resized, changed etc.. This will be called the draw() function is then
1156  * called from this and the cairo redrawing takes place.
1157  *
1158  * @draw_area: drawing area where al the cairo drawing takes place
1159  * @event: gdk expose event
1160  *
1161  * Returns:
1162  * boolean indicating whether other handlers should be run.
1163  */
on_drawing_area_exposed(GtkWidget * draw_area,GdkEventExpose * event)1164 static gboolean on_drawing_area_exposed (GtkWidget *draw_area, GdkEventExpose *event)
1165 {
1166   cairo_t *cairo_draw_context;
1167 
1168   /* get a cairo_t */
1169   cairo_draw_context = gdk_cairo_create (draw_area->window);
1170 
1171   /* set a clip region for the expose event */
1172   cairo_rectangle (cairo_draw_context,
1173       event->area.x, event->area.y,
1174       event->area.width, event->area.height);
1175   cairo_clip (cairo_draw_context);
1176   draw(cairo_draw_context);
1177   cairo_destroy (cairo_draw_context);
1178 
1179   return FALSE;
1180 }
1181 
1182 /**
1183  * gtkpod_window_configure_callback:
1184  *
1185  * Callback for the gtkpod app window. When the window
1186  * is resized the background of the cdwidget is given a size
1187  * of the same size. Ensure the background is always black
1188  * and no overlapping internal components occur.
1189  *
1190  * @widget: gtkpod app window
1191  * @event: gdk configure event
1192  * @data: any user data passed to the function
1193  *
1194  * Returns:
1195  * boolean indicating whether other handlers should be run.
1196  */
on_gtkpod_window_configure(GtkWidget * widget,GdkEventConfigure * event,gpointer data)1197 static gboolean on_gtkpod_window_configure (GtkWidget *widget, GdkEventConfigure *event, gpointer data)
1198 {
1199   if (cdwidget == NULL)
1200     return FALSE;
1201 
1202   redraw (FALSE);
1203 
1204   return FALSE;
1205 }
1206 
1207 /**
1208  * on_paned0_button_release_event:
1209  *
1210  * Callback fired when a button release event occurs on
1211  * paned0. Only worthwhile things are carried out when
1212  * the position of paned0 has changed, ie. the bar was moved.
1213  * Moving the bar will scale the cover images appropriately.
1214  * Signal connected via the glade XML file.
1215  *
1216  * @widget: gtkpod app window
1217  * @event: gdk event button
1218  * @data: any user data passed to the function
1219  *
1220  * Returns:
1221  * boolean indicating whether other handlers should be run.
1222  */
on_paned0_button_release_event(GtkWidget * widget,GdkEventButton * event,gpointer data)1223 G_MODULE_EXPORT gboolean on_paned0_button_release_event (GtkWidget *widget, GdkEventButton *event, gpointer data)
1224 {
1225   if ( ! prefs_get_int (KEY_DISPLAY_COVERART))
1226     return FALSE;
1227 
1228   gint width;
1229 
1230   width = gtk_paned_get_position (GTK_PANED(widget));
1231   if ((width >= DEFAULT_WIDTH) && (width != WIDTH))
1232   {
1233     WIDTH = width;
1234     redraw (FALSE);
1235   }
1236 
1237   return FALSE;
1238 }
1239 
1240 /**
1241  * on_contentpanel_scroll_wheel_turned:
1242  *
1243  * Call handler for the scroll wheel. Cause the images to
1244  * be cycled in the direction indicated by the scroll wheel.
1245  *
1246  * @widget: CoverArt Display
1247  * @event: scroll wheel event
1248  * @data: any data needed by the function (not required)
1249  *
1250  */
on_contentpanel_scroll_wheel_turned(GtkWidget * widget,GdkEventScroll * event,gpointer user_data)1251 static gboolean on_contentpanel_scroll_wheel_turned (GtkWidget *widget, GdkEventScroll *event, gpointer user_data)
1252 {
1253   gint displaytotal;
1254 
1255   if (event->direction == GDK_SCROLL_DOWN)
1256     cdwidget->first_imgindex++;
1257   else
1258     cdwidget->first_imgindex--;
1259 
1260   displaytotal = g_list_length(album_key_list) - 8;
1261 
1262   if (displaytotal <= 0)
1263     return TRUE;
1264 
1265   /* Use the index value from the slider for the main image index */
1266   if (cdwidget->first_imgindex < 0)
1267     cdwidget->first_imgindex = 0;
1268   else if (cdwidget->first_imgindex > (displaytotal - 1))
1269     cdwidget->first_imgindex = displaytotal - 1;
1270 
1271   /* Change the value of the slider to do the work of scrolling the covers */
1272   gtk_range_set_value (GTK_RANGE (cdwidget->cdslider), cdwidget->first_imgindex);
1273 
1274   return TRUE;
1275 }
1276 
1277 /**
1278  * on_cover_display_button_clicked:
1279  *
1280  * Call handler for the left and right buttons. Cause the images to
1281  * be cycled in the direction indicated by the button.
1282  *
1283  * @widget: button which emitted the signal
1284  * @data: any data needed by the function (not required)
1285  *
1286  */
on_cover_display_button_clicked(GtkWidget * widget,gpointer data)1287 static void on_cover_display_button_clicked (GtkWidget *widget, gpointer data)
1288 {
1289   GtkButton *button;
1290   const gchar *label;
1291   gint displaytotal;
1292 
1293   button = GTK_BUTTON(widget);
1294   label = gtk_button_get_label(button);
1295 
1296   if(g_str_equal(label, (gchar *) ">"))
1297     cdwidget->first_imgindex++;
1298   else
1299     cdwidget->first_imgindex--;
1300 
1301   displaytotal = g_list_length(album_key_list) - 8;
1302 
1303   if (displaytotal <= 0)
1304     return;
1305 
1306   /* Use the index value from the slider for the main image index */
1307   if (cdwidget->first_imgindex < 0)
1308     cdwidget->first_imgindex = 0;
1309   else if (cdwidget->first_imgindex > (displaytotal - 1))
1310     cdwidget->first_imgindex = displaytotal - 1;
1311 
1312   /* Change the value of the slider to do the work of scrolling the covers */
1313   gtk_range_set_value (GTK_RANGE (cdwidget->cdslider), cdwidget->first_imgindex);
1314 
1315   /* debug_albums(); */
1316 }
1317 
1318 /**
1319  * on_cover_display_slider_value_changed:
1320  *
1321  * Call handler used for cycling the cover images with the slider.
1322  *
1323  * @range: GTKHScale object used as the slider
1324  * @user_data: any data needed by the function (not required)
1325  *
1326  */
on_cover_display_slider_value_changed(GtkRange * range,gpointer user_data)1327 static void on_cover_display_slider_value_changed (GtkRange *range, gpointer user_data)
1328 {
1329   gint index, displaytotal;
1330 
1331   if (cdwidget->block_display_change)
1332     return;
1333 
1334   index = gtk_range_get_value (range);
1335   displaytotal = g_list_length(album_key_list);
1336 
1337   if (displaytotal <= 0)
1338     return;
1339 
1340   /* Use the index value from the slider for the main image index */
1341   cdwidget->first_imgindex = index;
1342 
1343   if (cdwidget->first_imgindex > (displaytotal - IMG_MAIN))
1344     cdwidget->first_imgindex = displaytotal - IMG_MAIN;
1345 
1346   redraw (FALSE);
1347 }
1348 
1349 /**
1350  * on_cover_up_button_clicked:
1351  *
1352  * callback for the cover_up_button. Shows all the cover_art widgets
1353  * when clicked.
1354  *
1355  * @widget, data unused standard parameters
1356  *
1357  */
on_cover_up_button_clicked(GtkWidget * widget,gpointer data)1358 G_MODULE_EXPORT void on_cover_up_button_clicked (GtkWidget *widget, gpointer data)
1359 {
1360   prefs_set_int (KEY_DISPLAY_COVERART, TRUE);
1361 
1362   if (cdwidget == NULL)
1363     coverart_display_update (TRUE);
1364 
1365   gtk_widget_show_all (cdwidget->contentpanel);
1366   gtk_widget_show (GTK_WIDGET(cdwidget->cdslider));
1367   gtk_widget_show (GTK_WIDGET(cdwidget->leftbutton));
1368   gtk_widget_show (GTK_WIDGET(cdwidget->rightbutton));
1369 
1370   gtk_widget_hide (widget);
1371 
1372   GtkWidget *downbutton = gtkpod_xml_get_widget (main_window_xml, "cover_down_button");
1373   gtk_widget_show (downbutton);
1374 }
1375 
1376 /**
1377  * on_cover_down_button_clicked:
1378  *
1379  * callback for the cover_down_button. Hides all the cover_art widgets
1380  * when clicked.
1381  *
1382  * @widget, data unused standard parameters
1383  *
1384  */
on_cover_down_button_clicked(GtkWidget * widget,gpointer data)1385 G_MODULE_EXPORT void on_cover_down_button_clicked (GtkWidget *widget, gpointer data)
1386 {
1387   prefs_set_int (KEY_DISPLAY_COVERART, FALSE);
1388 
1389   gtk_widget_hide_all (cdwidget->contentpanel);
1390   gtk_widget_hide (GTK_WIDGET(cdwidget->cdslider));
1391   gtk_widget_hide (GTK_WIDGET(cdwidget->leftbutton));
1392   gtk_widget_hide (GTK_WIDGET(cdwidget->rightbutton));
1393 
1394   if (cdwidget != NULL)
1395   {
1396     /* dispose of existing CD Widget */
1397     free_CDWidget();
1398   }
1399   gtk_widget_hide (widget);
1400 
1401   GtkWidget *upbutton = gtkpod_xml_get_widget (main_window_xml, "cover_up_button");
1402   gtk_widget_show (upbutton);
1403 }
1404 
1405 /**
1406  * on_main_cover_image_clicked:
1407  *
1408  * Call handler used for displaying the tracks associated with
1409  * the main displayed album cover.
1410  *
1411  * @GnomeCanvas: main cd cover image canvas
1412  * @event: event object used to determine the event type
1413  * @data: any data needed by the function (not required)
1414  *
1415  */
on_main_cover_image_clicked(GtkWidget * widget,GdkEvent * event,gpointer data)1416 static gint on_main_cover_image_clicked (GtkWidget *widget, GdkEvent *event, gpointer data)
1417 {
1418   Cover_Item *cover;
1419   guint mbutton;
1420 
1421   if(event->type != GDK_BUTTON_PRESS)
1422     return FALSE;
1423 
1424   mbutton = event->button.button;
1425 
1426   if (mbutton == 1)
1427   {
1428     Track *track;
1429     Album_Item *album;
1430     /* Left mouse button clicked so find all tracks with displayed cover */
1431     cover = g_ptr_array_index(cdwidget->cdcovers, IMG_MAIN);
1432     /* Stop redisplay of the artwork as its already
1433      * in the correct location
1434      */
1435     coverart_block_change (TRUE);
1436 
1437     /* Select the correct track in the sorttabs */
1438     album = cover->album;
1439     g_return_val_if_fail (album, FALSE);
1440 
1441     /* Clear the tracks listed in the display */
1442     tm_remove_all_tracks ();
1443 
1444     GList *tracks = album->tracks;
1445     while (tracks)
1446     {
1447       track = (Track *) tracks->data;
1448       tm_add_track_to_track_model (track, NULL);
1449       tracks = tracks->next;
1450     }
1451 
1452     /* Turn the display change back on */
1453     coverart_block_change (FALSE);
1454   }
1455   else if ((mbutton == 3) && (event->button.state & GDK_SHIFT_MASK))
1456   {
1457     /* Right mouse button clicked and shift pressed.
1458      * Go straight to edit details window
1459      */
1460     GList *tracks = coverart_get_displayed_tracks();
1461     details_edit (tracks);
1462   }
1463   else if (mbutton == 3)
1464   {
1465     /* Right mouse button clicked on its own so display
1466      * popup menu
1467      */
1468     /*int i;
1469 	  GList *tracks = coverart_get_displayed_tracks();
1470 	  for (i = 0; i < g_list_length(tracks); ++i)
1471 	  {
1472 	  Track *track;
1473 	  track = g_list_nth_data (tracks, i);
1474 	  printf ("display_coverart-main_image_clicked - Artist:%s  Album:%s  Title:%s\n", track->artist, track->album, track->title);
1475 	  }*/
1476     cad_context_menu_init ();
1477   }
1478 
1479   return FALSE;
1480 }
1481 
1482 /**
1483  * coverart_get_track_thumb:
1484  *
1485  * Retrieve the artwork pixbuf from the given track.
1486  *
1487  * @track: Track from where the pixbuf is obtained.
1488  * @device: Reference to the device upon which the track is located
1489  * @default_img_size: If the default image must be used then this may contain a default value
1490  * 		for its size.
1491  *
1492  * Returns:
1493  * pixbuf referenced by the provided track or the pixbuf of the
1494  * default file if track has no cover art.
1495  */
coverart_get_track_thumb(Track * track,Itdb_Device * device,gint default_size)1496 static GdkPixbuf *coverart_get_track_thumb (Track *track, Itdb_Device *device, gint default_size)
1497 {
1498   GdkPixbuf *pixbuf = NULL;
1499   GdkPixbuf *image = NULL;
1500   ExtraTrackData *etd;
1501   gint w, h;
1502   float ratio;
1503 
1504   etd = track->userdata;
1505   g_return_val_if_fail (etd, NULL);
1506 
1507   image = itdb_track_get_thumbnail (track, 200, 200);
1508   if (image)
1509   {
1510     w = gdk_pixbuf_get_width (image);
1511     h = gdk_pixbuf_get_height (image);
1512 
1513     if (default_size > 0)
1514     {
1515       if (w > default_size || h > default_size)
1516       {
1517         /* need to scale the image back down to size */
1518         if (w == h)
1519         {
1520           w = default_size;
1521           h = default_size;
1522         }
1523         else if (h > w)
1524         {
1525           ratio = h / w;
1526           h = default_size;
1527           w = (gint) (default_size / ratio);
1528         }
1529         else
1530         {
1531           ratio = w / h;
1532           w = default_size;
1533           h = (gint) (default_size / ratio);
1534         }
1535         pixbuf = gdk_pixbuf_scale_simple(image, w, h, GDK_INTERP_BILINEAR);
1536       }
1537       else
1538         pixbuf = gdk_pixbuf_copy (image);
1539 
1540       g_object_unref (image);
1541     }
1542     else
1543     {
1544       pixbuf = gdk_pixbuf_scale_simple(image, DEFAULT_IMG_SIZE, DEFAULT_IMG_SIZE, GDK_INTERP_BILINEAR);
1545       g_object_unref (image);
1546     }
1547   }
1548 
1549   if (pixbuf ==  NULL)
1550   {
1551     /* Could not get a viable thumbnail so get default pixbuf */
1552     pixbuf = coverart_get_default_track_thumb (default_size);
1553   }
1554 
1555   return pixbuf;
1556 }
1557 
1558 /**
1559  * coverart_get_displayed_tracks:
1560  *
1561  * Get all tracks suggested by the displayed album cover.
1562  *
1563  * Returns:
1564  * GList containing references to all the displayed covered tracks
1565  */
coverart_get_displayed_tracks(void)1566 GList *coverart_get_displayed_tracks (void)
1567 {
1568   Cover_Item *cover;
1569 
1570   cover = g_ptr_array_index(cdwidget->cdcovers, IMG_MAIN);
1571   g_return_val_if_fail (cover->album, NULL);
1572   return cover->album->tracks;
1573 }
1574 
1575 /**
1576  * coverart_get_default_track_thumb:
1577  *
1578  * Retrieve the artwork pixbuf from the default image file.
1579  *
1580  * Returns:
1581  * pixbuf of the default file for tracks with no cover art.
1582  */
coverart_get_default_track_thumb(gint default_img_size)1583 static GdkPixbuf *coverart_get_default_track_thumb (gint default_img_size)
1584 {
1585   GdkPixbuf *pixbuf = NULL;
1586   GdkPixbuf *scaled = NULL;
1587   gdouble default_size = 140;
1588   GError *error = NULL;
1589 
1590   if (default_img_size != 0)
1591     default_size = (gdouble) default_img_size;
1592 
1593   pixbuf = gdk_pixbuf_new_from_file(DEFAULT_FILE, &error);
1594 
1595   if (error != NULL)
1596   {
1597     printf("Error occurred loading the default file - \nCode: %d\nMessage: %s\n",
1598         error->code, error->message);
1599 
1600     g_return_val_if_fail(pixbuf, NULL);
1601   }
1602 
1603   scaled = gdk_pixbuf_scale_simple(pixbuf, default_size, default_size, GDK_INTERP_BILINEAR);
1604   g_object_unref (pixbuf);
1605 
1606   return scaled;
1607 }
1608 
1609 /**
1610  *
1611  * set_scale_range:
1612  *
1613  * Set the scale range - maximum value should be display
1614  * track list length - (8 NULL images + 1 as index value),
1615  * ie. the ones either end of the list.
1616  *
1617  */
set_slider_range(gint index)1618 static void set_slider_range (gint index)
1619 {
1620   gint slider_ubound = g_list_length (album_key_list) - IMG_TOTAL;
1621   if(slider_ubound < 1)
1622   {
1623     /* If only one album cover is displayed then slider_ubbound returns
1624      * 0 and causes a slider assertion error. Avoid this by disabling the
1625      * slider, which makes sense because only one cover is displayed.
1626      */
1627     slider_ubound = 1;
1628     gtk_widget_set_sensitive (GTK_WIDGET(cdwidget->cdslider), FALSE);
1629     gtk_widget_set_sensitive (GTK_WIDGET(cdwidget->leftbutton), FALSE);
1630     gtk_widget_set_sensitive (GTK_WIDGET(cdwidget->rightbutton), FALSE);
1631   }
1632   else
1633   {
1634     gtk_widget_set_sensitive (GTK_WIDGET(cdwidget->cdslider), TRUE);
1635     gtk_widget_set_sensitive (GTK_WIDGET(cdwidget->leftbutton), TRUE);
1636     gtk_widget_set_sensitive (GTK_WIDGET(cdwidget->rightbutton), TRUE);
1637   }
1638 
1639   gtk_range_set_range (GTK_RANGE (cdwidget->cdslider), 0, slider_ubound);
1640   if (index >= 0 && index <= slider_ubound)
1641     gtk_range_set_value (GTK_RANGE (cdwidget->cdslider), index);
1642   else
1643     gtk_range_set_value (GTK_RANGE (cdwidget->cdslider), 0);
1644 }
1645 
1646 /**
1647  * coverart_select_cover
1648  *
1649  * When a track / album is selected, the artwork cover
1650  * is selected in the display
1651  *
1652  * @track: chosen track
1653  *
1654  */
coverart_select_cover(Track * track)1655 void coverart_select_cover (Track *track)
1656 {
1657   gint displaytotal, index;
1658 
1659   /* Only select covers if the display is visible */
1660   if (! prefs_get_int (KEY_DISPLAY_COVERART) || cdwidget == NULL)
1661     return;
1662 
1663   /* Only select covers if fire display change is enabled */
1664   if (cdwidget->block_display_change)
1665     return;
1666 
1667   displaytotal = g_list_length(album_key_list);
1668   if (displaytotal <= 0)
1669     return;
1670 
1671   gchar *trk_key;
1672   trk_key = g_strconcat (track->artist, "_", track->album, NULL);
1673 
1674   /* Determine the index of the found track */
1675   GList *key = g_list_find_custom (album_key_list, trk_key, (GCompareFunc) compare_album_keys);
1676   g_return_if_fail (key);
1677   index = g_list_position (album_key_list, key);
1678   g_free (trk_key);
1679 
1680   /* Use the index value for the main image index */
1681   cdwidget->first_imgindex = index - IMG_MAIN;
1682   if (cdwidget->first_imgindex < 0)
1683     cdwidget->first_imgindex = 0;
1684   else if((cdwidget->first_imgindex + IMG_TOTAL) >= displaytotal)
1685     cdwidget->first_imgindex = displaytotal - IMG_TOTAL;
1686 
1687   redraw (FALSE);
1688 
1689   /* Set the index value of the slider but avoid causing an infinite
1690    * cover selection by blocking the event
1691    */
1692   g_signal_handler_block (cdwidget->cdslider, slide_signal_id);
1693   gtk_range_set_value (GTK_RANGE (cdwidget->cdslider), index);
1694   g_signal_handler_unblock (cdwidget->cdslider, slide_signal_id);
1695 }
1696 
1697 /**
1698  * coverart_display_big_artwork:
1699  *
1700  * Display a big version of the artwork in a dialog
1701  *
1702  */
coverart_display_big_artwork()1703 void coverart_display_big_artwork ()
1704 {
1705   Cover_Item *cover;
1706   ExtraTrackData *etd;
1707   GdkPixbuf *imgbuf = NULL;
1708 
1709   cover = g_ptr_array_index(cdwidget->cdcovers, IMG_MAIN);
1710   g_return_if_fail (cover);
1711 
1712   if (cover->album == NULL)
1713     return;
1714 
1715   Track *track;
1716   track = g_list_nth_data (cover->album->tracks, 0);
1717   etd = track->userdata;
1718   if (etd && etd->thumb_path_locale)
1719   {
1720     GError *error = NULL;
1721     imgbuf = gdk_pixbuf_new_from_file (etd->thumb_path_locale, &error);
1722     if (error != NULL)
1723     {
1724       g_error_free (error);
1725     }
1726   }
1727 
1728   /* Either thumb was null or the attempt at getting a pixbuf failed
1729    * due to invalid file. For example, some nut (like me) decided to
1730    * apply an mp3 file to the track as its cover file
1731    */
1732   if (imgbuf ==  NULL)
1733   {
1734     /* Could not get a viable thumbnail so get default pixbuf */
1735     imgbuf = coverart_get_default_track_thumb (256);
1736   }
1737 
1738   display_image_dialog (imgbuf);
1739 
1740   /* Unreference pixbuf if it is not pointing to
1741    * the album's artwork
1742    */
1743   if (cover->album->albumart == NULL)
1744     g_object_unref (imgbuf);
1745 }
1746 
1747 /**
1748  * compare_album_keys:
1749  *
1750  * Comparison function for comparing keys in
1751  * the key list to sort them into alphabetical order.
1752  * Could use g_ascii_strcasecmp directly but the NULL
1753  * strings cause assertion errors.
1754  *
1755  * @a: first album key to compare
1756  * @b: second album key to compare
1757  *
1758  */
compare_album_keys(gchar * a,gchar * b)1759 static gint compare_album_keys (gchar *a, gchar *b)
1760 {
1761   if (a == NULL) return -1;
1762   if (b == NULL) return -1;
1763 
1764   return g_ascii_strcasecmp (a, b);
1765 
1766 }
1767 
1768 /**
1769  * coverart_sort_images:
1770  *
1771  * When the alphabetize function is initiated this will
1772  * sort the covers in the same way. Used at any point to
1773  * sort the covers BUT must be called after an initial coverart_display_update
1774  * as the latter initialises the album_key_list list
1775  *
1776  * @order: order type
1777  *
1778  */
coverart_sort_images(GtkSortType order)1779 static void coverart_sort_images (GtkSortType order)
1780 {
1781   if (order == SORT_NONE)
1782   {
1783     /* No sorting means original order so this should have been called after a coverart_display_update (TRUE)
1784      * when the TRUE means the tracks were freshly established from the playlist and the hash and key_list
1785      * recreated.
1786      */
1787     return;
1788   }
1789   else
1790   {
1791     album_key_list = g_list_sort (album_key_list, (GCompareFunc) compare_album_keys);
1792   }
1793 
1794   if (order == GTK_SORT_DESCENDING)
1795   {
1796     album_key_list = g_list_reverse (album_key_list);
1797   }
1798 }
1799 
1800 /**
1801  *
1802  * remove_track_from_album:
1803  *
1804  * Removes track from an album item and removes the latter
1805  * if it no longer has any tracks in it.
1806  *
1807  * @album: album to be checked for removal.
1808  * @track: track to be removed from the Album_Item
1809  * @key: string concatentation of the artist_album of track. Key for hash
1810  * @index: position of the key in the album key list
1811  * @keylistitem: the actual GList item in the album key list
1812  */
remove_track_from_album(Album_Item * album,Track * track,gchar * key,gint index,GList * keylistitem)1813 static void remove_track_from_album (Album_Item *album, Track *track, gchar *key, gint index, GList *keylistitem)
1814 {
1815   album->tracks = g_list_remove (album->tracks, track);
1816   if (g_list_length (album->tracks) == 0)
1817   {
1818     /* No more tracks related to this album item so delete it */
1819     gboolean delstatus = g_hash_table_remove (album_hash, key);
1820     if (! delstatus)
1821       gtkpod_warning (_("Failed to remove the album from the album hash store."));
1822     /*else
1823 	  printf("Successfully removed album\n");
1824      */
1825     album_key_list = g_list_remove_link (album_key_list, keylistitem);
1826 
1827     if (index < (cdwidget->first_imgindex + IMG_MAIN) && index > IMG_MAIN)
1828     {
1829       /* index of track is less than visible cover's indexes so subtract 1 from
1830        * first img index. Will mean that when deleteing album item then
1831        * set covers will be called at the correct position.
1832        *
1833        * However, index must be greater than IMG_MAIN else a NULL track will
1834        * become the IMG_MAIN tracks displayed.
1835        */
1836       cdwidget->first_imgindex--;
1837     }
1838   }
1839 }
1840 
1841 /**
1842  * coverart_set_cover_from_file:
1843  *
1844  * Add a cover to the displayed track by setting it from a
1845  * picture file.
1846  *
1847  */
coverart_set_cover_from_file()1848 void coverart_set_cover_from_file ()
1849 {
1850   gchar *filename;
1851   Track *track;
1852   Cover_Item *cover;
1853   GList *tracks;
1854 
1855   filename = fileselection_get_cover_filename ();
1856 
1857   if (filename)
1858   {
1859     cover = g_ptr_array_index(cdwidget->cdcovers, IMG_MAIN);
1860     tracks = cover->album->tracks;
1861 
1862     while (tracks)
1863     {
1864       track = tracks->data;
1865 
1866       if (gp_track_set_thumbnails (track, filename))
1867         data_changed (track->itdb);
1868 
1869       tracks = tracks->next;
1870     }
1871     /* Nullify so that the album art is picked up from the tracks again */
1872     g_object_unref (cover->album->albumart);
1873     cover->album->albumart = NULL;
1874     if (cover->album->scaled_art != NULL)
1875     {
1876       g_object_unref (cover->album->scaled_art);
1877       cover->album->scaled_art = NULL;
1878     }
1879   }
1880 
1881   g_free (filename);
1882 
1883   redraw (FALSE);
1884 }
1885 
1886 /**
1887  * coverart_get_background_display_color:
1888  *
1889  * Used by coverart draw functions to determine the background color
1890  * of the coverart display, which is selected from the preferences.
1891  *
1892  */
coverart_get_background_display_color()1893 GdkColor *coverart_get_background_display_color ()
1894 {
1895   gchar *hex_string;
1896   GdkColor *color;
1897 
1898   if (album_key_list == NULL || g_list_length (album_key_list) == 0)
1899     hex_string = "#FFFFFF";
1900   else if ( ! prefs_get_string_value ("coverart_display_bg_color", NULL))
1901     hex_string = "#000000";
1902   else
1903     prefs_get_string_value ("coverart_display_bg_color", &hex_string);
1904 
1905   color = convert_hexstring_to_gdk_color (hex_string);
1906   return color;
1907 }
1908 
1909 /**
1910  * coverart_get_foreground_display_color:
1911  *
1912  * Used by coverart draw functions to determine the foreground color
1913  * of the coverart display, which is selected from the preferences. The
1914  * foreground color refers to the color used by the artist and album text.
1915  *
1916  */
coverart_get_foreground_display_color()1917 GdkColor *coverart_get_foreground_display_color ()
1918 {
1919   gchar *hex_string;
1920   GdkColor *color;
1921 
1922   if (album_key_list == NULL || g_list_length (album_key_list) == 0)
1923     hex_string = "#000000";
1924   else if ( ! prefs_get_string_value ("coverart_display_bg_color", NULL))
1925     hex_string = "#FFFFFF";
1926   else
1927     prefs_get_string_value ("coverart_display_fg_color", &hex_string);
1928 
1929   color = convert_hexstring_to_gdk_color (hex_string);
1930   return color;
1931 }
1932 
1933 /**
1934  *
1935  * free_album:
1936  *
1937  * Destroy an album struct once no longer needed.
1938  *
1939  */
free_album(Album_Item * album)1940 static void free_album (Album_Item *album)
1941 {
1942   if (album != NULL)
1943   {
1944     if (album->tracks)
1945     {
1946       g_list_free (album->tracks);
1947     }
1948 
1949     g_free (album->albumname);
1950     g_free (album->artist);
1951 
1952     if (album->albumart)
1953       g_object_unref (album->albumart);
1954 
1955     if (album->scaled_art)
1956       g_object_unref (album->scaled_art);
1957   }
1958 }
1959 
1960 /**
1961  *
1962  * free_CDWidget
1963  *
1964  * destroy the CD Widget and free everything currently
1965  * in memory.
1966  */
free_CDWidget()1967 static void free_CDWidget ()
1968 {
1969   gint i;
1970   g_signal_handler_disconnect (cdwidget->leftbutton, lbutton_signal_id);
1971   g_signal_handler_disconnect (cdwidget->rightbutton, rbutton_signal_id);
1972   g_signal_handler_disconnect (cdwidget->cdslider, slide_signal_id);
1973   g_signal_handler_disconnect (cdwidget->contentpanel, contentpanel_signal_id);
1974   g_signal_handler_disconnect (gtkpod_window, window_signal_id);
1975 
1976   /* Components not freed as they are part of the glade xml file */
1977   cdwidget->leftbutton = NULL;
1978   cdwidget->rightbutton = NULL;
1979   cdwidget->cdslider = NULL;
1980   cdwidget->contentpanel = NULL;
1981   cdwidget->canvasbox = NULL;
1982   cdwidget->controlbox = NULL;
1983 
1984   /* native variables rather than references so should be destroyed when freed */
1985   cdwidget->first_imgindex = 0;
1986   cdwidget->block_display_change = FALSE;
1987 
1988   Cover_Item *cover;
1989   for(i = 0; i < IMG_TOTAL; ++i)
1990   {
1991     cover = g_ptr_array_index(cdwidget->cdcovers, i);
1992     /* Nullify pointer to album reference. Will be freed below */
1993     cover->album = NULL;
1994   }
1995 
1996   g_ptr_array_free (cdwidget->cdcovers, TRUE);
1997 
1998   /* Destroying canvas should destroy the background and cvrtext */
1999   gtk_widget_destroy (GTK_WIDGET(cdwidget->draw_area));
2000 
2001   /* Remove all null tracks before using it to destory the hash table */
2002   album_key_list = g_list_remove_all (album_key_list, NULL);
2003   g_hash_table_foreach_remove(album_hash, (GHRFunc) gtk_true, NULL);
2004   g_hash_table_destroy (album_hash);
2005   g_list_free (album_key_list);
2006 
2007   g_free (cdwidget);
2008   cdwidget = NULL;
2009 }
2010 
2011 /**
2012  * dnd_coverart_drag_drop:
2013  *
2014  * Used by the drag and drop of a jpg. When a drop is
2015  * made, this determines whether the drop is valid
2016  * then requests the data from the source widget.
2017  *
2018  */
dnd_coverart_drag_drop(GtkWidget * widget,GdkDragContext * drag_context,gint x,gint y,guint time,gpointer user_data)2019 static gboolean dnd_coverart_drag_drop(GtkWidget *widget, GdkDragContext *drag_context, gint x, gint y, guint time, gpointer user_data)
2020 {
2021   GdkAtom target;
2022   target = gtk_drag_dest_find_target (widget, drag_context, NULL);
2023 
2024   if (target != GDK_NONE)
2025   {
2026     gtk_drag_get_data (widget, drag_context, target, time);
2027     return TRUE;
2028   }
2029 
2030   return FALSE;
2031 }
2032 
2033 /**
2034  * dnd_coverart_drag_motion:
2035  *
2036  * Used by the drag and drop of a jpg. While the jpg is being
2037  * dragged, this reports to the source widget whether it is an
2038  * acceptable location to allow a drop.
2039  *
2040  */
dnd_coverart_drag_motion(GtkWidget * widget,GdkDragContext * dc,gint x,gint y,guint time,gpointer user_data)2041 static gboolean dnd_coverart_drag_motion (GtkWidget *widget,
2042     GdkDragContext *dc,
2043     gint x,
2044     gint y,
2045     guint time,
2046     gpointer user_data)
2047 {
2048   GdkAtom target;
2049   iTunesDB *itdb;
2050   ExtraiTunesDBData *eitdb;
2051 
2052   itdb = gp_get_selected_itdb ();
2053   /* no drop is possible if no playlist/repository is selected */
2054   if (itdb == NULL)
2055   {
2056     gdk_drag_status (dc, 0, time);
2057     return FALSE;
2058   }
2059 
2060   eitdb = itdb->userdata;
2061   g_return_val_if_fail (eitdb, FALSE);
2062   /* no drop is possible if no repository is loaded */
2063   if (!eitdb->itdb_imported)
2064   {
2065     gdk_drag_status (dc, 0, time);
2066     return FALSE;
2067   }
2068 
2069   target = gtk_drag_dest_find_target (widget, dc, NULL);
2070   /* no drop possible if no valid target can be found */
2071   if (target == GDK_NONE)
2072   {
2073     gdk_drag_status (dc, 0, time);
2074     return FALSE;
2075   }
2076 
2077   gdk_drag_status (dc, dc->suggested_action, time);
2078 
2079   return TRUE;
2080 }
2081 
2082 /**
2083  * dnd_coverart_drag_data_received:
2084  *
2085  * Used by the drag and drop of a jpg. When the drop is performed, this
2086  * acts on the receipt of the data from the source widget and applies
2087  * the jpg to the track.
2088  *
2089  */
dnd_coverart_drag_data_received(GtkWidget * widget,GdkDragContext * dc,gint x,gint y,GtkSelectionData * data,guint info,guint time,gpointer user_data)2090 static void dnd_coverart_drag_data_received(GtkWidget *widget, GdkDragContext *dc, gint x, gint y, GtkSelectionData *data, guint info,
2091     guint time, gpointer user_data)
2092 {
2093   g_return_if_fail (widget);
2094   g_return_if_fail (dc);
2095   g_return_if_fail (data);
2096   g_return_if_fail (data->data);
2097   g_return_if_fail (data->length > 0);
2098 
2099   /* mozilla bug 402394 */
2100 
2101 #if DEBUG
2102   printf ("data length = %d\n", data->length);
2103   printf ("data->data = %s\n", data->data);
2104 #endif
2105 
2106   Cover_Item *cover;
2107   GList *tracks;
2108   gchar *url = NULL;
2109   Fetch_Cover *fcover;
2110   Track *track;
2111   gchar *filename = NULL;
2112   gboolean image_status = FALSE;
2113   gchar *image_error = NULL;
2114   /* For use with DND_IMAGE_JPEG */
2115   GdkPixbuf *pixbuf;
2116   GError *error = NULL;
2117 
2118   /* Find the display cover item in the cover display */
2119   cover = g_ptr_array_index(cdwidget->cdcovers, IMG_MAIN);
2120   if (!cover) {
2121       /* looks like there are no covers yet something got dragged into it */
2122       gtk_drag_finish (dc, FALSE, FALSE, time);
2123       return;
2124   }
2125 
2126   tracks = cover->album->tracks;
2127 
2128   switch (info)
2129   {
2130   case DND_IMAGE_JPEG:
2131 #if DEBUG
2132     printf ("Using DND_IMAGE_JPEG\n");
2133 #endif
2134     pixbuf = gtk_selection_data_get_pixbuf (data);
2135     if (pixbuf != NULL)
2136     {
2137       /* initialise the url string with a safe value as not used if already have image */
2138       url = "local image";
2139       /* Initialise a fetchcover object */
2140       fcover = fetchcover_new (url, tracks);
2141       coverart_block_change (TRUE);
2142 
2143       /* find the filename with which to save the pixbuf to */
2144       if (fetchcover_select_filename (fcover))
2145       {
2146         filename = g_build_filename(fcover->dir, fcover->filename, NULL);
2147         if (! gdk_pixbuf_save (pixbuf, filename, "jpeg", &error, NULL))
2148         {
2149           /* Save failed for some reason */
2150           if (error->message)
2151             fcover->err_msg = g_strdup (error->message);
2152           else
2153             fcover->err_msg = "Saving image to file failed. No internal error message was returned.";
2154 
2155           g_error_free (error);
2156         }
2157         else
2158         {
2159           /* Image successfully saved */
2160           image_status = TRUE;
2161         }
2162       }
2163       /* record any errors and free the fetchcover */
2164       if (fcover->err_msg != NULL)
2165         image_error = g_strdup(fcover->err_msg);
2166 
2167       free_fetchcover (fcover);
2168       g_object_unref (pixbuf);
2169       coverart_block_change (FALSE);
2170     }
2171     else
2172     {
2173       /* despite the data being of type image/jpeg, the pixbuf is NULL */
2174       image_error = "jpeg data flavour was used but the data did not contain a GdkPixbuf object";
2175     }
2176     break;
2177   case DND_TEXT_PLAIN:
2178 #if DEBUG
2179     printf ("Defaulting to using DND_TEXT_PLAIN\n");
2180 #endif
2181 
2182 #ifdef HAVE_CURL
2183     /* initialise the url string with the data from the dnd */
2184     url = g_strdup ((gchar *) data->data);
2185     /* Initialise a fetchcover object */
2186     fcover = fetchcover_new (url, tracks);
2187     coverart_block_change (TRUE);
2188 
2189     if (fetchcover_net_retrieve_image (fcover))
2190     {
2191 #if DEBUG
2192       printf ("Successfully retrieved\n");
2193       printf ("Url of fetch cover: %s\n", fcover->url->str);
2194       printf ("filename of fetch cover: %s\n", fcover->filename);
2195 #endif
2196 
2197       filename = g_build_filename(fcover->dir, fcover->filename, NULL);
2198       image_status = TRUE;
2199     }
2200 
2201     /* record any errors and free the fetchcover */
2202     if (fcover->err_msg != NULL)
2203       image_error = g_strdup(fcover->err_msg);
2204 
2205     free_fetchcover (fcover);
2206     coverart_block_change (FALSE);
2207 #else
2208     image_error = g_strdup ("Item had to be downloaded but gtkpod was not compiled with curl.");
2209     image_status = FALSE;
2210 #endif
2211   }
2212 
2213   if (!image_status || filename == NULL)
2214   {
2215     gtkpod_warning (_("Error occurred dropping an image onto the coverart display: %s\n"), image_error);
2216 
2217     if (image_error)
2218       g_free (image_error);
2219     if (filename)
2220       g_free (filename);
2221 
2222     gtk_drag_finish (dc, FALSE, FALSE, time);
2223     return;
2224   }
2225 
2226   while (tracks)
2227   {
2228     track = tracks->data;
2229 
2230     if (gp_track_set_thumbnails (track, filename))
2231       data_changed (track->itdb);
2232 
2233     tracks = tracks->next;
2234   }
2235   /* Nullify so that the album art is picked up from the tracks again */
2236   cover->album->albumart = NULL;
2237   if (cover->album->scaled_art != NULL)
2238   {
2239     g_object_unref (cover->album->scaled_art);
2240     cover->album->scaled_art = NULL;
2241   }
2242 
2243   redraw (FALSE);
2244 
2245   if (image_error)
2246     g_free (image_error);
2247 
2248   g_free (filename);
2249 
2250   gtkpod_statusbar_message (_("Successfully set new coverart for selected tracks"));
2251   gtk_drag_finish (dc, FALSE, FALSE, time);
2252   return;
2253 }
2254 
2255 /**
2256  * convert_hexstring_to_gdk_color:
2257  *
2258  * Convert a #FFEEFF string to a GdkColor.
2259  * Returns a freshly allocated GdkColor that should be freed.
2260  *
2261  * @GdkColor
2262  */
convert_hexstring_to_gdk_color(gchar * hexstring)2263 static GdkColor *convert_hexstring_to_gdk_color (gchar *hexstring)
2264 {
2265   GdkColor *colour;
2266   GdkColormap *map;
2267   map = gdk_colormap_get_system();
2268 
2269   colour = g_malloc (sizeof(GdkColor));
2270 
2271   if (! gdk_color_parse(hexstring, colour))
2272     return NULL;
2273 
2274   if (! gdk_colormap_alloc_color(map, colour, FALSE, TRUE))
2275     return NULL;
2276 
2277   return colour;
2278 }
2279 
2280 
2281