Re-implement Cairo-based rendering.
This commit is contained in:
		@ -1,466 +0,0 @@
 | 
				
			|||||||
/* 
 | 
					 | 
				
			||||||
 * Copyright (C) 2006 Sergey V. Udaltsov <svu@gnome.org>
 | 
					 | 
				
			||||||
 * Copyright (C) 2010 Daiki Ueno <ueno@unixuser.org>
 | 
					 | 
				
			||||||
 * Copyright (C) 2010 Red Hat, Inc.
 | 
					 | 
				
			||||||
 * 
 | 
					 | 
				
			||||||
 * This library is free software; you can redistribute it and/or
 | 
					 | 
				
			||||||
 * modify it under the terms of the GNU Lesser General Public License
 | 
					 | 
				
			||||||
 * as published by the Free Software Foundation; either version 2 of
 | 
					 | 
				
			||||||
 * the License, or (at your option) any later version.
 | 
					 | 
				
			||||||
 * 
 | 
					 | 
				
			||||||
 * This library 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
 | 
					 | 
				
			||||||
 * Lesser General Public License for more details.
 | 
					 | 
				
			||||||
 * 
 | 
					 | 
				
			||||||
 * You should have received a copy of the GNU Lesser General Public
 | 
					 | 
				
			||||||
 * License along with this library; if not, write to the Free Software
 | 
					 | 
				
			||||||
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 | 
					 | 
				
			||||||
 * 02110-1301 USA
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <math.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef HAVE_CONFIG_H
 | 
					 | 
				
			||||||
#include "config.h"
 | 
					 | 
				
			||||||
#endif  /* HAVE_CONFIG_H */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include "eek-keyboard.h"
 | 
					 | 
				
			||||||
#include "eek-key.h"
 | 
					 | 
				
			||||||
#include "eek-drawing.h"
 | 
					 | 
				
			||||||
#include "eek-keysym.h"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
eek_draw_text_on_layout (PangoLayout *layout,
 | 
					 | 
				
			||||||
                         const gchar *text)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    /* pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER); */
 | 
					 | 
				
			||||||
    pango_layout_set_text (layout, text, -1);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct _GetFontSizeCallbackData
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    PangoLayout *layout;
 | 
					 | 
				
			||||||
    EekKeysymCategory category;
 | 
					 | 
				
			||||||
    gint minimum_font_size;
 | 
					 | 
				
			||||||
    gint font_size;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
typedef struct _GetFontSizeCallbackData GetFontSizeCallbackData;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static gint
 | 
					 | 
				
			||||||
get_font_size (const gchar *text,
 | 
					 | 
				
			||||||
               EekBounds   *bounds,
 | 
					 | 
				
			||||||
               PangoLayout *layout)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    gdouble scale_x, scale_y;
 | 
					 | 
				
			||||||
    const PangoFontDescription *base_font_desc;
 | 
					 | 
				
			||||||
    PangoFontDescription *font_desc;
 | 
					 | 
				
			||||||
    PangoRectangle logical_rect = { 0, };
 | 
					 | 
				
			||||||
    gint font_size;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    layout = pango_layout_copy (layout);
 | 
					 | 
				
			||||||
    base_font_desc = pango_layout_get_font_description (layout);
 | 
					 | 
				
			||||||
    font_desc = pango_font_description_copy (base_font_desc);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    font_size = eek_bounds_long_side (bounds) * PANGO_SCALE;
 | 
					 | 
				
			||||||
    pango_font_description_set_size (font_desc, font_size);
 | 
					 | 
				
			||||||
    pango_layout_set_font_description (layout, font_desc);
 | 
					 | 
				
			||||||
    pango_font_description_free (font_desc);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    eek_draw_text_on_layout (layout, text);
 | 
					 | 
				
			||||||
    pango_layout_get_extents (layout, NULL, &logical_rect);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    scale_x = scale_y = 1.0;
 | 
					 | 
				
			||||||
    if (logical_rect.width > bounds->width * PANGO_SCALE)
 | 
					 | 
				
			||||||
        scale_x = bounds->width * PANGO_SCALE / logical_rect.width;
 | 
					 | 
				
			||||||
    if (logical_rect.height > bounds->height * PANGO_SCALE)
 | 
					 | 
				
			||||||
        scale_y = bounds->height * PANGO_SCALE / logical_rect.height;
 | 
					 | 
				
			||||||
    g_object_unref (layout);
 | 
					 | 
				
			||||||
    return font_size * (scale_x < scale_y ? scale_x : scale_y);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void
 | 
					 | 
				
			||||||
egf_key_callback (EekElement *element,
 | 
					 | 
				
			||||||
                  gpointer user_data)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    EekKey *key = EEK_KEY(element);
 | 
					 | 
				
			||||||
    GetFontSizeCallbackData *data = user_data;
 | 
					 | 
				
			||||||
    gdouble font_size;
 | 
					 | 
				
			||||||
    guint keysym;
 | 
					 | 
				
			||||||
    EekBounds bounds;
 | 
					 | 
				
			||||||
    gchar *label;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    keysym = eek_key_get_keysym (key);
 | 
					 | 
				
			||||||
    if (keysym == EEK_INVALID_KEYSYM ||
 | 
					 | 
				
			||||||
        eek_keysym_get_category (keysym) != data->category)
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    eek_element_get_bounds (EEK_ELEMENT(key), &bounds);
 | 
					 | 
				
			||||||
    label = eek_keysym_to_string (keysym);
 | 
					 | 
				
			||||||
    if (!label)
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    font_size = get_font_size (label, &bounds, data->layout);
 | 
					 | 
				
			||||||
    if (font_size < data->font_size && font_size >= data->minimum_font_size)
 | 
					 | 
				
			||||||
        data->font_size = font_size;
 | 
					 | 
				
			||||||
    g_free (label);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void
 | 
					 | 
				
			||||||
egf_section_callback (EekElement *element,
 | 
					 | 
				
			||||||
                      gpointer    user_data)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    eek_container_foreach_child (EEK_CONTAINER(element),
 | 
					 | 
				
			||||||
                                 egf_key_callback,
 | 
					 | 
				
			||||||
                                 user_data);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static PangoFontDescription *
 | 
					 | 
				
			||||||
get_font_for_category (EekKeyboard      *keyboard,
 | 
					 | 
				
			||||||
                       EekKeysymCategory category,
 | 
					 | 
				
			||||||
                       PangoLayout      *layout,
 | 
					 | 
				
			||||||
                       gdouble           minimum_font_size,
 | 
					 | 
				
			||||||
                       gdouble           maximum_font_size)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    GetFontSizeCallbackData data;
 | 
					 | 
				
			||||||
    PangoFontDescription *font_desc;
 | 
					 | 
				
			||||||
    const PangoFontDescription *base_font_desc;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    data.layout = layout;
 | 
					 | 
				
			||||||
    data.category = category;
 | 
					 | 
				
			||||||
    data.minimum_font_size = minimum_font_size;
 | 
					 | 
				
			||||||
    data.font_size = maximum_font_size;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    eek_container_foreach_child (EEK_CONTAINER(keyboard),
 | 
					 | 
				
			||||||
                                 egf_section_callback,
 | 
					 | 
				
			||||||
                                 &data);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    base_font_desc = pango_layout_get_font_description (layout);
 | 
					 | 
				
			||||||
    font_desc = pango_font_description_copy (base_font_desc);
 | 
					 | 
				
			||||||
    pango_font_description_set_size (font_desc, data.font_size);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return font_desc;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
eek_get_fonts (EekKeyboard           *keyboard,
 | 
					 | 
				
			||||||
               PangoLayout           *layout,
 | 
					 | 
				
			||||||
               PangoFontDescription **fonts)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    EekBounds bounds;
 | 
					 | 
				
			||||||
    PangoFontDescription *font_desc;
 | 
					 | 
				
			||||||
    gint font_size;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* font for EEK_KEYSYM_CATEGORY_LETTER */
 | 
					 | 
				
			||||||
    eek_element_get_bounds (EEK_ELEMENT(keyboard), &bounds);
 | 
					 | 
				
			||||||
    font_desc = get_font_for_category (keyboard,
 | 
					 | 
				
			||||||
                                       EEK_KEYSYM_CATEGORY_LETTER,
 | 
					 | 
				
			||||||
                                       layout,
 | 
					 | 
				
			||||||
                                       0,
 | 
					 | 
				
			||||||
                                       eek_bounds_long_side (&bounds) *
 | 
					 | 
				
			||||||
                                       PANGO_SCALE);
 | 
					 | 
				
			||||||
    font_size = pango_font_description_get_size (font_desc);
 | 
					 | 
				
			||||||
    fonts[EEK_KEYSYM_CATEGORY_LETTER] = font_desc;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* font for EEK_KEYSYM_CATEGORY_FUNCTION */
 | 
					 | 
				
			||||||
    font_desc = get_font_for_category (keyboard,
 | 
					 | 
				
			||||||
                                       EEK_KEYSYM_CATEGORY_FUNCTION,
 | 
					 | 
				
			||||||
                                       layout,
 | 
					 | 
				
			||||||
                                       font_size * 0.3,
 | 
					 | 
				
			||||||
                                       font_size);
 | 
					 | 
				
			||||||
    fonts[EEK_KEYSYM_CATEGORY_FUNCTION] = font_desc;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* font for EEK_KEYSYM_CATEGORY_KEYNAME */
 | 
					 | 
				
			||||||
    font_desc = get_font_for_category (keyboard,
 | 
					 | 
				
			||||||
                                       EEK_KEYSYM_CATEGORY_KEYNAME,
 | 
					 | 
				
			||||||
                                       layout,
 | 
					 | 
				
			||||||
                                       font_size * 0.3,
 | 
					 | 
				
			||||||
                                       font_size);
 | 
					 | 
				
			||||||
    fonts[EEK_KEYSYM_CATEGORY_KEYNAME] = font_desc;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
eek_draw_outline (cairo_t *cr, EekOutline *outline)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    cairo_pattern_t *pat;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    cairo_set_line_width (cr, 1);
 | 
					 | 
				
			||||||
    cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    pat = cairo_pattern_create_linear (0.0, 0.0,  0.0, 256.0);
 | 
					 | 
				
			||||||
    cairo_pattern_add_color_stop_rgba (pat, 1, 0.5, 0.5, 0.5, 1);
 | 
					 | 
				
			||||||
    cairo_pattern_add_color_stop_rgba (pat, 0, 1, 1, 1, 1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    cairo_set_source (cr, pat);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    eek_draw_rounded_polygon (cr,
 | 
					 | 
				
			||||||
                              TRUE,
 | 
					 | 
				
			||||||
                              outline->corner_radius,
 | 
					 | 
				
			||||||
                              outline->points,
 | 
					 | 
				
			||||||
                              outline->num_points);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    cairo_pattern_destroy (pat);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    cairo_set_source_rgba (cr, 0.3, 0.3, 0.3, 0.5);
 | 
					 | 
				
			||||||
    eek_draw_rounded_polygon (cr,
 | 
					 | 
				
			||||||
                              FALSE,
 | 
					 | 
				
			||||||
                              outline->corner_radius,
 | 
					 | 
				
			||||||
                              outline->points,
 | 
					 | 
				
			||||||
                              outline->num_points);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
eek_draw_key_label (cairo_t               *cr,
 | 
					 | 
				
			||||||
                    EekKey                *key,
 | 
					 | 
				
			||||||
                    PangoFontDescription **fonts)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    guint keysym;
 | 
					 | 
				
			||||||
    EekKeysymCategory category;
 | 
					 | 
				
			||||||
    gchar *label;
 | 
					 | 
				
			||||||
    PangoLayout *layout;
 | 
					 | 
				
			||||||
    PangoRectangle logical_rect = { 0, };
 | 
					 | 
				
			||||||
    EekBounds bounds;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    keysym = eek_key_get_keysym (key);
 | 
					 | 
				
			||||||
    if (keysym == EEK_INVALID_KEYSYM)
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    category = eek_keysym_get_category (keysym);
 | 
					 | 
				
			||||||
    if (category == EEK_KEYSYM_CATEGORY_UNKNOWN)
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    label = eek_keysym_to_string (keysym);
 | 
					 | 
				
			||||||
    if (!label)
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    eek_element_get_bounds (EEK_ELEMENT(key), &bounds);
 | 
					 | 
				
			||||||
    layout = pango_cairo_create_layout (cr);
 | 
					 | 
				
			||||||
    pango_layout_set_font_description (layout, fonts[category]);
 | 
					 | 
				
			||||||
    pango_layout_set_width (layout, PANGO_SCALE * bounds.width);
 | 
					 | 
				
			||||||
    pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END);
 | 
					 | 
				
			||||||
    pango_layout_set_text (layout, label, -1);
 | 
					 | 
				
			||||||
    pango_layout_get_extents (layout, NULL, &logical_rect);
 | 
					 | 
				
			||||||
    cairo_rel_move_to (cr,
 | 
					 | 
				
			||||||
                   (bounds.width - logical_rect.width / PANGO_SCALE) / 2,
 | 
					 | 
				
			||||||
                   (bounds.height - logical_rect.height / PANGO_SCALE) / 2);
 | 
					 | 
				
			||||||
    pango_cairo_show_layout (cr, layout);
 | 
					 | 
				
			||||||
    g_free (label);
 | 
					 | 
				
			||||||
    g_object_unref (layout);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * The functions below are borrowed from
 | 
					 | 
				
			||||||
 * libgnomekbd/gkbd-keyboard-drawing.c.
 | 
					 | 
				
			||||||
 * Copyright (C) 2006 Sergey V. Udaltsov <svu@gnome.org>
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * length(), point_line_distance(), normal_form(), inverse(), multiply(),
 | 
					 | 
				
			||||||
 * intersect(), rounded_corner(), draw_rounded_polygon()
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static gdouble
 | 
					 | 
				
			||||||
length (gdouble x, gdouble y)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    return sqrt (x * x + y * y);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static gdouble
 | 
					 | 
				
			||||||
point_line_distance (gdouble ax, gdouble ay, gdouble nx, gdouble ny)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    return ax * nx + ay * ny;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void
 | 
					 | 
				
			||||||
normal_form (gdouble ax, gdouble ay,
 | 
					 | 
				
			||||||
	     gdouble bx, gdouble by,
 | 
					 | 
				
			||||||
	     gdouble * nx, gdouble * ny, gdouble * d)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    gdouble l;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    *nx = by - ay;
 | 
					 | 
				
			||||||
    *ny = ax - bx;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    l = length (*nx, *ny);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    *nx /= l;
 | 
					 | 
				
			||||||
    *ny /= l;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    *d = point_line_distance (ax, ay, *nx, *ny);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void
 | 
					 | 
				
			||||||
inverse (gdouble a, gdouble b, gdouble c, gdouble d,
 | 
					 | 
				
			||||||
	 gdouble * e, gdouble * f, gdouble * g, gdouble * h)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    gdouble det;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    det = a * d - b * c;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    *e = d / det;
 | 
					 | 
				
			||||||
    *f = -b / det;
 | 
					 | 
				
			||||||
    *g = -c / det;
 | 
					 | 
				
			||||||
    *h = a / det;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void
 | 
					 | 
				
			||||||
multiply (gdouble a, gdouble b, gdouble c, gdouble d,
 | 
					 | 
				
			||||||
	  gdouble e, gdouble f, gdouble * x, gdouble * y)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    *x = a * e + b * f;
 | 
					 | 
				
			||||||
    *y = c * e + d * f;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void
 | 
					 | 
				
			||||||
intersect (gdouble n1x, gdouble n1y, gdouble d1,
 | 
					 | 
				
			||||||
	   gdouble n2x, gdouble n2y, gdouble d2, gdouble * x, gdouble * y)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    gdouble e, f, g, h;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    inverse (n1x, n1y, n2x, n2y, &e, &f, &g, &h);
 | 
					 | 
				
			||||||
    multiply (e, f, g, h, d1, d2, x, y);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* draw an angle from the current point to b and then to c,
 | 
					 | 
				
			||||||
 * with a rounded corner of the given radius.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
static void
 | 
					 | 
				
			||||||
rounded_corner (cairo_t * cr,
 | 
					 | 
				
			||||||
		gdouble bx, gdouble by,
 | 
					 | 
				
			||||||
		gdouble cx, gdouble cy, gdouble radius)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    gdouble ax, ay;
 | 
					 | 
				
			||||||
    gdouble n1x, n1y, d1;
 | 
					 | 
				
			||||||
    gdouble n2x, n2y, d2;
 | 
					 | 
				
			||||||
    gdouble pd1, pd2;
 | 
					 | 
				
			||||||
    gdouble ix, iy;
 | 
					 | 
				
			||||||
    gdouble dist1, dist2;
 | 
					 | 
				
			||||||
    gdouble nx, ny, d;
 | 
					 | 
				
			||||||
    gdouble a1x, a1y, c1x, c1y;
 | 
					 | 
				
			||||||
    gdouble phi1, phi2;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    cairo_get_current_point (cr, &ax, &ay);
 | 
					 | 
				
			||||||
#ifdef KBDRAW_DEBUG
 | 
					 | 
				
			||||||
    printf ("        current point: (%f, %f), radius %f:\n", ax, ay,
 | 
					 | 
				
			||||||
            radius);
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* make sure radius is not too large */
 | 
					 | 
				
			||||||
    dist1 = length (bx - ax, by - ay);
 | 
					 | 
				
			||||||
    dist2 = length (cx - bx, cy - by);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    radius = MIN (radius, MIN (dist1, dist2));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* construct normal forms of the lines */
 | 
					 | 
				
			||||||
    normal_form (ax, ay, bx, by, &n1x, &n1y, &d1);
 | 
					 | 
				
			||||||
    normal_form (bx, by, cx, cy, &n2x, &n2y, &d2);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* find which side of the line a,b the point c is on */
 | 
					 | 
				
			||||||
    if (point_line_distance (cx, cy, n1x, n1y) < d1)
 | 
					 | 
				
			||||||
        pd1 = d1 - radius;
 | 
					 | 
				
			||||||
    else
 | 
					 | 
				
			||||||
        pd1 = d1 + radius;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* find which side of the line b,c the point a is on */
 | 
					 | 
				
			||||||
    if (point_line_distance (ax, ay, n2x, n2y) < d2)
 | 
					 | 
				
			||||||
        pd2 = d2 - radius;
 | 
					 | 
				
			||||||
    else
 | 
					 | 
				
			||||||
        pd2 = d2 + radius;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* intersect the parallels to find the center of the arc */
 | 
					 | 
				
			||||||
    intersect (n1x, n1y, pd1, n2x, n2y, pd2, &ix, &iy);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    nx = (bx - ax) / dist1;
 | 
					 | 
				
			||||||
    ny = (by - ay) / dist1;
 | 
					 | 
				
			||||||
    d = point_line_distance (ix, iy, nx, ny);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* a1 is the point on the line a-b where the arc starts */
 | 
					 | 
				
			||||||
    intersect (n1x, n1y, d1, nx, ny, d, &a1x, &a1y);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    nx = (cx - bx) / dist2;
 | 
					 | 
				
			||||||
    ny = (cy - by) / dist2;
 | 
					 | 
				
			||||||
    d = point_line_distance (ix, iy, nx, ny);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* c1 is the point on the line b-c where the arc ends */
 | 
					 | 
				
			||||||
    intersect (n2x, n2y, d2, nx, ny, d, &c1x, &c1y);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* determine the first angle */
 | 
					 | 
				
			||||||
    if (a1x - ix == 0)
 | 
					 | 
				
			||||||
        phi1 = (a1y - iy > 0) ? M_PI_2 : 3 * M_PI_2;
 | 
					 | 
				
			||||||
    else if (a1x - ix > 0)
 | 
					 | 
				
			||||||
        phi1 = atan ((a1y - iy) / (a1x - ix));
 | 
					 | 
				
			||||||
    else
 | 
					 | 
				
			||||||
        phi1 = M_PI + atan ((a1y - iy) / (a1x - ix));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* determine the second angle */
 | 
					 | 
				
			||||||
    if (c1x - ix == 0)
 | 
					 | 
				
			||||||
        phi2 = (c1y - iy > 0) ? M_PI_2 : 3 * M_PI_2;
 | 
					 | 
				
			||||||
    else if (c1x - ix > 0)
 | 
					 | 
				
			||||||
        phi2 = atan ((c1y - iy) / (c1x - ix));
 | 
					 | 
				
			||||||
    else
 | 
					 | 
				
			||||||
        phi2 = M_PI + atan ((c1y - iy) / (c1x - ix));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* compute the difference between phi2 and phi1 mod 2pi */
 | 
					 | 
				
			||||||
    d = phi2 - phi1;
 | 
					 | 
				
			||||||
    while (d < 0)
 | 
					 | 
				
			||||||
        d += 2 * M_PI;
 | 
					 | 
				
			||||||
    while (d > 2 * M_PI)
 | 
					 | 
				
			||||||
        d -= 2 * M_PI;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef KBDRAW_DEBUG
 | 
					 | 
				
			||||||
    printf ("        line 1 to: (%f, %f):\n", a1x, a1y);
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
    if (!(isnan (a1x) || isnan (a1y)))
 | 
					 | 
				
			||||||
        cairo_line_to (cr, a1x, a1y);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /* pick the short arc from phi1 to phi2 */
 | 
					 | 
				
			||||||
    if (d < M_PI)
 | 
					 | 
				
			||||||
        cairo_arc (cr, ix, iy, radius, phi1, phi2);
 | 
					 | 
				
			||||||
    else
 | 
					 | 
				
			||||||
        cairo_arc_negative (cr, ix, iy, radius, phi1, phi2);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef KBDRAW_DEBUG
 | 
					 | 
				
			||||||
    printf ("        line 2 to: (%f, %f):\n", cx, cy);
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
    cairo_line_to (cr, cx, cy);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
eek_draw_rounded_polygon (cairo_t  *cr,
 | 
					 | 
				
			||||||
                          gboolean  filled,
 | 
					 | 
				
			||||||
                          gdouble   radius,
 | 
					 | 
				
			||||||
                          EekPoint *points,
 | 
					 | 
				
			||||||
                          gint      num_points)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    gint i, j;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    cairo_move_to (cr,
 | 
					 | 
				
			||||||
                   (gdouble) (points[num_points - 1].x +
 | 
					 | 
				
			||||||
                              points[0].x) / 2,
 | 
					 | 
				
			||||||
                   (gdouble) (points[num_points - 1].y +
 | 
					 | 
				
			||||||
                              points[0].y) / 2);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#ifdef KBDRAW_DEBUG
 | 
					 | 
				
			||||||
    printf ("    rounded polygon of radius %f:\n", radius);
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
    for (i = 0; i < num_points; i++) {
 | 
					 | 
				
			||||||
        j = (i + 1) % num_points;
 | 
					 | 
				
			||||||
        rounded_corner (cr, (gdouble) points[i].x,
 | 
					 | 
				
			||||||
                        (gdouble) points[i].y,
 | 
					 | 
				
			||||||
                        (gdouble) (points[i].x + points[j].x) / 2,
 | 
					 | 
				
			||||||
                        (gdouble) (points[i].y + points[j].y) / 2,
 | 
					 | 
				
			||||||
                        radius);
 | 
					 | 
				
			||||||
#ifdef KBDRAW_DEBUG
 | 
					 | 
				
			||||||
        printf ("      corner (%d, %d) -> (%d, %d):\n",
 | 
					 | 
				
			||||||
                points[i].x, points[i].y, points[j].x,
 | 
					 | 
				
			||||||
                points[j].y);
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    cairo_close_path (cr);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (filled)
 | 
					 | 
				
			||||||
        cairo_fill (cr);
 | 
					 | 
				
			||||||
    else
 | 
					 | 
				
			||||||
        cairo_stroke (cr);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										230
									
								
								eek/eek-keyboard-drawing.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										230
									
								
								eek/eek-keyboard-drawing.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,230 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (C) 2006 Sergey V. Udaltsov <svu@gnome.org>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This library is free software; you can redistribute it and/or
 | 
				
			||||||
 | 
					 * modify it under the terms of the GNU Lesser General Public
 | 
				
			||||||
 | 
					 * License as published by the Free Software Foundation; either
 | 
				
			||||||
 | 
					 * version 2 of the License, or (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This library 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
 | 
				
			||||||
 | 
					 * Lesser General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU Lesser General Public
 | 
				
			||||||
 | 
					 * License along with this library; if not, write to the
 | 
				
			||||||
 | 
					 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 | 
				
			||||||
 | 
					 * Boston, MA 02111-1307, USA.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <math.h>
 | 
				
			||||||
 | 
					#include <pango/pangocairo.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef HAVE_CONFIG_H
 | 
				
			||||||
 | 
					#include "config.h"
 | 
				
			||||||
 | 
					#endif  /* HAVE_CONFIG_H */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "eek-types.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static gdouble
 | 
				
			||||||
 | 
					length (gdouble x, gdouble y)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    return sqrt (x * x + y * y);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static gdouble
 | 
				
			||||||
 | 
					point_line_distance (gdouble ax, gdouble ay, gdouble nx, gdouble ny)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    return ax * nx + ay * ny;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					normal_form (gdouble ax, gdouble ay,
 | 
				
			||||||
 | 
						     gdouble bx, gdouble by,
 | 
				
			||||||
 | 
						     gdouble * nx, gdouble * ny, gdouble * d)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    gdouble l;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    *nx = by - ay;
 | 
				
			||||||
 | 
					    *ny = ax - bx;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    l = length (*nx, *ny);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    *nx /= l;
 | 
				
			||||||
 | 
					    *ny /= l;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    *d = point_line_distance (ax, ay, *nx, *ny);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					inverse (gdouble a, gdouble b, gdouble c, gdouble d,
 | 
				
			||||||
 | 
						 gdouble * e, gdouble * f, gdouble * g, gdouble * h)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    gdouble det;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    det = a * d - b * c;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    *e = d / det;
 | 
				
			||||||
 | 
					    *f = -b / det;
 | 
				
			||||||
 | 
					    *g = -c / det;
 | 
				
			||||||
 | 
					    *h = a / det;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					multiply (gdouble a, gdouble b, gdouble c, gdouble d,
 | 
				
			||||||
 | 
						  gdouble e, gdouble f, gdouble * x, gdouble * y)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    *x = a * e + b * f;
 | 
				
			||||||
 | 
					    *y = c * e + d * f;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					intersect (gdouble n1x, gdouble n1y, gdouble d1,
 | 
				
			||||||
 | 
						   gdouble n2x, gdouble n2y, gdouble d2, gdouble * x, gdouble * y)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    gdouble e, f, g, h;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    inverse (n1x, n1y, n2x, n2y, &e, &f, &g, &h);
 | 
				
			||||||
 | 
					    multiply (e, f, g, h, d1, d2, x, y);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* draw an angle from the current point to b and then to c,
 | 
				
			||||||
 | 
					 * with a rounded corner of the given radius.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					rounded_corner (cairo_t * cr,
 | 
				
			||||||
 | 
							gdouble bx, gdouble by,
 | 
				
			||||||
 | 
							gdouble cx, gdouble cy, gdouble radius)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    gdouble ax, ay;
 | 
				
			||||||
 | 
					    gdouble n1x, n1y, d1;
 | 
				
			||||||
 | 
					    gdouble n2x, n2y, d2;
 | 
				
			||||||
 | 
					    gdouble pd1, pd2;
 | 
				
			||||||
 | 
					    gdouble ix, iy;
 | 
				
			||||||
 | 
					    gdouble dist1, dist2;
 | 
				
			||||||
 | 
					    gdouble nx, ny, d;
 | 
				
			||||||
 | 
					    gdouble a1x, a1y, c1x, c1y;
 | 
				
			||||||
 | 
					    gdouble phi1, phi2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cairo_get_current_point (cr, &ax, &ay);
 | 
				
			||||||
 | 
					#ifdef KBDRAW_DEBUG
 | 
				
			||||||
 | 
					    printf ("        current point: (%f, %f), radius %f:\n", ax, ay,
 | 
				
			||||||
 | 
					            radius);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* make sure radius is not too large */
 | 
				
			||||||
 | 
					    dist1 = length (bx - ax, by - ay);
 | 
				
			||||||
 | 
					    dist2 = length (cx - bx, cy - by);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    radius = MIN (radius, MIN (dist1, dist2));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* construct normal forms of the lines */
 | 
				
			||||||
 | 
					    normal_form (ax, ay, bx, by, &n1x, &n1y, &d1);
 | 
				
			||||||
 | 
					    normal_form (bx, by, cx, cy, &n2x, &n2y, &d2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* find which side of the line a,b the point c is on */
 | 
				
			||||||
 | 
					    if (point_line_distance (cx, cy, n1x, n1y) < d1)
 | 
				
			||||||
 | 
					        pd1 = d1 - radius;
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        pd1 = d1 + radius;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* find which side of the line b,c the point a is on */
 | 
				
			||||||
 | 
					    if (point_line_distance (ax, ay, n2x, n2y) < d2)
 | 
				
			||||||
 | 
					        pd2 = d2 - radius;
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        pd2 = d2 + radius;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* intersect the parallels to find the center of the arc */
 | 
				
			||||||
 | 
					    intersect (n1x, n1y, pd1, n2x, n2y, pd2, &ix, &iy);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    nx = (bx - ax) / dist1;
 | 
				
			||||||
 | 
					    ny = (by - ay) / dist1;
 | 
				
			||||||
 | 
					    d = point_line_distance (ix, iy, nx, ny);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* a1 is the point on the line a-b where the arc starts */
 | 
				
			||||||
 | 
					    intersect (n1x, n1y, d1, nx, ny, d, &a1x, &a1y);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    nx = (cx - bx) / dist2;
 | 
				
			||||||
 | 
					    ny = (cy - by) / dist2;
 | 
				
			||||||
 | 
					    d = point_line_distance (ix, iy, nx, ny);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* c1 is the point on the line b-c where the arc ends */
 | 
				
			||||||
 | 
					    intersect (n2x, n2y, d2, nx, ny, d, &c1x, &c1y);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* determine the first angle */
 | 
				
			||||||
 | 
					    if (a1x - ix == 0)
 | 
				
			||||||
 | 
					        phi1 = (a1y - iy > 0) ? M_PI_2 : 3 * M_PI_2;
 | 
				
			||||||
 | 
					    else if (a1x - ix > 0)
 | 
				
			||||||
 | 
					        phi1 = atan ((a1y - iy) / (a1x - ix));
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        phi1 = M_PI + atan ((a1y - iy) / (a1x - ix));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* determine the second angle */
 | 
				
			||||||
 | 
					    if (c1x - ix == 0)
 | 
				
			||||||
 | 
					        phi2 = (c1y - iy > 0) ? M_PI_2 : 3 * M_PI_2;
 | 
				
			||||||
 | 
					    else if (c1x - ix > 0)
 | 
				
			||||||
 | 
					        phi2 = atan ((c1y - iy) / (c1x - ix));
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        phi2 = M_PI + atan ((c1y - iy) / (c1x - ix));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* compute the difference between phi2 and phi1 mod 2pi */
 | 
				
			||||||
 | 
					    d = phi2 - phi1;
 | 
				
			||||||
 | 
					    while (d < 0)
 | 
				
			||||||
 | 
					        d += 2 * M_PI;
 | 
				
			||||||
 | 
					    while (d > 2 * M_PI)
 | 
				
			||||||
 | 
					        d -= 2 * M_PI;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef KBDRAW_DEBUG
 | 
				
			||||||
 | 
					    printf ("        line 1 to: (%f, %f):\n", a1x, a1y);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					    if (!(isnan (a1x) || isnan (a1y)))
 | 
				
			||||||
 | 
					        cairo_line_to (cr, a1x, a1y);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* pick the short arc from phi1 to phi2 */
 | 
				
			||||||
 | 
					    if (d < M_PI)
 | 
				
			||||||
 | 
					        cairo_arc (cr, ix, iy, radius, phi1, phi2);
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        cairo_arc_negative (cr, ix, iy, radius, phi1, phi2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef KBDRAW_DEBUG
 | 
				
			||||||
 | 
					    printf ("        line 2 to: (%f, %f):\n", cx, cy);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					    cairo_line_to (cr, cx, cy);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* renamed from rounded_polygon, use EekPoint instead of GdkPoint not
 | 
				
			||||||
 | 
					   to depend on GTK+, and exported */
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					_eek_rounded_polygon (cairo_t  *cr,
 | 
				
			||||||
 | 
					                      gdouble   radius,
 | 
				
			||||||
 | 
					                      EekPoint *points,
 | 
				
			||||||
 | 
					                      gint      num_points)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    gint i, j;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cairo_move_to (cr,
 | 
				
			||||||
 | 
					                   (gdouble) (points[num_points - 1].x +
 | 
				
			||||||
 | 
					                              points[0].x) / 2,
 | 
				
			||||||
 | 
					                   (gdouble) (points[num_points - 1].y +
 | 
				
			||||||
 | 
					                              points[0].y) / 2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef KBDRAW_DEBUG
 | 
				
			||||||
 | 
					    printf ("    rounded polygon of radius %f:\n", radius);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					    for (i = 0; i < num_points; i++) {
 | 
				
			||||||
 | 
					        j = (i + 1) % num_points;
 | 
				
			||||||
 | 
					        rounded_corner (cr, (gdouble) points[i].x,
 | 
				
			||||||
 | 
					                        (gdouble) points[i].y,
 | 
				
			||||||
 | 
					                        (gdouble) (points[i].x + points[j].x) / 2,
 | 
				
			||||||
 | 
					                        (gdouble) (points[i].y + points[j].y) / 2,
 | 
				
			||||||
 | 
					                        radius);
 | 
				
			||||||
 | 
					#ifdef KBDRAW_DEBUG
 | 
				
			||||||
 | 
					        printf ("      corner (%d, %d) -> (%d, %d):\n",
 | 
				
			||||||
 | 
					                points[i].x, points[i].y, points[j].x,
 | 
				
			||||||
 | 
					                points[j].y);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    cairo_close_path (cr);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										886
									
								
								eek/eek-renderer.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										886
									
								
								eek/eek-renderer.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,886 @@
 | 
				
			|||||||
 | 
					#include <math.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "eek-key.h"
 | 
				
			||||||
 | 
					#include "eek-section.h"
 | 
				
			||||||
 | 
					#include "eek-renderer.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					G_DEFINE_TYPE (EekRenderer, eek_renderer, G_TYPE_OBJECT);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define EEK_RENDERER_GET_PRIVATE(obj)                                  \
 | 
				
			||||||
 | 
					    (G_TYPE_INSTANCE_GET_PRIVATE ((obj), EEK_TYPE_RENDERER, EekRendererPrivate))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct _EekRendererPrivate
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    EekKeyboard *keyboard;
 | 
				
			||||||
 | 
					    PangoContext *pcontext;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    EekColor *foreground;
 | 
				
			||||||
 | 
					    EekColor *background;
 | 
				
			||||||
 | 
					    gdouble border_width;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    gdouble scale;
 | 
				
			||||||
 | 
					    PangoFontDescription *font;
 | 
				
			||||||
 | 
					    GHashTable *outline_surface_cache;
 | 
				
			||||||
 | 
					    cairo_surface_t *keyboard_surface;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct {
 | 
				
			||||||
 | 
					    gint category;
 | 
				
			||||||
 | 
					    gdouble scale;
 | 
				
			||||||
 | 
					} keysym_category_scale_factors[EEK_KEYSYM_CATEGORY_LAST] = {
 | 
				
			||||||
 | 
					    { EEK_KEYSYM_CATEGORY_LETTER, 1.0 },
 | 
				
			||||||
 | 
					    { EEK_KEYSYM_CATEGORY_FUNCTION, 0.5 },
 | 
				
			||||||
 | 
					    { EEK_KEYSYM_CATEGORY_KEYNAME, 0.5 }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* eek-keyboard-drawing.c */
 | 
				
			||||||
 | 
					extern void _eek_rounded_polygon          (cairo_t     *cr,
 | 
				
			||||||
 | 
					                                           gdouble      radius,
 | 
				
			||||||
 | 
					                                           EekPoint    *points,
 | 
				
			||||||
 | 
					                                           gint         num_points);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void invalidate (EekRenderer *renderer);
 | 
				
			||||||
 | 
					static void render_key (EekRenderer *self,
 | 
				
			||||||
 | 
					                        cairo_t     *cr,
 | 
				
			||||||
 | 
					                        EekKey      *key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct _CreateKeyboardSurfaceCallbackData {
 | 
				
			||||||
 | 
					    cairo_t *cr;
 | 
				
			||||||
 | 
					    EekRenderer *renderer;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					typedef struct _CreateKeyboardSurfaceCallbackData CreateKeyboardSurfaceCallbackData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					create_keyboard_surface_key_callback (EekElement *element,
 | 
				
			||||||
 | 
					                                      gpointer    user_data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    CreateKeyboardSurfaceCallbackData *data = user_data;
 | 
				
			||||||
 | 
					    EekRendererPrivate *priv = EEK_RENDERER_GET_PRIVATE(data->renderer);
 | 
				
			||||||
 | 
					    EekBounds bounds;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cairo_save (data->cr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    eek_element_get_bounds (element, &bounds);
 | 
				
			||||||
 | 
					    cairo_translate (data->cr, bounds.x * priv->scale, bounds.y * priv->scale); 
 | 
				
			||||||
 | 
					    cairo_rectangle (data->cr,
 | 
				
			||||||
 | 
					                     0.0,
 | 
				
			||||||
 | 
					                     0.0,
 | 
				
			||||||
 | 
					                     bounds.width * priv->scale,
 | 
				
			||||||
 | 
					                     bounds.height * priv->scale);
 | 
				
			||||||
 | 
					    cairo_clip (data->cr);
 | 
				
			||||||
 | 
					    render_key (data->renderer, data->cr, EEK_KEY(element));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cairo_restore (data->cr);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					create_keyboard_surface_section_callback (EekElement *element,
 | 
				
			||||||
 | 
					                                          gpointer    user_data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    CreateKeyboardSurfaceCallbackData *data = user_data;
 | 
				
			||||||
 | 
					    EekRendererPrivate *priv = EEK_RENDERER_GET_PRIVATE(data->renderer);
 | 
				
			||||||
 | 
					    EekBounds bounds;
 | 
				
			||||||
 | 
					    gint angle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cairo_save (data->cr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    eek_element_get_bounds (element, &bounds);
 | 
				
			||||||
 | 
					    cairo_translate (data->cr, bounds.x * priv->scale, bounds.y * priv->scale);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    angle = eek_section_get_angle (EEK_SECTION(element));
 | 
				
			||||||
 | 
					    cairo_rotate (data->cr, angle * G_PI / 180);
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    eek_container_foreach_child (EEK_CONTAINER(element),
 | 
				
			||||||
 | 
					                                 create_keyboard_surface_key_callback,
 | 
				
			||||||
 | 
					                                 data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cairo_restore (data->cr);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static cairo_surface_t *
 | 
				
			||||||
 | 
					create_keyboard_surface (EekRenderer *renderer)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    EekRendererPrivate *priv = EEK_RENDERER_GET_PRIVATE(renderer);
 | 
				
			||||||
 | 
					    EekBounds bounds;
 | 
				
			||||||
 | 
					    cairo_surface_t *keyboard_surface;
 | 
				
			||||||
 | 
					    CreateKeyboardSurfaceCallbackData data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    eek_element_get_bounds (EEK_ELEMENT(priv->keyboard), &bounds);
 | 
				
			||||||
 | 
					    keyboard_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
 | 
				
			||||||
 | 
					                                                   bounds.width * priv->scale,
 | 
				
			||||||
 | 
					                                                   bounds.height * priv->scale);
 | 
				
			||||||
 | 
					    data.cr = cairo_create (keyboard_surface);
 | 
				
			||||||
 | 
					    data.renderer = renderer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cairo_translate (data.cr, bounds.x * priv->scale, bounds.y * priv->scale);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* blank background */
 | 
				
			||||||
 | 
					    cairo_set_source_rgba (data.cr, 0, 0, 0, 0);
 | 
				
			||||||
 | 
					    cairo_rectangle (data.cr,
 | 
				
			||||||
 | 
					                     0.0,
 | 
				
			||||||
 | 
					                     0.0,
 | 
				
			||||||
 | 
					                     cairo_image_surface_get_width (keyboard_surface),
 | 
				
			||||||
 | 
					                     cairo_image_surface_get_height (keyboard_surface));
 | 
				
			||||||
 | 
					    cairo_fill (data.cr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* draw sections */
 | 
				
			||||||
 | 
					    eek_container_foreach_child (EEK_CONTAINER(priv->keyboard),
 | 
				
			||||||
 | 
					                                 create_keyboard_surface_section_callback,
 | 
				
			||||||
 | 
					                                 &data);
 | 
				
			||||||
 | 
					    cairo_destroy (data.cr);
 | 
				
			||||||
 | 
					    return keyboard_surface;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					render_key_outline (EekRenderer *renderer,
 | 
				
			||||||
 | 
					                    cairo_t     *cr,
 | 
				
			||||||
 | 
					                    EekKey      *key)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    EekRendererPrivate *priv = EEK_RENDERER_GET_PRIVATE(renderer);
 | 
				
			||||||
 | 
					    EekOutline *outline;
 | 
				
			||||||
 | 
					    EekBounds bounds;
 | 
				
			||||||
 | 
					    cairo_pattern_t *pat;
 | 
				
			||||||
 | 
					    EekPoint *points;
 | 
				
			||||||
 | 
					    gdouble scale;
 | 
				
			||||||
 | 
					    gint i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* need to rescale so that the border fit inside the clipping
 | 
				
			||||||
 | 
					       region */
 | 
				
			||||||
 | 
					    eek_element_get_bounds (EEK_ELEMENT(key), &bounds);
 | 
				
			||||||
 | 
					    scale = MIN((bounds.width - priv->border_width) / bounds.width,
 | 
				
			||||||
 | 
					                (bounds.height - priv->border_width) / bounds.height);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    outline = eek_key_get_outline (key);
 | 
				
			||||||
 | 
					    points = g_slice_copy (sizeof (EekPoint) * outline->num_points,
 | 
				
			||||||
 | 
					                           outline->points);
 | 
				
			||||||
 | 
					    for (i = 0; i < outline->num_points; i++) {
 | 
				
			||||||
 | 
					        points[i].x *= priv->scale * scale;
 | 
				
			||||||
 | 
					        points[i].y *= priv->scale * scale;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cairo_translate (cr,
 | 
				
			||||||
 | 
					                     priv->border_width / 2 * priv->scale,
 | 
				
			||||||
 | 
					                     priv->border_width / 2 * priv->scale);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* paint the background with gradient */
 | 
				
			||||||
 | 
					    pat = cairo_pattern_create_linear (0.0,
 | 
				
			||||||
 | 
					                                       0.0,
 | 
				
			||||||
 | 
					                                       0.0,
 | 
				
			||||||
 | 
					                                       bounds.height * priv->scale * 5.0);
 | 
				
			||||||
 | 
					    cairo_pattern_add_color_stop_rgba (pat,
 | 
				
			||||||
 | 
					                                       1,
 | 
				
			||||||
 | 
					                                       priv->background->red * 0.5,
 | 
				
			||||||
 | 
					                                       priv->background->green * 0.5,
 | 
				
			||||||
 | 
					                                       priv->background->blue * 0.5,
 | 
				
			||||||
 | 
					                                       priv->background->alpha);
 | 
				
			||||||
 | 
					    cairo_pattern_add_color_stop_rgba (pat,
 | 
				
			||||||
 | 
					                                       0,
 | 
				
			||||||
 | 
					                                       priv->background->red,
 | 
				
			||||||
 | 
					                                       priv->background->green,
 | 
				
			||||||
 | 
					                                       priv->background->blue,
 | 
				
			||||||
 | 
					                                       priv->background->alpha);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cairo_set_source (cr, pat);
 | 
				
			||||||
 | 
					    _eek_rounded_polygon (cr,
 | 
				
			||||||
 | 
					                          outline->corner_radius,
 | 
				
			||||||
 | 
					                          points,
 | 
				
			||||||
 | 
					                          outline->num_points);
 | 
				
			||||||
 | 
					    cairo_fill (cr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cairo_pattern_destroy (pat);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* paint the border */
 | 
				
			||||||
 | 
					    cairo_set_line_width (cr, priv->border_width * priv->scale);
 | 
				
			||||||
 | 
					    cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cairo_set_source_rgba
 | 
				
			||||||
 | 
					        (cr, 
 | 
				
			||||||
 | 
					         ABS(priv->background->red - priv->foreground->red) * 0.7,
 | 
				
			||||||
 | 
					         ABS(priv->background->green - priv->foreground->green) * 0.7,
 | 
				
			||||||
 | 
					         ABS(priv->background->blue - priv->foreground->blue) * 0.7,
 | 
				
			||||||
 | 
					         priv->foreground->alpha);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _eek_rounded_polygon (cr,
 | 
				
			||||||
 | 
					                          outline->corner_radius,
 | 
				
			||||||
 | 
					                          points,
 | 
				
			||||||
 | 
					                          outline->num_points);
 | 
				
			||||||
 | 
					    cairo_stroke (cr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    g_slice_free1 (sizeof (EekPoint) * outline->num_points, points);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct _CalculateFontSizeCallbackData {
 | 
				
			||||||
 | 
					    gdouble size;
 | 
				
			||||||
 | 
					    EekRenderer *renderer;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					typedef struct _CalculateFontSizeCallbackData CalculateFontSizeCallbackData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					calculate_font_size_key_callback (EekElement *element, gpointer user_data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    CalculateFontSizeCallbackData *data = user_data;
 | 
				
			||||||
 | 
					    EekRendererPrivate *priv = EEK_RENDERER_GET_PRIVATE(data->renderer);
 | 
				
			||||||
 | 
					    gdouble sx, sy;
 | 
				
			||||||
 | 
					    PangoFontDescription *font;
 | 
				
			||||||
 | 
					    const PangoFontDescription *base_font;
 | 
				
			||||||
 | 
					    PangoRectangle extents = { 0, };
 | 
				
			||||||
 | 
					    PangoLayout *layout;
 | 
				
			||||||
 | 
					    gdouble size;
 | 
				
			||||||
 | 
					    guint keysym;
 | 
				
			||||||
 | 
					    EekBounds bounds;
 | 
				
			||||||
 | 
					    gchar *label;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    keysym = eek_key_get_keysym (EEK_KEY(element));
 | 
				
			||||||
 | 
					    if (keysym == EEK_INVALID_KEYSYM ||
 | 
				
			||||||
 | 
					        eek_keysym_get_category (keysym) != EEK_KEYSYM_CATEGORY_LETTER)
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    label = eek_keysym_to_string (keysym);
 | 
				
			||||||
 | 
					    if (!label)
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    base_font = pango_context_get_font_description (priv->pcontext);
 | 
				
			||||||
 | 
					    font = pango_font_description_copy (base_font);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    eek_element_get_bounds (element, &bounds);
 | 
				
			||||||
 | 
					    size = eek_bounds_long_side (&bounds) * PANGO_SCALE;
 | 
				
			||||||
 | 
					    pango_font_description_set_size (font, size);
 | 
				
			||||||
 | 
					    layout = pango_layout_new (priv->pcontext);
 | 
				
			||||||
 | 
					    pango_layout_set_font_description (layout, font);
 | 
				
			||||||
 | 
					    pango_font_description_free (font);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pango_layout_set_text (layout, label, -1);
 | 
				
			||||||
 | 
					    g_free (label);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pango_layout_get_extents (layout, NULL, &extents);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sx = sy = 1.0;
 | 
				
			||||||
 | 
					    if (extents.width > bounds.width * PANGO_SCALE)
 | 
				
			||||||
 | 
					        sx = bounds.width * PANGO_SCALE / extents.width;
 | 
				
			||||||
 | 
					    if (extents.height > bounds.height * PANGO_SCALE)
 | 
				
			||||||
 | 
					        sy = bounds.height * PANGO_SCALE / extents.height;
 | 
				
			||||||
 | 
					    g_object_unref (layout);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    size *= MIN(sx, sy);
 | 
				
			||||||
 | 
					    if (size < data->size &&
 | 
				
			||||||
 | 
					        size >= pango_font_description_get_size (base_font))
 | 
				
			||||||
 | 
					        data->size = size;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					calculate_font_size_section_callback (EekElement *element, gpointer user_data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    eek_container_foreach_child (EEK_CONTAINER(element),
 | 
				
			||||||
 | 
					                                 calculate_font_size_key_callback,
 | 
				
			||||||
 | 
					                                 user_data);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static gdouble
 | 
				
			||||||
 | 
					calculate_font_size (EekRenderer *renderer)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    EekRendererPrivate *priv = EEK_RENDERER_GET_PRIVATE(renderer);
 | 
				
			||||||
 | 
					    CalculateFontSizeCallbackData data;
 | 
				
			||||||
 | 
					    PangoFontDescription *base_font;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    base_font = pango_context_get_font_description (priv->pcontext);
 | 
				
			||||||
 | 
					    data.size = pango_font_description_get_size (base_font);
 | 
				
			||||||
 | 
					    data.renderer = renderer;
 | 
				
			||||||
 | 
					    eek_container_foreach_child (EEK_CONTAINER(priv->keyboard),
 | 
				
			||||||
 | 
					                                 calculate_font_size_section_callback,
 | 
				
			||||||
 | 
					                                 &data);
 | 
				
			||||||
 | 
					    return data.size;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					render_key_label (EekRenderer *renderer,
 | 
				
			||||||
 | 
					                  cairo_t     *cr,
 | 
				
			||||||
 | 
					                  EekKey      *key)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    EekRendererPrivate *priv = EEK_RENDERER_GET_PRIVATE(renderer);
 | 
				
			||||||
 | 
					    guint keysym;
 | 
				
			||||||
 | 
					    EekKeysymCategory category;
 | 
				
			||||||
 | 
					    gchar *label;
 | 
				
			||||||
 | 
					    PangoLayout *layout;
 | 
				
			||||||
 | 
					    PangoRectangle extents = { 0, };
 | 
				
			||||||
 | 
					    EekBounds bounds;
 | 
				
			||||||
 | 
					    PangoFontDescription *font;
 | 
				
			||||||
 | 
					    gdouble size, scale;
 | 
				
			||||||
 | 
					    gint i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    keysym = eek_key_get_keysym (key);
 | 
				
			||||||
 | 
					    if (keysym == EEK_INVALID_KEYSYM)
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    category = eek_keysym_get_category (keysym);
 | 
				
			||||||
 | 
					    if (category == EEK_KEYSYM_CATEGORY_UNKNOWN)
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    label = eek_keysym_to_string (keysym);
 | 
				
			||||||
 | 
					    if (!label)
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    eek_element_get_bounds (EEK_ELEMENT(key), &bounds);
 | 
				
			||||||
 | 
					    scale = MIN((bounds.width - priv->border_width) / bounds.width,
 | 
				
			||||||
 | 
					                (bounds.height - priv->border_width) / bounds.height);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    layout = pango_cairo_create_layout (cr);
 | 
				
			||||||
 | 
					    font = pango_font_description_copy (priv->font);
 | 
				
			||||||
 | 
					    size = pango_font_description_get_size (font);
 | 
				
			||||||
 | 
					    for (i = 0; i < G_N_ELEMENTS(keysym_category_scale_factors); i++)
 | 
				
			||||||
 | 
					        if (keysym_category_scale_factors[i].category == category) {
 | 
				
			||||||
 | 
					            size *= keysym_category_scale_factors[i].scale;
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    pango_font_description_set_size (font, size * priv->scale * scale);
 | 
				
			||||||
 | 
					    pango_layout_set_font_description (layout, font);
 | 
				
			||||||
 | 
					    pango_layout_set_text (layout, label, -1);
 | 
				
			||||||
 | 
					    g_free (label);
 | 
				
			||||||
 | 
					    pango_layout_set_width (layout,
 | 
				
			||||||
 | 
					                            PANGO_SCALE * bounds.width * priv->scale * scale);
 | 
				
			||||||
 | 
					    pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END);
 | 
				
			||||||
 | 
					    pango_layout_get_extents (layout, NULL, &extents);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cairo_save (cr);
 | 
				
			||||||
 | 
					    cairo_move_to
 | 
				
			||||||
 | 
					        (cr,
 | 
				
			||||||
 | 
					         (bounds.width * priv->scale - extents.width / PANGO_SCALE) / 2,
 | 
				
			||||||
 | 
					         (bounds.height * priv->scale - extents.height / PANGO_SCALE) / 2);
 | 
				
			||||||
 | 
					    cairo_set_source_rgba (cr,
 | 
				
			||||||
 | 
					                           priv->foreground->red,
 | 
				
			||||||
 | 
					                           priv->foreground->green,
 | 
				
			||||||
 | 
					                           priv->foreground->blue,
 | 
				
			||||||
 | 
					                           priv->foreground->alpha);
 | 
				
			||||||
 | 
					    pango_cairo_show_layout (cr, layout);
 | 
				
			||||||
 | 
					    cairo_restore (cr);
 | 
				
			||||||
 | 
					    g_object_unref (layout);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					render_key (EekRenderer *self,
 | 
				
			||||||
 | 
					            cairo_t     *cr,
 | 
				
			||||||
 | 
					            EekKey      *key)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    EekRendererPrivate *priv = EEK_RENDERER_GET_PRIVATE(self);
 | 
				
			||||||
 | 
					    EekOutline *outline;
 | 
				
			||||||
 | 
					    cairo_surface_t *outline_surface;
 | 
				
			||||||
 | 
					    EekBounds bounds;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    outline = eek_key_get_outline (key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    outline_surface = g_hash_table_lookup (priv->outline_surface_cache,
 | 
				
			||||||
 | 
					                                           outline);
 | 
				
			||||||
 | 
					    if (!outline_surface) {
 | 
				
			||||||
 | 
					        cairo_t *cr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        eek_element_get_bounds (EEK_ELEMENT(key), &bounds);
 | 
				
			||||||
 | 
					        outline_surface =
 | 
				
			||||||
 | 
					            cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
 | 
				
			||||||
 | 
					                                        bounds.width * priv->scale,
 | 
				
			||||||
 | 
					                                        bounds.height * priv->scale);
 | 
				
			||||||
 | 
					        cr = cairo_create (outline_surface);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /* blank background */
 | 
				
			||||||
 | 
					        eek_element_get_bounds (EEK_ELEMENT(key), &bounds);
 | 
				
			||||||
 | 
					        cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.0);
 | 
				
			||||||
 | 
					        cairo_rectangle (cr,
 | 
				
			||||||
 | 
					                         0.0,
 | 
				
			||||||
 | 
					                         0.0,
 | 
				
			||||||
 | 
					                         bounds.width * priv->scale,
 | 
				
			||||||
 | 
					                         bounds.height * priv->scale);
 | 
				
			||||||
 | 
					        cairo_fill (cr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        render_key_outline (self, cr, key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        g_hash_table_insert (priv->outline_surface_cache,
 | 
				
			||||||
 | 
					                             outline,
 | 
				
			||||||
 | 
					                             outline_surface);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!priv->font) {
 | 
				
			||||||
 | 
					        PangoFontDescription *base_font;
 | 
				
			||||||
 | 
					        gdouble size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        size = calculate_font_size (self);
 | 
				
			||||||
 | 
					        base_font = pango_context_get_font_description (priv->pcontext);
 | 
				
			||||||
 | 
					        priv->font = pango_font_description_copy (base_font);
 | 
				
			||||||
 | 
					        pango_font_description_set_size (priv->font, size);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cairo_set_source_surface (cr, outline_surface, 0.0, 0.0);
 | 
				
			||||||
 | 
					    cairo_paint (cr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    render_key_label (self, cr, key);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					eek_renderer_real_render_key (EekRenderer *self,
 | 
				
			||||||
 | 
					                              cairo_t     *cr,
 | 
				
			||||||
 | 
					                              EekKey      *key,
 | 
				
			||||||
 | 
					                              gdouble      scale)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    EekElement *section;
 | 
				
			||||||
 | 
					    EekBounds bounds, rotated_bounds;
 | 
				
			||||||
 | 
					    gint angle;
 | 
				
			||||||
 | 
					    gdouble s;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    eek_renderer_get_key_bounds (self, key, &bounds, FALSE);
 | 
				
			||||||
 | 
					    eek_renderer_get_key_bounds (self, key, &rotated_bounds, TRUE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    section = eek_element_get_parent (EEK_ELEMENT(key));
 | 
				
			||||||
 | 
					    angle = eek_section_get_angle (EEK_SECTION(section));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cairo_save (cr);
 | 
				
			||||||
 | 
					    cairo_scale (cr, scale, scale);
 | 
				
			||||||
 | 
					#if DEBUG
 | 
				
			||||||
 | 
					    cairo_set_source_rgba (cr, 1.0, 0.0, 0.0, 1.0);
 | 
				
			||||||
 | 
					    cairo_rectangle (cr, 0, 0,
 | 
				
			||||||
 | 
					                     rotated_bounds.width, rotated_bounds.height);
 | 
				
			||||||
 | 
					    cairo_stroke (cr);
 | 
				
			||||||
 | 
					    cairo_set_source_rgba (cr, 0.0, 1.0, 0.0, 1.0);
 | 
				
			||||||
 | 
					#endif  /* DEBUG */
 | 
				
			||||||
 | 
					    s = sin (angle * G_PI / 180);
 | 
				
			||||||
 | 
					    if (s < 0)
 | 
				
			||||||
 | 
					        cairo_translate (cr, 0, - bounds.width * s);
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        cairo_translate (cr, bounds.height * s, 0);
 | 
				
			||||||
 | 
					    cairo_rotate (cr, angle * G_PI / 180);
 | 
				
			||||||
 | 
					#if DEBUG
 | 
				
			||||||
 | 
					    cairo_rectangle (cr,
 | 
				
			||||||
 | 
					                     0,
 | 
				
			||||||
 | 
					                     0,
 | 
				
			||||||
 | 
					                     bounds.width, bounds.height);
 | 
				
			||||||
 | 
					    cairo_stroke (cr);
 | 
				
			||||||
 | 
					#endif  /* DEBUG */
 | 
				
			||||||
 | 
					    render_key (self, cr, key);
 | 
				
			||||||
 | 
					    cairo_restore (cr);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					eek_renderer_real_render_keyboard (EekRenderer *self,
 | 
				
			||||||
 | 
					                                   cairo_t     *cr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    EekRendererPrivate *priv = EEK_RENDERER_GET_PRIVATE(self);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!priv->keyboard_surface)
 | 
				
			||||||
 | 
					        priv->keyboard_surface = create_keyboard_surface (self);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cairo_set_source_surface (cr, priv->keyboard_surface, 0.0, 0.0);
 | 
				
			||||||
 | 
					    cairo_paint (cr);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					eek_renderer_dispose (GObject *object)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    EekRendererPrivate *priv = EEK_RENDERER_GET_PRIVATE(object);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (priv->keyboard) {
 | 
				
			||||||
 | 
					        if (g_object_is_floating (priv->keyboard))
 | 
				
			||||||
 | 
					            g_object_unref (priv->keyboard);
 | 
				
			||||||
 | 
					        priv->keyboard = NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (priv->pcontext) {
 | 
				
			||||||
 | 
					        g_object_unref (priv->pcontext);
 | 
				
			||||||
 | 
					        priv->pcontext = NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* this will release all allocated surfaces and font if any */
 | 
				
			||||||
 | 
					    invalidate (EEK_RENDERER(object));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    G_OBJECT_CLASS (eek_renderer_parent_class)->dispose (object);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					eek_renderer_finalize (GObject *object)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    EekRendererPrivate *priv = EEK_RENDERER_GET_PRIVATE(object);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    g_hash_table_destroy (priv->outline_surface_cache);
 | 
				
			||||||
 | 
					    G_OBJECT_CLASS (eek_renderer_parent_class)->finalize (object);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					eek_renderer_class_init (EekRendererClass *klass)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    GObjectClass      *gobject_class = G_OBJECT_CLASS (klass);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    g_type_class_add_private (gobject_class,
 | 
				
			||||||
 | 
					                              sizeof (EekRendererPrivate));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    klass->render_key = eek_renderer_real_render_key;
 | 
				
			||||||
 | 
					    klass->render_keyboard = eek_renderer_real_render_keyboard;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    gobject_class->dispose = eek_renderer_dispose;
 | 
				
			||||||
 | 
					    gobject_class->finalize = eek_renderer_finalize;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					free_surface (gpointer data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    cairo_surface_destroy (data);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					eek_renderer_init (EekRenderer *self)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    EekRendererPrivate *priv;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    priv = self->priv = EEK_RENDERER_GET_PRIVATE(self);
 | 
				
			||||||
 | 
					    priv->keyboard = NULL;
 | 
				
			||||||
 | 
					    priv->pcontext = NULL;
 | 
				
			||||||
 | 
					    priv->foreground = eek_color_new (0.3, 0.3, 0.3, 1.0);
 | 
				
			||||||
 | 
					    priv->background = eek_color_new (1.0, 1.0, 1.0, 1.0);
 | 
				
			||||||
 | 
					    priv->border_width = 3.0;
 | 
				
			||||||
 | 
					    priv->scale = 1.0;
 | 
				
			||||||
 | 
					    priv->font = NULL;
 | 
				
			||||||
 | 
					    priv->outline_surface_cache =
 | 
				
			||||||
 | 
					        g_hash_table_new_full (g_direct_hash,
 | 
				
			||||||
 | 
					                               g_direct_equal,
 | 
				
			||||||
 | 
					                               NULL,
 | 
				
			||||||
 | 
					                               free_surface);
 | 
				
			||||||
 | 
					    priv->keyboard_surface = NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					invalidate (EekRenderer *renderer)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    EekRendererPrivate *priv = EEK_RENDERER_GET_PRIVATE(renderer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (priv->outline_surface_cache)
 | 
				
			||||||
 | 
					        g_hash_table_remove_all (priv->outline_surface_cache);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (priv->keyboard_surface) {
 | 
				
			||||||
 | 
					        cairo_surface_destroy (priv->keyboard_surface);
 | 
				
			||||||
 | 
					        priv->keyboard_surface = NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					on_keysym_index_changed (EekKeyboard *keyboard,
 | 
				
			||||||
 | 
					                         gint         group,
 | 
				
			||||||
 | 
					                         gint         level,
 | 
				
			||||||
 | 
					                         gpointer     user_data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    EekRenderer *renderer = user_data;
 | 
				
			||||||
 | 
					    invalidate (renderer);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					EekRenderer *
 | 
				
			||||||
 | 
					eek_renderer_new (EekKeyboard  *keyboard,
 | 
				
			||||||
 | 
					                  PangoContext *pcontext)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    EekRenderer *renderer;
 | 
				
			||||||
 | 
					    EekRendererPrivate *priv;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    renderer = g_object_new (EEK_TYPE_RENDERER, NULL);
 | 
				
			||||||
 | 
					    priv = EEK_RENDERER_GET_PRIVATE(renderer);
 | 
				
			||||||
 | 
					    priv->keyboard = g_object_ref_sink (keyboard);
 | 
				
			||||||
 | 
					    priv->pcontext = g_object_ref (pcontext);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    g_signal_connect (keyboard, "keysym-index-changed",
 | 
				
			||||||
 | 
					                      G_CALLBACK(on_keysym_index_changed),
 | 
				
			||||||
 | 
					                      renderer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return renderer;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					eek_renderer_set_preferred_size (EekRenderer *renderer,
 | 
				
			||||||
 | 
					                                 gdouble      width,
 | 
				
			||||||
 | 
					                                 gdouble      height)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    EekRendererPrivate *priv;
 | 
				
			||||||
 | 
					    EekBounds bounds;
 | 
				
			||||||
 | 
					    gdouble scale;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    g_return_if_fail (EEK_IS_RENDERER(renderer));
 | 
				
			||||||
 | 
					    g_return_if_fail (width > 0.0 && height > 0.0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    priv = EEK_RENDERER_GET_PRIVATE(renderer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    eek_element_get_bounds (EEK_ELEMENT(priv->keyboard), &bounds);
 | 
				
			||||||
 | 
					    scale = width > height ? height / bounds.height :
 | 
				
			||||||
 | 
					        width / bounds.width;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (scale != priv->scale) {
 | 
				
			||||||
 | 
					        priv->scale = scale;
 | 
				
			||||||
 | 
					        invalidate (renderer);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					eek_renderer_get_key_bounds (EekRenderer *renderer,
 | 
				
			||||||
 | 
					                             EekKey      *key,
 | 
				
			||||||
 | 
					                             EekBounds   *bounds,
 | 
				
			||||||
 | 
					                             gboolean     rotate)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    EekRendererPrivate *priv;
 | 
				
			||||||
 | 
					    EekElement *section;
 | 
				
			||||||
 | 
					    EekBounds section_bounds, keyboard_bounds;
 | 
				
			||||||
 | 
					    gint angle = 0;
 | 
				
			||||||
 | 
					    EekPoint points[4], min, max;
 | 
				
			||||||
 | 
					    gint i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    g_return_if_fail (EEK_IS_RENDERER(renderer));
 | 
				
			||||||
 | 
					    g_return_if_fail (EEK_IS_KEY(key));
 | 
				
			||||||
 | 
					    g_return_if_fail (bounds != NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    priv = EEK_RENDERER_GET_PRIVATE(renderer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    section = eek_element_get_parent (EEK_ELEMENT(key));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    eek_element_get_bounds (EEK_ELEMENT(key), bounds);
 | 
				
			||||||
 | 
					    eek_element_get_bounds (section, §ion_bounds);
 | 
				
			||||||
 | 
					    eek_element_get_bounds (EEK_ELEMENT(priv->keyboard), &keyboard_bounds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!rotate) {
 | 
				
			||||||
 | 
					        bounds->x += keyboard_bounds.x + section_bounds.x;
 | 
				
			||||||
 | 
					        bounds->y += keyboard_bounds.y + section_bounds.y;
 | 
				
			||||||
 | 
					        bounds->x *= priv->scale;
 | 
				
			||||||
 | 
					        bounds->y *= priv->scale;
 | 
				
			||||||
 | 
					        bounds->width *= priv->scale;
 | 
				
			||||||
 | 
					        bounds->height *= priv->scale;
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    points[0].x = bounds->x;
 | 
				
			||||||
 | 
					    points[0].y = bounds->y;
 | 
				
			||||||
 | 
					    points[1].x = points[0].x + bounds->width;
 | 
				
			||||||
 | 
					    points[1].y = points[0].y;
 | 
				
			||||||
 | 
					    points[2].x = points[1].x;
 | 
				
			||||||
 | 
					    points[2].y = points[1].y + bounds->height;
 | 
				
			||||||
 | 
					    points[3].x = points[0].x;
 | 
				
			||||||
 | 
					    points[3].y = points[2].y;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (rotate)
 | 
				
			||||||
 | 
					        angle = eek_section_get_angle (EEK_SECTION(section));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    min = points[2];
 | 
				
			||||||
 | 
					    max = points[0];
 | 
				
			||||||
 | 
					    for (i = 0; i < G_N_ELEMENTS(points); i++) {
 | 
				
			||||||
 | 
					        eek_point_rotate (&points[i], angle);
 | 
				
			||||||
 | 
					        if (points[i].x < min.x)
 | 
				
			||||||
 | 
					            min.x = points[i].x;
 | 
				
			||||||
 | 
					        if (points[i].x > max.x)
 | 
				
			||||||
 | 
					            max.x = points[i].x;
 | 
				
			||||||
 | 
					        if (points[i].y < min.y)
 | 
				
			||||||
 | 
					            min.y = points[i].y;
 | 
				
			||||||
 | 
					        if (points[i].y > max.y)
 | 
				
			||||||
 | 
					            max.y = points[i].y;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    bounds->x = keyboard_bounds.x + section_bounds.x + min.x;
 | 
				
			||||||
 | 
					    bounds->y = keyboard_bounds.y + section_bounds.y + min.y;
 | 
				
			||||||
 | 
					    bounds->width = (max.x - min.x);
 | 
				
			||||||
 | 
					    bounds->height = (max.y - min.y);
 | 
				
			||||||
 | 
					    bounds->x *= priv->scale;
 | 
				
			||||||
 | 
					    bounds->y *= priv->scale;
 | 
				
			||||||
 | 
					    bounds->width *= priv->scale;
 | 
				
			||||||
 | 
					    bounds->height *= priv->scale;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					gdouble
 | 
				
			||||||
 | 
					eek_renderer_get_scale (EekRenderer *renderer)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    EekRendererPrivate *priv;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    g_assert (EEK_IS_RENDERER(renderer));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    priv = EEK_RENDERER_GET_PRIVATE(renderer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return priv->scale;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					eek_renderer_render_key (EekRenderer *renderer,
 | 
				
			||||||
 | 
					                         cairo_t     *cr,
 | 
				
			||||||
 | 
					                         EekKey      *key,
 | 
				
			||||||
 | 
					                         gdouble      scale)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    g_return_if_fail (EEK_IS_RENDERER(renderer));
 | 
				
			||||||
 | 
					    g_return_if_fail (scale >= 0.0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    EEK_RENDERER_GET_CLASS(renderer)->render_key (renderer, cr, key, scale);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					eek_renderer_render_keyboard (EekRenderer *renderer,
 | 
				
			||||||
 | 
					                              cairo_t     *cr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    g_return_if_fail (EEK_IS_RENDERER(renderer));
 | 
				
			||||||
 | 
					    EEK_RENDERER_GET_CLASS(renderer)->render_keyboard (renderer, cr);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					eek_renderer_set_foreground (EekRenderer *renderer,
 | 
				
			||||||
 | 
					                             EekColor    *foreground)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    EekRendererPrivate *priv;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    g_return_if_fail (EEK_IS_RENDERER(renderer));
 | 
				
			||||||
 | 
					    g_return_if_fail (foreground);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    priv = EEK_RENDERER_GET_PRIVATE(renderer);
 | 
				
			||||||
 | 
					    priv->foreground = g_boxed_copy (EEK_TYPE_COLOR, foreground);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					eek_renderer_set_background (EekRenderer *renderer,
 | 
				
			||||||
 | 
					                             EekColor    *background)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    EekRendererPrivate *priv;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    g_return_if_fail (EEK_IS_RENDERER(renderer));
 | 
				
			||||||
 | 
					    g_return_if_fail (background);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    priv = EEK_RENDERER_GET_PRIVATE(renderer);
 | 
				
			||||||
 | 
					    priv->background = g_boxed_copy (EEK_TYPE_COLOR, background);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					eek_renderer_get_foreground (EekRenderer *renderer,
 | 
				
			||||||
 | 
					                             EekColor    *foreground)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    EekRendererPrivate *priv;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    g_return_if_fail (EEK_IS_RENDERER(renderer));
 | 
				
			||||||
 | 
					    g_return_if_fail (foreground);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    priv = EEK_RENDERER_GET_PRIVATE(renderer);
 | 
				
			||||||
 | 
					    *foreground = *priv->foreground;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					eek_renderer_get_background (EekRenderer *renderer,
 | 
				
			||||||
 | 
					                             EekColor    *background)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    EekRendererPrivate *priv;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    g_return_if_fail (EEK_IS_RENDERER(renderer));
 | 
				
			||||||
 | 
					    g_return_if_fail (background);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    priv = EEK_RENDERER_GET_PRIVATE(renderer);
 | 
				
			||||||
 | 
					    *background = *priv->background;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct _FindKeyByPositionCallbackData {
 | 
				
			||||||
 | 
					    EekPoint point;
 | 
				
			||||||
 | 
					    EekPoint origin;
 | 
				
			||||||
 | 
					    gint angle;
 | 
				
			||||||
 | 
					    EekKey *key;
 | 
				
			||||||
 | 
					    EekRenderer *renderer;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					typedef struct _FindKeyByPositionCallbackData FindKeyByPositionCallbackData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static gboolean
 | 
				
			||||||
 | 
					sign (EekPoint *p1, EekPoint *p2, EekPoint *p3)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    return (p1->x - p3->x) * (p2->y - p3->y) -
 | 
				
			||||||
 | 
					        (p2->x - p3->x) * (p1->y - p3->y);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static gint
 | 
				
			||||||
 | 
					find_key_by_position_key_callback (EekElement *element,
 | 
				
			||||||
 | 
					                                   gpointer user_data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    FindKeyByPositionCallbackData *data = user_data;
 | 
				
			||||||
 | 
					    EekBounds bounds;
 | 
				
			||||||
 | 
					    EekRendererPrivate *priv;
 | 
				
			||||||
 | 
					    EekPoint points[4];
 | 
				
			||||||
 | 
					    gint i;
 | 
				
			||||||
 | 
					    gboolean b1, b2, b3;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    priv = EEK_RENDERER_GET_PRIVATE(data->renderer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    eek_element_get_bounds (element, &bounds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    points[0].x = bounds.x;
 | 
				
			||||||
 | 
					    points[0].y = bounds.y;
 | 
				
			||||||
 | 
					    points[1].x = points[0].x + bounds.width;
 | 
				
			||||||
 | 
					    points[1].y = points[0].y;
 | 
				
			||||||
 | 
					    points[2].x = points[1].x;
 | 
				
			||||||
 | 
					    points[2].y = points[1].y + bounds.height;
 | 
				
			||||||
 | 
					    points[3].x = points[0].x;
 | 
				
			||||||
 | 
					    points[3].y = points[2].y;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (i = 0; i < G_N_ELEMENTS(points); i++) {
 | 
				
			||||||
 | 
					        eek_point_rotate (&points[i], data->angle);
 | 
				
			||||||
 | 
					        points[i].x += data->origin.x;
 | 
				
			||||||
 | 
					        points[i].y += data->origin.y;
 | 
				
			||||||
 | 
					        points[i].x *= priv->scale;
 | 
				
			||||||
 | 
					        points[i].y *= priv->scale;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    b1 = sign (&data->point, &points[0], &points[1]) < 0.0;
 | 
				
			||||||
 | 
					    b2 = sign (&data->point, &points[1], &points[2]) < 0.0;
 | 
				
			||||||
 | 
					    b3 = sign (&data->point, &points[2], &points[0]) < 0.0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (b1 == b2 && b2 == b3) {
 | 
				
			||||||
 | 
					        data->key = EEK_KEY(element);
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    b1 = sign (&data->point, &points[2], &points[3]) < 0.0;
 | 
				
			||||||
 | 
					    b2 = sign (&data->point, &points[3], &points[0]) < 0.0;
 | 
				
			||||||
 | 
					    b3 = sign (&data->point, &points[0], &points[2]) < 0.0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (b1 == b2 && b2 == b3) {
 | 
				
			||||||
 | 
					        data->key = EEK_KEY(element);
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return -1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static gint
 | 
				
			||||||
 | 
					find_key_by_position_section_callback (EekElement *element,
 | 
				
			||||||
 | 
					                                       gpointer user_data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    FindKeyByPositionCallbackData *data = user_data;
 | 
				
			||||||
 | 
					    EekBounds bounds;
 | 
				
			||||||
 | 
					    EekRendererPrivate *priv;
 | 
				
			||||||
 | 
					    EekPoint origin;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    priv = EEK_RENDERER_GET_PRIVATE(data->renderer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    origin = data->origin;
 | 
				
			||||||
 | 
					    eek_element_get_bounds (element, &bounds);
 | 
				
			||||||
 | 
					    data->origin.x += bounds.x;
 | 
				
			||||||
 | 
					    data->origin.y += bounds.y;
 | 
				
			||||||
 | 
					    data->angle = eek_section_get_angle (EEK_SECTION(element));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    eek_container_find (EEK_CONTAINER(element),
 | 
				
			||||||
 | 
					                        find_key_by_position_key_callback,
 | 
				
			||||||
 | 
					                        data);
 | 
				
			||||||
 | 
					    data->origin = origin;
 | 
				
			||||||
 | 
					    return data->key ? 0 : -1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					EekKey *
 | 
				
			||||||
 | 
					eek_renderer_find_key_by_position (EekRenderer *renderer,
 | 
				
			||||||
 | 
					                                   gdouble      x,
 | 
				
			||||||
 | 
					                                   gdouble      y)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    EekRendererPrivate *priv;
 | 
				
			||||||
 | 
					    EekBounds bounds;
 | 
				
			||||||
 | 
					    FindKeyByPositionCallbackData data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    g_assert (EEK_IS_RENDERER(renderer));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    priv = EEK_RENDERER_GET_PRIVATE(renderer);
 | 
				
			||||||
 | 
					    eek_element_get_bounds (EEK_ELEMENT(priv->keyboard), &bounds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    g_assert (x >= bounds.x * priv->scale);
 | 
				
			||||||
 | 
					    g_assert (y >= bounds.y * priv->scale);
 | 
				
			||||||
 | 
					    g_assert (x <= bounds.width * priv->scale);
 | 
				
			||||||
 | 
					    g_assert (y <= bounds.height * priv->scale);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    data.point.x = x;
 | 
				
			||||||
 | 
					    data.point.y = y;
 | 
				
			||||||
 | 
					    data.origin.x = bounds.x;
 | 
				
			||||||
 | 
					    data.origin.y = bounds.y;
 | 
				
			||||||
 | 
					    data.key = NULL;
 | 
				
			||||||
 | 
					    data.renderer = renderer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    eek_container_find (EEK_CONTAINER(priv->keyboard),
 | 
				
			||||||
 | 
					                        find_key_by_position_section_callback,
 | 
				
			||||||
 | 
					                        &data);
 | 
				
			||||||
 | 
					    return data.key;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										83
									
								
								eek/eek-renderer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								eek/eek-renderer.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,83 @@
 | 
				
			|||||||
 | 
					#ifndef EEK_RENDERER_H
 | 
				
			||||||
 | 
					#define EEK_RENDERER_H 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <pango/pangocairo.h>
 | 
				
			||||||
 | 
					#include "eek-keyboard.h"
 | 
				
			||||||
 | 
					#include "eek-keysym.h"
 | 
				
			||||||
 | 
					#include "eek-types.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					G_BEGIN_DECLS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define EEK_TYPE_RENDERER (eek_renderer_get_type())
 | 
				
			||||||
 | 
					#define EEK_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EEK_TYPE_RENDERER, EekRenderer))
 | 
				
			||||||
 | 
					#define EEK_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), EEK_TYPE_RENDERER, EekRendererClass))
 | 
				
			||||||
 | 
					#define EEK_IS_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EEK_TYPE_RENDERER))
 | 
				
			||||||
 | 
					#define EEK_IS_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EEK_TYPE_RENDERER))
 | 
				
			||||||
 | 
					#define EEK_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), EEK_TYPE_RENDERER, EekRendererClass))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct _EekRenderer EekRenderer;
 | 
				
			||||||
 | 
					typedef struct _EekRendererClass EekRendererClass;
 | 
				
			||||||
 | 
					typedef struct _EekRendererPrivate EekRendererPrivate;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct _EekRenderer {
 | 
				
			||||||
 | 
					    GObject parent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    EekRendererPrivate *priv;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct _EekRendererClass
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    GObjectClass parent_class;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void (* render_key)      (EekRenderer *self,
 | 
				
			||||||
 | 
					                              cairo_t     *cr,
 | 
				
			||||||
 | 
					                              EekKey      *key,
 | 
				
			||||||
 | 
					                              gdouble      scale);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void (* render_keyboard) (EekRenderer *self,
 | 
				
			||||||
 | 
					                              cairo_t     *cr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /*< private >*/
 | 
				
			||||||
 | 
					    /* padding */
 | 
				
			||||||
 | 
					    gpointer pdummy[24];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					GType        eek_renderer_get_type             (void) G_GNUC_CONST;
 | 
				
			||||||
 | 
					EekRenderer *eek_renderer_new                  (EekKeyboard  *keyboard,
 | 
				
			||||||
 | 
					                                                PangoContext *pcontext);
 | 
				
			||||||
 | 
					void         eek_renderer_set_preferred_size   (EekRenderer  *renderer,
 | 
				
			||||||
 | 
					                                                gdouble       width,
 | 
				
			||||||
 | 
					                                                gdouble       height);
 | 
				
			||||||
 | 
					void         eek_renderer_get_size             (EekRenderer  *renderer,
 | 
				
			||||||
 | 
					                                                gdouble      *width,
 | 
				
			||||||
 | 
					                                                gdouble      *height);
 | 
				
			||||||
 | 
					void         eek_renderer_get_key_bounds       (EekRenderer  *renderer,
 | 
				
			||||||
 | 
					                                                EekKey       *key,
 | 
				
			||||||
 | 
					                                                EekBounds    *bounds,
 | 
				
			||||||
 | 
					                                                gboolean      rotate);
 | 
				
			||||||
 | 
					gdouble      eek_renderer_get_scale            (EekRenderer  *renderer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void         eek_renderer_render_key           (EekRenderer  *renderer,
 | 
				
			||||||
 | 
					                                                cairo_t      *cr,
 | 
				
			||||||
 | 
					                                                EekKey       *key,
 | 
				
			||||||
 | 
					                                                gdouble       scale);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void         eek_renderer_render_keyboard      (EekRenderer  *renderer,
 | 
				
			||||||
 | 
					                                                cairo_t      *cr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void         eek_renderer_set_foreground       (EekRenderer  *renderer,
 | 
				
			||||||
 | 
					                                                EekColor     *foreground);
 | 
				
			||||||
 | 
					void         eek_renderer_set_background       (EekRenderer  *renderer,
 | 
				
			||||||
 | 
					                                                EekColor     *background);
 | 
				
			||||||
 | 
					void         eek_renderer_get_foreground       (EekRenderer  *renderer,
 | 
				
			||||||
 | 
					                                                EekColor     *foreground);
 | 
				
			||||||
 | 
					void         eek_renderer_get_background       (EekRenderer  *renderer,
 | 
				
			||||||
 | 
					                                                EekColor     *background);
 | 
				
			||||||
 | 
					void         eek_renderer_set_border_width     (EekRenderer  *renderer,
 | 
				
			||||||
 | 
					                                                gdouble       border_width);
 | 
				
			||||||
 | 
					EekKey      *eek_renderer_find_key_by_position (EekRenderer  *renderer,
 | 
				
			||||||
 | 
					                                                gdouble       x,
 | 
				
			||||||
 | 
					                                                gdouble       y);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					G_END_DECLS
 | 
				
			||||||
 | 
					#endif  /* EEK_RENDERER_H */
 | 
				
			||||||
		Reference in New Issue
	
	Block a user