diff src/audacious/widgets/textbox.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 ad1d7687814c
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/audacious/widgets/textbox.c	Fri Jan 12 11:43:40 2007 -0800
@@ -0,0 +1,586 @@
+/*  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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "widgetcore.h"
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkprivate.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "main.h"
+#include "util.h"
+
+static void textbox_generate_pixmap(TextBox * tb);
+
+static void
+textbox_draw(Widget * w)
+{
+    TextBox *tb = TEXT_BOX(w);
+    gint cw;
+    GdkPixmap *obj;
+    GdkPixmap *src;
+
+    g_return_if_fail(tb != NULL);
+    g_return_if_fail(tb->tb_widget.visible != FALSE);
+
+    if (tb->tb_text &&
+        (!tb->tb_pixmap_text || strcmp(tb->tb_text, tb->tb_pixmap_text)))
+        textbox_generate_pixmap(tb);
+
+    if (tb->tb_pixmap) {
+        if (skin_get_id() != tb->tb_skin_id) {
+            tb->tb_skin_id = skin_get_id();
+            textbox_generate_pixmap(tb);
+        }
+        obj = tb->tb_widget.parent;
+        src = tb->tb_pixmap;
+
+        cw = tb->tb_pixmap_width - tb->tb_offset;
+        if (cw > tb->tb_widget.width)
+            cw = tb->tb_widget.width;
+        gdk_draw_drawable(obj, tb->tb_widget.gc, src, tb->tb_offset, 0,
+                          tb->tb_widget.x, tb->tb_widget.y, cw,
+                          tb->tb_widget.height);
+        if (cw < tb->tb_widget.width)
+            gdk_draw_drawable(obj, tb->tb_widget.gc, src, 0, 0,
+                              tb->tb_widget.x + cw, tb->tb_widget.y,
+                              tb->tb_widget.width - cw, tb->tb_widget.height);
+    }
+}
+
+static gboolean
+textbox_scroll(gpointer data)
+{
+    TextBox *tb = TEXT_BOX(data);
+
+    if (!tb->tb_is_dragging) {
+        tb->tb_offset += 1;
+        if (tb->tb_offset >= tb->tb_pixmap_width)
+            tb->tb_offset -= tb->tb_pixmap_width;
+        widget_draw(WIDGET(tb));
+    }
+
+    return TRUE;
+}
+
+static void
+textbox_button_press(GtkWidget * w, GdkEventButton * event, gpointer data)
+{
+    TextBox *tb = TEXT_BOX(data);
+
+    if (event->button != 1)
+        return;
+    if (widget_contains(&tb->tb_widget, event->x, event->y) &&
+        tb->tb_scroll_allowed &&
+        tb->tb_pixmap_width > tb->tb_widget.width && tb->tb_is_scrollable) {
+        tb->tb_is_dragging = TRUE;
+        tb->tb_drag_off = tb->tb_offset;
+        tb->tb_drag_x = event->x;
+    }
+}
+
+static void
+textbox_motion(GtkWidget * w, GdkEventMotion * event, gpointer data)
+{
+    TextBox *tb = TEXT_BOX(data);
+
+    if (tb->tb_is_dragging) {
+        if (tb->tb_scroll_allowed &&
+            tb->tb_pixmap_width > tb->tb_widget.width) {
+            tb->tb_offset = tb->tb_drag_off - (event->x - tb->tb_drag_x);
+
+            while (tb->tb_offset < 0)
+                tb->tb_offset += tb->tb_pixmap_width;
+
+            while (tb->tb_offset > tb->tb_pixmap_width)
+                tb->tb_offset -= tb->tb_pixmap_width;
+
+            widget_draw(WIDGET(tb));
+        }
+    }
+}
+
+static void
+textbox_button_release(GtkWidget * w, GdkEventButton * event, gpointer data)
+{
+    TextBox *tb = TEXT_BOX(data);
+
+    if (event->button == 1)
+        tb->tb_is_dragging = FALSE;
+}
+
+static gboolean
+textbox_should_scroll(TextBox * tb)
+{
+    g_return_val_if_fail(tb != NULL, FALSE);
+
+    if (!tb->tb_scroll_allowed)
+        return FALSE;
+
+    if (tb->tb_font) {
+        gint width;
+
+        text_get_extents(tb->tb_fontname, tb->tb_text, &width, NULL, NULL,
+                         NULL);
+
+        if (width <= tb->tb_widget.width)
+            return FALSE;
+        else
+            return TRUE;
+    }
+
+    if (g_utf8_strlen(tb->tb_text, -1) * bmp_active_skin->properties.textbox_bitmap_font_width > tb->tb_widget.width)
+        return TRUE;
+
+    return FALSE;
+}
+
+void
+textbox_set_text(TextBox * tb, const gchar * text)
+{
+    g_return_if_fail(tb != NULL);
+    g_return_if_fail(text != NULL);
+
+    widget_lock(WIDGET(tb));
+
+    if (tb->tb_text) {
+        if (!strcmp(text, tb->tb_text)) {
+            widget_unlock(WIDGET(tb));
+            return;
+        }
+        g_free(tb->tb_text);
+    }
+
+    tb->tb_text = str_to_utf8(text);
+
+    widget_unlock(WIDGET(tb));
+    widget_draw(WIDGET(tb));
+}
+
+static void
+textbox_generate_xfont_pixmap(TextBox * tb, const gchar * pixmaptext)
+{
+    gint length, i;
+    GdkGC *gc, *maskgc;
+    GdkColor *c, pattern;
+    GdkBitmap *mask;
+    PangoLayout *layout;
+    gint width;
+
+    g_return_if_fail(tb != NULL);
+    g_return_if_fail(pixmaptext != NULL);
+
+    length = g_utf8_strlen(pixmaptext, -1);
+
+    text_get_extents(tb->tb_fontname, pixmaptext, &width, NULL, NULL, NULL);
+
+    tb->tb_pixmap_width = MAX(width, tb->tb_widget.width);
+    tb->tb_pixmap = gdk_pixmap_new(mainwin->window, tb->tb_pixmap_width,
+                                   tb->tb_widget.height,
+                                   gdk_rgb_get_visual()->depth);
+    gc = tb->tb_widget.gc;
+    c = skin_get_color(bmp_active_skin, SKIN_TEXTBG);
+    for (i = 0; i < tb->tb_widget.height; i++) {
+        gdk_gc_set_foreground(gc, &c[6 * i / tb->tb_widget.height]);
+        gdk_draw_line(tb->tb_pixmap, gc, 0, i, tb->tb_pixmap_width, i);
+    }
+
+    mask = gdk_pixmap_new(mainwin->window, tb->tb_pixmap_width,
+                          tb->tb_widget.height, 1);
+    maskgc = gdk_gc_new(mask);
+    pattern.pixel = 0;
+    gdk_gc_set_foreground(maskgc, &pattern);
+
+    gdk_draw_rectangle(mask, maskgc, TRUE, 0, 0,
+                       tb->tb_pixmap_width, tb->tb_widget.height);
+    pattern.pixel = 1;
+    gdk_gc_set_foreground(maskgc, &pattern);
+
+    gdk_gc_set_foreground(gc, skin_get_color(bmp_active_skin, SKIN_TEXTFG));
+
+    layout = gtk_widget_create_pango_layout(mainwin, pixmaptext);
+    pango_layout_set_font_description(layout, tb->tb_font);
+
+    gdk_draw_layout(tb->tb_pixmap, gc, 0, (tb->tb_font_descent / 2), layout);
+    g_object_unref(layout);
+
+    g_object_unref(maskgc);
+
+    gdk_gc_set_clip_mask(gc, mask);
+    c = skin_get_color(bmp_active_skin, SKIN_TEXTFG);
+    for (i = 0; i < tb->tb_widget.height; i++) {
+        gdk_gc_set_foreground(gc, &c[6 * i / tb->tb_widget.height]);
+        gdk_draw_line(tb->tb_pixmap, gc, 0, i, tb->tb_pixmap_width, i);
+    }
+    g_object_unref(mask);
+    gdk_gc_set_clip_mask(gc, NULL);
+}
+
+static void
+textbox_handle_special_char(gchar c, gint * x, gint * y)
+{
+    gint tx, ty;
+
+    switch (c) {
+    case '"':
+        tx = 26;
+        ty = 0;
+        break;
+    case '\r':
+        tx = 10;
+        ty = 1;
+        break;
+    case ':':
+    case ';':
+        tx = 12;
+        ty = 1;
+        break;
+    case '(':
+        tx = 13;
+        ty = 1;
+        break;
+    case ')':
+        tx = 14;
+        ty = 1;
+        break;
+    case '-':
+        tx = 15;
+        ty = 1;
+        break;
+    case '`':
+    case '\'':
+        tx = 16;
+        ty = 1;
+        break;
+    case '!':
+        tx = 17;
+        ty = 1;
+        break;
+    case '_':
+        tx = 18;
+        ty = 1;
+        break;
+    case '+':
+        tx = 19;
+        ty = 1;
+        break;
+    case '\\':
+        tx = 20;
+        ty = 1;
+        break;
+    case '/':
+        tx = 21;
+        ty = 1;
+        break;
+    case '[':
+        tx = 22;
+        ty = 1;
+        break;
+    case ']':
+        tx = 23;
+        ty = 1;
+        break;
+    case '^':
+        tx = 24;
+        ty = 1;
+        break;
+    case '&':
+        tx = 25;
+        ty = 1;
+        break;
+    case '%':
+        tx = 26;
+        ty = 1;
+        break;
+    case '.':
+    case ',':
+        tx = 27;
+        ty = 1;
+        break;
+    case '=':
+        tx = 28;
+        ty = 1;
+        break;
+    case '$':
+        tx = 29;
+        ty = 1;
+        break;
+    case '#':
+        tx = 30;
+        ty = 1;
+        break;
+    case 'å':
+    case 'Å':
+        tx = 0;
+        ty = 2;
+        break;
+    case 'ö':
+    case 'Ö':
+        tx = 1;
+        ty = 2;
+        break;
+    case 'ä':
+    case 'Ä':
+        tx = 2;
+        ty = 2;
+        break;
+    case 'ü':
+    case 'Ü':
+        tx = 20;
+        ty = 0;
+        break;
+    case '?':
+        tx = 3;
+        ty = 2;
+        break;
+    case '*':
+        tx = 4;
+        ty = 2;
+        break;
+    default:
+        tx = 29;
+        ty = 0;
+        break;
+    }
+
+    *x = tx * bmp_active_skin->properties.textbox_bitmap_font_width;
+    *y = ty * bmp_active_skin->properties.textbox_bitmap_font_height;
+}
+
+static void
+textbox_generate_pixmap(TextBox * tb)
+{
+    gint length, i, x, y, wl;
+    gchar *pixmaptext;
+    GdkGC *gc;
+
+    g_return_if_fail(tb != NULL);
+
+    if (tb->tb_pixmap) {
+        g_object_unref(tb->tb_pixmap);
+        tb->tb_pixmap = NULL;
+    }
+
+    /*
+     * Don't reset the offset if only text after the last '(' has
+     * changed.  This is a hack to avoid visual noice on vbr files
+     * where we guess the length.
+     */
+    if (!(tb->tb_pixmap_text && strrchr(tb->tb_text, '(') &&
+          !strncmp(tb->tb_pixmap_text, tb->tb_text,
+                   strrchr(tb->tb_text, '(') - tb->tb_text)))
+        tb->tb_offset = 0;
+
+    g_free(tb->tb_pixmap_text);
+    tb->tb_pixmap_text = g_strdup(tb->tb_text);
+
+    /*
+     * wl is the number of (partial) letters visible. Only makes
+     * sense when using skinned font.
+     */
+
+    wl = tb->tb_widget.width / 5;
+    if (wl * 5 != tb->tb_widget.width)
+        wl++;
+
+    length = g_utf8_strlen(tb->tb_text, -1);
+
+    tb->tb_is_scrollable = FALSE;
+
+    if (textbox_should_scroll(tb)) {
+        tb->tb_is_scrollable = TRUE;
+        pixmaptext = g_strconcat(tb->tb_pixmap_text, "  ***  ", NULL);
+        length += 7;
+    }
+    else if (!tb->tb_font && length <= wl) {
+        gint pad = wl - length;
+        gchar *padchars = g_strnfill(pad, ' ');
+
+        pixmaptext = g_strconcat(tb->tb_pixmap_text, padchars, NULL);
+        g_free(padchars);
+        length += pad;
+    }
+    else
+        pixmaptext = g_strdup(tb->tb_pixmap_text);
+
+
+    if (tb->tb_is_scrollable) {
+        if (tb->tb_scroll_enabled && !tb->tb_timeout_tag) {
+            gint tag;
+            tag = TEXTBOX_SCROLL_SMOOTH_TIMEOUT;
+            tb->tb_timeout_tag = g_timeout_add(tag, textbox_scroll, tb);
+        }
+    }
+    else {
+        if (tb->tb_timeout_tag) {
+            g_source_remove(tb->tb_timeout_tag);
+            tb->tb_timeout_tag = 0;
+        }
+        tb->tb_offset = 0;
+    }
+
+    if (tb->tb_font) {
+        textbox_generate_xfont_pixmap(tb, pixmaptext);
+        g_free(pixmaptext);
+        return;
+    }
+
+    tb->tb_pixmap_width = length * bmp_active_skin->properties.textbox_bitmap_font_width;
+    tb->tb_pixmap = gdk_pixmap_new(mainwin->window,
+                                   tb->tb_pixmap_width, bmp_active_skin->properties.textbox_bitmap_font_height,
+                                   gdk_rgb_get_visual()->depth);
+    gc = tb->tb_widget.gc;
+
+    for (i = 0; i < length; i++) {
+        gchar c;
+        x = y = -1;
+        c = toupper((int) pixmaptext[i]);
+        if (c >= 'A' && c <= 'Z') {
+            x = bmp_active_skin->properties.textbox_bitmap_font_width * (c - 'A');
+            y = 0;
+        }
+        else if (c >= '0' && c <= '9') {
+            x = bmp_active_skin->properties.textbox_bitmap_font_width * (c - '0');
+            y = bmp_active_skin->properties.textbox_bitmap_font_height;
+        }
+        else
+            textbox_handle_special_char(c, &x, &y);
+
+        skin_draw_pixmap(bmp_active_skin,
+                         tb->tb_pixmap, gc, tb->tb_skin_index,
+                         x, y, i * bmp_active_skin->properties.textbox_bitmap_font_width, 0, bmp_active_skin->properties.textbox_bitmap_font_width, 
+			 bmp_active_skin->properties.textbox_bitmap_font_height);
+    }
+    g_free(pixmaptext);
+}
+
+void
+textbox_set_scroll(TextBox * tb, gboolean s)
+{
+    g_return_if_fail(tb != NULL);
+
+    tb->tb_scroll_enabled = s;
+    if (tb->tb_scroll_enabled && tb->tb_is_scrollable
+        && tb->tb_scroll_allowed) {
+        gint tag;
+        tag = TEXTBOX_SCROLL_SMOOTH_TIMEOUT;
+
+	if (tb->tb_timeout_tag)
+        {
+	    g_source_remove(tb->tb_timeout_tag);
+            tb->tb_timeout_tag = 0;
+	}
+
+        tb->tb_timeout_tag = g_timeout_add(tag, textbox_scroll, tb);
+    }
+    else
+    {
+        if (tb->tb_timeout_tag)
+        {
+            g_source_remove(tb->tb_timeout_tag);
+            tb->tb_timeout_tag = 0;
+        }
+
+        tb->tb_offset = 0;
+        widget_draw(WIDGET(tb));
+    }
+
+}
+
+void
+textbox_set_xfont(TextBox * tb, gboolean use_xfont, const gchar * fontname)
+{
+    gint ascent, descent;
+
+    g_return_if_fail(tb != NULL);
+
+    if (tb->tb_font) {
+        pango_font_description_free(tb->tb_font);
+        tb->tb_font = NULL;
+    }
+
+    tb->tb_widget.y = tb->tb_nominal_y;
+    tb->tb_widget.height = tb->tb_nominal_height;
+
+    /* Make sure the pixmap is regenerated */
+    if (tb->tb_pixmap_text) {
+        g_free(tb->tb_pixmap_text);
+        tb->tb_pixmap_text = NULL;
+    }
+
+    if (!use_xfont || strlen(fontname) == 0)
+        return;
+
+    tb->tb_font = pango_font_description_from_string(fontname);
+    tb->tb_fontname = g_strdup(fontname);
+
+    text_get_extents(fontname,
+                     "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz ",
+                     NULL, NULL, &ascent, &descent);
+    tb->tb_font_ascent = ascent;
+    tb->tb_font_descent = descent;
+
+
+    if (tb->tb_font == NULL)
+        return;
+
+    tb->tb_widget.height = tb->tb_font_ascent;
+    if (tb->tb_widget.height > tb->tb_nominal_height)
+        tb->tb_widget.y -= (tb->tb_widget.height - tb->tb_nominal_height) / 2;
+    else
+        tb->tb_widget.height = tb->tb_nominal_height;
+}
+
+TextBox *
+create_textbox(GList ** wlist, GdkPixmap * parent, GdkGC * gc,
+               gint x, gint y, gint w, gboolean allow_scroll, SkinPixmapId si)
+{
+    TextBox *tb;
+
+    tb = g_new0(TextBox, 1);
+    widget_init(&tb->tb_widget, parent, gc, x, y, w, bmp_active_skin->properties.textbox_bitmap_font_height, 1);
+    tb->tb_widget.button_press_cb = textbox_button_press;
+    tb->tb_widget.button_release_cb = textbox_button_release;
+    tb->tb_widget.motion_cb = textbox_motion;
+    tb->tb_widget.draw = textbox_draw;
+    tb->tb_scroll_allowed = allow_scroll;
+    tb->tb_scroll_enabled = TRUE;
+    tb->tb_skin_index = si;
+    tb->tb_nominal_y = y;
+    tb->tb_nominal_height = tb->tb_widget.height;
+    widget_list_add(wlist, WIDGET(tb));
+    tb->tb_timeout_tag = 0;
+    return tb;
+}
+
+void
+textbox_free(TextBox * tb)
+{
+    g_return_if_fail(tb != NULL);
+
+    if (tb->tb_pixmap)
+        g_object_unref(tb->tb_pixmap);
+    g_free(tb->tb_text);
+    g_free(tb);
+}