gtkstyleproperties.c 29.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/* GTK - The GIMP Toolkit
 * Copyright (C) 2010 Carlos Garnacho <carlosg@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 "config.h"

22
#include "gtkstylepropertiesprivate.h"
Matthias Clasen's avatar
Matthias Clasen committed
23

24
#include <stdlib.h>
25
#include <gobject/gvaluecollector.h>
26
#include <cairo-gobject.h>
27

28
#include "gtkstyleprovider.h"
Matthias Clasen's avatar
Matthias Clasen committed
29
#include "gtksymboliccolor.h"
30
#include "gtkthemingengine.h"
31
#include "gtkanimationdescription.h"
Matthias Clasen's avatar
Matthias Clasen committed
32
#include "gtkgradient.h"
33
#include "gtkshadowprivate.h"
34
#include "gtkcssshorthandpropertyprivate.h"
35
#include "gtkcsstypesprivate.h"
36
#include "gtkborderimageprivate.h"
37

38
#include "gtkprivatetypebuiltins.h"
39
#include "gtkstylepropertyprivate.h"
40
#include "gtkstyleproviderprivate.h"
41 42
#include "gtkintl.h"

43 44
#include "gtkwin32themeprivate.h"

45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
/**
 * SECTION:gtkstyleproperties
 * @Short_description: Store for style property information
 * @Title: GtkStyleProperties
 *
 * GtkStyleProperties provides the storage for style information
 * that is used by #GtkStyleContext and other #GtkStyleProvider
 * implementations.
 *
 * Before style properties can be stored in GtkStyleProperties, they
 * must be registered with gtk_style_properties_register_property().
 *
 * Unless you are writing a #GtkStyleProvider implementation, you
 * are unlikely to use this API directly, as gtk_style_context_get()
 * and its variants are the preferred way to access styling information
 * from widget implementations and theming engine implementations
 * should use the APIs provided by #GtkThemingEngine instead.
 */

64
typedef struct PropertyData PropertyData;
65
typedef struct ValueData ValueData;
66

67 68 69 70 71 72
struct ValueData
{
  GtkStateFlags state;
  GValue value;
};

73 74
struct PropertyData
{
75
  GArray *values;
76 77
};

78
struct _GtkStylePropertiesPrivate
79
{
80
  GHashTable *color_map;
81
  GHashTable *properties;
82 83
  GtkSymbolicColorLookupFunc color_lookup_func;
  gpointer color_lookup_data;
84 85
};

86 87 88
static void gtk_style_properties_provider_init         (GtkStyleProviderIface            *iface);
static void gtk_style_properties_provider_private_init (GtkStyleProviderPrivateInterface *iface);
static void gtk_style_properties_finalize              (GObject                          *object);
89 90


91
G_DEFINE_TYPE_EXTENDED (GtkStyleProperties, gtk_style_properties, G_TYPE_OBJECT, 0,
92
                        G_IMPLEMENT_INTERFACE (GTK_TYPE_STYLE_PROVIDER,
93 94 95
                                               gtk_style_properties_provider_init)
                        G_IMPLEMENT_INTERFACE (GTK_TYPE_STYLE_PROVIDER_PRIVATE,
                                               gtk_style_properties_provider_private_init));
96 97

static void
98
gtk_style_properties_class_init (GtkStylePropertiesClass *klass)
99 100 101
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

102
  object_class->finalize = gtk_style_properties_finalize;
103

104
  g_type_class_add_private (object_class, sizeof (GtkStylePropertiesPrivate));
105 106 107 108 109 110 111 112
}

static PropertyData *
property_data_new (void)
{
  PropertyData *data;

  data = g_slice_new0 (PropertyData);
113
  data->values = g_array_new (FALSE, FALSE, sizeof (ValueData));
114 115 116 117 118

  return data;
}

static void
119
property_data_remove_values (PropertyData *data)
120
{
121
  guint i;
122

123
  for (i = 0; i < data->values->len; i++)
124
    {
125 126 127 128 129 130
      ValueData *value_data;

      value_data = &g_array_index (data->values, ValueData, i);

      if (G_IS_VALUE (&value_data->value))
        g_value_unset (&value_data->value);
131
    }
132

133 134 135 136 137 138 139 140
  if (data->values->len > 0)
    g_array_remove_range (data->values, 0, data->values->len);
}

static void
property_data_free (PropertyData *data)
{
  property_data_remove_values (data);
Carlos Garnacho's avatar
Carlos Garnacho committed
141
  g_array_free (data->values, TRUE);
142 143 144
  g_slice_free (PropertyData, data);
}

145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
static gboolean
property_data_find_position (PropertyData  *data,
                             GtkStateFlags  state,
                             guint         *pos)
{
  gint min, max, mid;
  gboolean found = FALSE;
  guint position;

  if (pos)
    *pos = 0;

  if (data->values->len == 0)
    return FALSE;

  /* Find position for the given state, or the position where
   * it would be if not found, the array is ordered by the
   * state flags.
   */
  min = 0;
  max = data->values->len - 1;

  do
    {
      ValueData *value_data;

      mid = (min + max) / 2;
      value_data = &g_array_index (data->values, ValueData, mid);

      if (value_data->state == state)
        {
          found = TRUE;
          position = mid;
        }
      else if (value_data->state < state)
          position = min = mid + 1;
      else
        {
          max = mid - 1;
          position = mid;
        }
    }
  while (!found && min <= max);

  if (pos)
    *pos = position;

  return found;
}

static GValue *
property_data_get_value (PropertyData  *data,
                         GtkStateFlags  state)
{
  ValueData *val_data;
  guint pos;

  if (!property_data_find_position (data, state, &pos))
    {
      ValueData new = { 0 };

      new.state = state;
      g_array_insert_val (data->values, pos, new);
    }

  val_data = &g_array_index (data->values, ValueData, pos);

  return &val_data->value;
}

static GValue *
property_data_match_state (PropertyData  *data,
                           GtkStateFlags  state)
{
  guint pos;
  gint i;

  if (property_data_find_position (data, state, &pos))
    {
      ValueData *val_data;

      /* Exact match */
      val_data = &g_array_index (data->values, ValueData, pos);
      return &val_data->value;
    }

  if (pos >= data->values->len)
    pos = data->values->len - 1;

  /* No exact match, go downwards the list to find
   * the closest match to the given state flags, as
   * a side effect, there is an implicit precedence
   * of higher flags over the smaller ones.
   */
  for (i = pos; i >= 0; i--)
    {
      ValueData *val_data;

      val_data = &g_array_index (data->values, ValueData, i);

       /* Check whether any of the requested
        * flags are set, and no other flags are.
        *
        * Also, no flags acts as a wildcard, such
        * value should be always in the first position
        * in the array (if present) anyways.
        */
      if (val_data->state == 0 ||
          ((val_data->state & state) != 0 &&
           (val_data->state & ~state) == 0))
        return &val_data->value;
    }

  return NULL;
}

261
static void
262
gtk_style_properties_init (GtkStyleProperties *props)
263
{
264
  GtkStylePropertiesPrivate *priv;
265

266 267 268
  priv = props->priv = G_TYPE_INSTANCE_GET_PRIVATE (props,
                                                    GTK_TYPE_STYLE_PROPERTIES,
                                                    GtkStylePropertiesPrivate);
269

270
  priv->properties = g_hash_table_new_full (NULL, NULL, NULL,
271 272 273 274
                                            (GDestroyNotify) property_data_free);
}

static void
275
gtk_style_properties_finalize (GObject *object)
276
{
277 278
  GtkStylePropertiesPrivate *priv;
  GtkStyleProperties *props;
279

280 281
  props = GTK_STYLE_PROPERTIES (object);
  priv = props->priv;
282 283
  g_hash_table_destroy (priv->properties);

284 285 286
  if (priv->color_map)
    g_hash_table_destroy (priv->color_map);

287
  G_OBJECT_CLASS (gtk_style_properties_parent_class)->finalize (object);
288 289
}

290 291 292
GtkStyleProperties *
gtk_style_properties_get_style (GtkStyleProvider *provider,
                                GtkWidgetPath    *path)
293 294 295 296 297 298
{
  /* Return style set itself */
  return g_object_ref (provider);
}

static void
299
gtk_style_properties_provider_init (GtkStyleProviderIface *iface)
300
{
301
  iface->get_style = gtk_style_properties_get_style;
302 303
}

304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354
static GtkSymbolicColor *
gtk_style_properties_provider_get_color (GtkStyleProviderPrivate *provider,
                                         const char              *name)
{
  return gtk_style_properties_lookup_color (GTK_STYLE_PROPERTIES (provider), name);
}

static void
gtk_style_properties_provider_lookup (GtkStyleProviderPrivate *provider,
                                      GtkWidgetPath           *path,
                                      GtkStateFlags            state,
                                      GtkCssLookup            *lookup)
{
  GtkStyleProperties *props;
  GtkStylePropertiesPrivate *priv;
  GHashTableIter iter;
  gpointer key, value;

  props = GTK_STYLE_PROPERTIES (provider);
  priv = props->priv;

  /* Merge symbolic style properties */
  g_hash_table_iter_init (&iter, priv->properties);

  while (g_hash_table_iter_next (&iter, &key, &value))
    {
      GtkStyleProperty *prop = key;
      PropertyData *data = value;
      const GValue *value;
      guint id;

      id = _gtk_style_property_get_id (prop);

      if (!_gtk_css_lookup_is_missing (lookup, id))
          continue;

      value = property_data_match_state (data, state);
      if (value == NULL)
        continue;

      _gtk_css_lookup_set (lookup, id, value);
    }
}

static void
gtk_style_properties_provider_private_init (GtkStyleProviderPrivateInterface *iface)
{
  iface->get_color = gtk_style_properties_provider_get_color;
  iface->lookup = gtk_style_properties_provider_lookup;
}

355
/* Property registration functions */
Carlos Garnacho's avatar
Carlos Garnacho committed
356 357

/**
358
 * gtk_style_properties_register_property: (skip)
Carlos Garnacho's avatar
Carlos Garnacho committed
359
 * @parse_func: parsing function to use, or %NULL
360
 * @pspec: the #GParamSpec for the new property
Carlos Garnacho's avatar
Carlos Garnacho committed
361 362 363 364 365 366 367 368
 *
 * Registers a property so it can be used in the CSS file format.
 * This function is the low-level equivalent of
 * gtk_theming_engine_register_property(), if you are implementing
 * a theming engine, you want to use that function instead.
 *
 * Since: 3.0
 **/
369
void
370 371
gtk_style_properties_register_property (GtkStylePropertyParser  parse_func,
                                        GParamSpec             *pspec)
372
{
373
  g_return_if_fail (G_IS_PARAM_SPEC (pspec));
374

375
  _gtk_style_property_register (pspec,
Benjamin Otte's avatar
Benjamin Otte committed
376
                                0,
377 378
                                parse_func,
                                NULL,
379
                                NULL,
380
                                NULL);
381 382
}

Carlos Garnacho's avatar
Carlos Garnacho committed
383
/**
384
 * gtk_style_properties_lookup_property: (skip)
Carlos Garnacho's avatar
Carlos Garnacho committed
385
 * @property_name: property name to look up
386
 * @parse_func: (out): return location for the parse function
387
 * @pspec: (out) (transfer none): return location for the #GParamSpec
Carlos Garnacho's avatar
Carlos Garnacho committed
388
 *
389 390
 * Returns %TRUE if a property has been registered, if @pspec or
 * @parse_func are not %NULL, the #GParamSpec and parsing function
Carlos Garnacho's avatar
Carlos Garnacho committed
391 392 393 394 395 396
 * will be respectively returned.
 *
 * Returns: %TRUE if the property is registered, %FALSE otherwise
 *
 * Since: 3.0
 **/
397
gboolean
398 399 400
gtk_style_properties_lookup_property (const gchar             *property_name,
                                      GtkStylePropertyParser  *parse_func,
                                      GParamSpec             **pspec)
401
{
Benjamin Otte's avatar
Benjamin Otte committed
402
  GtkStyleProperty *node;
403
  gboolean found = FALSE;
404 405 406

  g_return_val_if_fail (property_name != NULL, FALSE);

407
  node = _gtk_style_property_lookup (property_name);
408

409 410 411 412
  if (node)
    {
      if (pspec)
        *pspec = node->pspec;
413

414
      if (parse_func)
Benjamin Otte's avatar
Benjamin Otte committed
415
        *parse_func = node->property_parse_func;
416

417
      found = TRUE;
418 419
    }

420
  return found;
421 422
}

423
/* GtkStyleProperties methods */
424

Carlos Garnacho's avatar
Carlos Garnacho committed
425
/**
426
 * gtk_style_properties_new:
Carlos Garnacho's avatar
Carlos Garnacho committed
427
 *
428
 * Returns a newly created #GtkStyleProperties
Carlos Garnacho's avatar
Carlos Garnacho committed
429
 *
430
 * Returns: a new #GtkStyleProperties
Carlos Garnacho's avatar
Carlos Garnacho committed
431
 **/
432 433
GtkStyleProperties *
gtk_style_properties_new (void)
434
{
435
  return g_object_new (GTK_TYPE_STYLE_PROPERTIES, NULL);
436 437
}

438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454
void
_gtk_style_properties_set_color_lookup_func (GtkStyleProperties         *props,
                                             GtkSymbolicColorLookupFunc  func,
                                             gpointer                    data)
{
  GtkStylePropertiesPrivate *priv;

  g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
  g_return_if_fail (func != NULL);

  priv = props->priv;
  g_return_if_fail (priv->color_map == NULL);

  priv->color_lookup_func = func;
  priv->color_lookup_data = data;
}

Carlos Garnacho's avatar
Carlos Garnacho committed
455
/**
456 457
 * gtk_style_properties_map_color:
 * @props: a #GtkStyleProperties
Carlos Garnacho's avatar
Carlos Garnacho committed
458 459 460 461
 * @name: color name
 * @color: #GtkSymbolicColor to map @name to
 *
 * Maps @color so it can be referenced by @name. See
462
 * gtk_style_properties_lookup_color()
Carlos Garnacho's avatar
Carlos Garnacho committed
463 464 465
 *
 * Since: 3.0
 **/
466
void
467 468 469
gtk_style_properties_map_color (GtkStyleProperties *props,
                                const gchar        *name,
                                GtkSymbolicColor   *color)
470
{
471
  GtkStylePropertiesPrivate *priv;
472

473
  g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
474 475 476
  g_return_if_fail (name != NULL);
  g_return_if_fail (color != NULL);

477
  priv = props->priv;
478
  g_return_if_fail (priv->color_lookup_func == NULL);
479 480 481 482 483 484 485 486 487 488 489 490

  if (G_UNLIKELY (!priv->color_map))
    priv->color_map = g_hash_table_new_full (g_str_hash,
                                             g_str_equal,
                                             (GDestroyNotify) g_free,
                                             (GDestroyNotify) gtk_symbolic_color_unref);

  g_hash_table_replace (priv->color_map,
                        g_strdup (name),
                        gtk_symbolic_color_ref (color));
}

Carlos Garnacho's avatar
Carlos Garnacho committed
491
/**
492 493
 * gtk_style_properties_lookup_color:
 * @props: a #GtkStyleProperties
Carlos Garnacho's avatar
Carlos Garnacho committed
494 495 496 497 498
 * @name: color name to lookup
 *
 * Returns the symbolic color that is mapped
 * to @name.
 *
499
 * Returns: (transfer none): The mapped color
Carlos Garnacho's avatar
Carlos Garnacho committed
500 501 502
 *
 * Since: 3.0
 **/
503
GtkSymbolicColor *
504 505
gtk_style_properties_lookup_color (GtkStyleProperties *props,
                                   const gchar        *name)
506
{
507
  GtkStylePropertiesPrivate *priv;
508

509
  g_return_val_if_fail (GTK_IS_STYLE_PROPERTIES (props), NULL);
510 511
  g_return_val_if_fail (name != NULL, NULL);

512
  priv = props->priv;
513

514 515 516
  if (priv->color_lookup_func)
    return priv->color_lookup_func (priv->color_lookup_data, name);

517 518 519 520 521 522
  if (!priv->color_map)
    return NULL;

  return g_hash_table_lookup (priv->color_map, name);
}

523
void
Benjamin Otte's avatar
Benjamin Otte committed
524 525 526 527
_gtk_style_properties_set_property_by_property (GtkStyleProperties *props,
                                                GtkStyleProperty   *style_prop,
                                                GtkStateFlags       state,
                                                const GValue       *value)
528
{
529
  GtkStylePropertiesPrivate *priv;
530
  PropertyData *prop;
531
  GType value_type;
532
  GValue *val;
533

534
  value_type = G_VALUE_TYPE (value);
535

536 537
  if (style_prop->pspec->value_type == GDK_TYPE_RGBA ||
      style_prop->pspec->value_type == GDK_TYPE_COLOR)
538
    {
539
      /* Allow GtkSymbolicColor and special values as well */
540 541
      g_return_if_fail (value_type == GDK_TYPE_RGBA ||
                        value_type == GDK_TYPE_COLOR ||
542
                        value_type == GTK_TYPE_CSS_SPECIAL_VALUE ||
543
                        value_type == GTK_TYPE_SYMBOLIC_COLOR);
544
    }
545
  else if (style_prop->pspec->value_type == CAIRO_GOBJECT_TYPE_PATTERN)
546
    {
547
      /* Allow GtkGradient and theme part as a substitute */
548
      g_return_if_fail (value_type == CAIRO_GOBJECT_TYPE_PATTERN ||
549 550
                        value_type == GTK_TYPE_GRADIENT	||
			value_type == GTK_TYPE_WIN32_THEME_PART);
551
    }
552 553 554 555 556
  else if (style_prop->pspec->value_type == G_TYPE_INT)
    {
      g_return_if_fail (value_type == G_TYPE_INT ||
                        value_type == GTK_TYPE_CSS_BORDER_RADIUS);
    }
557
  else
558
    g_return_if_fail (style_prop->pspec->value_type == value_type);
559

560
  if (GTK_IS_CSS_SHORTHAND_PROPERTY (style_prop))
561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578
    {
      GParameter *parameters;
      guint i, n_parameters;

      parameters = _gtk_style_property_unpack (style_prop, value, &n_parameters);

      for (i = 0; i < n_parameters; i++)
        {
          gtk_style_properties_set_property (props,
                                             parameters[i].name,
                                             state,
                                             &parameters[i].value);
          g_value_unset (&parameters[i].value);
        }
      g_free (parameters);
      return;
    }

579
  priv = props->priv;
580
  prop = g_hash_table_lookup (priv->properties, style_prop);
581 582 583 584

  if (!prop)
    {
      prop = property_data_new ();
585
      g_hash_table_insert (priv->properties, (gpointer) style_prop, prop);
586 587
    }

588
  val = property_data_get_value (prop, state);
589

590
  if (G_VALUE_TYPE (val) == value_type)
591 592
    g_value_reset (val);
  else
593 594 595 596 597 598
    {
      if (G_IS_VALUE (val))
        g_value_unset (val);

      g_value_init (val, value_type);
    }
599 600

  g_value_copy (value, val);
601 602
  if (style_prop->pspec->value_type == value_type)
    g_param_value_validate (style_prop->pspec, val);
603 604
}

605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621
/**
 * gtk_style_properties_set_property:
 * @props: a #GtkStyleProperties
 * @property: styling property to set
 * @state: state to set the value for
 * @value: new value for the property
 *
 * Sets a styling property in @props.
 *
 * Since: 3.0
 **/
void
gtk_style_properties_set_property (GtkStyleProperties *props,
                                   const gchar        *property,
                                   GtkStateFlags       state,
                                   const GValue       *value)
{
Benjamin Otte's avatar
Benjamin Otte committed
622
  GtkStyleProperty *node;
623 624 625 626 627

  g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
  g_return_if_fail (property != NULL);
  g_return_if_fail (value != NULL);

628
  node = _gtk_style_property_lookup (property);
629 630 631 632 633 634 635

  if (!node)
    {
      g_warning ("Style property \"%s\" is not registered", property);
      return;
    }

636 637 638 639
  _gtk_style_properties_set_property_by_property (props,
                                                  node,
                                                  state,
                                                  value);
640 641
}

Carlos Garnacho's avatar
Carlos Garnacho committed
642
/**
643 644
 * gtk_style_properties_set_valist:
 * @props: a #GtkStyleProperties
Carlos Garnacho's avatar
Carlos Garnacho committed
645 646 647
 * @state: state to set the values for
 * @args: va_list of property name/value pairs, followed by %NULL
 *
648
 * Sets several style properties on @props.
Carlos Garnacho's avatar
Carlos Garnacho committed
649 650 651
 *
 * Since: 3.0
 **/
652
void
653 654 655
gtk_style_properties_set_valist (GtkStyleProperties *props,
                                 GtkStateFlags       state,
                                 va_list             args)
656 657 658
{
  const gchar *property_name;

659
  g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
660 661 662 663 664

  property_name = va_arg (args, const gchar *);

  while (property_name)
    {
Benjamin Otte's avatar
Benjamin Otte committed
665
      GtkStyleProperty *node;
666
      gchar *error = NULL;
Javier Jardón's avatar
Javier Jardón committed
667
      GValue val = G_VALUE_INIT;
668

669
      node = _gtk_style_property_lookup (property_name);
670 671 672 673 674 675 676

      if (!node)
        {
          g_warning ("Style property \"%s\" is not registered", property_name);
          break;
        }

677
      G_VALUE_COLLECT_INIT (&val, node->pspec->value_type,
678
                            args, 0, &error);
679 680 681
      if (error)
        {
          g_warning ("Could not set style property \"%s\": %s", property_name, error);
682
          g_value_unset (&val);
683
          g_free (error);
684 685 686
          break;
        }

687 688 689
      _gtk_style_properties_set_property_by_property (props, node, state, &val);
      g_value_unset (&val);

690 691 692 693
      property_name = va_arg (args, const gchar *);
    }
}

Carlos Garnacho's avatar
Carlos Garnacho committed
694
/**
695 696
 * gtk_style_properties_set:
 * @props: a #GtkStyleProperties
Carlos Garnacho's avatar
Carlos Garnacho committed
697 698 699
 * @state: state to set the values for
 * @...: property name/value pairs, followed by %NULL
 *
700
 * Sets several style properties on @props.
Carlos Garnacho's avatar
Carlos Garnacho committed
701 702 703
 *
 * Since: 3.0
 **/
704
void
705 706 707
gtk_style_properties_set (GtkStyleProperties *props,
                          GtkStateFlags       state,
                          ...)
708 709 710
{
  va_list args;

711
  g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
712 713

  va_start (args, state);
714
  gtk_style_properties_set_valist (props, state, args);
715 716 717
  va_end (args);
}

718
const GValue *
Benjamin Otte's avatar
Benjamin Otte committed
719 720 721
_gtk_style_properties_peek_property (GtkStyleProperties *props,
                                     GtkStyleProperty   *property,
                                     GtkStateFlags       state)
722 723 724 725 726 727 728 729 730 731 732 733 734 735 736
{
  GtkStylePropertiesPrivate *priv;
  PropertyData *prop;

  g_return_val_if_fail (GTK_IS_STYLE_PROPERTIES (props), FALSE);
  g_return_val_if_fail (property != NULL, FALSE);

  priv = props->priv;
  prop = g_hash_table_lookup (priv->properties, property);
  if (prop == NULL)
    return NULL;

  return property_data_match_state (prop, state);
}

737
gboolean
738 739 740 741 742
_gtk_style_properties_get_property (GtkStyleProperties *props,
				    const gchar        *property,
				    GtkStateFlags       state,
				    GtkStylePropertyContext *context,
				    GValue             *value)
743
{
Benjamin Otte's avatar
Benjamin Otte committed
744
  GtkStyleProperty *node;
745

746
  g_return_val_if_fail (GTK_IS_STYLE_PROPERTIES (props), FALSE);
747 748 749
  g_return_val_if_fail (property != NULL, FALSE);
  g_return_val_if_fail (value != NULL, FALSE);

750
  node = _gtk_style_property_lookup (property);
751
  if (!node)
752 753 754 755 756
    {
      g_warning ("Style property \"%s\" is not registered", property);
      return FALSE;
    }

757
  _gtk_style_property_query (node, props, state, context, value);
758 759 760
  return TRUE;
}

Carlos Garnacho's avatar
Carlos Garnacho committed
761
/**
762
 * gtk_style_properties_get_property:
763
 * @props: a #GtkStyleProperties
764 765 766
 * @property: style property name
 * @state: state to retrieve the property value for
 * @value: (out) (transfer full):  return location for the style property value.
Carlos Garnacho's avatar
Carlos Garnacho committed
767
 *
768 769 770 771
 * Gets a style property from @props for the given state. When done with @value,
 * g_value_unset() needs to be called to free any allocated memory.
 *
 * Returns: %TRUE if the property exists in @props, %FALSE otherwise
Carlos Garnacho's avatar
Carlos Garnacho committed
772 773 774
 *
 * Since: 3.0
 **/
775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791
gboolean
gtk_style_properties_get_property (GtkStyleProperties *props,
                                   const gchar        *property,
                                   GtkStateFlags       state,
                                   GValue             *value)
{
  GtkStylePropertyContext context = { 100, 100};

  g_return_val_if_fail (GTK_IS_STYLE_PROPERTIES (props), FALSE);
  g_return_val_if_fail (property != NULL, FALSE);
  g_return_val_if_fail (value != NULL, FALSE);

  return _gtk_style_properties_get_property (props,
					     property,
					     state, &context, value);
}

792
void
793 794 795 796
_gtk_style_properties_get_valist (GtkStyleProperties *props,
				  GtkStateFlags       state,
				  GtkStylePropertyContext *context,
				  va_list             args)
797 798 799
{
  const gchar *property_name;

800
  g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
801 802 803 804 805 806

  property_name = va_arg (args, const gchar *);

  while (property_name)
    {
      gchar *error = NULL;
807
      GValue value = G_VALUE_INIT;
808

809 810 811 812 813 814
      if (!_gtk_style_properties_get_property (props,
					       property_name,
					       state,
					       context,
					       &value))
	break;
815

816 817
      G_VALUE_LCOPY (&value, args, 0, &error);
      g_value_unset (&value);
818

819 820 821
      if (error)
        {
          g_warning ("Could not get style property \"%s\": %s", property_name, error);
822
          g_free (error);
823 824 825 826 827 828
          break;
        }

      property_name = va_arg (args, const gchar *);
    }
}
829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863

/**
 * gtk_style_properties_get_valist:
 * @props: a #GtkStyleProperties
 * @state: state to retrieve the property values for
 * @args: va_list of property name/return location pairs, followed by %NULL
 *
 * Retrieves several style property values from @props for a given state.
 *
 * Since: 3.0
 **/
void
gtk_style_properties_get_valist (GtkStyleProperties *props,
                                 GtkStateFlags       state,
                                 va_list             args)
{
  GtkStylePropertyContext context = { 100, 100};
  
  return _gtk_style_properties_get_valist (props, state, &context, args);
}

void
_gtk_style_properties_get (GtkStyleProperties *props,
			   GtkStateFlags       state,
			   GtkStylePropertyContext *context,
			   ...)
{
  va_list args;

  g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));

  va_start (args, context);
  _gtk_style_properties_get_valist (props, state, context, args);
  va_end (args);
}
864

Carlos Garnacho's avatar
Carlos Garnacho committed
865
/**
866 867
 * gtk_style_properties_get:
 * @props: a #GtkStyleProperties
Carlos Garnacho's avatar
Carlos Garnacho committed
868 869 870
 * @state: state to retrieve the property values for
 * @...: property name /return value pairs, followed by %NULL
 *
871
 * Retrieves several style property values from @props for a
Carlos Garnacho's avatar
Carlos Garnacho committed
872 873 874 875
 * given state.
 *
 * Since: 3.0
 **/
876
void
877 878 879
gtk_style_properties_get (GtkStyleProperties *props,
                          GtkStateFlags       state,
                          ...)
880 881 882
{
  va_list args;

883
  g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
884 885

  va_start (args, state);
886
  gtk_style_properties_get_valist (props, state, args);
887 888 889
  va_end (args);
}

Carlos Garnacho's avatar
Carlos Garnacho committed
890
/**
891 892
 * gtk_style_properties_unset_property:
 * @props: a #GtkStyleProperties
Carlos Garnacho's avatar
Carlos Garnacho committed
893 894 895
 * @property: property to unset
 * @state: state to unset
 *
896
 * Unsets a style property in @props.
Carlos Garnacho's avatar
Carlos Garnacho committed
897 898 899
 *
 * Since: 3.0
 **/
900
void
901 902 903
gtk_style_properties_unset_property (GtkStyleProperties *props,
                                     const gchar        *property,
                                     GtkStateFlags       state)
904
{
905
  GtkStylePropertiesPrivate *priv;
Benjamin Otte's avatar
Benjamin Otte committed
906
  GtkStyleProperty *node;
907
  PropertyData *prop;
908
  guint pos;
909

910
  g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
911 912
  g_return_if_fail (property != NULL);

913
  node = _gtk_style_property_lookup (property);
914 915 916 917 918 919 920

  if (!node)
    {
      g_warning ("Style property \"%s\" is not registered", property);
      return;
    }

921 922 923 924 925 926
  if (node->unset_func)
    {
      node->unset_func (props, state);
      return;
    }

927
  priv = props->priv;
928
  prop = g_hash_table_lookup (priv->properties, node);
929 930 931 932

  if (!prop)
    return;

933 934 935 936 937 938 939 940 941 942 943
  if (property_data_find_position (prop, state, &pos))
    {
      ValueData *data;

      data = &g_array_index (prop->values, ValueData, pos);

      if (G_IS_VALUE (&data->value))
        g_value_unset (&data->value);

      g_array_remove_index (prop->values, pos);
    }
944 945
}

Carlos Garnacho's avatar
Carlos Garnacho committed
946
/**
947 948
 * gtk_style_properties_clear:
 * @props: a #GtkStyleProperties
Carlos Garnacho's avatar
Carlos Garnacho committed
949
 *
950
 * Clears all style information from @props.
Carlos Garnacho's avatar
Carlos Garnacho committed
951
 **/
952
void
953
gtk_style_properties_clear (GtkStyleProperties *props)
954
{
955
  GtkStylePropertiesPrivate *priv;
956

957
  g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
958

959
  priv = props->priv;
960 961 962
  g_hash_table_remove_all (priv->properties);
}

Carlos Garnacho's avatar
Carlos Garnacho committed
963
/**
964 965 966
 * gtk_style_properties_merge:
 * @props: a #GtkStyleProperties
 * @props_to_merge: a second #GtkStyleProperties
Carlos Garnacho's avatar
Carlos Garnacho committed
967 968
 * @replace: whether to replace values or not
 *
969 970
 * Merges into @props all the style information contained
 * in @props_to_merge. If @replace is %TRUE, the values
Carlos Garnacho's avatar
Carlos Garnacho committed
971 972 973 974 975
 * will be overwritten, if it is %FALSE, the older values
 * will prevail.
 *
 * Since: 3.0
 **/
976
void
977 978 979
gtk_style_properties_merge (GtkStyleProperties       *props,
                            const GtkStyleProperties *props_to_merge,
                            gboolean                  replace)
980
{
981
  GtkStylePropertiesPrivate *priv, *priv_to_merge;
982 983 984
  GHashTableIter iter;
  gpointer key, value;

985 986
  g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props));
  g_return_if_fail (GTK_IS_STYLE_PROPERTIES (props_to_merge));
987

988 989
  priv = props->priv;
  priv_to_merge = props_to_merge->priv;
990

991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007
  /* Merge symbolic color map */
  if (priv_to_merge->color_map)
    {
      g_hash_table_iter_init (&iter, priv_to_merge->color_map);

      while (g_hash_table_iter_next (&iter, &key, &value))
        {
          const gchar *name;
          GtkSymbolicColor *color;

          name = key;
          color = value;

          if (!replace &&
              g_hash_table_lookup (priv->color_map, name))
            continue;

1008
          gtk_style_properties_map_color (props, name, color);
1009 1010 1011 1012
        }
    }

  /* Merge symbolic style properties */
1013 1014 1015 1016 1017
  g_hash_table_iter_init (&iter, priv_to_merge->properties);

  while (g_hash_table_iter_next (&iter, &key, &value))
    {
      PropertyData *prop_to_merge = value;
1018 1019
      PropertyData *prop;
      guint i;
1020

1021 1022 1023
      prop = g_hash_table_lookup (priv->properties, key);

      if (!prop)
1024
        {
1025 1026 1027
          prop = property_data_new ();
          g_hash_table_insert (priv->properties, key, prop);
        }
1028

1029 1030 1031 1032
      for (i = 0; i < prop_to_merge->values->len; i++)
        {
          ValueData *data;
          GValue *value;
1033

1034
          data = &g_array_index (prop_to_merge->values, ValueData, i);
1035 1036 1037 1038 1039 1040 1041 1042 1043 1044

          if (replace && data->state == GTK_STATE_FLAG_NORMAL &&
              G_VALUE_TYPE (&data->value) != PANGO_TYPE_FONT_DESCRIPTION)
            {
              /* Let normal state override all states
               * previously set in the original set
               */
              property_data_remove_values (prop);
            }

1045
          value = property_data_get_value (prop, data->state);
1046

1047 1048 1049 1050 1051 1052 1053 1054 1055 1056
          if (G_VALUE_TYPE (&data->value) == PANGO_TYPE_FONT_DESCRIPTION &&
              G_IS_VALUE (value))
            {
              PangoFontDescription *font_desc;
              PangoFontDescription *font_desc_to_merge;

              /* Handle merging of font descriptions */
              font_desc = g_value_get_boxed (value);
              font_desc_to_merge = g_value_get_boxed (&data->value);

1057
              pango_font_description_merge (font_desc, font_desc_to_merge, replace);
1058
            }
1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073
          else if (G_VALUE_TYPE (&data->value) == G_TYPE_PTR_ARRAY &&
                   G_IS_VALUE (value))
            {
              GPtrArray *array, *array_to_merge;
              gint i;

              /* Append the array, mainly thought
               * for the gtk-key-bindings property
               */
              array = g_value_get_boxed (value);
              array_to_merge = g_value_get_boxed (&data->value);

              for (i = 0; i < array_to_merge->len; i++)
                g_ptr_array_add (array, g_ptr_array_index (array_to_merge, i));
            }
1074
          else if (replace || !G_IS_VALUE (value))
1075
            {
1076 1077 1078
              if (!G_IS_VALUE (value))
                g_value_init (value, G_VALUE_TYPE (&data->value));
              else if (G_VALUE_TYPE (value) != G_VALUE_TYPE (&data->value))
1079
                {
1080 1081
                  g_value_unset (value);
                  g_value_init (value, G_VALUE_TYPE (&data->value));
1082
                }
1083

1084
              g_value_copy (&data->value, value);
1085 1086 1087 1088
            }
        }
    }
}