Mercurial > audlegacy
view audacious/playlist_list.c @ 1090:09eb2c83097a trunk
[svn] Psychoaccoustics support (to disable, temporarily add -UPSYCHO to your CFLAGS.):
This commit brings psychoaccoustics support (as used in mp3surround decoders) to libmpgdec.
For example, we can now almost fully compensate for lack of bandwidth in ISO compliant MP3 encodings.
In addition, further inaccuracies with pitch and the lack of reverb feeling that some MP3s have are
detected and automatically compensated for.
author | nenolod |
---|---|
date | Sat, 20 May 2006 20:36:10 -0700 |
parents | 968a9449f270 |
children | 5767c05ce900 |
line wrap: on
line source
/* Audacious - Cross-platform multimedia player * Copyright (C) 2005-2006 Audacious development team. * * BMP - Cross-platform multimedia player * Copyright (C) 2003-2004 BMP development team. * * Based on XMMS: * Copyright (C) 1998-2003 XMMS development team. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * A note about Pango and some funky spacey fonts: Weirdly baselined * fonts, or fonts with weird ascents or descents _will_ display a * little bit weird in the playlist widget, but the display engine * won't make it look too bad, just a little deranged. I honestly * don't think it's worth fixing (around...), it doesn't have to be * perfectly fitting, just the general look has to be ok, which it * IMHO is. * * A second note: The numbers aren't perfectly aligned, but in the * end it looks better when using a single Pango layout for each * number. */ #include "playlist_list.h" #include <stdlib.h> #include <string.h> #include "main.h" #include "input.h" #include "playback.h" #include "playlist.h" #include "ui_playlist.h" #include "util.h" #include "debug.h" static PangoFontDescription *playlist_list_font = NULL; static gint ascent, descent, width_delta_digit_one; static gboolean has_slant; static guint padding; /* FIXME: the following globals should not be needed. */ static gint width_approx_letters; static gint width_colon, width_colon_third; static gint width_approx_digits, width_approx_digits_half; GdkPixmap *rootpix; void playlist_list_draw(Widget * w); /* Sort of stolen from XChat, but not really, as theres uses Xlib */ static void shade_gdkimage_generic (GdkVisual *visual, GdkImage *ximg, int bpl, int w, int h, int rm, int gm, int bm, int bg) { int x, y; int bgr = (256 - rm) * (bg & visual->red_mask); int bgg = (256 - gm) * (bg & visual->green_mask); int bgb = (256 - bm) * (bg & visual->blue_mask); for (x = 0; x < w; x++) { for (y = 0; y < h; y++) { unsigned long pixel = gdk_image_get_pixel (ximg, x, y); int r, g, b; r = rm * (pixel & visual->red_mask) + bgr; g = gm * (pixel & visual->green_mask) + bgg; b = bm * (pixel & visual->blue_mask) + bgb; gdk_image_put_pixel (ximg, x, y, ((r >> 8) & visual->red_mask) | ((g >> 8) & visual->green_mask) | ((b >> 8) & visual->blue_mask)); } } } /* and this is definately mine... -nenolod */ GdkPixmap * shade_pixmap(GdkPixmap *in, gint x, gint y, gint x_offset, gint y_offset, gint w, gint h, GdkColor *shade_color) { GdkImage *ximg; GdkPixmap *p = gdk_pixmap_new(in, w, h, -1); GdkGC *gc = gdk_gc_new(p); gdk_draw_pixmap(p, gc, in, x, y, 0, 0, w, h); ximg = gdk_drawable_copy_to_image(in, NULL, x, y, 0, 0, w, h); /* copy */ shade_gdkimage_generic(gdk_drawable_get_visual(GDK_WINDOW(playlistwin->window)), ximg, ximg->bpl, w, h, 60, 60, 60, shade_color->pixel); gdk_draw_image(p, gc, ximg, 0, 0, x, y, w, h); g_object_unref(gc); return p; } #ifdef GDK_WINDOWING_X11 #include <gdk/gdkx.h> #include <X11/Xlib.h> #include <X11/Xatom.h> GdkPixmap *get_transparency_pixmap(void) { Atom type; static Atom prop = None; int format; unsigned long length, after; unsigned char *data; GdkPixmap *retval = NULL; if(prop == None) prop = XInternAtom(GDK_DISPLAY(), "_XROOTPMAP_ID", True); if(prop == None) return NULL; XGetWindowProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(), prop, 0L, 1L, False, AnyPropertyType, &type, &format, &length, &after, &data); if (data) { if(type == XA_PIXMAP) retval = gdk_pixmap_foreign_new(*((Pixmap *)data)); XFree(data); } return retval; } static GdkFilterReturn root_event_cb (GdkXEvent *xev, GdkEventProperty *event, gpointer data) { static Atom at = None; XEvent *xevent = (XEvent *)xev; if (xevent->type == PropertyNotify) { if (at == None) at = XInternAtom (xevent->xproperty.display, "_XROOTPMAP_ID", True); if (at == xevent->xproperty.atom) { rootpix = shade_pixmap(get_transparency_pixmap(), 0, 0, 0, 0, gdk_screen_width(), gdk_screen_height(), skin_get_color(bmp_active_skin, SKIN_PLEDIT_NORMALBG)); if (cfg.playlist_transparent) { playlistwin_update_list(); draw_playlist_window(TRUE); } } } return GDK_FILTER_CONTINUE; } #else GdkPixmap *get_transparency_pixmap(void) { return NULL; } #endif static gboolean playlist_list_auto_drag_down_func(gpointer data) { PlayList_List *pl = data; if (pl->pl_auto_drag_down) { playlist_list_move_down(pl); pl->pl_first++; playlistwin_update_list(); return TRUE; } return FALSE; } static gboolean playlist_list_auto_drag_up_func(gpointer data) { PlayList_List *pl = data; if (pl->pl_auto_drag_up) { playlist_list_move_up(pl); pl->pl_first--; playlistwin_update_list(); return TRUE; } return FALSE; } void playlist_list_move_up(PlayList_List * pl) { GList *list; PLAYLIST_LOCK(); if ((list = playlist_get()) == NULL) { PLAYLIST_UNLOCK(); return; } if (PLAYLIST_ENTRY(list->data)->selected) { /* We are at the top */ PLAYLIST_UNLOCK(); return; } while (list) { if (PLAYLIST_ENTRY(list->data)->selected) glist_moveup(list); list = g_list_next(list); } PLAYLIST_UNLOCK(); if (pl->pl_prev_selected != -1) pl->pl_prev_selected--; if (pl->pl_prev_min != -1) pl->pl_prev_min--; if (pl->pl_prev_max != -1) pl->pl_prev_max--; } void playlist_list_move_down(PlayList_List * pl) { GList *list; PLAYLIST_LOCK(); if (!(list = g_list_last(playlist_get()))) { PLAYLIST_UNLOCK(); return; } if (PLAYLIST_ENTRY(list->data)->selected) { /* We are at the bottom */ PLAYLIST_UNLOCK(); return; } while (list) { if (PLAYLIST_ENTRY(list->data)->selected) glist_movedown(list); list = g_list_previous(list); } PLAYLIST_UNLOCK(); if (pl->pl_prev_selected != -1) pl->pl_prev_selected++; if (pl->pl_prev_min != -1) pl->pl_prev_min++; if (pl->pl_prev_max != -1) pl->pl_prev_max++; } static void playlist_list_button_press_cb(GtkWidget * widget, GdkEventButton * event, PlayList_List * pl) { gint nr, y; if (event->button == 1 && pl->pl_fheight && widget_contains(&pl->pl_widget, event->x, event->y)) { y = event->y - pl->pl_widget.y; nr = (y / pl->pl_fheight) + pl->pl_first; if (nr >= playlist_get_length()) nr = playlist_get_length() - 1; if (!(event->state & GDK_CONTROL_MASK)) playlist_select_all(FALSE); if (event->state & GDK_SHIFT_MASK && pl->pl_prev_selected != -1) { playlist_select_range(pl->pl_prev_selected, nr, TRUE); pl->pl_prev_min = pl->pl_prev_selected; pl->pl_prev_max = nr; pl->pl_drag_pos = nr - pl->pl_first; } else { if (playlist_select_invert(nr)) { if (event->state & GDK_CONTROL_MASK) { if (pl->pl_prev_min == -1) { pl->pl_prev_min = pl->pl_prev_selected; pl->pl_prev_max = pl->pl_prev_selected; } if (nr < pl->pl_prev_min) pl->pl_prev_min = nr; else if (nr > pl->pl_prev_max) pl->pl_prev_max = nr; } else pl->pl_prev_min = -1; pl->pl_prev_selected = nr; pl->pl_drag_pos = nr - pl->pl_first; } } if (event->type == GDK_2BUTTON_PRESS) { /* * Ungrab the pointer to prevent us from * hanging on to it during the sometimes slow * bmp_playback_initiate(). */ gdk_pointer_ungrab(GDK_CURRENT_TIME); gdk_flush(); playlist_set_position(nr); if (!bmp_playback_get_playing()) bmp_playback_initiate(); } pl->pl_dragging = TRUE; playlistwin_update_list(); } } gint playlist_list_get_playlist_position(PlayList_List * pl, gint x, gint y) { gint iy, length; if (!widget_contains(WIDGET(pl), x, y) || !pl->pl_fheight) return -1; if ((length = playlist_get_length()) == 0) return -1; iy = y - pl->pl_widget.y; return (MIN((iy / pl->pl_fheight) + pl->pl_first, length - 1)); } static void playlist_list_motion_cb(GtkWidget * widget, GdkEventMotion * event, PlayList_List * pl) { gint nr, y, off, i; if (pl->pl_dragging) { y = event->y - pl->pl_widget.y; nr = (y / pl->pl_fheight); if (nr < 0) { nr = 0; if (!pl->pl_auto_drag_up) { pl->pl_auto_drag_up = TRUE; pl->pl_auto_drag_up_tag = gtk_timeout_add(100, playlist_list_auto_drag_up_func, pl); } } else if (pl->pl_auto_drag_up) pl->pl_auto_drag_up = FALSE; if (nr >= pl->pl_num_visible) { nr = pl->pl_num_visible - 1; if (!pl->pl_auto_drag_down) { pl->pl_auto_drag_down = TRUE; pl->pl_auto_drag_down_tag = gtk_timeout_add(100, playlist_list_auto_drag_down_func, pl); } } else if (pl->pl_auto_drag_down) pl->pl_auto_drag_down = FALSE; off = nr - pl->pl_drag_pos; if (off) { for (i = 0; i < abs(off); i++) { if (off < 0) playlist_list_move_up(pl); else playlist_list_move_down(pl); } playlistwin_update_list(); } pl->pl_drag_pos = nr; } } static void playlist_list_button_release_cb(GtkWidget * widget, GdkEventButton * event, PlayList_List * pl) { pl->pl_dragging = FALSE; pl->pl_auto_drag_down = FALSE; pl->pl_auto_drag_up = FALSE; } static void playlist_list_draw_string(PlayList_List * pl, PangoFontDescription * font, gint line, gint width, const gchar * text, guint ppos) { guint plist_length_int; PangoLayout *layout; REQUIRE_STATIC_LOCK(playlist); if (cfg.show_numbers_in_pl) { gchar *pos_string = g_strdup_printf(cfg.show_separator_in_pl == TRUE ? "%d" : "%d.", ppos); plist_length_int = gint_count_digits(playlist_get_length_nolock()) + !cfg.show_separator_in_pl + 1; /* cf.show_separator_in_pl will be 0 if false */ padding = plist_length_int; padding = ((padding + 1) * width_approx_digits); layout = gtk_widget_create_pango_layout(playlistwin, pos_string); pango_layout_set_font_description(layout, playlist_list_font); pango_layout_set_width(layout, plist_length_int * 100); pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT); gdk_draw_layout(pl->pl_widget.parent, pl->pl_widget.gc, pl->pl_widget.x + (width_approx_digits * (-1 + plist_length_int - strlen(pos_string))) + (width_approx_digits / 4), pl->pl_widget.y + (line - 1) * pl->pl_fheight + ascent + abs(descent), layout); g_free(pos_string); g_object_unref(layout); if (!cfg.show_separator_in_pl) padding -= (width_approx_digits * 1.5); } else { padding = 3; } width -= padding; layout = gtk_widget_create_pango_layout(playlistwin, text); pango_layout_set_font_description(layout, playlist_list_font); pango_layout_set_width(layout, width * PANGO_SCALE); pango_layout_set_single_paragraph_mode(layout, TRUE); pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); gdk_draw_layout(pl->pl_widget.parent, pl->pl_widget.gc, pl->pl_widget.x + padding + (width_approx_letters / 4), pl->pl_widget.y + (line - 1) * pl->pl_fheight + ascent + abs(descent), layout); g_object_unref(layout); } void playlist_list_draw(Widget * w) { PlayList_List *pl = PLAYLIST_LIST(w); GList *list; GdkGC *gc; GdkPixmap *obj; PangoLayout *layout; gchar *title; gint width, height; gint i, max_first; guint padding, padding_dwidth, padding_plength; guint max_time_len = 0; gfloat queue_tailpadding = 0; gint tpadding; gsize tpadding_dwidth = 0; gint x, y; guint tail_width; guint tail_len; gchar tail[100]; gchar queuepos[255]; gchar length[40]; gchar **frags; gchar *frag0; gint plw_w, plw_h; GdkRectangle *playlist_rect; gc = pl->pl_widget.gc; width = pl->pl_widget.width; height = pl->pl_widget.height; obj = pl->pl_widget.parent; gtk_window_get_size(GTK_WINDOW(playlistwin), &plw_w, &plw_h); playlist_rect = g_new0(GdkRectangle, 1); playlist_rect->x = 0; playlist_rect->y = 0; playlist_rect->width = plw_w - 17; playlist_rect->height = plw_h - 36; gdk_gc_set_clip_origin(gc, 31, 58); gdk_gc_set_clip_rectangle(gc, playlist_rect); if (cfg.playlist_transparent == FALSE) { gdk_gc_set_foreground(gc, skin_get_color(bmp_active_skin, SKIN_PLEDIT_NORMALBG)); gdk_draw_rectangle(obj, gc, TRUE, pl->pl_widget.x, pl->pl_widget.y, width, height); } else { if (!rootpix) rootpix = shade_pixmap(get_transparency_pixmap(), 0, 0, 0, 0, gdk_screen_width(), gdk_screen_height(), skin_get_color(bmp_active_skin, SKIN_PLEDIT_NORMALBG)); gdk_draw_pixmap(obj, gc, rootpix, cfg.playlist_x + pl->pl_widget.x, cfg.playlist_y + pl->pl_widget.y, pl->pl_widget.x, pl->pl_widget.y, width, height); } if (!playlist_list_font) { g_critical("Couldn't open playlist font"); return; } pl->pl_fheight = (ascent + abs(descent)); pl->pl_num_visible = height / pl->pl_fheight; max_first = playlist_get_length() - pl->pl_num_visible; max_first = MAX(max_first, 0); pl->pl_first = CLAMP(pl->pl_first, 0, max_first); PLAYLIST_LOCK(); list = playlist_get(); list = g_list_nth(list, pl->pl_first); /* It sucks having to run the iteration twice but this is the only way you can reliably get the maximum width so we can get our playlist nice and aligned... -- plasmaroo */ for (i = pl->pl_first; list && i < pl->pl_first + pl->pl_num_visible; list = g_list_next(list), i++) { PlaylistEntry *entry = list->data; if (entry->length != -1) { g_snprintf(length, sizeof(length), "%d:%-2.2d", entry->length / 60000, (entry->length / 1000) % 60); tpadding_dwidth = MAX(tpadding_dwidth, strlen(length)); } } /* Reset */ list = playlist_get(); list = g_list_nth(list, pl->pl_first); for (i = pl->pl_first; list && i < pl->pl_first + pl->pl_num_visible; list = g_list_next(list), i++) { gint pos; PlaylistEntry *entry = list->data; if (entry->selected) { gdk_gc_set_foreground(gc, skin_get_color(bmp_active_skin, SKIN_PLEDIT_SELECTEDBG)); gdk_draw_rectangle(obj, gc, TRUE, pl->pl_widget.x, pl->pl_widget.y + ((i - pl->pl_first) * pl->pl_fheight), width, pl->pl_fheight); } /* FIXME: entry->title should NEVER be NULL, and there should NEVER be a need to do a UTF-8 conversion. Playlist title strings should be kept properly. */ if (!entry->title) { gchar *basename = g_path_get_basename(entry->filename); title = filename_to_utf8(basename); g_free(basename); } else title = str_to_utf8(entry->title); pos = playlist_get_queue_position(entry); tail[0] = 0; queuepos[0] = 0; length[0] = 0; if (pos != -1) g_snprintf(queuepos, sizeof(queuepos), "%d", pos + 1); if (entry->length != -1) { g_snprintf(length, sizeof(length), "%d:%-2.2d", entry->length / 60000, (entry->length / 1000) % 60); } strncat(tail, length, sizeof(tail)); tail_len = strlen(tail); max_time_len = MAX(max_time_len, tail_len); if (pos != -1 && tpadding_dwidth <= 0) tail_width = width - (width_approx_digits * (strlen(queuepos) + 2.25)); else if (pos != -1) tail_width = width - (width_approx_digits * (tpadding_dwidth + strlen(queuepos) + 4)); else if (tpadding_dwidth > 0) tail_width = width - (width_approx_digits * (tpadding_dwidth + 2.5)); else tail_width = width; if (i == playlist_get_position_nolock()) gdk_gc_set_foreground(gc, skin_get_color(bmp_active_skin, SKIN_PLEDIT_CURRENT)); else gdk_gc_set_foreground(gc, skin_get_color(bmp_active_skin, SKIN_PLEDIT_NORMAL)); playlist_list_draw_string(pl, playlist_list_font, i - pl->pl_first, tail_width, title, i + 1); x = pl->pl_widget.x + width - width_approx_digits * 2; y = pl->pl_widget.y + ((i - pl->pl_first) - 1) * pl->pl_fheight + ascent; frags = NULL; frag0 = NULL; if ((strlen(tail) > 0) && (tail != NULL)) { frags = g_strsplit(tail, ":", 0); frag0 = g_strconcat(frags[0], ":", NULL); layout = gtk_widget_create_pango_layout(playlistwin, frags[1]); pango_layout_set_font_description(layout, playlist_list_font); pango_layout_set_width(layout, tail_len * 100); pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT); gdk_draw_layout(obj, gc, x - (0.5 * width_approx_digits), y + abs(descent), layout); g_object_unref(layout); layout = gtk_widget_create_pango_layout(playlistwin, frag0); pango_layout_set_font_description(layout, playlist_list_font); pango_layout_set_width(layout, tail_len * 100); pango_layout_set_alignment(layout, PANGO_ALIGN_RIGHT); gdk_draw_layout(obj, gc, x - (0.75 * width_approx_digits), y + abs(descent), layout); g_object_unref(layout); g_free(frag0); g_strfreev(frags); } if (pos != -1) { /* DON'T remove the commented code yet please -- Milosz */ if (tpadding_dwidth > 0) queue_tailpadding = tpadding_dwidth + 1; else queue_tailpadding = -0.75; gdk_draw_rectangle(obj, gc, FALSE, x - (((queue_tailpadding + strlen(queuepos)) * width_approx_digits) + (width_approx_digits / 4)), y + abs(descent), (strlen(queuepos)) * width_approx_digits + (width_approx_digits / 2), pl->pl_fheight - 2); layout = gtk_widget_create_pango_layout(playlistwin, queuepos); pango_layout_set_font_description(layout, playlist_list_font); pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER); gdk_draw_layout(obj, gc, x - ((queue_tailpadding + strlen(queuepos)) * width_approx_digits) + (width_approx_digits / 4), y + abs(descent), layout); g_object_unref(layout); } g_free(title); } /* * Drop target hovering over the playlist, so draw some hint where the * drop will occur. * * This is (currently? unfixably?) broken when dragging files from Qt/KDE apps, * probably due to DnD signaling problems (actually i have no clue). * */ if (pl->pl_drag_motion) { guint pos, plength, lpadding; gint x, y, plx, ply; if (cfg.show_numbers_in_pl) { lpadding = gint_count_digits(playlist_get_length_nolock()) + 1; lpadding = ((lpadding + 1) * width_approx_digits); } else { lpadding = 3; }; /* We already hold the mutex and have the playlist locked, so call the non-locking function. */ plength = playlist_get_length_nolock(); x = pl->drag_motion_x; y = pl->drag_motion_y; plx = pl->pl_widget.x; ply = pl->pl_widget.y; if ((x > pl->pl_widget.x) && !(x > pl->pl_widget.width)) { if ((y > pl->pl_widget.y) && !(y > (pl->pl_widget.height + ply))) { pos = ((y - ((Widget *) pl)->y) / pl->pl_fheight) + pl->pl_first; if (pos > (plength)) { pos = plength; } gdk_gc_set_foreground(gc, skin_get_color(bmp_active_skin, SKIN_PLEDIT_CURRENT)); gdk_draw_line(obj, gc, pl->pl_widget.x, pl->pl_widget.y + ((pos - pl->pl_first) * pl->pl_fheight), pl->pl_widget.width + pl->pl_widget.x - 1, pl->pl_widget.y + ((pos - pl->pl_first) * pl->pl_fheight)); } } /* When dropping on the borders of the playlist, outside the text area, * files get appended at the end of the list. Show that too. */ if ((y < ply) || (y > pl->pl_widget.height + ply)) { if ((y >= 0) || (y <= (pl->pl_widget.height + ply))) { pos = plength; gdk_gc_set_foreground(gc, skin_get_color(bmp_active_skin, SKIN_PLEDIT_CURRENT)); gdk_draw_line(obj, gc, pl->pl_widget.x, pl->pl_widget.y + ((pos - pl->pl_first) * pl->pl_fheight), pl->pl_widget.width + pl->pl_widget.x - 1, pl->pl_widget.y + ((pos - pl->pl_first) * pl->pl_fheight)); } } } gdk_gc_set_foreground(gc, skin_get_color(bmp_active_skin, SKIN_PLEDIT_NORMAL)); if (cfg.show_numbers_in_pl) { padding_plength = playlist_get_length_nolock(); if (padding_plength == 0) { padding_dwidth = 0; } else { padding_dwidth = gint_count_digits(playlist_get_length_nolock()); } padding = (padding_dwidth * width_approx_digits) + width_approx_digits; /* For italic or oblique fonts we add another half of the * approximate width */ if (has_slant) padding += width_approx_digits_half; if (cfg.show_separator_in_pl) { gdk_draw_line(obj, gc, pl->pl_widget.x + padding, pl->pl_widget.y, pl->pl_widget.x + padding, pl->pl_widget.y + pl->pl_widget.height - 1); } } if (tpadding_dwidth != 0) { tpadding = (tpadding_dwidth * width_approx_digits) + (width_approx_digits * 1.5); if (has_slant) tpadding += width_approx_digits_half; if (cfg.show_separator_in_pl) { gdk_draw_line(obj, gc, pl->pl_widget.x + pl->pl_widget.width - tpadding, pl->pl_widget.y, pl->pl_widget.x + pl->pl_widget.width - tpadding, pl->pl_widget.y + pl->pl_widget.height - 1); } } gdk_gc_set_clip_origin(gc, 0, 0); gdk_gc_set_clip_rectangle(gc, NULL); PLAYLIST_UNLOCK(); gdk_flush(); g_free(playlist_rect); } PlayList_List * create_playlist_list(GList ** wlist, GdkPixmap * parent, GdkGC * gc, gint x, gint y, gint w, gint h) { PlayList_List *pl; pl = g_new0(PlayList_List, 1); widget_init(&pl->pl_widget, parent, gc, x, y, w, h, TRUE); pl->pl_widget.button_press_cb = (WidgetButtonPressFunc) playlist_list_button_press_cb; pl->pl_widget.button_release_cb = (WidgetButtonReleaseFunc) playlist_list_button_release_cb; pl->pl_widget.motion_cb = (WidgetMotionFunc) playlist_list_motion_cb; pl->pl_widget.draw = playlist_list_draw; pl->pl_prev_selected = -1; pl->pl_prev_min = -1; pl->pl_prev_max = -1; widget_list_add(wlist, WIDGET(pl)); #ifdef GDK_WINDOWING_X11 gdk_window_set_events (gdk_get_default_root_window(), GDK_PROPERTY_CHANGE_MASK); gdk_window_add_filter (gdk_get_default_root_window(), (GdkFilterFunc)root_event_cb, pl); #endif return pl; } void playlist_list_set_font(const gchar * font) { /* Welcome to bad hack central 2k3 */ gchar *font_lower; gint width_temp; gint width_temp_0; playlist_list_font = pango_font_description_from_string(font); text_get_extents(font, "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz ", &width_approx_letters, NULL, &ascent, &descent); width_approx_letters = (width_approx_letters / 53); /* Experimental: We don't weigh the 1 into total because it's width is almost always * very different from the rest */ text_get_extents(font, "023456789", &width_approx_digits, NULL, NULL, NULL); width_approx_digits = (width_approx_digits / 9); /* Precache some often used calculations */ width_approx_digits_half = width_approx_digits / 2; /* FIXME: We assume that any other number is broader than the "1" */ text_get_extents(font, "1", &width_temp, NULL, NULL, NULL); text_get_extents(font, "2", &width_temp_0, NULL, NULL, NULL); if (abs(width_temp_0 - width_temp) < 2) { width_delta_digit_one = 0; } else { width_delta_digit_one = ((width_temp_0 - width_temp) / 2) + 2; } text_get_extents(font, ":", &width_colon, NULL, NULL, NULL); width_colon_third = width_colon / 4; font_lower = g_utf8_strdown(font, strlen(font)); /* This doesn't take any i18n into account, but i think there is none with TTF fonts * FIXME: This can probably be retrieved trough Pango too */ has_slant = g_strstr_len(font_lower, strlen(font_lower), "oblique") || g_strstr_len(font_lower, strlen(font_lower), "italic"); g_free(font_lower); }