/* -*- mode:C; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By: Tomas Frydrych * Emmanuele Bassi * * Copyright (C) 2007 OpenedHand * * 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. */ /** * SECTION:clutter-units * @short_description: A logical distance unit. * * Clutter units are logical units with granularity greater than that of the * device units; they are used by #ClutterActorBox and the units-based family * of #ClutterActor functions. To convert between Clutter units and device * units, use %CLUTTER_UNITS_FROM_DEVICE and %CLUTTER_UNITS_TO_DEVICE macros. * * #ClutterUnits can be converted from other units like millimeters, * typographic points (at the current resolution) and percentages. It is * also possible to convert fixed point values to and from #ClutterUnit * values. * * In order to register a #ClutterUnit property, the #ClutterParamSpecUnit * #GParamSpec sub-class should be used: * * |[ * GParamSpec *pspec; * * pspec = clutter_param_spec_unit ("width", * "Width", * "Width of the actor, in units", * 0, CLUTTER_MAXUNIT, * 0, * G_PARAM_READWRITE); * g_object_class_install_property (gobject_class, PROP_WIDTH, pspec); * ]| * * A #GValue holding units can be manipulated using clutter_value_set_unit() * and clutter_value_get_unit(). #GValues containing a #ClutterUnit * value can also be transformed to #GValues containing integer * values - with a loss of precision: * * |[ * static gboolean * units_to_int (const GValue *src, * GValue *dest) * { * g_return_val_if_fail (CLUTTER_VALUE_HOLDS_UNIT (src), FALSE); * * g_value_init (dest, G_TYPE_INT); * return g_value_transform (src, &dest); * } * ]| * * The code above is equivalent to: * * |[ * static gboolean * units_to_int (const GValue *src, * GValue *dest) * { * g_return_val_if_fail (CLUTTER_VALUE_HOLDS_UNIT (src), FALSE); * * g_value_init (dest, G_TYPE_INT); * g_value_set_int (dest, * CLUTTER_UNITS_TO_INT (clutter_value_get_unit (src))); * * return TRUE; * } * ]| * * #ClutterUnit is available since Clutter 0.4 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "clutter-units.h" #include "clutter-private.h" #define DPI_FALLBACK 96.0 /** * clutter_units_mm: * @mm: millimeters to convert * * Converts a value in millimeters to #ClutterUnits at * the current DPI. * * Return value: the value in units * * Since: 1.0 */ ClutterUnit clutter_units_mm (gdouble mm) { ClutterBackend *backend; gdouble dpi; backend = clutter_get_default_backend (); dpi = clutter_backend_get_resolution (backend); if (dpi < 0) dpi = DPI_FALLBACK; return mm * dpi / 25.4; } /** * clutter_units_pt: * @pt: typographic points to convert * * Converts a value in typographic points to #ClutterUnits * at the current DPI. * * Return value: the value in units * * Since: 1.0 */ ClutterUnit clutter_units_pt (gdouble pt) { ClutterBackend *backend; gdouble dpi; backend = clutter_get_default_backend (); dpi = clutter_backend_get_resolution (backend); if (dpi < 0) dpi = DPI_FALLBACK; return pt * dpi / 72.0; } /** * clutter_units_em: * @em: em to convert * * Converts a value in em to #ClutterUnits at the * current DPI. * * Return value: the value in units * * Since: 1.0 */ ClutterUnit clutter_units_em (gdouble em) { ClutterBackend *backend; const gchar *font_name; gdouble dpi; ClutterUnit retval = 0; backend = clutter_get_default_backend (); dpi = clutter_backend_get_resolution (backend); font_name = clutter_backend_get_font_name (backend); if (G_LIKELY ((font_name && *font_name != '\0'))) { PangoFontDescription *font_desc; gdouble font_size = 0; font_desc = pango_font_description_from_string (font_name); if (G_LIKELY (font_desc != NULL)) { gint pango_size; gboolean is_absolute; pango_size = pango_font_description_get_size (font_desc); is_absolute = pango_font_description_get_size_is_absolute (font_desc); if (!is_absolute) font_size = ((gdouble) font_size) / PANGO_SCALE; pango_font_description_free (font_desc); } /* 10 points at 96 DPI is 12 pixels */ retval = 1.2 * font_size * dpi / 96.0; } return retval; } static GTypeInfo _info = { 0, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, }; static GTypeFundamentalInfo _finfo = { 0, }; static void clutter_value_init_unit (GValue *value) { value->data[0].v_int = 0; } static void clutter_value_copy_unit (const GValue *src, GValue *dest) { dest->data[0].v_int = src->data[0].v_int; } static gchar * clutter_value_collect_unit (GValue *value, guint n_collect_values, GTypeCValue *collect_values, guint collect_flags) { value->data[0].v_int = collect_values[0].v_int; return NULL; } static gchar * clutter_value_lcopy_unit (const GValue *value, guint n_collect_values, GTypeCValue *collect_values, guint collect_flags) { gint32 *units_p = collect_values[0].v_pointer; if (!units_p) return g_strdup_printf ("value location for `%s' passed as NULL", G_VALUE_TYPE_NAME (value)); *units_p = value->data[0].v_int; return NULL; } static void clutter_value_transform_unit_int (const GValue *src, GValue *dest) { dest->data[0].v_int = CLUTTER_UNITS_TO_INT (src->data[0].v_int); } static void clutter_value_transform_int_unit (const GValue *src, GValue *dest) { dest->data[0].v_int = CLUTTER_UNITS_FROM_INT (src->data[0].v_int); } static const GTypeValueTable _clutter_unit_value_table = { clutter_value_init_unit, NULL, clutter_value_copy_unit, NULL, "i", clutter_value_collect_unit, "p", clutter_value_lcopy_unit }; GType clutter_unit_get_type (void) { static GType _clutter_unit_type = 0; if (G_UNLIKELY (_clutter_unit_type == 0)) { _info.value_table = & _clutter_unit_value_table; _clutter_unit_type = g_type_register_fundamental (g_type_fundamental_next (), I_("ClutterUnit"), &_info, &_finfo, 0); g_value_register_transform_func (_clutter_unit_type, G_TYPE_INT, clutter_value_transform_unit_int); g_value_register_transform_func (G_TYPE_INT, _clutter_unit_type, clutter_value_transform_int_unit); } return _clutter_unit_type; } /** * clutter_value_set_unit: * @value: a #GValue initialized to #CLUTTER_TYPE_UNIT * @units: the units to set * * Sets @value to @units * * Since: 0.8 */ void clutter_value_set_unit (GValue *value, ClutterUnit units) { g_return_if_fail (CLUTTER_VALUE_HOLDS_UNIT (value)); value->data[0].v_int = units; } /** * clutter_value_get_unit: * @value: a #GValue initialized to #CLUTTER_TYPE_UNIT * * Gets the #ClutterUnits contained in @value. * * Return value: the units inside the passed #GValue * * Since: 0.8 */ ClutterUnit clutter_value_get_unit (const GValue *value) { g_return_val_if_fail (CLUTTER_VALUE_HOLDS_UNIT (value), 0); return value->data[0].v_int; } static void param_unit_init (GParamSpec *pspec) { ClutterParamSpecUnit *uspec = CLUTTER_PARAM_SPEC_UNIT (pspec); uspec->minimum = CLUTTER_MINUNIT; uspec->maximum = CLUTTER_MAXUNIT; uspec->default_value = 0; } static void param_unit_set_default (GParamSpec *pspec, GValue *value) { value->data[0].v_int = CLUTTER_PARAM_SPEC_UNIT (pspec)->default_value; } static gboolean param_unit_validate (GParamSpec *pspec, GValue *value) { ClutterParamSpecUnit *uspec = CLUTTER_PARAM_SPEC_UNIT (pspec); gint oval = CLUTTER_UNITS_TO_INT (value->data[0].v_int); gint min, max, val; g_assert (CLUTTER_IS_PARAM_SPEC_UNIT (pspec)); /* we compare the integer part of the value because the minimum * and maximum values cover just that part of the representation */ min = uspec->minimum; max = uspec->maximum; val = CLUTTER_UNITS_TO_INT (value->data[0].v_int); val = CLAMP (val, min, max); if (val != oval) { value->data[0].v_int = val; return TRUE; } return FALSE; } static gint param_unit_values_cmp (GParamSpec *pspec, const GValue *value1, const GValue *value2) { if (value1->data[0].v_int < value2->data[0].v_int) return -1; else return value1->data[0].v_int > value2->data[0].v_int; } GType clutter_param_unit_get_type (void) { static GType pspec_type = 0; if (G_UNLIKELY (pspec_type == 0)) { const GParamSpecTypeInfo pspec_info = { sizeof (ClutterParamSpecUnit), 16, param_unit_init, CLUTTER_TYPE_UNIT, NULL, param_unit_set_default, param_unit_validate, param_unit_values_cmp, }; pspec_type = g_param_type_register_static (I_("ClutterParamSpecUnit"), &pspec_info); } return pspec_type; } /** * clutter_param_spec_unit: * @name: name of the property * @nick: short name * @blurb: description (can be translatable) * @minimum: lower boundary * @maximum: higher boundary * @default_value: default value * @flags: flags for the param spec * * Creates a #GParamSpec for properties using #ClutterUnits. * * Return value: the newly created #GParamSpec * * Since: 0.8 */ GParamSpec * clutter_param_spec_unit (const gchar *name, const gchar *nick, const gchar *blurb, ClutterUnit minimum, ClutterUnit maximum, ClutterUnit default_value, GParamFlags flags) { ClutterParamSpecUnit *uspec; g_return_val_if_fail (default_value >= minimum && default_value <= maximum, NULL); uspec = g_param_spec_internal (CLUTTER_TYPE_PARAM_UNIT, name, nick, blurb, flags); uspec->minimum = minimum; uspec->maximum = maximum; uspec->default_value = default_value; return G_PARAM_SPEC (uspec); }