gtkcssstyleproperty.c 18.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/*
 * Copyright © 2011 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.1 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
Javier Jardón's avatar
Javier Jardón committed
15
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 17 18 19 20 21 22 23
 *
 * Authors: Benjamin Otte <otte@gnome.org>
 */

#include "config.h"

#include "gtkcssstylepropertyprivate.h"

24 25
#include "gtkcssstylefuncsprivate.h"
#include "gtkcsstypesprivate.h"
26
#include "gtkintl.h"
27
#include "gtkprivatetypebuiltins.h"
28 29
#include "gtkstylepropertiesprivate.h"

30
#include <math.h>
31
#include <cairo-gobject.h>
32 33 34
#include "gtkcssimagegradientprivate.h"
#include "gtkcssimageprivate.h"

35 36 37 38 39
/* this is in case round() is not provided by the compiler, 
 * such as in the case of C89 compilers, like MSVC
 */
#include "fallback-c89.c"

40 41 42
enum {
  PROP_0,
  PROP_ID,
43 44
  PROP_SPECIFIED_TYPE,
  PROP_COMPUTED_TYPE,
45 46
  PROP_INHERIT,
  PROP_INITIAL
47 48
};

49 50
G_DEFINE_TYPE (GtkCssStyleProperty, _gtk_css_style_property, GTK_TYPE_STYLE_PROPERTY)

51 52 53 54 55 56 57 58 59 60 61 62
static void
gtk_css_style_property_constructed (GObject *object)
{
  GtkCssStyleProperty *property = GTK_CSS_STYLE_PROPERTY (object);
  GtkCssStylePropertyClass *klass = GTK_CSS_STYLE_PROPERTY_GET_CLASS (property);

  property->id = klass->style_properties->len;
  g_ptr_array_add (klass->style_properties, property);

  G_OBJECT_CLASS (_gtk_css_style_property_parent_class)->constructed (object);
}

63 64 65 66 67 68 69 70 71 72 73 74 75 76
static void
gtk_css_style_property_set_property (GObject      *object,
                                     guint         prop_id,
                                     const GValue *value,
                                     GParamSpec   *pspec)
{
  GtkCssStyleProperty *property = GTK_CSS_STYLE_PROPERTY (object);

  switch (prop_id)
    {
    case PROP_INHERIT:
      property->inherit = g_value_get_boolean (value);
      break;
    case PROP_INITIAL:
77 78
      property->initial_value = g_value_dup_boxed (value);
      g_assert (property->initial_value != NULL);
79
      break;
80 81 82 83
    case PROP_COMPUTED_TYPE:
      property->computed_type = g_value_get_gtype (value);
      g_assert (property->computed_type != G_TYPE_NONE);
      break;
84 85 86 87 88 89
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

90 91 92 93 94 95 96 97 98 99
static void
gtk_css_style_property_get_property (GObject    *object,
                                     guint       prop_id,
                                     GValue     *value,
                                     GParamSpec *pspec)
{
  GtkCssStyleProperty *property = GTK_CSS_STYLE_PROPERTY (object);

  switch (prop_id)
    {
100 101 102 103 104 105
    case PROP_SPECIFIED_TYPE:
      g_value_set_gtype (value, G_VALUE_TYPE (&property->initial_value));
      break;
    case PROP_COMPUTED_TYPE:
      g_value_set_gtype (value, property->computed_type);
      break;
106 107 108
    case PROP_ID:
      g_value_set_boolean (value, property->id);
      break;
109 110 111 112
    case PROP_INHERIT:
      g_value_set_boolean (value, property->inherit);
      break;
    case PROP_INITIAL:
113
      g_value_set_boxed (value, property->initial_value);
114
      break;
115 116 117 118 119 120
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

121 122 123 124 125 126
static void
_gtk_css_style_property_assign (GtkStyleProperty   *property,
                                GtkStyleProperties *props,
                                GtkStateFlags       state,
                                const GValue       *value)
{
127
  GtkCssValue *css_value = _gtk_css_value_new_from_gvalue (value);
128 129 130
  _gtk_style_properties_set_property_by_property (props,
                                                  GTK_CSS_STYLE_PROPERTY (property),
                                                  state,
131 132
                                                  css_value);
  _gtk_css_value_unref (css_value);
133 134
}

135
static GtkCssValue *
136
_gtk_css_style_property_query (GtkStyleProperty   *property,
137 138
                               GtkStyleQueryFunc   query_func,
                               gpointer            query_data)
139
{
140
  GtkCssValue *css_value;
141
  
142 143
  css_value = (* query_func) (GTK_CSS_STYLE_PROPERTY (property)->id, query_data);
  if (css_value)
144 145
    {
      /* Somebody make this a vfunc */
146
      if (_gtk_css_value_holds (css_value, GTK_TYPE_CSS_IMAGE))
147
        {
148
          GtkCssImage *image = _gtk_css_value_get_image (css_value);
149 150 151 152 153
          cairo_pattern_t *pattern;
          cairo_surface_t *surface;
          cairo_matrix_t matrix;
          
          if (image == NULL)
154
	    return _gtk_css_value_new_from_pattern (NULL);
155
          else if (GTK_IS_CSS_IMAGE_GRADIENT (image))
156
	    return _gtk_css_value_new_from_pattern (GTK_CSS_IMAGE_GRADIENT (image)->pattern);
157 158 159 160 161 162 163 164 165 166 167
          else
            {
              double width, height;

              /* the 100, 100 is rather random */
              _gtk_css_image_get_concrete_size (image, 0, 0, 100, 100, &width, &height);
              surface = _gtk_css_image_get_surface (image, NULL, width, height);
              pattern = cairo_pattern_create_for_surface (surface);
              cairo_matrix_init_scale (&matrix, width, height);
              cairo_pattern_set_matrix (pattern, &matrix);
              cairo_surface_destroy (surface);
168
	      return _gtk_css_value_new_take_pattern (pattern);
169 170
            }
        }
171
      else if (_gtk_css_value_holds (css_value, GTK_TYPE_CSS_NUMBER))
172
        {
173 174
	  int v = round (_gtk_css_number_get (_gtk_css_value_get_number (css_value), 100));
	  return _gtk_css_value_new_from_int (v);
175
        }
176
      else
177
	return _gtk_css_value_ref (css_value);
178
    }
179
  else
180
    return _gtk_css_value_ref (_gtk_css_style_property_get_initial_value (GTK_CSS_STYLE_PROPERTY (property)));
181 182
}

183 184 185 186 187 188
static gboolean
gtk_css_style_property_parse_value (GtkStyleProperty *property,
                                    GValue           *value,
                                    GtkCssParser     *parser,
                                    GFile            *base)
{
189
  GtkCssStyleProperty *style_property = GTK_CSS_STYLE_PROPERTY (property);
190

191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
  if (_gtk_css_parser_try (parser, "initial", TRUE))
    {
      /* the initial value can be explicitly specified with the
       * ‘initial’ keyword which all properties accept.
       */
      g_value_init (value, GTK_TYPE_CSS_SPECIAL_VALUE);
      g_value_set_enum (value, GTK_CSS_INITIAL);
      return TRUE;
    }
  else if (_gtk_css_parser_try (parser, "inherit", TRUE))
    {
      /* All properties accept the ‘inherit’ value which
       * explicitly specifies that the value will be determined
       * by inheritance. The ‘inherit’ value can be used to
       * strengthen inherited values in the cascade, and it can
       * also be used on properties that are not normally inherited.
       */
      g_value_init (value, GTK_TYPE_CSS_SPECIAL_VALUE);
      g_value_set_enum (value, GTK_CSS_INHERIT);
      return TRUE;
    }
212

213
  g_value_init (value, _gtk_css_style_property_get_specified_type (style_property));
214 215 216 217 218
  if (!(* style_property->parse_value) (style_property, value, parser, base))
    {
      g_value_unset (value);
      return FALSE;
    }
219

220
  return TRUE;
221 222
}

223 224 225
static void
_gtk_css_style_property_class_init (GtkCssStylePropertyClass *klass)
{
226
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
227
  GtkStylePropertyClass *property_class = GTK_STYLE_PROPERTY_CLASS (klass);
228 229

  object_class->constructed = gtk_css_style_property_constructed;
230
  object_class->set_property = gtk_css_style_property_set_property;
231 232 233 234 235 236 237 238 239
  object_class->get_property = gtk_css_style_property_get_property;

  g_object_class_install_property (object_class,
                                   PROP_ID,
                                   g_param_spec_uint ("id",
                                                      P_("ID"),
                                                      P_("The numeric id for quick access"),
                                                      0, G_MAXUINT, 0,
                                                      G_PARAM_READABLE));
240 241 242 243 244 245 246 247 248 249 250 251 252 253
  g_object_class_install_property (object_class,
                                   PROP_SPECIFIED_TYPE,
                                   g_param_spec_gtype ("specified-type",
                                                       P_("Specified type"),
                                                       P_("The type of values after parsing"),
                                                       G_TYPE_NONE,
                                                       G_PARAM_READABLE));
  g_object_class_install_property (object_class,
                                   PROP_COMPUTED_TYPE,
                                   g_param_spec_gtype ("computed-type",
                                                       P_("Computed type"),
                                                       P_("The type of values after style lookup"),
                                                       G_TYPE_NONE,
                                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
254 255 256 257 258 259 260 261 262 263 264 265
  g_object_class_install_property (object_class,
                                   PROP_INHERIT,
                                   g_param_spec_boolean ("inherit",
                                                         P_("Inherit"),
                                                         P_("Set if the value is inherited by default"),
                                                         FALSE,
                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
  g_object_class_install_property (object_class,
                                   PROP_INITIAL,
                                   g_param_spec_boxed ("initial-value",
                                                       P_("Initial value"),
                                                       P_("The initial specified value used for this property"),
266
                                                       GTK_TYPE_CSS_VALUE,
267
                                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
268

269 270
  property_class->assign = _gtk_css_style_property_assign;
  property_class->query = _gtk_css_style_property_query;
271
  property_class->parse_value = gtk_css_style_property_parse_value;
272

273
  klass->style_properties = g_ptr_array_new ();
274 275
}

276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
static gboolean
gtk_css_style_property_real_parse_value (GtkCssStyleProperty *property,
                                         GValue              *value,
                                         GtkCssParser        *parser,
                                         GFile               *base)
{
  return _gtk_css_style_parse_value (value, parser, base);
}

static void
gtk_css_style_property_real_print_value (GtkCssStyleProperty *property,
                                         const GValue        *value,
                                         GString             *string)
{
  _gtk_css_style_print_value (value, string);
}

293
static GtkCssValue *
294 295
gtk_css_style_property_real_compute_value (GtkCssStyleProperty *property,
                                           GtkStyleContext     *context,
296
                                           GtkCssValue         *specified)
297
{
298
  return _gtk_css_style_compute_value (context, _gtk_css_style_property_get_computed_type (property), specified);
299
}
300

301
static void
302
_gtk_css_style_property_init (GtkCssStyleProperty *property)
303
{
304 305
  property->parse_value = gtk_css_style_property_real_parse_value;
  property->print_value = gtk_css_style_property_real_print_value;
306
  property->compute_value = gtk_css_style_property_real_compute_value;
307 308
}

309 310 311 312 313 314 315 316 317 318 319 320 321 322
/**
 * _gtk_css_style_property_get_n_properties:
 *
 * Gets the number of style properties. This number can increase when new
 * theme engines are loaded. Shorthand properties are not included here.
 *
 * Returns: The number of style properties.
 **/
guint
_gtk_css_style_property_get_n_properties (void)
{
  GtkCssStylePropertyClass *klass;

  klass = g_type_class_peek (GTK_TYPE_CSS_STYLE_PROPERTY);
323 324 325 326 327 328
  if (G_UNLIKELY (klass == NULL))
    {
      _gtk_style_property_init_properties ();
      klass = g_type_class_peek (GTK_TYPE_CSS_STYLE_PROPERTY);
      g_assert (klass);
    }
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348

  return klass->style_properties->len;
}

/**
 * _gtk_css_style_property_lookup_by_id:
 * @id: the id of the property
 *
 * Gets the style property with the given id. All style properties (but not
 * shorthand properties) are indexable by id so that it's easy to use arrays
 * when doing style lookups.
 *
 * Returns: (transfer none): The style property with the given id
 **/
GtkCssStyleProperty *
_gtk_css_style_property_lookup_by_id (guint id)
{
  GtkCssStylePropertyClass *klass;

  klass = g_type_class_peek (GTK_TYPE_CSS_STYLE_PROPERTY);
349 350 351 352 353 354 355
  if (G_UNLIKELY (klass == NULL))
    {
      _gtk_style_property_init_properties ();
      klass = g_type_class_peek (GTK_TYPE_CSS_STYLE_PROPERTY);
      g_assert (klass);
    }
  g_return_val_if_fail (id < klass->style_properties->len, NULL);
356 357 358 359

  return g_ptr_array_index (klass->style_properties, id);
}

360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377
/**
 * _gtk_css_style_property_is_inherit:
 * @property: the property
 *
 * Queries if the given @property is inherited. See
 * <ulink url="http://www.w3.org/TR/css3-cascade/#inheritance>
 * the CSS documentation</ulink> for an explanation of this concept.
 *
 * Returns: %TRUE if the property is inherited by default.
 **/
gboolean
_gtk_css_style_property_is_inherit (GtkCssStyleProperty *property)
{
  g_return_val_if_fail (GTK_IS_CSS_STYLE_PROPERTY (property), 0);

  return property->inherit;
}

378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394
/**
 * _gtk_css_style_property_get_id:
 * @property: the property
 *
 * Gets the id for the given property. IDs are used to allow using arrays
 * for style lookups.
 *
 * Returns: The id of the property
 **/
guint
_gtk_css_style_property_get_id (GtkCssStyleProperty *property)
{
  g_return_val_if_fail (GTK_IS_CSS_STYLE_PROPERTY (property), 0);

  return property->id;
}

395 396 397 398 399 400 401 402 403 404
/**
 * _gtk_css_style_property_get_initial_value:
 * @property: the property
 *
 * Queries the initial value of the given @property. See
 * <ulink url="http://www.w3.org/TR/css3-cascade/#intial>
 * the CSS documentation</ulink> for an explanation of this concept.
 *
 * Returns: a reference to the initial value. The value will never change.
 **/
405
GtkCssValue *
406 407
_gtk_css_style_property_get_initial_value (GtkCssStyleProperty *property)
{
408
  g_return_val_if_fail (GTK_IS_CSS_STYLE_PROPERTY (property), NULL);
409

410
  return property->initial_value;
411 412
}

413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448
/**
 * _gtk_css_style_property_get_computed_type:
 * @property: the property to query
 *
 * Gets the #GType used for values for this property after a CSS lookup has
 * happened. _gtk_css_style_property_compute_value() will convert values to
 * this type.
 *
 * Returns: the #GType used for computed values.
 **/
GType
_gtk_css_style_property_get_computed_type (GtkCssStyleProperty *property)
{
  g_return_val_if_fail (GTK_IS_CSS_STYLE_PROPERTY (property), G_TYPE_NONE);

  return property->computed_type;
}

/**
 * _gtk_css_style_property_get_specified_type:
 * @property: the property to query
 *
 * Gets the #GType used for values for this property after CSS parsing if
 * the value is not a special keyword. _gtk_css_style_property_compute_value()
 * will convert values of this type to the computed type.
 *
 * The initial value returned by _gtk_css_style_property_get_initial_value()
 * will be of this type.
 *
 * Returns: the #GType used for specified values.
 **/
GType
_gtk_css_style_property_get_specified_type (GtkCssStyleProperty *property)
{
  g_return_val_if_fail (GTK_IS_CSS_STYLE_PROPERTY (property), G_TYPE_NONE);

449
  return _gtk_css_value_get_content_type (property->initial_value);
450 451
}

452 453 454 455 456 457 458
gboolean
_gtk_css_style_property_is_specified_type (GtkCssStyleProperty *property,
                                           GType                type)
{
  g_return_val_if_fail (GTK_IS_CSS_STYLE_PROPERTY (property), FALSE);

  /* If it's our specified type, of course it's valid */
459
  if (type == _gtk_css_value_get_content_type (property->initial_value))
460 461 462 463 464 465 466
    return TRUE;

  /* The special values 'inherit' and 'initial' are always valid */
  if (type == GTK_TYPE_CSS_SPECIAL_VALUE)
    return TRUE;

  /* XXX: Someone needs to fix that legacy */
467 468 469 470
  if ((_gtk_css_value_holds (property->initial_value, GDK_TYPE_RGBA) ||
       _gtk_css_value_holds (property->initial_value, GDK_TYPE_COLOR)) &&
      type == GTK_TYPE_GRADIENT)
    return TRUE;
471
  if (_gtk_css_value_holds (property->initial_value, CAIRO_GOBJECT_TYPE_PATTERN) &&
472 473 474 475 476 477
      type == GTK_TYPE_GRADIENT)
    return TRUE;

  return FALSE;
}

478 479 480 481 482 483 484 485 486 487 488 489
/**
 * _gtk_css_style_property_compute_value:
 * @property: the property
 * @computed: (out): an uninitialized value to be filled with the result
 * @context: the context to use for resolving
 * @specified: the value to compute from
 *
 * Converts the @specified value into the @computed value using the
 * information in @context. This step is explained in detail in
 * <ulink url="http://www.w3.org/TR/css3-cascade/#computed>
 * the CSS documentation</ulink>.
 **/
490
GtkCssValue *
491 492
_gtk_css_style_property_compute_value (GtkCssStyleProperty *property,
                                       GtkStyleContext     *context,
493
                                       GtkCssValue         *specified)
494
{
495 496
  g_return_val_if_fail (GTK_IS_CSS_STYLE_PROPERTY (property), NULL);
  g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
497

498
  return property->compute_value (property, context, specified);
499 500
}

501 502 503 504 505 506 507 508 509 510 511 512
/**
 * _gtk_css_style_property_print_value:
 * @property: the property
 * @value: the value to print
 * @string: the string to print to
 *
 * Prints @value to the given @string in CSS format. The @value must be a
 * valid specified value as parsed using the parse functions or as assigned
 * via _gtk_style_property_assign().
 **/
void
_gtk_css_style_property_print_value (GtkCssStyleProperty    *property,
513
                                     GtkCssValue            *css_value,
514 515 516
                                     GString                *string)
{
  g_return_if_fail (GTK_IS_CSS_STYLE_PROPERTY (property));
517
  g_return_if_fail (css_value != NULL);
518 519
  g_return_if_fail (string != NULL);

520
  if (_gtk_css_value_is_special (css_value))
521 522 523 524 525
    {
      GEnumClass *enum_class;
      GEnumValue *enum_value;

      enum_class = g_type_class_ref (GTK_TYPE_CSS_SPECIAL_VALUE);
526
      enum_value = g_enum_get_value (enum_class, _gtk_css_value_get_special_kind (css_value));
527 528 529 530 531 532

      g_string_append (string, enum_value->value_nick);

      g_type_class_unref (enum_class);
    }
  else
533 534 535 536 537 538
    {
      GValue value = G_VALUE_INIT;
      _gtk_css_value_init_gvalue (css_value, &value);
      property->print_value (property, &value, string);
      g_value_unset (&value);
    }
539 540
}