gtkstylecontext.c 127 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 "gtkcontainerprivate.h"
27
#include "gtkcssanimatedvaluesprivate.h"
28
#include "gtkcssenginevalueprivate.h"
29
#include "gtkcssnumbervalueprivate.h"
30
#include "gtkcssrgbavalueprivate.h"
31
#include "gtkdebug.h"
32
#include "gtkstylepropertiesprivate.h"
Carlos Garnacho's avatar
Carlos Garnacho committed
33
#include "gtktypebuiltins.h"
34
#include "gtkthemingengineprivate.h"
Carlos Garnacho's avatar
Carlos Garnacho committed
35
#include "gtkintl.h"
36
#include "gtkwidget.h"
37
#include "gtkwindow.h"
38
#include "gtkprivate.h"
39
#include "gtksymboliccolorprivate.h"
40
#include "gtkiconfactory.h"
41
#include "gtkwidgetpath.h"
42
#include "gtkwidgetprivate.h"
43
#include "gtkstylecascadeprivate.h"
44
#include "gtkstyleproviderprivate.h"
45
#include "gtksettings.h"
46
#include "gtksettingsprivate.h"
Carlos Garnacho's avatar
Carlos Garnacho committed
47

Carlos Garnacho's avatar
Carlos Garnacho committed
48 49
/**
 * SECTION:gtkstylecontext
Matthias Clasen's avatar
Matthias Clasen committed
50
 * @Short_description: Rendering UI elements
Carlos Garnacho's avatar
Carlos Garnacho committed
51 52 53 54 55 56
 * @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
57 58 59 60
 * 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
61
 * combination of all providers' information in priority order.
Carlos Garnacho's avatar
Carlos Garnacho committed
62 63 64
 *
 * For GTK+ widgets, any #GtkStyleContext returned by
 * gtk_widget_get_style_context() will already have a #GtkWidgetPath, a
65
 * #GdkScreen and RTL/LTR information set. The style context will be also
Carlos Garnacho's avatar
Carlos Garnacho committed
66 67
 * updated automatically if any of these settings change on the widget.
 *
68
 * If you are using the theming layer standalone, you will need to set a
Carlos Garnacho's avatar
Carlos Garnacho committed
69 70 71 72
 * 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
73
 * #GtkSettings:gtk-theme-name setting or a hierarchy change in the rendered
Carlos Garnacho's avatar
Carlos Garnacho committed
74 75 76 77 78 79
 * widget.
 *
 * <refsect2 id="gtkstylecontext-animations">
 * <title>Transition animations</title>
 * <para>
 * #GtkStyleContext has built-in support for state change transitions.
80 81
 * Note that these animations respect the #GtkSettings:gtk-enable-animations
 * setting.
Carlos Garnacho's avatar
Carlos Garnacho committed
82
 * </para>
83
 * <para>
Carlos Garnacho's avatar
Carlos Garnacho committed
84
 * For simple widgets where state changes affect the whole widget area,
85
 * calling gtk_style_context_notify_state_change() with a %NULL region
Matthias Clasen's avatar
Matthias Clasen committed
86 87 88
 * 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.
89
 * </para>
Carlos Garnacho's avatar
Carlos Garnacho committed
90 91 92
 * <para>
 * If a widget needs to declare several animatable regions (i.e. not
 * affecting the whole widget area), its #GtkWidget::draw signal handler
93 94
 * 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
95
 * gtk_style_context_pop_animatable_region(). These functions take an
96 97 98
 * 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
99 100
 * </para>
 * <example>
101
 * <title>Using an enumeration to identify  animatable regions</title>
Carlos Garnacho's avatar
Carlos Garnacho committed
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 129 130 131 132
 * <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
133 134 135
 * 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
136 137
 * </para>
 * <example>
138
 * <title>Using struct pointers to identify animatable regions</title>
Carlos Garnacho's avatar
Carlos Garnacho committed
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 178 179 180 181
 * <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>
182 183 184 185 186 187
 * <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:
188 189 190
 * #GTK_STYLE_CLASS_CELL,
 * #GTK_STYLE_CLASS_ENTRY,
 * #GTK_STYLE_CLASS_BUTTON,
191
 * #GTK_STYLE_CLASS_COMBOBOX_ENTRY,
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
 * #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,
232 233 234 235 236
 * #GTK_STYLE_CLASS_VERTICAL,
 * #GTK_STYLE_CLASS_TOP,
 * #GTK_STYLE_CLASS_BOTTOM,
 * #GTK_STYLE_CLASS_LEFT,
 * #GTK_STYLE_CLASS_RIGHT,
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 277 278 279 280
 * </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
281 282 283 284 285 286 287 288 289 290 291 292 293 294
 * <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
295 296
 * 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
297 298 299 300 301 302 303 304 305 306 307 308
 * 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>
 */

309 310
/* When these change we do a full restyling. Otherwise we try to figure out
 * if we need to change things. */
311
#define GTK_STYLE_CONTEXT_RADICAL_CHANGE (GTK_CSS_CHANGE_NAME | GTK_CSS_CHANGE_CLASS | GTK_CSS_CHANGE_SOURCE)
312 313 314
/* 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)
315

316
typedef struct GtkStyleInfo GtkStyleInfo;
317
typedef struct GtkRegion GtkRegion;
318
typedef struct PropertyValue PropertyValue;
319
typedef struct StyleData StyleData;
320

321
struct GtkRegion
322 323
{
  GQuark class_quark;
324
  GtkRegionFlags flags;
325
};
Carlos Garnacho's avatar
Carlos Garnacho committed
326

327 328 329 330
struct PropertyValue
{
  GType       widget_type;
  GParamSpec *pspec;
331
  GtkStateFlags state;
332 333 334
  GValue      value;
};

335
struct GtkStyleInfo
336
{
337
  GtkStyleInfo *next;
338
  GArray *style_classes;
339
  GArray *regions;
340
  GtkJunctionSides junction_sides;
341
  GtkStateFlags state_flags;
342
  StyleData *data;
343 344
};

345 346
struct StyleData
{
347
  GtkCssComputedValues *store;
348
  GArray *property_cache;
349
  guint ref_count;
350 351
};

352
struct _GtkStyleContextPrivate
Carlos Garnacho's avatar
Carlos Garnacho committed
353
{
354 355
  GdkScreen *screen;

356
  GtkStyleCascade *cascade;
357

358 359 360
  GtkStyleContext *animation_list_prev;
  GtkStyleContext *animation_list_next;

361
  GtkStyleContext *parent;
362
  GSList *children;
363
  GtkWidget *widget;            
364
  GtkWidgetPath *widget_path;
365
  GHashTable *style_data;
366
  GtkStyleInfo *info;
367

368
  GtkTextDirection direction;
369

370
  GtkCssChange relevant_changes;
371
  GtkCssChange pending_changes;
372

373
  guint invalidating_context : 1;
374
  guint invalid : 1;
Carlos Garnacho's avatar
Carlos Garnacho committed
375 376
};

377 378
enum {
  PROP_0,
379
  PROP_SCREEN,
380 381
  PROP_DIRECTION,
  PROP_PARENT
382 383
};

384 385 386 387 388
enum {
  CHANGED,
  LAST_SIGNAL
};

389
static guint signals[LAST_SIGNAL] = { 0 };
390 391
static GtkStyleContext *_running_animations = NULL;
guint _running_animations_timer_id = 0;
392

Carlos Garnacho's avatar
Carlos Garnacho committed
393 394
static void gtk_style_context_finalize (GObject *object);

395 396 397 398 399 400 401 402
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);
403 404 405
static GtkSymbolicColor *
            gtk_style_context_color_lookup_func (gpointer      contextp,
                                                 const char   *name);
406

Carlos Garnacho's avatar
Carlos Garnacho committed
407 408 409

G_DEFINE_TYPE (GtkStyleContext, gtk_style_context, G_TYPE_OBJECT)

410 411 412 413 414 415 416 417 418
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
419 420 421 422 423 424
static void
gtk_style_context_class_init (GtkStyleContextClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  object_class->finalize = gtk_style_context_finalize;
425 426 427
  object_class->set_property = gtk_style_context_impl_set_property;
  object_class->get_property = gtk_style_context_impl_get_property;

428 429
  klass->changed = gtk_style_context_real_changed;

430 431
  signals[CHANGED] =
    g_signal_new (I_("changed"),
432 433 434 435
                  G_TYPE_FROM_CLASS (object_class),
                  G_SIGNAL_RUN_FIRST,
                  G_STRUCT_OFFSET (GtkStyleContextClass, changed),
                  NULL, NULL,
436 437 438
                  g_cclosure_marshal_VOID__VOID,
                  G_TYPE_NONE, 0);

439
  g_object_class_install_property (object_class,
440 441
                                   PROP_SCREEN,
                                   g_param_spec_object ("screen",
442 443 444 445
                                                        P_("Screen"),
                                                        P_("The associated GdkScreen"),
                                                        GDK_TYPE_SCREEN,
                                                        GTK_PARAM_READWRITE));
446
  g_object_class_install_property (object_class,
447 448
                                   PROP_DIRECTION,
                                   g_param_spec_enum ("direction",
449 450 451 452 453
                                                      P_("Direction"),
                                                      P_("Text direction"),
                                                      GTK_TYPE_TEXT_DIRECTION,
                                                      GTK_TEXT_DIR_LTR,
                                                      GTK_PARAM_READWRITE));
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468
  /**
   * 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
469 470 471 472

  g_type_class_add_private (object_class, sizeof (GtkStyleContextPrivate));
}

473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525
static StyleData *
style_data_new (void)
{
  StyleData *data;

  data = g_slice_new0 (StyleData);
  data->ref_count = 1;

  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 StyleData *
style_data_ref (StyleData *style_data)
{
  style_data->ref_count++;

  return style_data;
}

static void
style_data_unref (StyleData *data)
{
  data->ref_count--;

  if (data->ref_count > 0)
    return;

  g_object_unref (data->store);
  clear_property_cache (data);

  g_slice_free (StyleData, data);
}

526 527 528 529 530 531
static gboolean
style_data_is_animating (StyleData *style_data)
{
  return GTK_IS_CSS_ANIMATED_VALUES (style_data->store);
}

532 533
static GtkStyleInfo *
style_info_new (void)
534
{
535
  GtkStyleInfo *info;
536

537 538
  info = g_slice_new0 (GtkStyleInfo);
  info->style_classes = g_array_new (FALSE, FALSE, sizeof (GQuark));
539
  info->regions = g_array_new (FALSE, FALSE, sizeof (GtkRegion));
540

541
  return info;
542 543 544
}

static void
545 546
style_info_set_data (GtkStyleInfo *info,
                     StyleData    *data)
547
{
548 549 550 551 552 553
  if (info->data == data)
    return;

  if (data)
    style_data_ref (data);

554 555
  if (info->data)
    style_data_unref (info->data);
556 557 558 559 560 561 562 563

  info->data = data;
}

static void
style_info_free (GtkStyleInfo *info)
{
  style_info_set_data (info, NULL);
564
  g_array_free (info->style_classes, TRUE);
565
  g_array_free (info->regions, TRUE);
566
  g_slice_free (GtkStyleInfo, info);
567 568
}

569
static GtkStyleInfo *
570 571 572 573 574 575 576 577 578 579 580
style_info_pop (GtkStyleInfo *info)
{
  GtkStyleInfo *next = info->next;

  style_info_free (info);

  return next;
}

static GtkStyleInfo *
style_info_copy (GtkStyleInfo *info)
581
{
582
  GtkStyleInfo *copy;
583

584
  copy = style_info_new ();
585
  g_array_insert_vals (copy->style_classes, 0,
586 587
                       info->style_classes->data,
                       info->style_classes->len);
588

589 590 591
  g_array_insert_vals (copy->regions, 0,
                       info->regions->data,
                       info->regions->len);
592

593
  copy->next = info;
594
  copy->junction_sides = info->junction_sides;
595
  copy->state_flags = info->state_flags;
596
  style_info_set_data (copy, info->data);
597

598 599 600
  return copy;
}

601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624
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;
    }

625
  return hash ^ info->state_flags;
626 627 628 629 630 631 632 633 634 635 636
}

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

  info1 = elem1;
  info2 = elem2;

637 638 639
  if (info1->state_flags != info2->state_flags)
    return FALSE;

640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661
  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;
}

662 663 664 665
static void
gtk_style_context_cascade_changed (GtkStyleCascade *cascade,
                                   GtkStyleContext *context)
{
666
  _gtk_style_context_queue_invalidate (context, GTK_CSS_CHANGE_SOURCE);
667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702
}

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
703 704 705 706 707
static void
gtk_style_context_init (GtkStyleContext *style_context)
{
  GtkStyleContextPrivate *priv;

708 709 710 711
  priv = style_context->priv = G_TYPE_INSTANCE_GET_PRIVATE (style_context,
                                                            GTK_TYPE_STYLE_CONTEXT,
                                                            GtkStyleContextPrivate);

712 713 714
  priv->style_data = g_hash_table_new_full (style_info_hash,
                                            style_info_equal,
                                            (GDestroyNotify) style_info_free,
715
                                            (GDestroyNotify) style_data_unref);
716

717
  priv->direction = GTK_TEXT_DIR_LTR;
718

719
  priv->screen = gdk_screen_get_default ();
720
  priv->relevant_changes = GTK_CSS_CHANGE_ANY;
721

722
  /* Create default info store */
723
  priv->info = style_info_new ();
724 725 726

  gtk_style_context_set_cascade (style_context,
                                 _gtk_style_cascade_get_for_screen (priv->screen));
Carlos Garnacho's avatar
Carlos Garnacho committed
727 728
}

729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781
static gboolean
gtk_style_context_do_animations (gpointer unused)
{
  GtkStyleContext *context;

  for (context = _running_animations;
       context != NULL;
       context = context->priv->animation_list_next)
    {
      _gtk_style_context_queue_invalidate (context, GTK_CSS_CHANGE_ANIMATE);
    }

  return TRUE;
}

static gboolean
gtk_style_context_is_animating (GtkStyleContext *context)
{
  GtkStyleContextPrivate *priv = context->priv;

  return priv->animation_list_prev != NULL
      || _running_animations == context;
}

static void
gtk_style_context_stop_animating (GtkStyleContext *context)
{
  GtkStyleContextPrivate *priv = context->priv;

  if (!gtk_style_context_is_animating (context))
    return;

  if (priv->animation_list_prev == NULL)
    {
      _running_animations = priv->animation_list_next;

      if (_running_animations == NULL)
        {
          /* we were the last animation */
          g_source_remove (_running_animations_timer_id);
          _running_animations_timer_id = 0;
        }
    }
  else
    priv->animation_list_prev->priv->animation_list_next = priv->animation_list_next;

  if (priv->animation_list_next)
    priv->animation_list_next->priv->animation_list_prev = priv->animation_list_prev;

  priv->animation_list_next = NULL;
  priv->animation_list_prev = NULL;
}

782
static void
783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804
gtk_style_context_start_animating (GtkStyleContext *context)
{
  GtkStyleContextPrivate *priv = context->priv;

  if (gtk_style_context_is_animating (context))
    return;

  if (_running_animations == NULL)
    {
      _running_animations_timer_id = gdk_threads_add_timeout (25,
                                                              gtk_style_context_do_animations,
                                                              NULL);
      _running_animations = context;
    }
  else
    {
      priv->animation_list_next = _running_animations;
      _running_animations->priv->animation_list_prev = context;
      _running_animations = context;
    }
}

Carlos Garnacho's avatar
Carlos Garnacho committed
805 806 807 808
static void
gtk_style_context_finalize (GObject *object)
{
  GtkStyleContextPrivate *priv;
809
  GtkStyleContext *style_context;
Carlos Garnacho's avatar
Carlos Garnacho committed
810

811 812
  style_context = GTK_STYLE_CONTEXT (object);
  priv = style_context->priv;
Carlos Garnacho's avatar
Carlos Garnacho committed
813

814
  _gtk_style_context_stop_animations (style_context);
815

816 817 818
  /* children hold a reference to us */
  g_assert (priv->children == NULL);

819 820
  gtk_style_context_set_parent (style_context, NULL);

821 822
  gtk_style_context_set_cascade (style_context, NULL);

Carlos Garnacho's avatar
Carlos Garnacho committed
823 824 825
  if (priv->widget_path)
    gtk_widget_path_free (priv->widget_path);

826
  g_hash_table_destroy (priv->style_data);
Carlos Garnacho's avatar
Carlos Garnacho committed
827

828 829
  while (priv->info)
    priv->info = style_info_pop (priv->info);
830

Carlos Garnacho's avatar
Carlos Garnacho committed
831 832 833
  G_OBJECT_CLASS (gtk_style_context_parent_class)->finalize (object);
}

834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849
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;
850 851 852 853
    case PROP_DIRECTION:
      gtk_style_context_set_direction (style_context,
                                       g_value_get_enum (value));
      break;
854 855 856 857
    case PROP_PARENT:
      gtk_style_context_set_parent (style_context,
                                    g_value_get_object (value));
      break;
858 859 860 861 862 863 864 865 866 867 868 869 870
    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;
871
  GtkStyleContextPrivate *priv;
872 873 874 875 876 877 878 879 880

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

  switch (prop_id)
    {
    case PROP_SCREEN:
      g_value_set_object (value, priv->screen);
      break;
881 882 883
    case PROP_DIRECTION:
      g_value_set_enum (value, priv->direction);
      break;
884 885 886
    case PROP_PARENT:
      g_value_set_object (value, priv->parent);
      break;
887 888 889 890 891 892
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

893
static GtkWidgetPath *
894 895 896 897 898 899 900 901
create_query_path (GtkStyleContext *context)
{
  GtkStyleContextPrivate *priv;
  GtkWidgetPath *path;
  GtkStyleInfo *info;
  guint i, pos;

  priv = context->priv;
902
  path = priv->widget ? _gtk_widget_create_path (priv->widget) : gtk_widget_path_copy (priv->widget_path);
903 904
  pos = gtk_widget_path_length (path) - 1;

905
  info = priv->info;
906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930

  /* 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;
}

931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957
static void
build_properties (GtkStyleContext      *context,
                  GtkCssComputedValues *values,
                  GtkStateFlags         state,
                  const GtkBitmask     *relevant_changes)
{
  GtkStyleContextPrivate *priv;
  GtkCssMatcher matcher;
  GtkWidgetPath *path;
  GtkCssLookup *lookup;

  priv = context->priv;

  path = create_query_path (context);
  lookup = _gtk_css_lookup_new (relevant_changes);

  if (_gtk_css_matcher_init (&matcher, path, state))
    _gtk_style_provider_private_lookup (GTK_STYLE_PROVIDER_PRIVATE (priv->cascade),
                                        &matcher,
                                        lookup);

  _gtk_css_lookup_resolve (lookup, context, values);

  _gtk_css_lookup_free (lookup);
  gtk_widget_path_free (path);
}

Carlos Garnacho's avatar
Carlos Garnacho committed
958
static StyleData *
959
style_data_lookup (GtkStyleContext *context)
960 961
{
  GtkStyleContextPrivate *priv;
962
  GtkStyleInfo *info;
963
  StyleData *data;
964 965

  priv = context->priv;
966
  info = priv->info;
967 968

  /* Current data in use is cached, just return it */
969 970
  if (info->data)
    return info->data;
971

972
  g_assert (priv->widget != NULL || priv->widget_path != NULL);
973

974 975
  data = g_hash_table_lookup (priv->style_data, info);
  if (data)
976
    {
977 978
      style_info_set_data (info, data);
      return data;
979
    }
980

981
  data = style_data_new ();
982
  data->store = _gtk_css_computed_values_new ();
983 984 985 986
  style_info_set_data (info, data);
  g_hash_table_insert (priv->style_data,
                       style_info_copy (info),
                       data);
987

988
  build_properties (context, data->store, info->state_flags, NULL);
989

990
  return data;
991 992
}

993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009
static StyleData *
style_data_lookup_for_state (GtkStyleContext *context,
                             GtkStateFlags    state)
{
  StyleData *data;

  if (context->priv->info->state_flags == state)
    return style_data_lookup (context);

  gtk_style_context_save (context);
  gtk_style_context_set_state (context, state);
  data = style_data_lookup (context);
  gtk_style_context_restore (context);

  return data;
}

1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026
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);
1027
      else if (GTK_IS_RESIZE_CONTAINER (priv->widget))
1028
        _gtk_container_queue_restyle (GTK_CONTAINER (priv->widget));
1029 1030 1031
    }
}

1032 1033 1034 1035 1036 1037 1038 1039
/* 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)
{
1040
  return context->priv->info->next != NULL;
1041 1042 1043 1044 1045 1046 1047
}

static void
gtk_style_context_queue_invalidate_internal (GtkStyleContext *context,
                                             GtkCssChange     change)
{
  GtkStyleContextPrivate *priv = context->priv;
1048
  GtkStyleInfo *info = priv->info;
1049

1050 1051
  if (gtk_style_context_is_saved (context))
    {
1052
      style_info_set_data (info, NULL);
1053 1054
    }
  else
1055
    {
1056
      _gtk_style_context_queue_invalidate (context, change);
1057 1058 1059 1060
      /* XXX: We need to invalidate siblings here somehow */
    }
}

1061 1062 1063 1064
/**
 * gtk_style_context_new:
 *
 * Creates a standalone #GtkStyleContext, this style context
1065 1066
 * won't be attached to any widget, so you may want
 * to call gtk_style_context_set_path() yourself.
1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082
 *
 * <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);
}

1083 1084 1085 1086 1087 1088 1089 1090
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;
1091

1092 1093
  _gtk_style_context_stop_animations (context);

1094
  _gtk_style_context_queue_invalidate (context, GTK_CSS_CHANGE_ANY_SELF);
1095 1096
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107
/**
 * 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.
1108 1109 1110 1111
 * Note that a style provider added by this function only affects
 * the style of the widget to which @context belongs. If you want
 * to affect the style of all widgets, use
 * gtk_style_context_add_provider_for_screen().
Carlos Garnacho's avatar
Carlos Garnacho committed
1112
 *
1113 1114 1115 1116
 * <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
1117 1118
 * Since: 3.0
 **/
1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129
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;
1130 1131 1132 1133 1134 1135 1136

  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);
1137 1138 1139 1140 1141 1142 1143
      _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);
1144
    }
Carlos Garnacho's avatar
Carlos Garnacho committed
1145 1146
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1147 1148 1149 1150 1151 1152 1153 1154 1155
/**
 * 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
1156 1157 1158 1159 1160 1161 1162 1163 1164
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));

1165
  priv = context->priv;
Carlos Garnacho's avatar
Carlos Garnacho committed
1166

1167 1168
  if (priv->cascade == _gtk_style_cascade_get_for_screen (priv->screen))
    return;
Carlos Garnacho's avatar
Carlos Garnacho committed
1169

1170
  _gtk_style_cascade_remove_provider (priv->cascade, provider);
1171
}
Carlos Garnacho's avatar
Carlos Garnacho committed
1172

Carlos Garnacho's avatar
Carlos Garnacho committed
1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185
/**
 * 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
 **/
1186 1187
void
gtk_style_context_reset_widgets (GdkScreen *screen)
1188 1189
{
  GList *list, *toplevels;
Carlos Garnacho's avatar
Carlos Garnacho committed
1190

1191 1192
  _gtk_icon_set_invalidate_caches ();

1193
  toplevels = gtk_window_list_toplevels ();
1194
  g_list_foreach (toplevels, (GFunc) g_object_ref, NULL);
Carlos Garnacho's avatar
Carlos Garnacho committed
1195

1196 1197 1198 1199
  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
1200

1201
      g_object_unref (list->data);
Carlos Garnacho's avatar
Carlos Garnacho committed
1202 1203
    }

1204 1205 1206
  g_list_free (toplevels);
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217
/**
 * 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
1218
 * in style construction for all #GtkStyleContexts under @screen.
Carlos Garnacho's avatar
Carlos Garnacho committed
1219
 *
1220 1221 1222
 * GTK+ uses this to make styling information from #GtkSettings
 * available.
 *
1223 1224 1225 1226
 * <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
1227 1228
 * Since: 3.0
 **/
1229 1230 1231 1232 1233
void
gtk_style_context_add_provider_for_screen (GdkScreen        *screen,
                                           GtkStyleProvider *provider,
                                           guint             priority)
{
1234
  GtkStyleCascade *cascade;
1235 1236 1237

  g_return_if_fail (GDK_IS_SCREEN (screen));
  g_return_if_fail (GTK_IS_STYLE_PROVIDER (provider));
1238
  g_return_if_fail (!GTK_IS_SETTINGS (provider) || _gtk_settings_get_screen (GTK_SETTINGS (provider)) == screen);
1239

1240 1241
  cascade = _gtk_style_cascade_get_for_screen (screen);
  _gtk_style_cascade_add_provider (cascade, provider, priority);
1242 1243
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1244 1245 1246 1247 1248 1249 1250 1251 1252
/**
 * 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
 **/
1253 1254 1255 1256
void
gtk_style_context_remove_provider_for_screen (GdkScreen        *screen,
                                              GtkStyleProvider *provider)
{
1257
  GtkStyleCascade *cascade;
1258 1259 1260

  g_return_if_fail (GDK_IS_SCREEN (screen));
  g_return_if_fail (GTK_IS_STYLE_PROVIDER (provider));
1261
  g_return_if_fail (!GTK_IS_SETTINGS (provider));
1262

1263 1264
  cascade = _gtk_style_cascade_get_for_screen (screen);
  _gtk_style_cascade_remove_provider (cascade, provider);
Carlos Garnacho's avatar
Carlos Garnacho committed
1265 1266
}

1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298
/**
 * 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;
1299
  g_return_val_if_fail (priv->widget != NULL || priv->widget_path != NULL, NULL);
1300 1301 1302 1303 1304

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

1305
  data = style_data_lookup (context);
1306 1307 1308
  return _gtk_css_computed_values_get_section (data->store, _gtk_css_style_property_get_id (GTK_CSS_STYLE_PROPERTY (prop)));
}

1309
static GtkCssValue *
1310 1311 1312 1313 1314 1315
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
1316 1317 1318 1319 1320
/**
 * gtk_style_context_get_property:
 * @context: a #GtkStyleContext
 * @property: style property name
 * @state: state to retrieve the property value for
1321
 * @value: (out) (transfer full):  return location for the style property value
Carlos Garnacho's avatar
Carlos Garnacho committed
1322
 *
1323 1324 1325 1326
 * 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
1327 1328 1329
 *
 * Since: 3.0
 **/
1330 1331 1332
void
gtk_style_context_get_property (GtkStyleContext *context,
                                const gchar     *property,
1333
                                GtkStateFlags    state,
1334 1335 1336
                                GValue          *value)
{
  GtkStyleContextPrivate *priv;
1337
  GtkStyleProperty *prop;
1338
  StyleData *data;
1339 1340 1341 1342 1343

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

1344
  priv = context->priv;
1345
  g_return_if_fail (priv->widget != NULL || priv->widget_path != NULL);
1346

1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358
  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;
    }

1359
  data = style_data_lookup_for_state (context, state);
1360
  _gtk_style_property_query (prop, value, gtk_style_context_query_func, data->store);
1361 1362
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1363 1364 1365 1366 1367 1368 1369 1370 1371 1372
/**
 * 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
 **/
1373 1374
void
gtk_style_context_get_valist (GtkStyleContext *context,
1375
                              GtkStateFlags    state,
1376 1377
                              va_list          args)
{
1378
  const gchar *property_name;
1379 1380 1381

  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));

1382
  property_name = va_arg (args, const gchar *);
1383

1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405
  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 *);
    }
1406 1407
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418
/**
 * 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
 **/
1419 1420
void
gtk_style_context_get (GtkStyleContext *context,
1421
                       GtkStateFlags    state,
1422 1423 1424 1425 1426 1427 1428
                       ...)
{
  va_list args;

  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));

  va_start (args, state);
1429
  gtk_style_context_get_valist (context, state, args);
1430 1431 1432
  va_end (args);
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1433 1434 1435 1436 1437
/**
 * gtk_style_context_set_state:
 * @context: a #GtkStyleContext
 * @flags: state to represent
 *
1438 1439
 * Sets the state to be used when rendering with any
 * of the gtk_render_*() functions.
Carlos Garnacho's avatar
Carlos Garnacho committed
1440 1441 1442
 *
 * Since: 3.0
 **/
1443 1444 1445 1446 1447 1448
void
gtk_style_context_set_state (GtkStyleContext *context,
                             GtkStateFlags    flags)
{
  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));

1449
  context->priv->info->state_flags = flags;
1450 1451
  
  gtk_style_context_queue_invalidate_internal (context, GTK_CSS_CHANGE_STATE);
1452 1453
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1454 1455 1456 1457
/**
 * gtk_style_context_get_state:
 * @context: a #GtkStyleContext
 *
1458
 * Returns the state used when rendering.
Carlos Garnacho's avatar
Carlos Garnacho committed
1459 1460 1461 1462 1463
 *
 * Returns: the state flags
 *
 * Since: 3.0
 **/
1464 1465 1466 1467 1468
GtkStateFlags
gtk_style_context_get_state (GtkStyleContext *context)
{
  g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), 0);

1469
  return context->priv->info->state_flags;
1470 1471
}

1472 1473 1474 1475 1476 1477 1478 1479 1480 1481
/**
 * 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
1482 1483 1484 1485
 * 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.
1486 1487 1488 1489
 *
 * Returns: %TRUE if there is a running transition animation for @state.
 *
 * Since: 3.0
1490 1491
 *
 * Deprecated: 3.6: This function always returns %FALSE
1492
 **/
1493
gboolean
1494 1495 1496
gtk_style_context_state_is_running (GtkStyleContext *context,
                                    GtkStateType     state,
                                    gdouble         *progress)
1497 1498 1499
{
  g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), FALSE);

1500
  return FALSE;
1501 1502
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1503 1504 1505 1506 1507 1508 1509
/**
 * 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
1510 1511 1512 1513 1514
 * 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
1515 1516 1517
 *
 * Since: 3.0
 **/
1518 1519 1520 1521 1522 1523 1524 1525 1526
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);

1527
  priv = context->priv;
1528
  g_return_if_fail (priv->widget == NULL);
1529 1530 1531 1532 1533 1534 1535 1536

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

  if (path)
1537 1538
    priv->widget_path = gtk_widget_path_copy (path);

1539
  _gtk_style_context_queue_invalidate (context, GTK_CSS_CHANGE_ANY);
1540 1541
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1542 1543 1544 1545 1546 1547 1548 1549 1550 1551
/**
 * gtk_style_context_get_path:
 * @context: a #GtkStyleContext
 *
 * Returns the widget path used for style matching.
 *
 * Returns: (transfer none): A #GtkWidgetPath
 *
 * Since: 3.0
 **/
1552
const GtkWidgetPath *
1553 1554 1555 1556
gtk_style_context_get_path (GtkStyleContext *context)
{
  GtkStyleContextPrivate *priv;

1557
  priv = context->priv;
1558 1559 1560 1561
  if (priv->widget)
    return gtk_widget_get_path (priv->widget);
  else
    return priv->widget_path;
1562 1563
}

1564 1565 1566 1567 1568 1569 1570
/**
 * 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
1571 1572
 * <ulink url="http://www.w3.org/TR/css3-cascade/#inheritance">inheritance</ulink>
 * of properties.
1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593
 *
 * 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)
1594 1595 1596
    {
      parent->priv->children = g_slist_prepend (parent->priv->children, context);
      g_object_ref (parent);
1597 1598
      if (priv->invalid)
        gtk_style_context_set_invalid (parent, TRUE);
1599
    }
1600 1601

  if (priv->parent)
1602 1603 1604 1605
    {
      priv->parent->priv->children = g_slist_remove (priv->parent->priv->children, context);
      g_object_unref (priv->parent);
    }
1606 1607 1608 1609

  priv->parent = parent;

  g_object_notify (G_OBJECT (context), "parent");
1610
  _gtk_style_context_queue_invalidate (context, GTK_CSS_CHANGE_ANY_PARENT | GTK_CSS_CHANGE_ANY_SIBLING);
1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631
}

/**
 * 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
1632 1633 1634 1635 1636
/**
 * gtk_style_context_save:
 * @context: a #GtkStyleContext
 *
 * Saves the @context state, so all modifications done through
1637 1638
 * 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
1639 1640 1641 1642 1643
 * or gtk_style_context_set_junction_sides() can be reverted in one
 * go through gtk_style_context_restore().
 *
 * Since: 3.0
 **/
1644
void
1645
gtk_style_context_save (GtkStyleContext *context)
1646 1647 1648 1649 1650
{
  GtkStyleContextPrivate *priv;

  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));

1651
  priv = context->priv;
1652

1653
  priv->info = style_info_copy (priv->info);
1654 1655 1656 1657 1658
  /* Need to unset animations here because we can not know what style
   * class potential transitions came from once we save().
   */
  if (priv->info->data && style_data_is_animating (priv->info->data))
    style_info_set_data (priv->info, NULL);
1659 1660
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1661 1662 1663 1664
/**
 * gtk_style_context_restore:
 * @context: a #GtkStyleContext
 *
1665 1666
 * Restores @context state to a previous stage.
 * See gtk_style_context_save().
Carlos Garnacho's avatar
Carlos Garnacho committed
1667 1668 1669
 *
 * Since: 3.0
 **/
1670 1671 1672 1673 1674 1675 1676 1677 1678
void
gtk_style_context_restore (GtkStyleContext *context)
{
  GtkStyleContextPrivate *priv;

  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));

  priv = context->priv;

1679
  priv->info = style_info_pop (priv->info);
1680

1681
  if (!priv->info)
1682
    {
1683
      g_warning ("Unpaired gtk_style_context_restore() call");
1684

1685
      /* Create default region */
1686
      priv->info = style_info_new ();
1687 1688
    }
}
1689

1690
static gboolean
1691 1692 1693
style_class_find (GArray *array,
                  GQuark  class_quark,
                  guint  *position)
1694
{
1695
  gint min, max, mid;
1696
  gboolean found = FALSE;
1697
  guint pos;
1698

1699 1700 1701 1702 1703 1704
  if (position)
    *position = 0;

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

1705 1706 1707 1708 1709 1710 1711
  min = 0;
  max = array->len - 1;

  do
    {
      GQuark item;

1712
      mid = (min + max) / 2;
1713 1714 1715
      item = g_array_index (array, GQuark, mid);

      if (class_quark == item)
1716 1717 1718 1719
        {
          found = TRUE;
          pos = mid;
        }
1720
      else if (class_quark > item)
1721
        min = pos = mid + 1;
1722
      else