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