comparison audacious/widgets/skin.c @ 1541:06329cbf186a trunk

[svn] this massive commit does the following: - seriously cleans up dependencies on the WA2-like gui code - moves all of the WA2 stuff into a seperate library (libwidgets.a) - makes things less icky in the player tree
author nenolod
date Wed, 09 Aug 2006 02:47:22 -0700
parents
children ec4d858524fa
comparison
equal deleted inserted replaced
1540:237bb7c97759 1541:06329cbf186a
1 /* BMP - Cross-platform multimedia player
2 * Copyright (C) 2003-2004 BMP development team.
3 *
4 * Based on XMMS:
5 * Copyright (C) 1998-2003 XMMS development team.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 */
21
22 #ifdef HAVE_CONFIG_H
23 # include "config.h"
24 #endif
25
26 /* TODO: enforce default sizes! */
27
28 #include <glib.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <ctype.h>
33
34 #include "equalizer.h"
35 #include "main.h"
36 #include "ui_playlist.h"
37 #include "skin.h"
38 #include "skinwin.h"
39 #include "util.h"
40
41 #include "debug.h"
42
43 #include <gdk/gdkx.h>
44 #include <X11/Xlib.h>
45
46 #define EXTENSION_TARGETS 7
47
48 static gchar *ext_targets[EXTENSION_TARGETS] = { "bmp", "xpm", "png", "svg",
49 "gif", "jpg", "jpeg" };
50
51 struct _SkinPixmapIdMapping {
52 SkinPixmapId id;
53 const gchar *name;
54 const gchar *alt_name;
55 gint width, height;
56 };
57
58 struct _SkinMaskInfo {
59 gint width, height;
60 gchar *inistr;
61 };
62
63 typedef struct _SkinPixmapIdMapping SkinPixmapIdMapping;
64 typedef struct _SkinMaskInfo SkinMaskInfo;
65
66
67 Skin *bmp_active_skin = NULL;
68
69 static gint skin_current_num;
70
71 static SkinMaskInfo skin_mask_info[] = {
72 {275, 116, "Normal"},
73 {275, 16, "WindowShade"},
74 {275, 116, "Equalizer"},
75 {275, 16, "EqualizerWS"}
76 };
77
78 static SkinPixmapIdMapping skin_pixmap_id_map[] = {
79 {SKIN_MAIN, "main", NULL, 0, 0},
80 {SKIN_CBUTTONS, "cbuttons", NULL, 0, 0},
81 {SKIN_SHUFREP, "shufrep", NULL, 0, 0},
82 {SKIN_TEXT, "text", NULL, 0, 0},
83 {SKIN_TITLEBAR, "titlebar", NULL, 0, 0},
84 {SKIN_VOLUME, "volume", NULL, 0, 0},
85 {SKIN_BALANCE, "balance", "volume", 0, 0},
86 {SKIN_MONOSTEREO, "monoster", NULL, 0, 0},
87 {SKIN_PLAYPAUSE, "playpaus", NULL, 0, 0},
88 {SKIN_NUMBERS, "nums_ex", "numbers", 0, 0},
89 {SKIN_POSBAR, "posbar", NULL, 0, 0},
90 {SKIN_EQMAIN, "eqmain", NULL, 0, 0},
91 {SKIN_PLEDIT, "pledit", NULL, 0, 0},
92 {SKIN_EQ_EX, "eq_ex", NULL, 0, 0}
93 };
94
95 static guint skin_pixmap_id_map_size = G_N_ELEMENTS(skin_pixmap_id_map);
96
97 static const guchar skin_default_viscolor[24][3] = {
98 {9, 34, 53},
99 {10, 18, 26},
100 {0, 54, 108},
101 {0, 58, 116},
102 {0, 62, 124},
103 {0, 66, 132},
104 {0, 70, 140},
105 {0, 74, 148},
106 {0, 78, 156},
107 {0, 82, 164},
108 {0, 86, 172},
109 {0, 92, 184},
110 {0, 98, 196},
111 {0, 104, 208},
112 {0, 110, 220},
113 {0, 116, 232},
114 {0, 122, 244},
115 {0, 128, 255},
116 {0, 128, 255},
117 {0, 104, 208},
118 {0, 80, 160},
119 {0, 56, 112},
120 {0, 32, 64},
121 {200, 200, 200}
122 };
123
124 static GdkBitmap *
125 skin_create_transparent_mask(const gchar *,
126 const gchar *,
127 const gchar *,
128 GdkWindow *,
129 gint, gint);
130
131 static void
132 skin_setup_masks(Skin * skin);
133
134 static void
135 skin_set_default_vis_color(Skin * skin);
136
137
138 void
139 skin_lock(Skin * skin)
140 {
141 g_mutex_lock(skin->lock);
142 }
143
144 void
145 skin_unlock(Skin * skin)
146 {
147 g_mutex_unlock(skin->lock);
148 }
149
150 gboolean
151 bmp_active_skin_reload(void)
152 {
153 return bmp_active_skin_load(bmp_active_skin->path);
154 }
155
156 gboolean
157 bmp_active_skin_load(const gchar * path)
158 {
159 g_return_val_if_fail(bmp_active_skin != NULL, FALSE);
160
161 memset(&bmp_active_skin->properties, 0, sizeof(SkinProperties));
162
163 if (!skin_load(bmp_active_skin, path))
164 return FALSE;
165
166 skin_setup_masks(bmp_active_skin);
167
168 if (cfg.playlist_transparent)
169 {
170 if (rootpix != NULL)
171 g_object_unref(rootpix);
172
173 rootpix = shade_pixmap(get_transparency_pixmap(), 0, 0, 0, 0, gdk_screen_width(), gdk_screen_height(),
174 skin_get_color(bmp_active_skin, SKIN_PLEDIT_NORMALBG));
175 }
176
177 draw_main_window(TRUE);
178 draw_playlist_window(TRUE);
179 draw_equalizer_window(TRUE);
180
181 vis_set_window(mainwin_vis, mainwin->window);
182 playlistwin_update_list();
183
184 return TRUE;
185 }
186
187 void
188 skin_pixmap_free(SkinPixmap * p)
189 {
190 g_return_if_fail(p != NULL);
191 g_return_if_fail(p->pixmap != NULL);
192
193 g_object_unref(p->pixmap);
194 p->pixmap = NULL;
195 }
196
197 Skin *
198 skin_new(void)
199 {
200 Skin *skin;
201 skin = g_new0(Skin, 1);
202 skin->lock = g_mutex_new();
203 return skin;
204 }
205
206 void
207 skin_free(Skin * skin)
208 {
209 gint i;
210
211 g_return_if_fail(skin != NULL);
212
213 skin_lock(skin);
214
215 for (i = 0; i < SKIN_PIXMAP_COUNT; i++)
216 skin_pixmap_free(&skin->pixmaps[i]);
217
218 for (i = 0; i < SKIN_PIXMAP_COUNT; i++) {
219 if (skin->masks[i])
220 g_object_unref(skin->masks[i]);
221
222 skin->masks[i] = NULL;
223 }
224
225 skin_set_default_vis_color(skin);
226 skin_unlock(skin);
227 }
228
229 void
230 skin_destroy(Skin * skin)
231 {
232 g_return_if_fail(skin != NULL);
233 skin_free(skin);
234 g_mutex_free(skin->lock);
235 g_free(skin);
236 }
237
238 const SkinPixmapIdMapping *
239 skin_pixmap_id_lookup(guint id)
240 {
241 guint i;
242
243 for (i = 0; i < skin_pixmap_id_map_size; i++) {
244 if (id == skin_pixmap_id_map[i].id) {
245 return &skin_pixmap_id_map[i];
246 }
247 }
248
249 return NULL;
250 }
251
252 const gchar *
253 skin_pixmap_id_to_name(SkinPixmapId id)
254 {
255 guint i;
256
257 for (i = 0; i < skin_pixmap_id_map_size; i++) {
258 if (id == skin_pixmap_id_map[i].id)
259 return skin_pixmap_id_map[i].name;
260 }
261 return NULL;
262 }
263
264 static void
265 skin_set_default_vis_color(Skin * skin)
266 {
267 memcpy(skin->vis_color, skin_default_viscolor,
268 sizeof(skin_default_viscolor));
269 }
270
271 /*
272 * I have rewritten this to take an array of possible targets,
273 * once we find a matching target we now return, instead of loop
274 * recursively. This allows for us to support many possible format
275 * targets for our skinning engine than just the original winamp
276 * formats.
277 *
278 * -- nenolod, 16 January 2006
279 */
280 gchar *
281 skin_pixmap_locate(const gchar * dirname, gchar ** basenames)
282 {
283 gchar *filename;
284 gint i;
285
286 for (i = 0; basenames[i]; i++)
287 if (!(filename = find_file_recursively(dirname, basenames[i])))
288 g_free(filename);
289 else
290 return filename;
291
292 /* can't find any targets -- sorry */
293 return NULL;
294 }
295
296 /* FIXME: this function is temporary. It will be removed when the skinning system
297 uses GdkPixbuf in place of GdkPixmap */
298
299 static GdkPixmap *
300 pixmap_new_from_file(const gchar * filename)
301 {
302 GdkPixbuf *pixbuf;
303 GdkPixmap *pixmap;
304 gint width, height;
305
306 if (!(pixbuf = gdk_pixbuf_new_from_file(filename, NULL)))
307 return NULL;
308
309 width = gdk_pixbuf_get_width(pixbuf);
310 height = gdk_pixbuf_get_height(pixbuf);
311
312 if (!(pixmap = gdk_pixmap_new(mainwin->window, width, height,
313 gdk_rgb_get_visual()->depth))) {
314 g_object_unref(pixbuf);
315 return NULL;
316 }
317
318 gdk_pixbuf_render_to_drawable(pixbuf, pixmap, mainwin_gc, 0, 0, 0, 0,
319 width, height, GDK_RGB_DITHER_MAX, 0, 0);
320 g_object_unref(pixbuf);
321
322 return pixmap;
323 }
324
325 static gboolean
326 skin_load_pixmap_id(Skin * skin, SkinPixmapId id, const gchar * path_p)
327 {
328 const gchar *path;
329 gchar *filename;
330 gint width, height;
331 const SkinPixmapIdMapping *pixmap_id_mapping;
332 GdkPixmap *gpm;
333 SkinPixmap *pm = NULL;
334 gchar *basenames[EXTENSION_TARGETS * 2 + 1]; /* alternate basenames */
335 gint i, y;
336
337 g_return_val_if_fail(skin != NULL, FALSE);
338 g_return_val_if_fail(id < SKIN_PIXMAP_COUNT, FALSE);
339
340 pixmap_id_mapping = skin_pixmap_id_lookup(id);
341 g_return_val_if_fail(pixmap_id_mapping != NULL, FALSE);
342
343 memset(&basenames, 0, sizeof(basenames));
344
345 for (i = 0, y = 0; i < EXTENSION_TARGETS; i++, y++)
346 {
347 basenames[y] = g_strdup_printf("%s.%s", pixmap_id_mapping->name,
348 ext_targets[i]);
349
350 if (pixmap_id_mapping->alt_name)
351 basenames[++y] = g_strdup_printf("%s.%s",
352 pixmap_id_mapping->alt_name, ext_targets[i]);
353 }
354
355 path = path_p ? path_p : skin->path;
356 filename = skin_pixmap_locate(path, basenames);
357
358 for (i = 0; basenames[i] != NULL; i++)
359 {
360 g_free(basenames[i]);
361 basenames[i] = NULL;
362 }
363
364 if (!(gpm = pixmap_new_from_file(filename))) {
365 g_warning("loading of %s failed", filename);
366 g_free(filename);
367 return FALSE;
368 }
369
370 g_free(filename);
371
372 gdk_window_get_size(gpm, &width, &height);
373 pm = &skin->pixmaps[id];
374 pm->pixmap = gpm;
375 pm->width = width;
376 pm->height = height;
377 pm->current_width = width;
378 pm->current_height = height;
379
380 return TRUE;
381 }
382
383 void
384 skin_mask_create(Skin * skin,
385 const gchar * path,
386 gint id,
387 GdkWindow * window)
388 {
389 skin->masks[id] =
390 skin_create_transparent_mask(path, "region.txt",
391 skin_mask_info[id].inistr, window,
392 skin_mask_info[id].width,
393 skin_mask_info[id].height);
394 }
395
396 static void
397 skin_setup_masks(Skin * skin)
398 {
399 GdkBitmap *mask;
400
401 if (cfg.show_wm_decorations)
402 return;
403
404 if (cfg.player_visible) {
405 mask = skin_get_mask(skin, SKIN_MASK_MAIN + cfg.player_shaded);
406 gtk_widget_shape_combine_mask(mainwin, mask, 0, 0);
407 }
408
409 mask = skin_get_mask(skin, SKIN_MASK_EQ + cfg.equalizer_shaded);
410 gtk_widget_shape_combine_mask(equalizerwin, mask, 0, 0);
411 }
412
413 static GdkBitmap *
414 create_default_mask(GdkWindow * parent, gint w, gint h)
415 {
416 GdkBitmap *ret;
417 GdkGC *gc;
418 GdkColor pattern;
419
420 ret = gdk_pixmap_new(parent, w, h, 1);
421 gc = gdk_gc_new(ret);
422 pattern.pixel = 1;
423 gdk_gc_set_foreground(gc, &pattern);
424 gdk_draw_rectangle(ret, gc, TRUE, 0, 0, w, h);
425 gdk_gc_destroy(gc);
426
427 return ret;
428 }
429
430 static void
431 skin_query_color(GdkColormap * cm, GdkColor * c)
432 {
433 XColor xc = { 0,0,0,0,0,0 };
434
435 xc.pixel = c->pixel;
436 XQueryColor(GDK_COLORMAP_XDISPLAY(cm), GDK_COLORMAP_XCOLORMAP(cm), &xc);
437 c->red = xc.red;
438 c->green = xc.green;
439 c->blue = xc.blue;
440 }
441
442 static glong
443 skin_calc_luminance(GdkColor * c)
444 {
445 return (0.212671 * c->red + 0.715160 * c->green + 0.072169 * c->blue);
446 }
447
448 static void
449 skin_get_textcolors(GdkPixmap * text, GdkColor * bgc, GdkColor * fgc)
450 {
451 /*
452 * Try to extract reasonable background and foreground colors
453 * from the font pixmap
454 */
455
456 GdkImage *gi;
457 GdkColormap *cm;
458 gint i;
459
460 g_return_if_fail(text != NULL);
461
462 /* Get the first line of text */
463 gi = gdk_drawable_get_image(text, 0, 0, 152, 6);
464 cm = gdk_window_get_colormap(playlistwin->window);
465 g_return_if_fail(GDK_IS_WINDOW(playlistwin->window));
466
467 for (i = 0; i < 6; i++) {
468 GdkColor c;
469 gint x;
470 glong d, max_d;
471
472 /* Get a pixel from the middle of the space character */
473 bgc[i].pixel = gdk_image_get_pixel(gi, 151, i);
474 skin_query_color(cm, &bgc[i]);
475
476 max_d = 0;
477 for (x = 1; x < 150; x++) {
478 c.pixel = gdk_image_get_pixel(gi, x, i);
479 skin_query_color(cm, &c);
480
481 d = labs(skin_calc_luminance(&c) - skin_calc_luminance(&bgc[i]));
482 if (d > max_d) {
483 memcpy(&fgc[i], &c, sizeof(GdkColor));
484 max_d = d;
485 }
486 }
487 }
488 gdk_image_destroy(gi);
489 }
490
491 gboolean
492 init_skins(const gchar * path)
493 {
494 bmp_active_skin = skin_new();
495
496 if (!bmp_active_skin_load(path)) {
497 /* FIXME: Oddly, g_message() causes a crash if path is NULL on
498 * Solaris (see bug #165) */
499 if (path)
500 g_message("Unable to load skin (%s), trying default...", path);
501
502 /* can't load configured skin, retry with default */
503 if (!bmp_active_skin_load(BMP_DEFAULT_SKIN_PATH)) {
504 g_message("Unable to load default skin (%s)! Giving up.",
505 BMP_DEFAULT_SKIN_PATH);
506 return FALSE;
507 }
508 }
509
510 if (cfg.random_skin_on_play)
511 skinlist_update();
512
513 return TRUE;
514 }
515
516 /*
517 * Opens and parses a skin's hints file.
518 * Hints files are somewhat like "scripts" in Winamp3/5.
519 * We'll probably add scripts to it next.
520 */
521 void
522 skin_parse_hints(Skin * skin, gchar *path_p)
523 {
524 gchar *filename, *tmp;
525
526 path_p = path_p ? path_p : skin->path;
527
528 filename = find_file_recursively(path_p, "skin.hints");
529
530 if (filename == NULL)
531 return;
532
533 #if 0
534 skin->description = read_ini_string(filename, "skin", "skinDescription");
535 #endif
536
537 tmp = read_ini_string(filename, "skin", "mainwinOthertext");
538
539 if (tmp != NULL)
540 skin->properties.mainwin_othertext = atoi(tmp);
541 }
542
543 static guint
544 hex_chars_to_int(gchar hi, gchar lo)
545 {
546 /*
547 * Converts a value in the range 0x00-0xFF
548 * to a integer in the range 0-65535
549 */
550 gchar str[3];
551
552 str[0] = hi;
553 str[1] = lo;
554 str[2] = 0;
555
556 return (CLAMP(strtol(str, NULL, 16), 0, 0xFF) << 8);
557 }
558
559 GdkColor *
560 skin_load_color(const gchar * path, const gchar * file,
561 const gchar * section, const gchar * key,
562 gchar * default_hex)
563 {
564 gchar *filename, *value;
565 GdkColor *color = NULL;
566
567 filename = find_file_recursively(path, file);
568 if (filename || default_hex) {
569 if (filename) {
570 value = read_ini_string(filename, section, key);
571 if (value == NULL) {
572 value = g_strdup(default_hex);
573 }
574 } else {
575 value = g_strdup(default_hex);
576 }
577 if (value) {
578 gchar *ptr = value;
579 gint len;
580
581 color = g_new0(GdkColor, 1);
582 g_strstrip(value);
583
584 if (value[0] == '#')
585 ptr++;
586 len = strlen(ptr);
587 /*
588 * The handling of incomplete values is done this way
589 * to maximize winamp compatibility
590 */
591 if (len >= 6) {
592 color->red = hex_chars_to_int(*ptr, *(ptr + 1));
593 ptr += 2;
594 }
595 if (len >= 4) {
596 color->green = hex_chars_to_int(*ptr, *(ptr + 1));
597 ptr += 2;
598 }
599 if (len >= 2)
600 color->blue = hex_chars_to_int(*ptr, *(ptr + 1));
601
602 gdk_color_alloc(gdk_window_get_colormap(playlistwin->window),
603 color);
604 g_free(value);
605 }
606 if (filename)
607 g_free(filename);
608 }
609 return color;
610 }
611
612
613
614 GdkBitmap *
615 skin_create_transparent_mask(const gchar * path,
616 const gchar * file,
617 const gchar * section,
618 GdkWindow * window,
619 gint width,
620 gint height)
621 {
622 GdkBitmap *mask = NULL;
623 GdkGC *gc = NULL;
624 GdkColor pattern;
625 GdkPoint *gpoints;
626
627 gchar *filename = NULL;
628 gboolean created_mask = FALSE;
629 GArray *num, *point;
630 guint i, j;
631 gint k;
632
633 if (path)
634 filename = find_file_recursively(path, file);
635
636 /* filename will be null if path wasn't set */
637 if (!filename) {
638 return create_default_mask(window, width, height);
639 }
640
641 if ((num = read_ini_array(filename, section, "NumPoints")) == NULL) {
642 g_free(filename);
643 return NULL;
644 }
645
646 if ((point = read_ini_array(filename, section, "PointList")) == NULL) {
647 g_array_free(num, TRUE);
648 g_free(filename);
649 return NULL;
650 }
651
652 mask = gdk_pixmap_new(window, width, height, 1);
653 gc = gdk_gc_new(mask);
654
655 pattern.pixel = 0;
656 gdk_gc_set_foreground(gc, &pattern);
657 gdk_draw_rectangle(mask, gc, TRUE, 0, 0, width, height);
658 pattern.pixel = 1;
659 gdk_gc_set_foreground(gc, &pattern);
660
661 j = 0;
662 for (i = 0; i < num->len; i++) {
663 if ((int)(point->len - j) >= (g_array_index(num, gint, i) * 2)) {
664 created_mask = TRUE;
665 gpoints = g_new(GdkPoint, g_array_index(num, gint, i));
666 for (k = 0; k < g_array_index(num, gint, i); k++) {
667 gpoints[k].x = g_array_index(point, gint, j + k * 2);
668 gpoints[k].y = g_array_index(point, gint, j + k * 2 + 1);
669 }
670 j += k * 2;
671 gdk_draw_polygon(mask, gc, TRUE, gpoints,
672 g_array_index(num, gint, i));
673 g_free(gpoints);
674 }
675 }
676 g_array_free(num, TRUE);
677 g_array_free(point, TRUE);
678 g_free(filename);
679
680 if (!created_mask)
681 gdk_draw_rectangle(mask, gc, TRUE, 0, 0, width, height);
682
683 gdk_gc_destroy(gc);
684
685 return mask;
686 }
687
688 void
689 skin_load_viscolor(Skin * skin, const gchar * path, const gchar * basename)
690 {
691 FILE *file;
692 gint i, c;
693 gchar line[256], *filename;
694 GArray *a;
695
696 g_return_if_fail(skin != NULL);
697 g_return_if_fail(path != NULL);
698 g_return_if_fail(basename != NULL);
699
700 skin_set_default_vis_color(skin);
701
702 filename = find_file_recursively(path, basename);
703 if (!filename)
704 return;
705
706 if (!(file = fopen(filename, "r"))) {
707 g_free(filename);
708 return;
709 }
710
711 g_free(filename);
712
713 for (i = 0; i < 24; i++) {
714 if (fgets(line, 255, file)) {
715 a = string_to_garray(line);
716 if (a->len > 2) {
717 for (c = 0; c < 3; c++)
718 skin->vis_color[i][c] = g_array_index(a, gint, c);
719 }
720 g_array_free(a, TRUE);
721 }
722 else
723 break;
724 }
725
726 fclose(file);
727 }
728
729 #if 0
730 static void
731 skin_numbers_generate_dash(Skin * skin)
732 {
733 GdkGC *gc;
734 GdkPixmap *pixmap;
735 SkinPixmap *numbers;
736
737 g_return_if_fail(skin != NULL);
738
739 numbers = &skin->pixmaps[SKIN_NUMBERS];
740 if (!numbers->pixmap || numbers->current_width < 99)
741 return;
742
743 gc = gdk_gc_new(numbers->pixmap);
744 pixmap = gdk_pixmap_new(mainwin->window, 108,
745 numbers->current_height,
746 -1);
747
748 skin_draw_pixmap(skin, pixmap, gc, SKIN_NUMBERS, 0, 0, 0, 0, 99, 13);
749 skin_draw_pixmap(skin, pixmap, gc, SKIN_NUMBERS, 90, 0, 99, 0, 9, 13);
750 skin_draw_pixmap(skin, pixmap, gc, SKIN_NUMBERS, 20, 6, 101, 6, 5, 1);
751
752 g_object_unref(numbers->pixmap);
753 g_object_unref(gc);
754
755 numbers->pixmap = pixmap;
756 numbers->current_width = 108;
757 }
758 #endif
759
760 static void
761 skin_load_cursor(Skin * skin, const gchar * dirname)
762 {
763 const gchar * basename = "normal.cur";
764 gchar * filename = NULL;
765 GdkPixbuf * cursor_pixbuf = NULL;
766 GdkPixbufAnimation * cursor_animated = NULL;
767 GdkCursor * cursor_gdk = NULL;
768 GError * error = NULL;
769
770 filename = find_file_recursively(dirname, basename);
771
772 if (filename && cfg.custom_cursors) {
773 cursor_animated = gdk_pixbuf_animation_new_from_file(filename, &error);
774 cursor_pixbuf = gdk_pixbuf_animation_get_static_image(cursor_animated);
775 cursor_gdk = gdk_cursor_new_from_pixbuf(gdk_display_get_default(),
776 cursor_pixbuf, 0, 0);
777 } else {
778 cursor_gdk = gdk_cursor_new(GDK_LEFT_PTR);
779 }
780
781 gdk_window_set_cursor(mainwin->window, cursor_gdk);
782 gdk_window_set_cursor(playlistwin->window, cursor_gdk);
783 gdk_window_set_cursor(equalizerwin->window, cursor_gdk);
784 gdk_cursor_unref(cursor_gdk);
785 }
786
787 static void
788 skin_load_pixmaps(Skin * skin, const gchar * path)
789 {
790 GdkPixmap *text_pm;
791 guint i;
792
793 for (i = 0; i < SKIN_PIXMAP_COUNT; i++)
794 skin_load_pixmap_id(skin, i, path);
795
796 text_pm = skin->pixmaps[SKIN_TEXT].pixmap;
797
798 if (text_pm)
799 skin_get_textcolors(text_pm, skin->textbg, skin->textfg);
800
801 #if 0
802 if (skin->pixmaps[SKIN_NUMBERS].pixmap)
803 skin_numbers_generate_dash(skin);
804 #endif
805
806 skin->colors[SKIN_PLEDIT_NORMAL] =
807 skin_load_color(path, "pledit.txt", "text", "normal", "#2499ff");
808 skin->colors[SKIN_PLEDIT_CURRENT] =
809 skin_load_color(path, "pledit.txt", "text", "current", "#ffeeff");
810 skin->colors[SKIN_PLEDIT_NORMALBG] =
811 skin_load_color(path, "pledit.txt", "text", "normalbg", "#0a120a");
812 skin->colors[SKIN_PLEDIT_SELECTEDBG] =
813 skin_load_color(path, "pledit.txt", "text", "selectedbg", "#0a124a");
814
815 skin_mask_create(skin, path, SKIN_MASK_MAIN, mainwin->window);
816 skin_mask_create(skin, path, SKIN_MASK_MAIN_SHADE, mainwin->window);
817
818 skin_mask_create(skin, path, SKIN_MASK_EQ, equalizerwin->window);
819 skin_mask_create(skin, path, SKIN_MASK_EQ_SHADE, equalizerwin->window);
820
821 skin_load_viscolor(skin, path, "viscolor.txt");
822 }
823
824 static gboolean
825 skin_load_nolock(Skin * skin, const gchar * path, gboolean force)
826 {
827 gchar *cpath;
828
829 g_return_val_if_fail(skin != NULL, FALSE);
830 g_return_val_if_fail(path != NULL, FALSE);
831 REQUIRE_LOCK(skin->lock);
832
833 if (!g_file_test(path, G_FILE_TEST_IS_REGULAR | G_FILE_TEST_IS_DIR))
834 return FALSE;
835
836 if (!force) {
837 if (skin->path)
838 if (!strcmp(skin->path, path))
839 return FALSE;
840 }
841
842 skin_current_num++;
843
844 skin->path = g_strdup(path);
845
846 if (!file_is_archive(path)) {
847 skin_load_pixmaps(skin, path);
848 skin_load_cursor(skin, path);
849
850 /* Parse the hints for this skin. */
851 skin_parse_hints(skin, NULL);
852
853 return TRUE;
854 }
855
856 if (!(cpath = archive_decompress(path))) {
857 g_message("Unable to extract skin archive (%s)", path);
858 return FALSE;
859 }
860
861 skin_load_pixmaps(skin, cpath);
862 skin_load_cursor(skin, cpath);
863
864 /* Parse the hints for this skin. */
865 skin_parse_hints(skin, cpath);
866
867 del_directory(cpath);
868 g_free(cpath);
869
870 return TRUE;
871 }
872
873 void
874 skin_install_skin(const gchar * path)
875 {
876 gchar *command;
877
878 g_return_if_fail(path != NULL);
879
880 command = g_strdup_printf("cp %s %s", path, bmp_paths[BMP_PATH_USER_SKIN_DIR]);
881 if (system(command)) {
882 g_message("Unable to install skin (%s) into user directory (%s)",
883 path, bmp_paths[BMP_PATH_USER_SKIN_DIR]);
884 }
885 g_free(command);
886 }
887
888
889 gboolean
890 skin_load(Skin * skin, const gchar * path)
891 {
892 gboolean error;
893
894 g_return_val_if_fail(skin != NULL, FALSE);
895
896 if (!path)
897 return FALSE;
898
899 skin_lock(skin);
900 error = skin_load_nolock(skin, path, FALSE);
901 skin_unlock(skin);
902
903 return error;
904 }
905
906 gboolean
907 skin_reload_forced(void)
908 {
909 gboolean error;
910
911 skin_lock(bmp_active_skin);
912 error = skin_load_nolock(bmp_active_skin, bmp_active_skin->path, TRUE);
913 skin_unlock(bmp_active_skin);
914
915 return error;
916 }
917
918 void
919 skin_reload(Skin * skin)
920 {
921 g_return_if_fail(skin != NULL);
922 skin_load_nolock(skin, skin->path, TRUE);
923 }
924
925
926 static SkinPixmap *
927 skin_get_pixmap(Skin * skin, SkinPixmapId map_id)
928 {
929 g_return_val_if_fail(skin != NULL, NULL);
930 g_return_val_if_fail(map_id < SKIN_PIXMAP_COUNT, NULL);
931
932 return &skin->pixmaps[map_id];
933 }
934
935 GdkBitmap *
936 skin_get_mask(Skin * skin, SkinMaskId mi)
937 {
938 g_return_val_if_fail(skin != NULL, NULL);
939 g_return_val_if_fail(mi < SKIN_PIXMAP_COUNT, NULL);
940
941 return skin->masks[mi];
942 }
943
944 GdkColor *
945 skin_get_color(Skin * skin, SkinColorId color_id)
946 {
947 GdkColor *ret = NULL;
948
949 g_return_val_if_fail(skin != NULL, NULL);
950
951 switch (color_id) {
952 case SKIN_TEXTBG:
953 if (skin->pixmaps[SKIN_TEXT].pixmap)
954 ret = skin->textbg;
955 else
956 ret = skin->def_textbg;
957 break;
958 case SKIN_TEXTFG:
959 if (skin->pixmaps[SKIN_TEXT].pixmap)
960 ret = skin->textfg;
961 else
962 ret = skin->def_textfg;
963 break;
964 default:
965 if (color_id < SKIN_COLOR_COUNT)
966 ret = skin->colors[color_id];
967 break;
968 }
969 return ret;
970 }
971
972 void
973 skin_get_viscolor(Skin * skin, guchar vis_color[24][3])
974 {
975 gint i;
976
977 g_return_if_fail(skin != NULL);
978
979 for (i = 0; i < 24; i++) {
980 vis_color[i][0] = skin->vis_color[i][0];
981 vis_color[i][1] = skin->vis_color[i][1];
982 vis_color[i][2] = skin->vis_color[i][2];
983 }
984 }
985
986 gint
987 skin_get_id(void)
988 {
989 return skin_current_num;
990 }
991
992 void
993 skin_draw_pixmap(Skin * skin, GdkDrawable * drawable, GdkGC * gc,
994 SkinPixmapId pixmap_id,
995 gint xsrc, gint ysrc, gint xdest, gint ydest,
996 gint width, gint height)
997 {
998 SkinPixmap *pixmap;
999
1000 g_return_if_fail(skin != NULL);
1001
1002 pixmap = skin_get_pixmap(skin, pixmap_id);
1003 g_return_if_fail(pixmap != NULL);
1004 g_return_if_fail(pixmap->pixmap != NULL);
1005
1006 if (xsrc > pixmap->width || ysrc > pixmap->height)
1007 return;
1008
1009 width = MIN(width, pixmap->width - xsrc);
1010 height = MIN(height, pixmap->height - ysrc);
1011 gdk_draw_pixmap(drawable, gc, pixmap->pixmap, xsrc, ysrc,
1012 xdest, ydest, width, height);
1013 }
1014
1015 void
1016 skin_get_eq_spline_colors(Skin * skin, guint32 colors[19])
1017 {
1018 gint i;
1019 GdkPixmap *pixmap;
1020 GdkImage *img;
1021 SkinPixmap *eqmainpm;
1022
1023 g_return_if_fail(skin != NULL);
1024
1025 eqmainpm = &skin->pixmaps[SKIN_EQMAIN];
1026 if (eqmainpm->pixmap &&
1027 eqmainpm->current_width >= 116 && eqmainpm->current_height >= 313)
1028 pixmap = eqmainpm->pixmap;
1029 else
1030 return;
1031
1032 if (!GDK_IS_DRAWABLE(pixmap))
1033 return;
1034
1035 if (!(img = gdk_drawable_get_image(pixmap, 115, 294, 1, 19)))
1036 return;
1037
1038 for (i = 0; i < 19; i++)
1039 colors[i] = gdk_image_get_pixel(img, 0, i);
1040
1041 gdk_image_destroy(img);
1042 }
1043
1044
1045 static void
1046 skin_draw_playlistwin_frame_top(Skin * skin,
1047 GdkDrawable * drawable,
1048 GdkGC * gc,
1049 gint width, gint height, gboolean focus)
1050 {
1051 /* The title bar skin consists of 2 sets of 4 images, 1 set
1052 * for focused state and the other for unfocused. The 4 images
1053 * are:
1054 *
1055 * a. right corner (25,20)
1056 * b. left corner (25,20)
1057 * c. tiler (25,20)
1058 * d. title (100,20)
1059 *
1060 * min allowed width = 100+25+25 = 150
1061 */
1062
1063 gint i, y, c;
1064
1065 /* get y offset of the pixmap set to use */
1066 if (focus)
1067 y = 0;
1068 else
1069 y = 21;
1070
1071 /* left corner */
1072 skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 0, y, 0, 0, 25, 20);
1073
1074 /* titlebar title */
1075 skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 26, y,
1076 (width - 100) / 2, 0, 100, 20);
1077
1078 /* titlebar right corner */
1079 skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 153, y,
1080 width - 25, 0, 25, 20);
1081
1082 /* tile draw the remaining frame */
1083
1084 /* compute tile count */
1085 c = (width - (100 + 25 + 25)) / 25;
1086
1087 for (i = 0; i < c / 2; i++) {
1088 /* left of title */
1089 skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 127, y,
1090 25 + i * 25, 0, 25, 20);
1091
1092 /* right of title */
1093 skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 127, y,
1094 (width + 100) / 2 + i * 25, 0, 25, 20);
1095 }
1096
1097 if (c & 1) {
1098 /* Odd tile count, so one remaining to draw. Here we split
1099 * it into two and draw half on either side of the title */
1100 skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 127, y,
1101 ((c / 2) * 25) + 25, 0, 12, 20);
1102 skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 127, y,
1103 (width / 2) + ((c / 2) * 25) + 50, 0, 13, 20);
1104 }
1105 }
1106
1107 static void
1108 skin_draw_playlistwin_frame_bottom(Skin * skin,
1109 GdkDrawable * drawable,
1110 GdkGC * gc,
1111 gint width, gint height, gboolean focus)
1112 {
1113 /* The bottom frame skin consists of 1 set of 4 images. The 4
1114 * images are:
1115 *
1116 * a. left corner with menu buttons (125,38)
1117 * b. visualization window (75,38)
1118 * c. right corner with play buttons (150,38)
1119 * d. frame tile (25,38)
1120 *
1121 * (min allowed width = 125+150+25=300
1122 */
1123
1124 gint i, c;
1125
1126 /* bottom left corner (menu buttons) */
1127 skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 0, 72,
1128 0, height - 38, 125, 38);
1129
1130 c = (width - 275) / 25;
1131
1132 /* draw visualization window, if width allows */
1133 if (c >= 3) {
1134 c -= 3;
1135 skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 205, 0,
1136 width - (150 + 75), height - 38, 75, 38);
1137 }
1138
1139 /* Bottom right corner (playbuttons etc) */
1140 skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT,
1141 126, 72, width - 150, height - 38, 150, 38);
1142
1143 /* Tile draw the remaining undrawn portions */
1144 for (i = 0; i < c; i++)
1145 skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 179, 0,
1146 125 + i * 25, height - 38, 25, 38);
1147 }
1148
1149 static void
1150 skin_draw_playlistwin_frame_sides(Skin * skin,
1151 GdkDrawable * drawable,
1152 GdkGC * gc,
1153 gint width, gint height, gboolean focus)
1154 {
1155 /* The side frames consist of 2 tile images. 1 for the left, 1 for
1156 * the right.
1157 * a. left (12,29)
1158 * b. right (19,29)
1159 */
1160
1161 gint i;
1162
1163 /* frame sides */
1164 for (i = 0; i < (height - (20 + 38)) / 29; i++) {
1165 /* left */
1166 skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 0, 42,
1167 0, 20 + i * 29, 12, 29);
1168
1169 /* right */
1170 skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 32, 42,
1171 width - 19, 20 + i * 29, 19, 29);
1172 }
1173 }
1174
1175
1176 void
1177 skin_draw_playlistwin_frame(Skin * skin,
1178 GdkDrawable * drawable, GdkGC * gc,
1179 gint width, gint height, gboolean focus)
1180 {
1181 skin_draw_playlistwin_frame_top(skin, drawable, gc, width, height, focus);
1182 skin_draw_playlistwin_frame_bottom(skin, drawable, gc, width, height,
1183 focus);
1184 skin_draw_playlistwin_frame_sides(skin, drawable, gc, width, height,
1185 focus);
1186 }
1187
1188
1189 void
1190 skin_draw_playlistwin_shaded(Skin * skin,
1191 GdkDrawable * drawable, GdkGC * gc,
1192 gint width, gboolean focus)
1193 {
1194 /* The shade mode titlebar skin consists of 4 images:
1195 * a) left corner offset (72,42) size (25,14)
1196 * b) right corner, focused offset (99,57) size (50,14)
1197 * c) right corner, unfocused offset (99,42) size (50,14)
1198 * d) bar tile offset (72,57) size (25,14)
1199 */
1200
1201 gint i;
1202
1203 /* left corner */
1204 skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 72, 42, 0, 0, 25, 14);
1205
1206 /* bar tile */
1207 for (i = 0; i < (width - 75) / 25; i++)
1208 skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 72, 57,
1209 (i * 25) + 25, 0, 25, 14);
1210
1211 /* right corner */
1212 skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 99, focus ? 57 : 42,
1213 width - 50, 0, 50, 14);
1214 }
1215
1216
1217 void
1218 skin_draw_mainwin_titlebar(Skin * skin,
1219 GdkDrawable * drawable, GdkGC * gc,
1220 gboolean shaded, gboolean focus)
1221 {
1222 /* The titlebar skin consists of 2 sets of 2 images, one for for
1223 * shaded and the other for unshaded mode, giving a total of 4.
1224 * The images are exactly 275x14 pixels, aligned and arranged
1225 * vertically on each other in the pixmap in the following order:
1226 *
1227 * a) unshaded, focused offset (27, 0)
1228 * b) unshaded, unfocused offset (27, 15)
1229 * c) shaded, focused offset (27, 29)
1230 * d) shaded, unfocused offset (27, 42)
1231 */
1232
1233 gint y_offset;
1234
1235 if (shaded) {
1236 if (focus)
1237 y_offset = 29;
1238 else
1239 y_offset = 42;
1240 }
1241 else {
1242 if (focus)
1243 y_offset = 0;
1244 else
1245 y_offset = 15;
1246 }
1247
1248 skin_draw_pixmap(skin, drawable, gc, SKIN_TITLEBAR, 27, y_offset,
1249 0, 0, MAINWIN_WIDTH, MAINWIN_TITLEBAR_HEIGHT);
1250 }
1251
1252 #if 0
1253 void
1254 skin_draw_mainwin(Skin * skin,
1255 GdkDrawable * drawable, GdkGC gc,
1256 gboolean doublesize, gboolean shaded, gboolean focus)
1257 {
1258
1259 }
1260 #endif