comparison src/audacious/widgets/playlist_list.c @ 2313:3149d4b1a9a9 trunk

[svn] - objective-make autodepend fixes - move all sourcecode into src/ and adjust Makefiles accordingly
author nenolod
date Fri, 12 Jan 2007 11:43:40 -0800
parents
children 4b2c7d9523e7
comparison
equal deleted inserted replaced
2312:e1a5a66fb9cc 2313:3149d4b1a9a9
1 /* Audacious - Cross-platform multimedia player
2 * Copyright (C) 2005-2006 Audacious development team.
3 *
4 * BMP - Cross-platform multimedia player
5 * Copyright (C) 2003-2004 BMP development team.
6 *
7 * Based on XMMS:
8 * Copyright (C) 1998-2003 XMMS development team.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 */
24
25 /*
26 * A note about Pango and some funky spacey fonts: Weirdly baselined
27 * fonts, or fonts with weird ascents or descents _will_ display a
28 * little bit weird in the playlist widget, but the display engine
29 * won't make it look too bad, just a little deranged. I honestly
30 * don't think it's worth fixing (around...), it doesn't have to be
31 * perfectly fitting, just the general look has to be ok, which it
32 * IMHO is.
33 *
34 * A second note: The numbers aren't perfectly aligned, but in the
35 * end it looks better when using a single Pango layout for each
36 * number.
37 */
38
39 #include "widgetcore.h"
40
41 #include <stdlib.h>
42 #include <string.h>
43
44 #include "main.h"
45 #include "input.h"
46 #include "playback.h"
47 #include "playlist.h"
48 #include "ui_playlist.h"
49 #include "util.h"
50
51 #include "debug.h"
52
53 static PangoFontDescription *playlist_list_font = NULL;
54 static gint ascent, descent, width_delta_digit_one;
55 static gboolean has_slant;
56 static guint padding;
57
58 /* FIXME: the following globals should not be needed. */
59 static gint width_approx_letters;
60 static gint width_colon, width_colon_third;
61 static gint width_approx_digits, width_approx_digits_half;
62
63 GdkPixmap *rootpix;
64
65 void playlist_list_draw(Widget * w);
66
67 /* Sort of stolen from XChat, but not really, as theres uses Xlib */
68 static void
69 shade_gdkimage_generic (GdkVisual *visual, GdkImage *ximg, int bpl, int w, int h, int rm, int gm, int bm, int bg)
70 {
71 int x, y;
72 int bgr = (256 - rm) * (bg & visual->red_mask);
73 int bgg = (256 - gm) * (bg & visual->green_mask);
74 int bgb = (256 - bm) * (bg & visual->blue_mask);
75
76 for (x = 0; x < w; x++)
77 {
78 for (y = 0; y < h; y++)
79 {
80 unsigned long pixel = gdk_image_get_pixel (ximg, x, y);
81 int r, g, b;
82
83 r = rm * (pixel & visual->red_mask) + bgr;
84 g = gm * (pixel & visual->green_mask) + bgg;
85 b = bm * (pixel & visual->blue_mask) + bgb;
86
87 gdk_image_put_pixel (ximg, x, y,
88 ((r >> 8) & visual->red_mask) |
89 ((g >> 8) & visual->green_mask) |
90 ((b >> 8) & visual->blue_mask));
91 }
92 }
93 }
94
95 /* and this is definately mine... -nenolod */
96 GdkPixmap *
97 shade_pixmap(GdkPixmap *in, gint x, gint y, gint x_offset, gint y_offset, gint w, gint h, GdkColor *shade_color)
98 {
99 GdkImage *ximg;
100 GdkPixmap *p;
101 GdkGC *gc;
102
103 g_return_val_if_fail(in != NULL, NULL);
104
105 p = gdk_pixmap_new(in, w, h, -1);
106 gc = gdk_gc_new(p);
107
108 gdk_draw_drawable(p, gc, in, x, y, 0, 0, w, h);
109
110 gdk_error_trap_push();
111
112 ximg = gdk_drawable_copy_to_image(in, NULL, x, y, 0, 0, w, h); /* copy */
113
114 gdk_error_trap_pop();
115
116 if (GDK_IS_IMAGE(ximg))
117 {
118 shade_gdkimage_generic(gdk_drawable_get_visual(GDK_WINDOW(playlistwin->window)),
119 ximg, ximg->bpl, w, h, 60, 60, 60, shade_color->pixel);
120
121 gdk_draw_image(p, gc, ximg, 0, 0, x, y, w, h);
122
123 g_object_unref(ximg);
124 }
125 else {
126 cfg.playlist_transparent = FALSE;
127 }
128
129 g_object_unref(in);
130 g_object_unref(gc);
131
132 return p;
133 }
134
135 #ifdef GDK_WINDOWING_X11
136
137 #include <gdk/gdkx.h>
138 #include <X11/Xlib.h>
139 #include <X11/Xatom.h>
140
141 GdkDrawable *get_transparency_pixmap(void)
142 {
143 GdkDrawable *root;
144 XID *pixmaps;
145 GdkAtom prop_type;
146 gint prop_size;
147 GdkPixmap *pixmap;
148 gboolean ret;
149
150 root = gdk_get_default_root_window();
151
152 pixmap = NULL;
153 pixmaps = NULL;
154
155 gdk_error_trap_push();
156
157 ret = gdk_property_get(root, gdk_atom_intern("_XROOTPMAP_ID", TRUE),
158 0, 0, INT_MAX - 3,
159 FALSE,
160 &prop_type, NULL, &prop_size,
161 (guchar **) &pixmaps);
162
163 gdk_error_trap_pop();
164
165 if ((ret == TRUE) && (prop_type == GDK_TARGET_PIXMAP) && (prop_size >= sizeof(XID)) && (pixmaps != NULL))
166 {
167 pixmap = gdk_pixmap_foreign_new_for_display(gdk_drawable_get_display(root),
168 pixmaps[0]);
169
170 if (pixmaps != NULL)
171 g_free(pixmaps);
172 }
173
174 return GDK_DRAWABLE(pixmap);
175 }
176
177 static GdkFilterReturn
178 root_event_cb (GdkXEvent *xev, GdkEventProperty *event, gpointer data)
179 {
180 XEvent *xevent = (XEvent *)xev;
181
182 if (xevent->type == PropertyNotify)
183 {
184 Atom at = XInternAtom (xevent->xproperty.display, "_XROOTPMAP_ID", True);
185
186 if (at == xevent->xproperty.atom)
187 {
188 if (rootpix != NULL)
189 g_object_unref(rootpix);
190
191 rootpix = shade_pixmap(get_transparency_pixmap(), 0, 0, 0, 0, gdk_screen_width(), gdk_screen_height(),
192 skin_get_color(bmp_active_skin, SKIN_PLEDIT_NORMALBG));
193
194 if (cfg.playlist_transparent)
195 {
196 playlistwin_update_list(playlist_get_active());
197 draw_playlist_window(TRUE);
198 }
199 }
200 }
201
202 return GDK_FILTER_CONTINUE;
203 }
204
205 #else
206
207 GdkPixmap *get_transparency_pixmap(void)
208 {
209 return NULL;
210 }
211
212 #endif
213
214 static gboolean
215 playlist_list_auto_drag_down_func(gpointer data)
216 {
217 PlayList_List *pl = data;
218
219 if (pl->pl_auto_drag_down) {
220 playlist_list_move_down(pl);
221 pl->pl_first++;
222 playlistwin_update_list(playlist_get_active());
223 return TRUE;
224 }
225 return FALSE;
226 }
227
228 static gboolean
229 playlist_list_auto_drag_up_func(gpointer data)
230 {
231 PlayList_List *pl = data;
232
233 if (pl->pl_auto_drag_up) {
234 playlist_list_move_up(pl);
235 pl->pl_first--;
236 playlistwin_update_list(playlist_get_active());
237 return TRUE;
238
239 }
240 return FALSE;
241 }
242
243 void
244 playlist_list_move_up(PlayList_List * pl)
245 {
246 GList *list;
247 Playlist *playlist = playlist_get_active();
248
249 if (!playlist)
250 return;
251
252 PLAYLIST_LOCK(playlist->mutex);
253 if ((list = playlist->entries) == NULL) {
254 PLAYLIST_UNLOCK(playlist->mutex);
255 return;
256 }
257 if (PLAYLIST_ENTRY(list->data)->selected) {
258 /* We are at the top */
259 PLAYLIST_UNLOCK(playlist->mutex);
260 return;
261 }
262 while (list) {
263 if (PLAYLIST_ENTRY(list->data)->selected)
264 glist_moveup(list);
265 list = g_list_next(list);
266 }
267 PLAYLIST_UNLOCK(playlist->mutex);
268 if (pl->pl_prev_selected != -1)
269 pl->pl_prev_selected--;
270 if (pl->pl_prev_min != -1)
271 pl->pl_prev_min--;
272 if (pl->pl_prev_max != -1)
273 pl->pl_prev_max--;
274 }
275
276 void
277 playlist_list_move_down(PlayList_List * pl)
278 {
279 GList *list;
280 Playlist *playlist = playlist_get_active();
281
282 if (!playlist)
283 return;
284
285 PLAYLIST_LOCK(playlist->mutex);
286
287 if (!(list = g_list_last(playlist->entries))) {
288 PLAYLIST_UNLOCK(playlist->mutex);
289 return;
290 }
291
292 if (PLAYLIST_ENTRY(list->data)->selected) {
293 /* We are at the bottom */
294 PLAYLIST_UNLOCK(playlist->mutex);
295 return;
296 }
297
298 while (list) {
299 if (PLAYLIST_ENTRY(list->data)->selected)
300 glist_movedown(list);
301 list = g_list_previous(list);
302 }
303
304 PLAYLIST_UNLOCK(playlist->mutex);
305
306 if (pl->pl_prev_selected != -1)
307 pl->pl_prev_selected++;
308 if (pl->pl_prev_min != -1)
309 pl->pl_prev_min++;
310 if (pl->pl_prev_max != -1)
311 pl->pl_prev_max++;
312 }
313
314 static void
315 playlist_list_button_press_cb(GtkWidget * widget,
316 GdkEventButton * event,
317 PlayList_List * pl)
318 {
319 gint nr, y;
320 Playlist *playlist = playlist_get_active();
321
322 if (event->button == 1 && pl->pl_fheight &&
323 widget_contains(&pl->pl_widget, event->x, event->y)) {
324
325 y = event->y - pl->pl_widget.y;
326 nr = (y / pl->pl_fheight) + pl->pl_first;
327
328 if (nr >= playlist_get_length(playlist))
329 nr = playlist_get_length(playlist) - 1;
330
331 if (!(event->state & GDK_CONTROL_MASK))
332 playlist_select_all(playlist, FALSE);
333
334 if (event->state & GDK_SHIFT_MASK && pl->pl_prev_selected != -1) {
335 playlist_select_range(playlist, pl->pl_prev_selected, nr, TRUE);
336 pl->pl_prev_min = pl->pl_prev_selected;
337 pl->pl_prev_max = nr;
338 pl->pl_drag_pos = nr - pl->pl_first;
339 }
340 else {
341 if (playlist_select_invert(playlist, nr)) {
342 if (event->state & GDK_CONTROL_MASK) {
343 if (pl->pl_prev_min == -1) {
344 pl->pl_prev_min = pl->pl_prev_selected;
345 pl->pl_prev_max = pl->pl_prev_selected;
346 }
347 if (nr < pl->pl_prev_min)
348 pl->pl_prev_min = nr;
349 else if (nr > pl->pl_prev_max)
350 pl->pl_prev_max = nr;
351 }
352 else
353 pl->pl_prev_min = -1;
354 pl->pl_prev_selected = nr;
355 pl->pl_drag_pos = nr - pl->pl_first;
356 }
357 }
358 if (event->type == GDK_2BUTTON_PRESS) {
359 /*
360 * Ungrab the pointer to prevent us from
361 * hanging on to it during the sometimes slow
362 * playback_initiate().
363 */
364 gdk_pointer_ungrab(GDK_CURRENT_TIME);
365 gdk_flush();
366 playlist_set_position(playlist, nr);
367 if (!playback_get_playing())
368 playback_initiate();
369 }
370
371 pl->pl_dragging = TRUE;
372 playlistwin_update_list(playlist);
373 }
374 }
375
376 gint
377 playlist_list_get_playlist_position(PlayList_List * pl,
378 gint x,
379 gint y)
380 {
381 gint iy, length;
382 gint ret;
383 Playlist *playlist = playlist_get_active();
384
385 if (!widget_contains(WIDGET(pl), x, y) || !pl->pl_fheight)
386 return -1;
387
388 if ((length = playlist_get_length(playlist)) == 0)
389 return -1;
390 iy = y - pl->pl_widget.y;
391
392 ret = (iy / pl->pl_fheight) + pl->pl_first;
393
394 if(ret > length-1)
395 ret = -1;
396
397 return ret;
398 }
399
400 static void
401 playlist_list_motion_cb(GtkWidget * widget,
402 GdkEventMotion * event,
403 PlayList_List * pl)
404 {
405 gint nr, y, off, i;
406
407 if (pl->pl_dragging) {
408 y = event->y - pl->pl_widget.y;
409 nr = (y / pl->pl_fheight);
410 if (nr < 0) {
411 nr = 0;
412 if (!pl->pl_auto_drag_up) {
413 pl->pl_auto_drag_up = TRUE;
414 pl->pl_auto_drag_up_tag =
415 g_timeout_add(100, playlist_list_auto_drag_up_func, pl);
416 }
417 }
418 else if (pl->pl_auto_drag_up)
419 pl->pl_auto_drag_up = FALSE;
420
421 if (nr >= pl->pl_num_visible) {
422 nr = pl->pl_num_visible - 1;
423 if (!pl->pl_auto_drag_down) {
424 pl->pl_auto_drag_down = TRUE;
425 pl->pl_auto_drag_down_tag =
426 g_timeout_add(100, playlist_list_auto_drag_down_func,
427 pl);
428 }
429 }
430 else if (pl->pl_auto_drag_down)
431 pl->pl_auto_drag_down = FALSE;
432
433 off = nr - pl->pl_drag_pos;
434 if (off) {
435 for (i = 0; i < abs(off); i++) {
436 if (off < 0)
437 playlist_list_move_up(pl);
438 else
439 playlist_list_move_down(pl);
440
441 }
442 playlistwin_update_list(playlist_get_active());
443 }
444 pl->pl_drag_pos = nr;
445 }
446 }
447
448 static void
449 playlist_list_button_release_cb(GtkWidget * widget,
450 GdkEventButton * event,
451 PlayList_List * pl)
452 {
453 pl->pl_dragging = FALSE;
454 pl->pl_auto_drag_down = FALSE;
455 pl->pl_auto_drag_up = FALSE;
456 }
457
458 static void
459 playlist_list_draw_string(PlayList_List * pl,
460 PangoFontDescription * font,
461 gint line,
462 gint width,
463 const gchar * text,
464 guint ppos)
465 {
466 guint plist_length_int;
467 Playlist *playlist = playlist_get_active();
468
469 PangoLayout *layout;
470
471 REQUIRE_LOCK(playlist->mutex);
472
473 if (cfg.show_numbers_in_pl) {
474 gchar *pos_string = g_strdup_printf(cfg.show_separator_in_pl == TRUE ? "%d" : "%d.", ppos);
475 plist_length_int =
476 gint_count_digits(playlist_get_length_nolock(playlist)) + !cfg.show_separator_in_pl + 1; /* cf.show_separator_in_pl will be 0 if false */
477
478 padding = plist_length_int;
479 padding = ((padding + 1) * width_approx_digits);
480
481 layout = gtk_widget_create_pango_layout(playlistwin, pos_string);
482 pango_layout_set_font_description(layout, playlist_list_font);
483 pango_layout_set_width(layout, plist_length_int * 100);
484
485 pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT);
486 gdk_draw_layout(pl->pl_widget.parent, pl->pl_widget.gc,
487 pl->pl_widget.x +
488 (width_approx_digits *
489 (-1 + plist_length_int - strlen(pos_string))) +
490 (width_approx_digits / 4),
491 pl->pl_widget.y + (line - 1) * pl->pl_fheight +
492 ascent + abs(descent), layout);
493 g_free(pos_string);
494 g_object_unref(layout);
495
496 if (!cfg.show_separator_in_pl)
497 padding -= (width_approx_digits * 1.5);
498 }
499 else {
500 padding = 3;
501 }
502
503 width -= padding;
504
505 layout = gtk_widget_create_pango_layout(playlistwin, text);
506
507 pango_layout_set_font_description(layout, playlist_list_font);
508 pango_layout_set_width(layout, width * PANGO_SCALE);
509 pango_layout_set_single_paragraph_mode(layout, TRUE);
510 pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END);
511 gdk_draw_layout(pl->pl_widget.parent, pl->pl_widget.gc,
512 pl->pl_widget.x + padding + (width_approx_letters / 4),
513 pl->pl_widget.y + (line - 1) * pl->pl_fheight +
514 ascent + abs(descent), layout);
515
516 g_object_unref(layout);
517 }
518
519 void
520 playlist_list_draw(Widget * w)
521 {
522 Playlist *playlist = playlist_get_active();
523 PlayList_List *pl = PLAYLIST_LIST(w);
524 GList *list;
525 GdkGC *gc;
526 GdkPixmap *obj;
527 PangoLayout *layout;
528 gchar *title;
529 gint width, height;
530 gint i, max_first;
531 guint padding, padding_dwidth, padding_plength;
532 guint max_time_len = 0;
533 gfloat queue_tailpadding = 0;
534 gint tpadding;
535 gsize tpadding_dwidth = 0;
536 gint x, y;
537 guint tail_width;
538 guint tail_len;
539
540 gchar tail[100];
541 gchar queuepos[255];
542 gchar length[40];
543
544 gchar **frags;
545 gchar *frag0;
546
547 gint plw_w, plw_h;
548
549 GdkRectangle *playlist_rect;
550
551 gc = pl->pl_widget.gc;
552
553 width = pl->pl_widget.width;
554 height = pl->pl_widget.height;
555
556 obj = pl->pl_widget.parent;
557
558 plw_w = playlistwin_get_width();
559 plw_h = playlistwin_get_height();
560
561 playlist_rect = g_new0(GdkRectangle, 1);
562
563 playlist_rect->x = 0;
564 playlist_rect->y = 0;
565 playlist_rect->width = plw_w - 17;
566 playlist_rect->height = plw_h - 36;
567
568 gdk_gc_set_clip_origin(gc, 31, 58);
569 gdk_gc_set_clip_rectangle(gc, playlist_rect);
570
571 if (cfg.playlist_transparent == FALSE)
572 {
573 gdk_gc_set_foreground(gc,
574 skin_get_color(bmp_active_skin,
575 SKIN_PLEDIT_NORMALBG));
576 gdk_draw_rectangle(obj, gc, TRUE, pl->pl_widget.x, pl->pl_widget.y,
577 width, height);
578 }
579 else
580 {
581 if (!rootpix)
582 rootpix = shade_pixmap(get_transparency_pixmap(), 0, 0, 0, 0, gdk_screen_width(), gdk_screen_height(),
583 skin_get_color(bmp_active_skin, SKIN_PLEDIT_NORMALBG));
584 gdk_draw_drawable(obj, gc, rootpix, cfg.playlist_x + pl->pl_widget.x,
585 cfg.playlist_y + pl->pl_widget.y, pl->pl_widget.x, pl->pl_widget.y,
586 width, height);
587 }
588
589 if (!playlist_list_font) {
590 g_critical("Couldn't open playlist font");
591 return;
592 }
593
594 pl->pl_fheight = (ascent + abs(descent));
595 pl->pl_num_visible = height / pl->pl_fheight;
596
597 max_first = playlist_get_length(playlist) - pl->pl_num_visible;
598 max_first = MAX(max_first, 0);
599
600 pl->pl_first = CLAMP(pl->pl_first, 0, max_first);
601
602 PLAYLIST_LOCK(playlist->mutex);
603 list = playlist->entries;
604 list = g_list_nth(list, pl->pl_first);
605
606 /* It sucks having to run the iteration twice but this is the only
607 way you can reliably get the maximum width so we can get our
608 playlist nice and aligned... -- plasmaroo */
609
610 for (i = pl->pl_first;
611 list && i < pl->pl_first + pl->pl_num_visible;
612 list = g_list_next(list), i++) {
613 PlaylistEntry *entry = list->data;
614
615 if (entry->length != -1)
616 {
617 g_snprintf(length, sizeof(length), "%d:%-2.2d",
618 entry->length / 60000, (entry->length / 1000) % 60);
619 tpadding_dwidth = MAX(tpadding_dwidth, strlen(length));
620 }
621 }
622
623 /* Reset */
624 list = playlist->entries;
625 list = g_list_nth(list, pl->pl_first);
626
627 for (i = pl->pl_first;
628 list && i < pl->pl_first + pl->pl_num_visible;
629 list = g_list_next(list), i++) {
630 gint pos;
631 PlaylistEntry *entry = list->data;
632
633 if (entry->selected) {
634 gdk_gc_set_foreground(gc,
635 skin_get_color(bmp_active_skin,
636 SKIN_PLEDIT_SELECTEDBG));
637 gdk_draw_rectangle(obj, gc, TRUE, pl->pl_widget.x,
638 pl->pl_widget.y +
639 ((i - pl->pl_first) * pl->pl_fheight),
640 width, pl->pl_fheight);
641 }
642
643 /* FIXME: entry->title should NEVER be NULL, and there should
644 NEVER be a need to do a UTF-8 conversion. Playlist title
645 strings should be kept properly. */
646
647 if (!entry->title) {
648 gchar *basename = g_path_get_basename(entry->filename);
649 title = filename_to_utf8(basename);
650 g_free(basename);
651 }
652 else
653 title = str_to_utf8(entry->title);
654
655 title = convert_title_text(title);
656
657 pos = playlist_get_queue_position(playlist, entry);
658
659 tail[0] = 0;
660 queuepos[0] = 0;
661 length[0] = 0;
662
663 if (pos != -1)
664 g_snprintf(queuepos, sizeof(queuepos), "%d", pos + 1);
665
666 if (entry->length != -1)
667 {
668 g_snprintf(length, sizeof(length), "%d:%-2.2d",
669 entry->length / 60000, (entry->length / 1000) % 60);
670 }
671
672 strncat(tail, length, sizeof(tail));
673 tail_len = strlen(tail);
674
675 max_time_len = MAX(max_time_len, tail_len);
676
677 if (pos != -1 && tpadding_dwidth <= 0)
678 tail_width = width - (width_approx_digits * (strlen(queuepos) + 2.25));
679 else if (pos != -1)
680 tail_width = width - (width_approx_digits * (tpadding_dwidth + strlen(queuepos) + 4));
681 else if (tpadding_dwidth > 0)
682 tail_width = width - (width_approx_digits * (tpadding_dwidth + 2.5));
683 else
684 tail_width = width;
685
686 if (i == playlist_get_position_nolock(playlist))
687 gdk_gc_set_foreground(gc,
688 skin_get_color(bmp_active_skin,
689 SKIN_PLEDIT_CURRENT));
690 else
691 gdk_gc_set_foreground(gc,
692 skin_get_color(bmp_active_skin,
693 SKIN_PLEDIT_NORMAL));
694 playlist_list_draw_string(pl, playlist_list_font,
695 i - pl->pl_first, tail_width, title,
696 i + 1);
697
698 x = pl->pl_widget.x + width - width_approx_digits * 2;
699 y = pl->pl_widget.y + ((i - pl->pl_first) -
700 1) * pl->pl_fheight + ascent;
701
702 frags = NULL;
703 frag0 = NULL;
704
705 if ((strlen(tail) > 0) && (tail != NULL)) {
706 frags = g_strsplit(tail, ":", 0);
707 frag0 = g_strconcat(frags[0], ":", NULL);
708
709 layout = gtk_widget_create_pango_layout(playlistwin, frags[1]);
710 pango_layout_set_font_description(layout, playlist_list_font);
711 pango_layout_set_width(layout, tail_len * 100);
712 pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT);
713 gdk_draw_layout(obj, gc, x - (0.5 * width_approx_digits),
714 y + abs(descent), layout);
715 g_object_unref(layout);
716
717 layout = gtk_widget_create_pango_layout(playlistwin, frag0);
718 pango_layout_set_font_description(layout, playlist_list_font);
719 pango_layout_set_width(layout, tail_len * 100);
720 pango_layout_set_alignment(layout, PANGO_ALIGN_RIGHT);
721 gdk_draw_layout(obj, gc, x - (0.75 * width_approx_digits),
722 y + abs(descent), layout);
723 g_object_unref(layout);
724
725 g_free(frag0);
726 g_strfreev(frags);
727 }
728
729 if (pos != -1) {
730
731 /* DON'T remove the commented code yet please -- Milosz */
732
733 if (tpadding_dwidth > 0)
734 queue_tailpadding = tpadding_dwidth + 1;
735 else
736 queue_tailpadding = -0.75;
737
738 gdk_draw_rectangle(obj, gc, FALSE,
739 x -
740 (((queue_tailpadding +
741 strlen(queuepos)) *
742 width_approx_digits) +
743 (width_approx_digits / 4)),
744 y + abs(descent),
745 (strlen(queuepos)) *
746 width_approx_digits +
747 (width_approx_digits / 2),
748 pl->pl_fheight - 2);
749
750 layout =
751 gtk_widget_create_pango_layout(playlistwin, queuepos);
752 pango_layout_set_font_description(layout, playlist_list_font);
753 pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
754
755 gdk_draw_layout(obj, gc,
756 x -
757 ((queue_tailpadding +
758 strlen(queuepos)) * width_approx_digits) +
759 (width_approx_digits / 4),
760 y + abs(descent), layout);
761 g_object_unref(layout);
762 }
763
764 g_free(title);
765 }
766
767
768 /*
769 * Drop target hovering over the playlist, so draw some hint where the
770 * drop will occur.
771 *
772 * This is (currently? unfixably?) broken when dragging files from Qt/KDE apps,
773 * probably due to DnD signaling problems (actually i have no clue).
774 *
775 */
776
777 if (pl->pl_drag_motion) {
778 guint pos, plength, lpadding;
779 gint x, y, plx, ply;
780
781 if (cfg.show_numbers_in_pl) {
782 lpadding = gint_count_digits(playlist_get_length_nolock(playlist)) + 1;
783 lpadding = ((lpadding + 1) * width_approx_digits);
784 }
785 else {
786 lpadding = 3;
787 };
788
789 /* We already hold the mutex and have the playlist locked, so call
790 the non-locking function. */
791 plength = playlist_get_length_nolock(playlist);
792
793 x = pl->drag_motion_x;
794 y = pl->drag_motion_y;
795
796 plx = pl->pl_widget.x;
797 ply = pl->pl_widget.y;
798
799 if ((x > pl->pl_widget.x) && !(x > pl->pl_widget.width)) {
800
801 if ((y > pl->pl_widget.y)
802 && !(y > (pl->pl_widget.height + ply))) {
803
804 pos = ((y - ((Widget *) pl)->y) / pl->pl_fheight) +
805 pl->pl_first;
806
807 if (pos > (plength)) {
808 pos = plength;
809 }
810
811 gdk_gc_set_foreground(gc,
812 skin_get_color(bmp_active_skin,
813 SKIN_PLEDIT_CURRENT));
814
815 gdk_draw_line(obj, gc, pl->pl_widget.x,
816 pl->pl_widget.y + ((pos - pl->pl_first) * pl->pl_fheight),
817 pl->pl_widget.width + pl->pl_widget.x - 1,
818 pl->pl_widget.y +
819 ((pos - pl->pl_first) * pl->pl_fheight));
820 }
821
822 }
823
824 /* When dropping on the borders of the playlist, outside the text area,
825 * files get appended at the end of the list. Show that too.
826 */
827
828 if ((y < ply) || (y > pl->pl_widget.height + ply)) {
829 if ((y >= 0) || (y <= (pl->pl_widget.height + ply))) {
830 pos = plength;
831 gdk_gc_set_foreground(gc,
832 skin_get_color(bmp_active_skin,
833 SKIN_PLEDIT_CURRENT));
834
835 gdk_draw_line(obj, gc, pl->pl_widget.x,
836 pl->pl_widget.y +
837 ((pos - pl->pl_first) * pl->pl_fheight),
838 pl->pl_widget.width + pl->pl_widget.x - 1,
839 pl->pl_widget.y +
840 ((pos - pl->pl_first) * pl->pl_fheight));
841
842 }
843 }
844 }
845
846 gdk_gc_set_foreground(gc,
847 skin_get_color(bmp_active_skin,
848 SKIN_PLEDIT_NORMAL));
849
850 if (cfg.show_numbers_in_pl)
851 {
852 padding_plength = playlist_get_length_nolock(playlist);
853
854 if (padding_plength == 0) {
855 padding_dwidth = 0;
856 }
857 else {
858 padding_dwidth = gint_count_digits(playlist_get_length_nolock(playlist));
859 }
860
861 padding =
862 (padding_dwidth *
863 width_approx_digits) + width_approx_digits;
864
865
866 /* For italic or oblique fonts we add another half of the
867 * approximate width */
868 if (has_slant)
869 padding += width_approx_digits_half;
870
871 if (cfg.show_separator_in_pl) {
872 gdk_draw_line(obj, gc,
873 pl->pl_widget.x + padding,
874 pl->pl_widget.y,
875 pl->pl_widget.x + padding,
876 pl->pl_widget.y + pl->pl_widget.height - 1);
877 }
878 }
879
880 if (tpadding_dwidth != 0)
881 {
882 tpadding = (tpadding_dwidth * width_approx_digits) + (width_approx_digits * 1.5);
883
884 if (has_slant)
885 tpadding += width_approx_digits_half;
886
887 if (cfg.show_separator_in_pl) {
888 gdk_draw_line(obj, gc,
889 pl->pl_widget.x + pl->pl_widget.width - tpadding,
890 pl->pl_widget.y,
891 pl->pl_widget.x + pl->pl_widget.width - tpadding,
892 pl->pl_widget.y + pl->pl_widget.height - 1);
893 }
894 }
895
896 gdk_gc_set_clip_origin(gc, 0, 0);
897 gdk_gc_set_clip_rectangle(gc, NULL);
898
899 PLAYLIST_UNLOCK(playlist->mutex);
900
901 gdk_flush();
902
903 g_free(playlist_rect);
904 }
905
906
907 PlayList_List *
908 create_playlist_list(GList ** wlist,
909 GdkPixmap * parent,
910 GdkGC * gc,
911 gint x, gint y,
912 gint w, gint h)
913 {
914 PlayList_List *pl;
915
916 pl = g_new0(PlayList_List, 1);
917 widget_init(&pl->pl_widget, parent, gc, x, y, w, h, TRUE);
918
919 pl->pl_widget.button_press_cb =
920 (WidgetButtonPressFunc) playlist_list_button_press_cb;
921 pl->pl_widget.button_release_cb =
922 (WidgetButtonReleaseFunc) playlist_list_button_release_cb;
923 pl->pl_widget.motion_cb = (WidgetMotionFunc) playlist_list_motion_cb;
924 pl->pl_widget.draw = playlist_list_draw;
925
926 pl->pl_prev_selected = -1;
927 pl->pl_prev_min = -1;
928 pl->pl_prev_max = -1;
929
930 widget_list_add(wlist, WIDGET(pl));
931
932 #ifdef GDK_WINDOWING_X11
933 gdk_window_set_events (gdk_get_default_root_window(), GDK_PROPERTY_CHANGE_MASK);
934 gdk_window_add_filter (gdk_get_default_root_window(), (GdkFilterFunc)root_event_cb, pl);
935 #endif
936
937 return pl;
938 }
939
940 void
941 playlist_list_set_font(const gchar * font)
942 {
943
944 /* Welcome to bad hack central 2k3 */
945
946 gchar *font_lower;
947 gint width_temp;
948 gint width_temp_0;
949
950 playlist_list_font = pango_font_description_from_string(font);
951
952 text_get_extents(font,
953 "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz ",
954 &width_approx_letters, NULL, &ascent, &descent);
955
956 width_approx_letters = (width_approx_letters / 53);
957
958 /* Experimental: We don't weigh the 1 into total because it's width is almost always
959 * very different from the rest
960 */
961 text_get_extents(font, "023456789", &width_approx_digits, NULL, NULL,
962 NULL);
963 width_approx_digits = (width_approx_digits / 9);
964
965 /* Precache some often used calculations */
966 width_approx_digits_half = width_approx_digits / 2;
967
968 /* FIXME: We assume that any other number is broader than the "1" */
969 text_get_extents(font, "1", &width_temp, NULL, NULL, NULL);
970 text_get_extents(font, "2", &width_temp_0, NULL, NULL, NULL);
971
972 if (abs(width_temp_0 - width_temp) < 2) {
973 width_delta_digit_one = 0;
974 }
975 else {
976 width_delta_digit_one = ((width_temp_0 - width_temp) / 2) + 2;
977 }
978
979 text_get_extents(font, ":", &width_colon, NULL, NULL, NULL);
980 width_colon_third = width_colon / 4;
981
982 font_lower = g_utf8_strdown(font, strlen(font));
983 /* This doesn't take any i18n into account, but i think there is none with TTF fonts
984 * FIXME: This can probably be retrieved trough Pango too
985 */
986 has_slant = g_strstr_len(font_lower, strlen(font_lower), "oblique")
987 || g_strstr_len(font_lower, strlen(font_lower), "italic");
988
989 g_free(font_lower);
990 }