gtkstylecontext.c 119 KB
Newer Older
Carlos Garnacho's avatar
Carlos Garnacho committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/* 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
Javier Jardón's avatar
Javier Jardón committed
15
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
Carlos Garnacho's avatar
Carlos Garnacho committed
16 17 18 19 20
 */

#include "config.h"

#include <gdk/gdk.h>
21
#include <math.h>
22
#include <stdlib.h>
23
#include <gobject/gvaluecollector.h>
Carlos Garnacho's avatar
Carlos Garnacho committed
24

25
#include "gtkstylecontextprivate.h"
26
#include "gtkcssenginevalueprivate.h"
27
#include "gtkcssrgbavalueprivate.h"
28
#include "gtkstylepropertiesprivate.h"
Carlos Garnacho's avatar
Carlos Garnacho committed
29
#include "gtktypebuiltins.h"
30
#include "gtkthemingengineprivate.h"
Carlos Garnacho's avatar
Carlos Garnacho committed
31
#include "gtkintl.h"
32
#include "gtkwidget.h"
33
#include "gtkwindow.h"
34
#include "gtkprivate.h"
35
#include "gtksymboliccolorprivate.h"
36
#include "gtkcssnumbervalueprivate.h"
37
#include "gtkiconfactory.h"
38
#include "gtkwidgetpath.h"
39
#include "gtkwidgetprivate.h"
40
#include "gtkstylecascadeprivate.h"
41
#include "gtkstyleproviderprivate.h"
42
#include "gtksettings.h"
Carlos Garnacho's avatar
Carlos Garnacho committed
43

Carlos Garnacho's avatar
Carlos Garnacho committed
44 45
/**
 * SECTION:gtkstylecontext
Matthias Clasen's avatar
Matthias Clasen committed
46
 * @Short_description: Rendering UI elements
Carlos Garnacho's avatar
Carlos Garnacho committed
47 48 49 50 51 52
 * @Title: GtkStyleContext
 *
 * #GtkStyleContext is an object that stores styling information affecting
 * a widget defined by #GtkWidgetPath.
 *
 * In order to construct the final style information, #GtkStyleContext
53 54 55 56
 * queries information from all attached #GtkStyleProviders. Style providers
 * can be either attached explicitly to the context through
 * gtk_style_context_add_provider(), or to the screen through
 * gtk_style_context_add_provider_for_screen(). The resulting style is a
57
 * combination of all providers' information in priority order.
Carlos Garnacho's avatar
Carlos Garnacho committed
58 59 60
 *
 * For GTK+ widgets, any #GtkStyleContext returned by
 * gtk_widget_get_style_context() will already have a #GtkWidgetPath, a
61
 * #GdkScreen and RTL/LTR information set. The style context will be also
Carlos Garnacho's avatar
Carlos Garnacho committed
62 63
 * updated automatically if any of these settings change on the widget.
 *
64
 * If you are using the theming layer standalone, you will need to set a
Carlos Garnacho's avatar
Carlos Garnacho committed
65 66 67 68
 * widget path and a screen yourself to the created style context through
 * gtk_style_context_set_path() and gtk_style_context_set_screen(), as well
 * as updating the context yourself using gtk_style_context_invalidate()
 * whenever any of the conditions change, such as a change in the
69
 * #GtkSettings:gtk-theme-name setting or a hierarchy change in the rendered
Carlos Garnacho's avatar
Carlos Garnacho committed
70 71 72 73 74 75
 * widget.
 *
 * <refsect2 id="gtkstylecontext-animations">
 * <title>Transition animations</title>
 * <para>
 * #GtkStyleContext has built-in support for state change transitions.
76 77
 * Note that these animations respect the #GtkSettings:gtk-enable-animations
 * setting.
Carlos Garnacho's avatar
Carlos Garnacho committed
78
 * </para>
79
 * <para>
Carlos Garnacho's avatar
Carlos Garnacho committed
80
 * For simple widgets where state changes affect the whole widget area,
81
 * calling gtk_style_context_notify_state_change() with a %NULL region
Matthias Clasen's avatar
Matthias Clasen committed
82 83 84
 * is sufficient to trigger the transition animation. And GTK+ already
 * does that when gtk_widget_set_state() or gtk_widget_set_state_flags()
 * are called.
85
 * </para>
Carlos Garnacho's avatar
Carlos Garnacho committed
86 87 88
 * <para>
 * If a widget needs to declare several animatable regions (i.e. not
 * affecting the whole widget area), its #GtkWidget::draw signal handler
89 90
 * needs to wrap the render operations for the different regions with
 * calls to gtk_style_context_push_animatable_region() and
Carlos Garnacho's avatar
Carlos Garnacho committed
91
 * gtk_style_context_pop_animatable_region(). These functions take an
92 93 94
 * identifier for the region which must be unique within the style context.
 * For simple widgets with a fixed set of animatable regions, using an
 * enumeration works well:
Carlos Garnacho's avatar
Carlos Garnacho committed
95 96
 * </para>
 * <example>
97
 * <title>Using an enumeration to identify  animatable regions</title>
Carlos Garnacho's avatar
Carlos Garnacho committed
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
 * <programlisting>
 * enum {
 *   REGION_ENTRY,
 *   REGION_BUTTON_UP,
 *   REGION_BUTTON_DOWN
 * };
 *
 * ...
 *
 * gboolean
 * spin_button_draw (GtkWidget *widget,
 *                   cairo_t   *cr)
 * {
 *   GtkStyleContext *context;
 *
 *   context = gtk_widget_get_style_context (widget);
 *
 *   gtk_style_context_push_animatable_region (context,
 *                                             GUINT_TO_POINTER (REGION_ENTRY));
 *
 *   gtk_render_background (cr, 0, 0, 100, 30);
 *   gtk_render_frame (cr, 0, 0, 100, 30);
 *
 *   gtk_style_context_pop_animatable_region (context);
 *
 *   ...
 * }
 * </programlisting>
 * </example>
 * <para>
 * For complex widgets with an arbitrary number of animatable regions, it
129 130 131
 * is up to the implementation to come up with a way to uniquely identify
 * each animatable region. Using pointers to internal structs is one way
 * to achieve this:
Carlos Garnacho's avatar
Carlos Garnacho committed
132 133
 * </para>
 * <example>
134
 * <title>Using struct pointers to identify animatable regions</title>
Carlos Garnacho's avatar
Carlos Garnacho committed
135 136 137 138 139 140 141 142 143 144 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
 * <programlisting>
 * void
 * notebook_draw_tab (GtkWidget    *widget,
 *                    NotebookPage *page,
 *                    cairo_t      *cr)
 * {
 *   gtk_style_context_push_animatable_region (context, page);
 *   gtk_render_extension (cr, page->x, page->y, page->width, page->height);
 *   gtk_style_context_pop_animatable_region (context);
 * }
 * </programlisting>
 * </example>
 * <para>
 * The widget also needs to notify the style context about a state change
 * for a given animatable region so the animation is triggered.
 * </para>
 * <example>
 * <title>Triggering a state change animation on a region</title>
 * <programlisting>
 * gboolean
 * notebook_motion_notify (GtkWidget      *widget,
 *                         GdkEventMotion *event)
 * {
 *   GtkStyleContext *context;
 *   NotebookPage *page;
 *
 *   context = gtk_widget_get_style_context (widget);
 *   page = find_page_under_pointer (widget, event);
 *   gtk_style_context_notify_state_change (context,
 *                                          gtk_widget_get_window (widget),
 *                                          page,
 *                                          GTK_STATE_PRELIGHT,
 *                                          TRUE);
 *   ...
 * }
 * </programlisting>
 * </example>
 * <para>
 * gtk_style_context_notify_state_change() accepts %NULL region IDs as a
 * special value, in this case, the whole widget area will be updated
 * by the animation.
 * </para>
 * </refsect2>
178 179 180 181 182 183
 * <refsect2 id="gtkstylecontext-classes">
 * <title>Style classes and regions</title>
 * <para>
 * Widgets can add style classes to their context, which can be used
 * to associate different styles by class (see <xref linkend="gtkcssprovider-selectors"/>). Theme engines can also use style classes to vary their
 * rendering. GTK+ has a number of predefined style classes:
184 185 186
 * #GTK_STYLE_CLASS_CELL,
 * #GTK_STYLE_CLASS_ENTRY,
 * #GTK_STYLE_CLASS_BUTTON,
187
 * #GTK_STYLE_CLASS_COMBOBOX_ENTRY,
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
 * #GTK_STYLE_CLASS_CALENDAR,
 * #GTK_STYLE_CLASS_SLIDER,
 * #GTK_STYLE_CLASS_BACKGROUND,
 * #GTK_STYLE_CLASS_RUBBERBAND,
 * #GTK_STYLE_CLASS_TOOLTIP,
 * #GTK_STYLE_CLASS_MENU,
 * #GTK_STYLE_CLASS_MENUBAR,
 * #GTK_STYLE_CLASS_MENUITEM,
 * #GTK_STYLE_CLASS_TOOLBAR,
 * #GTK_STYLE_CLASS_PRIMARY_TOOLBAR,
 * #GTK_STYLE_CLASS_INLINE_TOOLBAR,
 * #GTK_STYLE_CLASS_RADIO,
 * #GTK_STYLE_CLASS_CHECK,
 * #GTK_STYLE_CLASS_TROUGH,
 * #GTK_STYLE_CLASS_SCROLLBAR,
 * #GTK_STYLE_CLASS_SCALE,
 * #GTK_STYLE_CLASS_SCALE_HAS_MARKS_ABOVE,
 * #GTK_STYLE_CLASS_SCALE_HAS_MARKS_BELOW,
 * #GTK_STYLE_CLASS_HEADER,
 * #GTK_STYLE_CLASS_ACCELERATOR,
 * #GTK_STYLE_CLASS_GRIP,
 * #GTK_STYLE_CLASS_DOCK,
 * #GTK_STYLE_CLASS_PROGRESSBAR,
 * #GTK_STYLE_CLASS_SPINNER,
 * #GTK_STYLE_CLASS_EXPANDER,
 * #GTK_STYLE_CLASS_SPINBUTTON,
 * #GTK_STYLE_CLASS_NOTEBOOK,
 * #GTK_STYLE_CLASS_VIEW,
 * #GTK_STYLE_CLASS_SIDEBAR,
 * #GTK_STYLE_CLASS_IMAGE,
 * #GTK_STYLE_CLASS_HIGHLIGHT,
 * #GTK_STYLE_CLASS_FRAME,
 * #GTK_STYLE_CLASS_DND,
 * #GTK_STYLE_CLASS_PANE_SEPARATOR,
 * #GTK_STYLE_CLASS_SEPARATOR,
 * #GTK_STYLE_CLASS_INFO,
 * #GTK_STYLE_CLASS_WARNING,
 * #GTK_STYLE_CLASS_QUESTION,
 * #GTK_STYLE_CLASS_ERROR,
 * #GTK_STYLE_CLASS_HORIZONTAL,
228 229 230 231 232
 * #GTK_STYLE_CLASS_VERTICAL,
 * #GTK_STYLE_CLASS_TOP,
 * #GTK_STYLE_CLASS_BOTTOM,
 * #GTK_STYLE_CLASS_LEFT,
 * #GTK_STYLE_CLASS_RIGHT,
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 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
 * </para>
 * <para>
 * Widgets can also add regions with flags to their context.
 * The regions used by GTK+ widgets are:
 * <informaltable>
 *   <tgroup cols="4">
 *     <thead>
 *       <row>
 *         <entry>Region</entry>
 *         <entry>Flags</entry>
 *         <entry>Macro</entry>
 *         <entry>Used by</entry>
 *       </row>
 *     </thead>
 *     <tbody>
 *       <row>
 *         <entry>row</entry>
 *         <entry>even, odd</entry>
 *         <entry>GTK_STYLE_REGION_ROW</entry>
 *         <entry>#GtkTreeView</entry>
 *       </row>
 *       <row>
 *         <entry>column</entry>
 *         <entry>first, last, sorted</entry>
 *         <entry>GTK_STYLE_REGION_COLUMN</entry>
 *         <entry>#GtkTreeView</entry>
 *       </row>
 *       <row>
 *         <entry>column-header</entry>
 *         <entry></entry>
 *         <entry>GTK_STYLE_REGION_COLUMN_HEADER</entry>
 *         <entry></entry>
 *       </row>
 *       <row>
 *         <entry>tab</entry>
 *         <entry>even, odd, first, last</entry>
 *         <entry>GTK_STYLE_REGION_TAB</entry>
 *         <entry>#GtkNotebook</entry>
 *       </row>
 *     </tbody>
 *   </tgroup>
 * </informaltable>
 * </para>
 * </refsect2>
Carlos Garnacho's avatar
Carlos Garnacho committed
277 278 279 280 281 282 283 284 285 286 287 288 289 290
 * <refsect2 id="gtkstylecontext-custom-styling">
 * <title>Custom styling in UI libraries and applications</title>
 * <para>
 * If you are developing a library with custom #GtkWidget<!-- -->s that
 * render differently than standard components, you may need to add a
 * #GtkStyleProvider yourself with the %GTK_STYLE_PROVIDER_PRIORITY_FALLBACK
 * priority, either a #GtkCssProvider or a custom object implementing the
 * #GtkStyleProvider interface. This way theming engines may still attempt
 * to style your UI elements in a different way if needed so.
 * </para>
 * <para>
 * If you are using custom styling on an applications, you probably want then
 * to make your style information prevail to the theme's, so you must use
 * a #GtkStyleProvider with the %GTK_STYLE_PROVIDER_PRIORITY_APPLICATION
291 292
 * priority, keep in mind that the user settings in
 * <filename><replaceable>XDG_CONFIG_HOME</replaceable>/gtk-3.0/gtk.css</filename> will
Carlos Garnacho's avatar
Carlos Garnacho committed
293 294 295 296 297 298 299 300 301 302 303 304
 * still take precedence over your changes, as it uses the
 * %GTK_STYLE_PROVIDER_PRIORITY_USER priority.
 * </para>
 * <para>
 * If a custom theming engine is needed, you probably want to implement a
 * #GtkStyleProvider yourself so it points to your #GtkThemingEngine
 * implementation, as #GtkCssProvider uses gtk_theming_engine_load()
 * which loads the theming engine module from the standard paths.
 * </para>
 * </refsect2>
 */

305 306
/* When these change we do a full restyling. Otherwise we try to figure out
 * if we need to change things. */
307
#define GTK_STYLE_CONTEXT_RADICAL_CHANGE (GTK_CSS_CHANGE_NAME | GTK_CSS_CHANGE_CLASS | GTK_CSS_CHANGE_SOURCE)
308 309 310
/* When these change we don't clear the cache. This takes more memory but makes
 * things go faster. */
#define GTK_STYLE_CONTEXT_CACHED_CHANGE (GTK_CSS_CHANGE_STATE)
311

312
typedef struct GtkStyleInfo GtkStyleInfo;
313
typedef struct GtkRegion GtkRegion;
314
typedef struct PropertyValue PropertyValue;
315
typedef struct StyleData StyleData;
316

317
struct GtkRegion
318 319
{
  GQuark class_quark;
320
  GtkRegionFlags flags;
321
};
Carlos Garnacho's avatar
Carlos Garnacho committed
322

323 324 325 326
struct PropertyValue
{
  GType       widget_type;
  GParamSpec *pspec;
327
  GtkStateFlags state;
328 329 330
  GValue      value;
};

331
struct GtkStyleInfo
332 333
{
  GArray *style_classes;
334
  GArray *regions;
335
  GtkJunctionSides junction_sides;
336
  GtkStateFlags state_flags;
337
  StyleData *data;
338 339
};

340 341
struct StyleData
{
342
  GtkCssComputedValues *store;
343 344 345
  GArray *property_cache;
};

346
struct _GtkStyleContextPrivate
Carlos Garnacho's avatar
Carlos Garnacho committed
347
{
348 349
  GdkScreen *screen;

350
  GtkStyleCascade *cascade;
351

352
  GtkStyleContext *parent;
353
  GSList *children;
354
  GtkWidget *widget;            
355
  GtkWidgetPath *widget_path;
356 357
  GHashTable *style_data;
  GSList *info_stack;
358

359
  GtkTextDirection direction;
360

361
  GtkCssChange relevant_changes;
362
  GtkCssChange pending_changes;
363

364
  guint invalidating_context : 1;
365
  guint invalid : 1;
Carlos Garnacho's avatar
Carlos Garnacho committed
366 367
};

368 369
enum {
  PROP_0,
370
  PROP_SCREEN,
371 372
  PROP_DIRECTION,
  PROP_PARENT
373 374
};

375 376 377 378 379
enum {
  CHANGED,
  LAST_SIGNAL
};

380
static guint signals[LAST_SIGNAL] = { 0 };
381

Carlos Garnacho's avatar
Carlos Garnacho committed
382 383
static void gtk_style_context_finalize (GObject *object);

384 385 386 387 388 389 390 391
static void gtk_style_context_impl_set_property (GObject      *object,
                                                 guint         prop_id,
                                                 const GValue *value,
                                                 GParamSpec   *pspec);
static void gtk_style_context_impl_get_property (GObject      *object,
                                                 guint         prop_id,
                                                 GValue       *value,
                                                 GParamSpec   *pspec);
392 393 394
static GtkSymbolicColor *
            gtk_style_context_color_lookup_func (gpointer      contextp,
                                                 const char   *name);
395

Carlos Garnacho's avatar
Carlos Garnacho committed
396 397 398

G_DEFINE_TYPE (GtkStyleContext, gtk_style_context, G_TYPE_OBJECT)

399 400 401 402 403 404 405 406 407
static void
gtk_style_context_real_changed (GtkStyleContext *context)
{
  GtkStyleContextPrivate *priv = context->priv;

  if (priv->widget)
    _gtk_widget_style_context_invalidated (priv->widget);
}

Carlos Garnacho's avatar
Carlos Garnacho committed
408 409 410 411 412 413
static void
gtk_style_context_class_init (GtkStyleContextClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  object_class->finalize = gtk_style_context_finalize;
414 415 416
  object_class->set_property = gtk_style_context_impl_set_property;
  object_class->get_property = gtk_style_context_impl_get_property;

417 418
  klass->changed = gtk_style_context_real_changed;

419 420
  signals[CHANGED] =
    g_signal_new (I_("changed"),
421 422 423 424
                  G_TYPE_FROM_CLASS (object_class),
                  G_SIGNAL_RUN_FIRST,
                  G_STRUCT_OFFSET (GtkStyleContextClass, changed),
                  NULL, NULL,
425 426 427
                  g_cclosure_marshal_VOID__VOID,
                  G_TYPE_NONE, 0);

428
  g_object_class_install_property (object_class,
429 430
                                   PROP_SCREEN,
                                   g_param_spec_object ("screen",
431 432 433 434
                                                        P_("Screen"),
                                                        P_("The associated GdkScreen"),
                                                        GDK_TYPE_SCREEN,
                                                        GTK_PARAM_READWRITE));
435
  g_object_class_install_property (object_class,
436 437
                                   PROP_DIRECTION,
                                   g_param_spec_enum ("direction",
438 439 440 441 442
                                                      P_("Direction"),
                                                      P_("Text direction"),
                                                      GTK_TYPE_TEXT_DIRECTION,
                                                      GTK_TEXT_DIR_LTR,
                                                      GTK_PARAM_READWRITE));
443 444 445 446 447 448 449 450 451 452 453 454 455 456 457
  /**
   * GtkStyleContext:parent:
   *
   * Sets or gets the style context's parent. See gtk_style_context_set_parent()
   * for details.
   *
   * Since: 3.4
   */
  g_object_class_install_property (object_class,
                                   PROP_PARENT,
                                   g_param_spec_object ("parent",
                                                        P_("Parent"),
                                                        P_("The parent style context"),
                                                        GTK_TYPE_STYLE_CONTEXT,
                                                        GTK_PARAM_READWRITE));
Carlos Garnacho's avatar
Carlos Garnacho committed
458 459 460 461

  g_type_class_add_private (object_class, sizeof (GtkStyleContextPrivate));
}

462 463
static GtkStyleInfo *
style_info_new (void)
464
{
465
  GtkStyleInfo *info;
466

467 468
  info = g_slice_new0 (GtkStyleInfo);
  info->style_classes = g_array_new (FALSE, FALSE, sizeof (GQuark));
469
  info->regions = g_array_new (FALSE, FALSE, sizeof (GtkRegion));
470

471
  return info;
472 473 474
}

static void
475
style_info_free (GtkStyleInfo *info)
476
{
477
  g_array_free (info->style_classes, TRUE);
478
  g_array_free (info->regions, TRUE);
479
  g_slice_free (GtkStyleInfo, info);
480 481
}

482 483
static GtkStyleInfo *
style_info_copy (const GtkStyleInfo *info)
484
{
485
  GtkStyleInfo *copy;
486

487
  copy = style_info_new ();
488
  g_array_insert_vals (copy->style_classes, 0,
489 490
                       info->style_classes->data,
                       info->style_classes->len);
491

492 493 494
  g_array_insert_vals (copy->regions, 0,
                       info->regions->data,
                       info->regions->len);
495

496
  copy->junction_sides = info->junction_sides;
497
  copy->state_flags = info->state_flags;
498
  copy->data = info->data;
499

500 501 502
  return copy;
}

503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526
static guint
style_info_hash (gconstpointer elem)
{
  const GtkStyleInfo *info;
  guint i, hash = 0;

  info = elem;

  for (i = 0; i < info->style_classes->len; i++)
    {
      hash += g_array_index (info->style_classes, GQuark, i);
      hash <<= 5;
    }

  for (i = 0; i < info->regions->len; i++)
    {
      GtkRegion *region;

      region = &g_array_index (info->regions, GtkRegion, i);
      hash += region->class_quark;
      hash += region->flags;
      hash <<= 5;
    }

527
  return hash ^ info->state_flags;
528 529 530 531 532 533 534 535 536 537 538
}

static gboolean
style_info_equal (gconstpointer elem1,
                  gconstpointer elem2)
{
  const GtkStyleInfo *info1, *info2;

  info1 = elem1;
  info2 = elem2;

539 540 541
  if (info1->state_flags != info2->state_flags)
    return FALSE;

542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602
  if (info1->junction_sides != info2->junction_sides)
    return FALSE;

  if (info1->style_classes->len != info2->style_classes->len)
    return FALSE;

  if (memcmp (info1->style_classes->data,
              info2->style_classes->data,
              info1->style_classes->len * sizeof (GQuark)) != 0)
    return FALSE;

  if (info1->regions->len != info2->regions->len)
    return FALSE;

  if (memcmp (info1->regions->data,
              info2->regions->data,
              info1->regions->len * sizeof (GtkRegion)) != 0)
    return FALSE;

  return TRUE;
}

static StyleData *
style_data_new (void)
{
  StyleData *data;

  data = g_slice_new0 (StyleData);

  return data;
}

static void
clear_property_cache (StyleData *data)
{
  guint i;

  if (!data->property_cache)
    return;

  for (i = 0; i < data->property_cache->len; i++)
    {
      PropertyValue *node = &g_array_index (data->property_cache, PropertyValue, i);

      g_param_spec_unref (node->pspec);
      g_value_unset (&node->value);
    }

  g_array_free (data->property_cache, TRUE);
  data->property_cache = NULL;
}

static void
style_data_free (StyleData *data)
{
  g_object_unref (data->store);
  clear_property_cache (data);

  g_slice_free (StyleData, data);
}

603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648
static void
gtk_style_context_cascade_changed (GtkStyleCascade *cascade,
                                   GtkStyleContext *context)
{
  GtkStyleContextPrivate *priv = context->priv;

  if (priv->widget)
    _gtk_style_context_queue_invalidate (context, GTK_CSS_CHANGE_SOURCE);
  else
    gtk_style_context_invalidate (context);
}

static void
gtk_style_context_set_cascade (GtkStyleContext *context,
                               GtkStyleCascade *cascade)
{
  GtkStyleContextPrivate *priv;

  priv = context->priv;

  if (priv->cascade == cascade)
    return;

  if (cascade)
    {
      g_object_ref (cascade);
      g_signal_connect (cascade,
                        "-gtk-private-changed",
                        G_CALLBACK (gtk_style_context_cascade_changed),
                        context);
    }

  if (priv->cascade)
    {
      g_signal_handlers_disconnect_by_func (priv->cascade, 
                                            gtk_style_context_cascade_changed,
                                            context);
      g_object_unref (priv->cascade);
    }

  priv->cascade = cascade;

  if (cascade)
    gtk_style_context_cascade_changed (cascade, context);
}

Carlos Garnacho's avatar
Carlos Garnacho committed
649 650 651 652
static void
gtk_style_context_init (GtkStyleContext *style_context)
{
  GtkStyleContextPrivate *priv;
653
  GtkStyleInfo *info;
Carlos Garnacho's avatar
Carlos Garnacho committed
654

655 656 657 658
  priv = style_context->priv = G_TYPE_INSTANCE_GET_PRIVATE (style_context,
                                                            GTK_TYPE_STYLE_CONTEXT,
                                                            GtkStyleContextPrivate);

659 660 661 662
  priv->style_data = g_hash_table_new_full (style_info_hash,
                                            style_info_equal,
                                            (GDestroyNotify) style_info_free,
                                            (GDestroyNotify) style_data_free);
663

664
  priv->direction = GTK_TEXT_DIR_LTR;
665

666
  priv->screen = gdk_screen_get_default ();
667
  priv->relevant_changes = GTK_CSS_CHANGE_ANY;
668

669 670 671
  /* Create default info store */
  info = style_info_new ();
  priv->info_stack = g_slist_prepend (priv->info_stack, info);
672 673 674

  gtk_style_context_set_cascade (style_context,
                                 _gtk_style_cascade_get_for_screen (priv->screen));
Carlos Garnacho's avatar
Carlos Garnacho committed
675 676 677 678 679 680
}

static void
gtk_style_context_finalize (GObject *object)
{
  GtkStyleContextPrivate *priv;
681
  GtkStyleContext *style_context;
Carlos Garnacho's avatar
Carlos Garnacho committed
682

683 684
  style_context = GTK_STYLE_CONTEXT (object);
  priv = style_context->priv;
Carlos Garnacho's avatar
Carlos Garnacho committed
685

686 687 688
  /* children hold a reference to us */
  g_assert (priv->children == NULL);

689 690
  gtk_style_context_set_parent (style_context, NULL);

691 692
  gtk_style_context_set_cascade (style_context, NULL);

Carlos Garnacho's avatar
Carlos Garnacho committed
693 694 695
  if (priv->widget_path)
    gtk_widget_path_free (priv->widget_path);

696
  g_hash_table_destroy (priv->style_data);
Carlos Garnacho's avatar
Carlos Garnacho committed
697

698
  g_slist_free_full (priv->info_stack, (GDestroyNotify) style_info_free);
699

Carlos Garnacho's avatar
Carlos Garnacho committed
700 701 702
  G_OBJECT_CLASS (gtk_style_context_parent_class)->finalize (object);
}

703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718
static void
gtk_style_context_impl_set_property (GObject      *object,
                                     guint         prop_id,
                                     const GValue *value,
                                     GParamSpec   *pspec)
{
  GtkStyleContext *style_context;

  style_context = GTK_STYLE_CONTEXT (object);

  switch (prop_id)
    {
    case PROP_SCREEN:
      gtk_style_context_set_screen (style_context,
                                    g_value_get_object (value));
      break;
719 720 721 722
    case PROP_DIRECTION:
      gtk_style_context_set_direction (style_context,
                                       g_value_get_enum (value));
      break;
723 724 725 726
    case PROP_PARENT:
      gtk_style_context_set_parent (style_context,
                                    g_value_get_object (value));
      break;
727 728 729 730 731 732 733 734 735 736 737 738 739
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void
gtk_style_context_impl_get_property (GObject    *object,
                                     guint       prop_id,
                                     GValue     *value,
                                     GParamSpec *pspec)
{
  GtkStyleContext *style_context;
740
  GtkStyleContextPrivate *priv;
741 742 743 744 745 746 747 748 749

  style_context = GTK_STYLE_CONTEXT (object);
  priv = style_context->priv;

  switch (prop_id)
    {
    case PROP_SCREEN:
      g_value_set_object (value, priv->screen);
      break;
750 751 752
    case PROP_DIRECTION:
      g_value_set_enum (value, priv->direction);
      break;
753 754 755
    case PROP_PARENT:
      g_value_set_object (value, priv->parent);
      break;
756 757 758 759 760 761
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

Carlos Garnacho's avatar
Carlos Garnacho committed
762
static void
763 764
build_properties (GtkStyleContext *context,
                  StyleData       *style_data,
765 766
                  GtkWidgetPath   *path,
                  GtkStateFlags    state)
Carlos Garnacho's avatar
Carlos Garnacho committed
767 768
{
  GtkStyleContextPrivate *priv;
769
  GtkCssMatcher matcher;
770
  GtkCssLookup *lookup;
Carlos Garnacho's avatar
Carlos Garnacho committed
771

772
  priv = context->priv;
773

774
  _gtk_css_matcher_init (&matcher, path, state);
775
  lookup = _gtk_css_lookup_new ();
776

777
  _gtk_style_provider_private_lookup (GTK_STYLE_PROVIDER_PRIVATE (priv->cascade),
778
                                      &matcher,
779
                                      lookup);
780

781
  style_data->store = _gtk_css_computed_values_new ();
782
  _gtk_css_lookup_resolve (lookup, context, style_data->store);
783
  _gtk_css_lookup_free (lookup);
Carlos Garnacho's avatar
Carlos Garnacho committed
784 785
}

786
static GtkWidgetPath *
787 788 789 790 791 792 793 794
create_query_path (GtkStyleContext *context)
{
  GtkStyleContextPrivate *priv;
  GtkWidgetPath *path;
  GtkStyleInfo *info;
  guint i, pos;

  priv = context->priv;
795
  path = priv->widget ? _gtk_widget_create_path (priv->widget) : gtk_widget_path_copy (priv->widget_path);
796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823
  pos = gtk_widget_path_length (path) - 1;

  info = priv->info_stack->data;

  /* Set widget regions */
  for (i = 0; i < info->regions->len; i++)
    {
      GtkRegion *region;

      region = &g_array_index (info->regions, GtkRegion, i);
      gtk_widget_path_iter_add_region (path, pos,
                                       g_quark_to_string (region->class_quark),
                                       region->flags);
    }

  /* Set widget classes */
  for (i = 0; i < info->style_classes->len; i++)
    {
      GQuark quark;

      quark = g_array_index (info->style_classes, GQuark, i);
      gtk_widget_path_iter_add_class (path, pos,
                                      g_quark_to_string (quark));
    }

  return path;
}

Carlos Garnacho's avatar
Carlos Garnacho committed
824
static StyleData *
825
style_data_lookup (GtkStyleContext *context)
826 827
{
  GtkStyleContextPrivate *priv;
828
  GtkStyleInfo *info;
829 830

  priv = context->priv;
831
  info = priv->info_stack->data;
832 833

  /* Current data in use is cached, just return it */
834 835
  if (info->data)
    return info->data;
836

837
  g_assert (priv->widget != NULL || priv->widget_path != NULL);
838

839
  info->data = g_hash_table_lookup (priv->style_data, info);
840

841
  if (!info->data)
842 843 844
    {
      GtkWidgetPath *path;

845 846
      path = create_query_path (context);

847
      info->data = style_data_new ();
848
      g_hash_table_insert (priv->style_data,
849 850
                           style_info_copy (info),
                           info->data);
851

852
      build_properties (context, info->data, path, info->state_flags);
853 854

      gtk_widget_path_free (path);
855
    }
856

857
  return info->data;
858 859
}

860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876
static void
gtk_style_context_set_invalid (GtkStyleContext *context,
                               gboolean         invalid)
{
  GtkStyleContextPrivate *priv;
  
  priv = context->priv;

  if (priv->invalid == invalid)
    return;

  priv->invalid = invalid;

  if (invalid)
    {
      if (priv->parent)
        gtk_style_context_set_invalid (priv->parent, TRUE);
877 878
      else if (priv->widget)
        gtk_widget_queue_resize (priv->widget);
879 880 881
    }
}

882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897
/* returns TRUE if someone called gtk_style_context_save() but hasn't
 * called gtk_style_context_restore() yet.
 * In those situations we don't invalidate the context when somebody
 * changes state/regions/classes.
 */
static gboolean
gtk_style_context_is_saved (GtkStyleContext *context)
{
  return context->priv->info_stack->next != NULL;
}

static void
gtk_style_context_queue_invalidate_internal (GtkStyleContext *context,
                                             GtkCssChange     change)
{
  GtkStyleContextPrivate *priv = context->priv;
898
  GtkStyleInfo *info = priv->info_stack->data;
899

900 901 902 903 904
  if (gtk_style_context_is_saved (context))
    {
      info->data = NULL;
    }
  else
905 906 907 908 909 910
    {
      _gtk_style_context_queue_invalidate (context, GTK_CSS_CHANGE_STATE);
      /* XXX: We need to invalidate siblings here somehow */
    }
}

911 912 913 914
/**
 * gtk_style_context_new:
 *
 * Creates a standalone #GtkStyleContext, this style context
915 916
 * won't be attached to any widget, so you may want
 * to call gtk_style_context_set_path() yourself.
917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932
 *
 * <note>
 * This function is only useful when using the theming layer
 * separated from GTK+, if you are using #GtkStyleContext to
 * theme #GtkWidget<!-- -->s, use gtk_widget_get_style_context()
 * in order to get a style context ready to theme the widget.
 * </note>
 *
 * Returns: A newly created #GtkStyleContext.
 **/
GtkStyleContext *
gtk_style_context_new (void)
{
  return g_object_new (GTK_TYPE_STYLE_CONTEXT, NULL);
}

933 934 935 936 937 938 939 940
void
_gtk_style_context_set_widget (GtkStyleContext *context,
                               GtkWidget       *widget)
{
  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
  g_return_if_fail (widget == NULL || GTK_IS_WIDGET (widget));

  context->priv->widget = widget;
941 942

  _gtk_style_context_queue_invalidate (context, GTK_CSS_CHANGE_ANY_SELF);
943 944
}

Carlos Garnacho's avatar
Carlos Garnacho committed
945 946 947 948 949 950 951 952 953 954 955 956
/**
 * gtk_style_context_add_provider:
 * @context: a #GtkStyleContext
 * @provider: a #GtkStyleProvider
 * @priority: the priority of the style provider. The lower
 *            it is, the earlier it will be used in the style
 *            construction. Typically this will be in the range
 *            between %GTK_STYLE_PROVIDER_PRIORITY_FALLBACK and
 *            %GTK_STYLE_PROVIDER_PRIORITY_USER
 *
 * Adds a style provider to @context, to be used in style construction.
 *
957 958 959 960
 * <note><para>If both priorities are the same, A #GtkStyleProvider
 * added through this function takes precedence over another added
 * through gtk_style_context_add_provider_for_screen().</para></note>
 *
Carlos Garnacho's avatar
Carlos Garnacho committed
961 962
 * Since: 3.0
 **/
963 964 965 966 967 968 969 970 971 972 973
void
gtk_style_context_add_provider (GtkStyleContext  *context,
                                GtkStyleProvider *provider,
                                guint             priority)
{
  GtkStyleContextPrivate *priv;

  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
  g_return_if_fail (GTK_IS_STYLE_PROVIDER (provider));

  priv = context->priv;
974 975 976 977 978 979 980

  if (priv->cascade == _gtk_style_cascade_get_for_screen (priv->screen))
    {
      GtkStyleCascade *new_cascade;
      
      new_cascade = _gtk_style_cascade_new ();
      _gtk_style_cascade_set_parent (new_cascade, priv->cascade);
981 982 983 984 985 986 987
      _gtk_style_cascade_add_provider (new_cascade, provider, priority);
      gtk_style_context_set_cascade (context, new_cascade);
      g_object_unref (new_cascade);
    }
  else
    {
      _gtk_style_cascade_add_provider (priv->cascade, provider, priority);
988
    }
Carlos Garnacho's avatar
Carlos Garnacho committed
989 990
}

Carlos Garnacho's avatar
Carlos Garnacho committed
991 992 993 994 995 996 997 998 999
/**
 * gtk_style_context_remove_provider:
 * @context: a #GtkStyleContext
 * @provider: a #GtkStyleProvider
 *
 * Removes @provider from the style providers list in @context.
 *
 * Since: 3.0
 **/
Carlos Garnacho's avatar
Carlos Garnacho committed
1000 1001 1002 1003 1004 1005 1006 1007 1008
void
gtk_style_context_remove_provider (GtkStyleContext  *context,
                                   GtkStyleProvider *provider)
{
  GtkStyleContextPrivate *priv;

  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
  g_return_if_fail (GTK_IS_STYLE_PROVIDER (provider));

1009
  priv = context->priv;
Carlos Garnacho's avatar
Carlos Garnacho committed
1010

1011 1012
  if (priv->cascade == _gtk_style_cascade_get_for_screen (priv->screen))
    return;
Carlos Garnacho's avatar
Carlos Garnacho committed
1013

1014
  _gtk_style_cascade_remove_provider (priv->cascade, provider);
1015
}
Carlos Garnacho's avatar
Carlos Garnacho committed
1016

Carlos Garnacho's avatar
Carlos Garnacho committed
1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029
/**
 * gtk_style_context_reset_widgets:
 * @screen: a #GdkScreen
 *
 * This function recomputes the styles for all widgets under a particular
 * #GdkScreen. This is useful when some global parameter has changed that
 * affects the appearance of all widgets, because when a widget gets a new
 * style, it will both redraw and recompute any cached information about
 * its appearance. As an example, it is used when the color scheme changes
 * in the related #GtkSettings object.
 *
 * Since: 3.0
 **/
1030 1031
void
gtk_style_context_reset_widgets (GdkScreen *screen)
1032 1033
{
  GList *list, *toplevels;
Carlos Garnacho's avatar
Carlos Garnacho committed
1034

1035 1036
  _gtk_icon_set_invalidate_caches ();

1037
  toplevels = gtk_window_list_toplevels ();
1038
  g_list_foreach (toplevels, (GFunc) g_object_ref, NULL);
Carlos Garnacho's avatar
Carlos Garnacho committed
1039

1040 1041 1042 1043
  for (list = toplevels; list; list = list->next)
    {
      if (gtk_widget_get_screen (list->data) == screen)
        gtk_widget_reset_style (list->data);
Carlos Garnacho's avatar
Carlos Garnacho committed
1044

1045
      g_object_unref (list->data);
Carlos Garnacho's avatar
Carlos Garnacho committed
1046 1047
    }

1048 1049 1050
  g_list_free (toplevels);
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064
/**
 * gtk_style_context_add_provider_for_screen:
 * @screen: a #GdkScreen
 * @provider: a #GtkStyleProvider
 * @priority: the priority of the style provider. The lower
 *            it is, the earlier it will be used in the style
 *            construction. Typically this will be in the range
 *            between %GTK_STYLE_PROVIDER_PRIORITY_FALLBACK and
 *            %GTK_STYLE_PROVIDER_PRIORITY_USER
 *
 * Adds a global style provider to @screen, which will be used
 * in style construction for all #GtkStyleContext<!-- -->s under
 * @screen.
 *
1065 1066 1067
 * GTK+ uses this to make styling information from #GtkSettings
 * available.
 *
1068 1069 1070 1071
 * <note><para>If both priorities are the same, A #GtkStyleProvider
 * added through gtk_style_context_add_provider() takes precedence
 * over another added through this function.</para></note>
 *
Carlos Garnacho's avatar
Carlos Garnacho committed
1072 1073
 * Since: 3.0
 **/
1074 1075 1076 1077 1078
void
gtk_style_context_add_provider_for_screen (GdkScreen        *screen,
                                           GtkStyleProvider *provider,
                                           guint             priority)
{
1079
  GtkStyleCascade *cascade;
1080 1081 1082 1083

  g_return_if_fail (GDK_IS_SCREEN (screen));
  g_return_if_fail (GTK_IS_STYLE_PROVIDER (provider));

1084 1085
  cascade = _gtk_style_cascade_get_for_screen (screen);
  _gtk_style_cascade_add_provider (cascade, provider, priority);
1086 1087
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1088 1089 1090 1091 1092 1093 1094 1095 1096
/**
 * gtk_style_context_remove_provider_for_screen:
 * @screen: a #GdkScreen
 * @provider: a #GtkStyleProvider
 *
 * Removes @provider from the global style providers list in @screen.
 *
 * Since: 3.0
 **/
1097 1098 1099 1100
void
gtk_style_context_remove_provider_for_screen (GdkScreen        *screen,
                                              GtkStyleProvider *provider)
{
1101
  GtkStyleCascade *cascade;
1102 1103 1104 1105

  g_return_if_fail (GDK_IS_SCREEN (screen));
  g_return_if_fail (GTK_IS_STYLE_PROVIDER (provider));

1106 1107
  cascade = _gtk_style_cascade_get_for_screen (screen);
  _gtk_style_cascade_remove_provider (cascade, provider);
Carlos Garnacho's avatar
Carlos Garnacho committed
1108 1109
}

1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141
/**
 * gtk_style_context_get_section:
 * @context: a #GtkStyleContext
 * @property: style property name
 *
 * Queries the location in the CSS where @property was defined for the
 * current @context. Note that the state to be queried is taken from
 * gtk_style_context_get_state().
 *
 * If the location is not available, %NULL will be returned. The
 * location might not be available for various reasons, such as the
 * property being overridden, @property not naming a supported CSS
 * property or tracking of definitions being disabled for performance
 * reasons.
 *
 * Shorthand CSS properties cannot be queried for a location and will
 * always return %NULL.
 *
 * Returns: %NULL or the section where value was defined
 **/
GtkCssSection *
gtk_style_context_get_section (GtkStyleContext *context,
                               const gchar     *property)
{
  GtkStyleContextPrivate *priv;
  GtkStyleProperty *prop;
  StyleData *data;

  g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);
  g_return_val_if_fail (property != NULL, NULL);

  priv = context->priv;
1142
  g_return_val_if_fail (priv->widget != NULL || priv->widget_path != NULL, NULL);
1143 1144 1145 1146 1147

  prop = _gtk_style_property_lookup (property);
  if (!GTK_IS_CSS_STYLE_PROPERTY (prop))
    return NULL;

1148
  data = style_data_lookup (context);
1149 1150 1151
  return _gtk_css_computed_values_get_section (data->store, _gtk_css_style_property_get_id (GTK_CSS_STYLE_PROPERTY (prop)));
}

1152
static GtkCssValue *
1153 1154 1155 1156 1157 1158
gtk_style_context_query_func (guint    id,
                              gpointer values)
{
  return _gtk_css_computed_values_get_value (values, id);
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1159 1160 1161 1162 1163
/**
 * gtk_style_context_get_property:
 * @context: a #GtkStyleContext
 * @property: style property name
 * @state: state to retrieve the property value for
1164
 * @value: (out) (transfer full):  return location for the style property value
Carlos Garnacho's avatar
Carlos Garnacho committed
1165
 *
1166 1167 1168 1169
 * Gets a style property from @context for the given state.
 *
 * When @value is no longer needed, g_value_unset() must be called
 * to free any allocated memory.
Carlos Garnacho's avatar
Carlos Garnacho committed
1170 1171 1172
 *
 * Since: 3.0
 **/
1173 1174 1175
void
gtk_style_context_get_property (GtkStyleContext *context,
                                const gchar     *property,
1176
                                GtkStateFlags    state,
1177 1178 1179
                                GValue          *value)
{
  GtkStyleContextPrivate *priv;
1180
  GtkStyleProperty *prop;
1181
  StyleData *data;
1182 1183 1184 1185 1186

  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
  g_return_if_fail (property != NULL);
  g_return_if_fail (value != NULL);

1187
  priv = context->priv;
1188
  g_return_if_fail (priv->widget != NULL || priv->widget_path != NULL);
1189

1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201
  prop = _gtk_style_property_lookup (property);
  if (prop == NULL)
    {
      g_warning ("Style property \"%s\" is not registered", property);
      return;
    }
  if (_gtk_style_property_get_value_type (prop) == G_TYPE_NONE)
    {
      g_warning ("Style property \"%s\" is not gettable", property);
      return;
    }

1202 1203 1204
  gtk_style_context_save (context);
  gtk_style_context_set_state (context, state);
  data = style_data_lookup (context);
1205
  _gtk_style_property_query (prop, value, gtk_style_context_query_func, data->store);
1206
  gtk_style_context_restore (context);
1207 1208
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1209 1210 1211 1212 1213 1214 1215 1216 1217 1218
/**
 * gtk_style_context_get_valist:
 * @context: a #GtkStyleContext
 * @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 @context for a given state.
 *
 * Since: 3.0
 **/
1219 1220
void
gtk_style_context_get_valist (GtkStyleContext *context,
1221
                              GtkStateFlags    state,
1222 1223
                              va_list          args)
{
1224
  const gchar *property_name;
1225 1226 1227

  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));

1228
  property_name = va_arg (args, const gchar *);
1229

1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251
  while (property_name)
    {
      gchar *error = NULL;
      GValue value = G_VALUE_INIT;

      gtk_style_context_get_property (context,
                                      property_name,
                                      state,
                                      &value);

      G_VALUE_LCOPY (&value, args, 0, &error);
      g_value_unset (&value);

      if (error)
        {
          g_warning ("Could not get style property \"%s\": %s", property_name, error);
          g_free (error);
          break;
        }

      property_name = va_arg (args, const gchar *);
    }
1252 1253
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264
/**
 * gtk_style_context_get:
 * @context: a #GtkStyleContext
 * @state: state to retrieve the property values for
 * @...: property name /return value pairs, followed by %NULL
 *
 * Retrieves several style property values from @context for a
 * given state.
 *
 * Since: 3.0
 **/
1265 1266
void
gtk_style_context_get (GtkStyleContext *context,
1267
                       GtkStateFlags    state,
1268 1269 1270 1271 1272 1273 1274
                       ...)
{
  va_list args;

  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));

  va_start (args, state);
1275
  gtk_style_context_get_valist (context, state, args);
1276 1277 1278
  va_end (args);
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1279 1280 1281 1282 1283
/**
 * gtk_style_context_set_state:
 * @context: a #GtkStyleContext
 * @flags: state to represent
 *
1284 1285
 * Sets the state to be used when rendering with any
 * of the gtk_render_*() functions.
Carlos Garnacho's avatar
Carlos Garnacho committed
1286 1287 1288
 *
 * Since: 3.0
 **/
1289 1290 1291 1292 1293
void
gtk_style_context_set_state (GtkStyleContext *context,
                             GtkStateFlags    flags)
{
  GtkStyleContextPrivate *priv;
1294
  GtkStyleInfo *info;
1295 1296 1297

  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));

1298
  priv = context->priv;
1299 1300
  info = priv->info_stack->data;
  info->state_flags = flags;
1301 1302
  
  gtk_style_context_queue_invalidate_internal (context, GTK_CSS_CHANGE_STATE);
1303 1304
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1305 1306 1307 1308
/**
 * gtk_style_context_get_state:
 * @context: a #GtkStyleContext
 *
1309
 * Returns the state used when rendering.
Carlos Garnacho's avatar
Carlos Garnacho committed
1310 1311 1312 1313 1314
 *
 * Returns: the state flags
 *
 * Since: 3.0
 **/
1315 1316 1317 1318
GtkStateFlags
gtk_style_context_get_state (GtkStyleContext *context)
{
  GtkStyleContextPrivate *priv;
1319
  GtkStyleInfo *info;
1320 1321 1322

  g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), 0);

1323
  priv = context->priv;
1324 1325 1326
  info = priv->info_stack->data;

  return info->state_flags;
1327 1328
}

1329 1330 1331 1332 1333 1334 1335 1336 1337 1338
/**
 * gtk_style_context_state_is_running:
 * @context: a #GtkStyleContext
 * @state: a widget state
 * @progress: (out): return location for the transition progress
 *
 * Returns %TRUE if there is a transition animation running for the
 * current region (see gtk_style_context_push_animatable_region()).
 *
 * If @progress is not %NULL, the animation progress will be returned
1339 1340 1341 1342
 * there, 0.0 means the state is closest to being unset, while 1.0 means
 * it's closest to being set. This means transition animation will
 * run from 0 to 1 when @state is being set and from 1 to 0 when
 * it's being unset.
1343 1344 1345 1346
 *
 * Returns: %TRUE if there is a running transition animation for @state.
 *
 * Since: 3.0
1347 1348
 *
 * Deprecated: 3.6: This function always returns %FALSE
1349
 **/
1350
gboolean
1351 1352 1353
gtk_style_context_state_is_running (GtkStyleContext *context,
                                    GtkStateType     state,
                                    gdouble         *progress)
1354 1355 1356
{
  g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), FALSE);

1357
  return FALSE;
1358 1359
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1360 1361 1362 1363 1364 1365 1366
/**
 * gtk_style_context_set_path:
 * @context: a #GtkStyleContext
 * @path: a #GtkWidgetPath
 *
 * Sets the #GtkWidgetPath used for style matching. As a
 * consequence, the style will be regenerated to match
1367 1368 1369 1370 1371
 * the new given path.
 *
 * If you are using a #GtkStyleContext returned from
 * gtk_widget_get_style_context(), you do not need to call
 * this yourself.
Carlos Garnacho's avatar
Carlos Garnacho committed
1372 1373 1374
 *
 * Since: 3.0
 **/
1375 1376 1377 1378 1379 1380 1381 1382 1383
void
gtk_style_context_set_path (GtkStyleContext *context,
                            GtkWidgetPath   *path)
{
  GtkStyleContextPrivate *priv;

  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
  g_return_if_fail (path != NULL);

1384
  priv = context->priv;
1385
  g_return_if_fail (priv->widget == NULL);
1386 1387 1388 1389 1390 1391 1392 1393

  if (priv->widget_path)
    {
      gtk_widget_path_free (priv->widget_path);
      priv->widget_path = NULL;
    }

  if (path)
1394 1395
    priv->widget_path = gtk_widget_path_copy (path);

1396
  _gtk_style_context_queue_invalidate (context, GTK_CSS_CHANGE_ANY);
1397 1398
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1399 1400 1401 1402 1403 1404 1405 1406 1407 1408
/**
 * gtk_style_context_get_path:
 * @context: a #GtkStyleContext
 *
 * Returns the widget path used for style matching.
 *
 * Returns: (transfer none): A #GtkWidgetPath
 *
 * Since: 3.0
 **/
1409
const GtkWidgetPath *
1410 1411 1412 1413
gtk_style_context_get_path (GtkStyleContext *context)
{
  GtkStyleContextPrivate *priv;

1414
  priv = context->priv;
1415 1416 1417 1418
  if (priv->widget)
    return gtk_widget_get_path (priv->widget);
  else
    return priv->widget_path;
1419 1420
}

1421 1422 1423 1424 1425 1426 1427
/**
 * gtk_style_context_set_parent:
 * @context: a #GtkStyleContext
 * @parent: (allow-none): the new parent or %NULL
 *
 * Sets the parent style context for @context. The parent style
 * context is used to implement
Matthias Clasen's avatar
Matthias Clasen committed
1428 1429
 * <ulink url="http://www.w3.org/TR/css3-cascade/#inheritance">inheritance</ulink>
 * of properties.
1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450
 *
 * If you are using a #GtkStyleContext returned from
 * gtk_widget_get_style_context(), the parent will be set for you.
 *
 * Since: 3.4
 **/
void
gtk_style_context_set_parent (GtkStyleContext *context,
                              GtkStyleContext *parent)
{
  GtkStyleContextPrivate *priv;

  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));
  g_return_if_fail (parent == NULL || GTK_IS_STYLE_CONTEXT (parent));

  priv = context->priv;

  if (priv->parent == parent)
    return;

  if (parent)
1451 1452 1453
    {
      parent->priv->children = g_slist_prepend (parent->priv->children, context);
      g_object_ref (parent);
1454 1455
      if (priv->invalid)
        gtk_style_context_set_invalid (parent, TRUE);
1456
    }
1457 1458

  if (priv->parent)
1459 1460 1461 1462
    {
      priv->parent->priv->children = g_slist_remove (priv->parent->priv->children, context);
      g_object_unref (priv->parent);
    }
1463 1464 1465 1466

  priv->parent = parent;

  g_object_notify (G_OBJECT (context), "parent");
1467
  _gtk_style_context_queue_invalidate (context, GTK_CSS_CHANGE_ANY_PARENT | GTK_CSS_CHANGE_ANY_SIBLING);
1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488
}

/**
 * gtk_style_context_get_parent:
 * @context: a #GtkStyleContext
 *
 * Gets the parent context set via gtk_style_context_set_parent().
 * See that function for details.
 *
 * Returns: (transfer none): the parent context or %NULL
 *
 * Since: 3.4
 **/
GtkStyleContext *
gtk_style_context_get_parent (GtkStyleContext *context)
{
  g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);

  return context->priv->parent;
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1489 1490 1491 1492 1493
/**
 * gtk_style_context_save:
 * @context: a #GtkStyleContext
 *
 * Saves the @context state, so all modifications done through
1494 1495
 * gtk_style_context_add_class(), gtk_style_context_remove_class(),
 * gtk_style_context_add_region(), gtk_style_context_remove_region()
Carlos Garnacho's avatar
Carlos Garnacho committed
1496 1497 1498 1499 1500
 * or gtk_style_context_set_junction_sides() can be reverted in one
 * go through gtk_style_context_restore().
 *
 * Since: 3.0
 **/
1501
void
1502
gtk_style_context_save (GtkStyleContext *context)
1503 1504
{
  GtkStyleContextPrivate *priv;
1505
  GtkStyleInfo *info;
1506 1507 1508

  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));

1509
  priv = context->priv;
1510

1511
  g_assert (priv->info_stack != NULL);
1512

1513 1514
  info = style_info_copy (priv->info_stack->data);
  priv->info_stack = g_slist_prepend (priv->info_stack, info);
1515 1516
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1517 1518 1519 1520
/**
 * gtk_style_context_restore:
 * @context: a #GtkStyleContext
 *
1521 1522
 * Restores @context state to a previous stage.
 * See gtk_style_context_save().
Carlos Garnacho's avatar
Carlos Garnacho committed
1523 1524 1525
 *
 * Since: 3.0
 **/
1526 1527 1528 1529
void
gtk_style_context_restore (GtkStyleContext *context)
{
  GtkStyleContextPrivate *priv;
1530
  GtkStyleInfo *info;
1531 1532 1533 1534 1535

  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));

  priv = context->priv;

1536
  if (priv->info_stack)
1537
    {
1538 1539 1540
      info = priv->info_stack->data;
      priv->info_stack = g_slist_remove (priv->info_stack, info);
      style_info_free (info);
1541 1542
    }

1543
  if (!priv->info_stack)
1544
    {
1545
      g_warning ("Unpaired gtk_style_context_restore() call");
1546

1547
      /* Create default region */
1548 1549
      info = style_info_new ();
      priv->info_stack = g_slist_prepend (priv->info_stack, info);
1550 1551
    }
}
1552

1553
static gboolean
1554 1555 1556
style_class_find (GArray *array,
                  GQuark  class_quark,
                  guint  *position)
1557
{
1558
  gint min, max, mid;
1559
  gboolean found = FALSE;
1560
  guint pos;
1561

1562 1563 1564 1565 1566 1567
  if (position)
    *position = 0;

  if (!array || array->len == 0)
    return FALSE;

1568 1569 1570 1571 1572 1573 1574
  min = 0;
  max = array->len - 1;

  do
    {
      GQuark item;

1575
      mid = (min + max) / 2;
1576 1577 1578
      item = g_array_index (array, GQuark, mid);

      if (class_quark == item)
1579 1580 1581 1582
        {
          found = TRUE;
          pos = mid;
        }
1583
      else if (class_quark > item)
1584
        min = pos = mid + 1;
1585
      else
1586 1587 1588 1589
        {
          max = mid - 1;
          pos = mid;
        }
1590
    }
1591 1592
  while (!found && min <= max);

1593
  if (position)
1594
    *position = pos;
1595 1596 1597 1598 1599

  return found;
}

static gboolean
1600 1601 1602
region_find (GArray *array,
             GQuark  class_quark,
             guint  *position)
1603
{
1604
  gint min, max, mid;
1605
  gboolean found = FALSE;
1606
  guint pos;
1607

1608 1609 1610 1611 1612 1613
  if (position)
    *position = 0;

  if (!array || array->len == 0)
    return FALSE;

1614 1615 1616 1617 1618
  min = 0;
  max = array->len - 1;

  do
    {
1619
      GtkRegion *region;
1620

1621
      mid = (min + max) / 2;
1622
      region = &g_array_index (array, GtkRegion, mid);
1623

1624
      if (region->class_quark == class_quark)
1625 1626 1627 1628
        {
          found = TRUE;
          pos = mid;
        }
1629
      else if (region->class_quark > class_quark)
1630
        min = pos = mid + 1;
1631
      else
1632 1633 1634 1635
        {
          max = mid - 1;
          pos = mid;
        }
1636
    }
1637 1638
  while (!found && min <= max);

1639
  if (position)
1640
    *position = pos;
1641 1642 1643 1644

  return found;
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1645
/**
1646
 * gtk_style_context_add_class:
Carlos Garnacho's avatar
Carlos Garnacho committed
1647 1648 1649
 * @context: a #GtkStyleContext
 * @class_name: class name to use in styling
 *
1650 1651
 * Adds a style class to @context, so posterior calls to
 * gtk_style_context_get() or any of the gtk_render_*()