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

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

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

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

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

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

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

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

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

355
  GtkStyleCascade *cascade;
356

357 358 359
  GtkStyleContext *animation_list_prev;
  GtkStyleContext *animation_list_next;

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

367
  GtkTextDirection direction;
368

369
  GtkCssChange relevant_changes;
370
  GtkCssChange pending_changes;
371

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

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

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

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

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

394 395 396 397 398 399 400 401
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);
402 403 404
static GtkSymbolicColor *
            gtk_style_context_color_lookup_func (gpointer      contextp,
                                                 const char   *name);
405
static StyleData *style_data_lookup             (GtkStyleContext *context);
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
static gboolean
style_data_is_animating (StyleData *style_data)
{
529
  return !_gtk_css_computed_values_is_static (style_data->store);
530 531
}

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);
}

703 704 705 706 707 708 709 710
GtkStyleProviderPrivate *
_gtk_style_context_get_style_provider (GtkStyleContext *context)
{
  g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL);

  return GTK_STYLE_PROVIDER_PRIVATE (context->priv->cascade);
}

Carlos Garnacho's avatar
Carlos Garnacho committed
711 712 713 714 715
static void
gtk_style_context_init (GtkStyleContext *style_context)
{
  GtkStyleContextPrivate *priv;

716 717 718 719
  priv = style_context->priv = G_TYPE_INSTANCE_GET_PRIVATE (style_context,
                                                            GTK_TYPE_STYLE_CONTEXT,
                                                            GtkStyleContextPrivate);

720 721 722
  priv->style_data = g_hash_table_new_full (style_info_hash,
                                            style_info_equal,
                                            (GDestroyNotify) style_info_free,
723
                                            (GDestroyNotify) style_data_unref);
724

725
  priv->direction = GTK_TEXT_DIR_LTR;
726

727
  priv->screen = gdk_screen_get_default ();
728
  priv->relevant_changes = GTK_CSS_CHANGE_ANY;
729

730
  /* Create default info store */
731
  priv->info = style_info_new ();
732 733 734

  gtk_style_context_set_cascade (style_context,
                                 _gtk_style_cascade_get_for_screen (priv->screen));
Carlos Garnacho's avatar
Carlos Garnacho committed
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 782 783 784 785 786 787 788 789
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;
}

790
static void
791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812
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;
    }
}

813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847
static gboolean
gtk_style_context_should_animate (GtkStyleContext *context)
{
  GtkStyleContextPrivate *priv;
  StyleData *data;
  gboolean animate;

  priv = context->priv;

  if (priv->widget == NULL)
    return FALSE;

  if (!gtk_widget_get_mapped (priv->widget))
    return FALSE;

  data = style_data_lookup (context);
  if (!style_data_is_animating (data))
    return FALSE;

  g_object_get (gtk_widget_get_settings (context->priv->widget),
                "gtk-enable-animations", &animate,
                NULL);

  return animate;
}

void
_gtk_style_context_update_animating (GtkStyleContext *context)
{
  if (gtk_style_context_should_animate (context))
    gtk_style_context_start_animating (context);
  else
    gtk_style_context_stop_animating (context);
}

Carlos Garnacho's avatar
Carlos Garnacho committed
848 849 850 851
static void
gtk_style_context_finalize (GObject *object)
{
  GtkStyleContextPrivate *priv;
852
  GtkStyleContext *style_context;
Carlos Garnacho's avatar
Carlos Garnacho committed
853

854 855
  style_context = GTK_STYLE_CONTEXT (object);
  priv = style_context->priv;
Carlos Garnacho's avatar
Carlos Garnacho committed
856

857
  gtk_style_context_stop_animating (style_context);
858

859 860 861
  /* children hold a reference to us */
  g_assert (priv->children == NULL);

862 863
  gtk_style_context_set_parent (style_context, NULL);

864 865
  gtk_style_context_set_cascade (style_context, NULL);

Carlos Garnacho's avatar
Carlos Garnacho committed
866 867 868
  if (priv->widget_path)
    gtk_widget_path_free (priv->widget_path);

869
  g_hash_table_destroy (priv->style_data);
Carlos Garnacho's avatar
Carlos Garnacho committed
870

871 872
  while (priv->info)
    priv->info = style_info_pop (priv->info);
873

Carlos Garnacho's avatar
Carlos Garnacho committed
874 875 876
  G_OBJECT_CLASS (gtk_style_context_parent_class)->finalize (object);
}

877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892
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;
893 894 895 896
    case PROP_DIRECTION:
      gtk_style_context_set_direction (style_context,
                                       g_value_get_enum (value));
      break;
897 898 899 900
    case PROP_PARENT:
      gtk_style_context_set_parent (style_context,
                                    g_value_get_object (value));
      break;
901 902 903 904 905 906 907 908 909 910 911 912 913
    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;
914
  GtkStyleContextPrivate *priv;
915 916 917 918 919 920 921 922 923

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

  switch (prop_id)
    {
    case PROP_SCREEN:
      g_value_set_object (value, priv->screen);
      break;
924 925 926
    case PROP_DIRECTION:
      g_value_set_enum (value, priv->direction);
      break;
927 928 929
    case PROP_PARENT:
      g_value_set_object (value, priv->parent);
      break;
930 931 932 933 934 935
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

936
static GtkWidgetPath *
937 938 939 940 941 942 943 944
create_query_path (GtkStyleContext *context)
{
  GtkStyleContextPrivate *priv;
  GtkWidgetPath *path;
  GtkStyleInfo *info;
  guint i, pos;

  priv = context->priv;
945
  path = priv->widget ? _gtk_widget_create_path (priv->widget) : gtk_widget_path_copy (priv->widget_path);
946 947
  pos = gtk_widget_path_length (path) - 1;

948
  info = priv->info;
949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973

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

974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000
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
1001
static StyleData *
1002
style_data_lookup (GtkStyleContext *context)
1003 1004
{
  GtkStyleContextPrivate *priv;
1005
  GtkStyleInfo *info;
1006
  StyleData *data;
1007 1008

  priv = context->priv;
1009
  info = priv->info;
1010 1011

  /* Current data in use is cached, just return it */
1012 1013
  if (info->data)
    return info->data;
1014

1015
  g_assert (priv->widget != NULL || priv->widget_path != NULL);
1016

1017 1018
  data = g_hash_table_lookup (priv->style_data, info);
  if (data)
1019
    {
1020 1021
      style_info_set_data (info, data);
      return data;
1022
    }
1023

1024
  data = style_data_new ();
1025
  data->store = _gtk_css_computed_values_new ();
1026 1027 1028 1029
  style_info_set_data (info, data);
  g_hash_table_insert (priv->style_data,
                       style_info_copy (info),
                       data);
1030

1031
  build_properties (context, data->store, info->state_flags, NULL);
1032

1033
  return data;
1034 1035
}

1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052
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;
}

1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069
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);
1070
      else if (GTK_IS_RESIZE_CONTAINER (priv->widget))
1071
        _gtk_container_queue_restyle (GTK_CONTAINER (priv->widget));
1072 1073 1074
    }
}

1075 1076 1077 1078 1079 1080 1081 1082
/* 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)
{
1083
  return context->priv->info->next != NULL;
1084 1085 1086 1087 1088 1089 1090
}

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

1093 1094
  if (gtk_style_context_is_saved (context))
    {
1095
      style_info_set_data (info, NULL);
1096 1097
    }
  else
1098
    {
1099
      _gtk_style_context_queue_invalidate (context, change);
1100 1101 1102 1103
      /* XXX: We need to invalidate siblings here somehow */
    }
}

1104 1105 1106 1107
/**
 * gtk_style_context_new:
 *
 * Creates a standalone #GtkStyleContext, this style context
1108 1109
 * won't be attached to any widget, so you may want
 * to call gtk_style_context_set_path() yourself.
1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125
 *
 * <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);
}

1126 1127 1128 1129 1130 1131 1132 1133
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;
1134

1135
  _gtk_style_context_update_animating (context);
1136

1137
  _gtk_style_context_queue_invalidate (context, GTK_CSS_CHANGE_ANY_SELF);
1138 1139
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150
/**
 * 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.
1151 1152 1153 1154
 * 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
1155
 *
1156 1157 1158 1159
 * <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
1160 1161
 * Since: 3.0
 **/
1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172
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;
1173 1174 1175 1176 1177 1178 1179

  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);
1180 1181 1182 1183 1184 1185 1186
      _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);
1187
    }
Carlos Garnacho's avatar
Carlos Garnacho committed
1188 1189
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1190 1191 1192 1193 1194 1195 1196 1197 1198
/**
 * 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
1199 1200 1201 1202 1203 1204 1205 1206 1207
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));

1208
  priv = context->priv;
Carlos Garnacho's avatar
Carlos Garnacho committed
1209

1210 1211
  if (priv->cascade == _gtk_style_cascade_get_for_screen (priv->screen))
    return;
Carlos Garnacho's avatar
Carlos Garnacho committed
1212

1213
  _gtk_style_cascade_remove_provider (priv->cascade, provider);
1214
}
Carlos Garnacho's avatar
Carlos Garnacho committed
1215

Carlos Garnacho's avatar
Carlos Garnacho committed
1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228
/**
 * 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
 **/
1229 1230
void
gtk_style_context_reset_widgets (GdkScreen *screen)
1231 1232
{
  GList *list, *toplevels;
Carlos Garnacho's avatar
Carlos Garnacho committed
1233

1234 1235
  _gtk_icon_set_invalidate_caches ();

1236
  toplevels = gtk_window_list_toplevels ();
1237
  g_list_foreach (toplevels, (GFunc) g_object_ref, NULL);
Carlos Garnacho's avatar
Carlos Garnacho committed
1238

1239 1240 1241 1242
  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
1243

1244
      g_object_unref (list->data);
Carlos Garnacho's avatar
Carlos Garnacho committed
1245 1246
    }

1247 1248 1249
  g_list_free (toplevels);
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260
/**
 * 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
1261
 * in style construction for all #GtkStyleContexts under @screen.
Carlos Garnacho's avatar
Carlos Garnacho committed
1262
 *
1263 1264 1265
 * GTK+ uses this to make styling information from #GtkSettings
 * available.
 *
1266 1267 1268 1269
 * <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
1270 1271
 * Since: 3.0
 **/
1272 1273 1274 1275 1276
void
gtk_style_context_add_provider_for_screen (GdkScreen        *screen,
                                           GtkStyleProvider *provider,
                                           guint             priority)
{
1277
  GtkStyleCascade *cascade;
1278 1279 1280

  g_return_if_fail (GDK_IS_SCREEN (screen));
  g_return_if_fail (GTK_IS_STYLE_PROVIDER (provider));
1281
  g_return_if_fail (!GTK_IS_SETTINGS (provider) || _gtk_settings_get_screen (GTK_SETTINGS (provider)) == screen);
1282

1283 1284
  cascade = _gtk_style_cascade_get_for_screen (screen);
  _gtk_style_cascade_add_provider (cascade, provider, priority);
1285 1286
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1287 1288 1289 1290 1291 1292 1293 1294 1295
/**
 * 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
 **/
1296 1297 1298 1299
void
gtk_style_context_remove_provider_for_screen (GdkScreen        *screen,
                                              GtkStyleProvider *provider)
{
1300
  GtkStyleCascade *cascade;
1301 1302 1303

  g_return_if_fail (GDK_IS_SCREEN (screen));
  g_return_if_fail (GTK_IS_STYLE_PROVIDER (provider));
1304
  g_return_if_fail (!GTK_IS_SETTINGS (provider));
1305

1306 1307
  cascade = _gtk_style_cascade_get_for_screen (screen);
  _gtk_style_cascade_remove_provider (cascade, provider);
Carlos Garnacho's avatar
Carlos Garnacho committed
1308 1309
}

1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341
/**
 * 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;
1342
  g_return_val_if_fail (priv->widget != NULL || priv->widget_path != NULL, NULL);
1343 1344 1345 1346 1347

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

1348
  data = style_data_lookup (context);
1349 1350 1351
  return _gtk_css_computed_values_get_section (data->store, _gtk_css_style_property_get_id (GTK_CSS_STYLE_PROPERTY (prop)));
}

1352
static GtkCssValue *
1353 1354 1355 1356 1357 1358
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
1359 1360 1361 1362 1363
/**
 * gtk_style_context_get_property:
 * @context: a #GtkStyleContext
 * @property: style property name
 * @state: state to retrieve the property value for
1364
 * @value: (out) (transfer full):  return location for the style property value
Carlos Garnacho's avatar
Carlos Garnacho committed
1365
 *
1366 1367 1368 1369
 * 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
1370 1371 1372
 *
 * Since: 3.0
 **/
1373 1374 1375
void
gtk_style_context_get_property (GtkStyleContext *context,
                                const gchar     *property,
1376
                                GtkStateFlags    state,
1377 1378 1379
                                GValue          *value)
{
  GtkStyleContextPrivate *priv;
1380
  GtkStyleProperty *prop;
1381
  StyleData *data;
1382 1383 1384 1385 1386

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

1387
  priv = context->priv;
1388
  g_return_if_fail (priv->widget != NULL || priv->widget_path != NULL);
1389

1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401
  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;
    }

1402
  data = style_data_lookup_for_state (context, state);
1403
  _gtk_style_property_query (prop, value, gtk_style_context_query_func, data->store);
1404 1405
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1406 1407 1408 1409 1410 1411 1412 1413 1414 1415
/**
 * 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
 **/
1416 1417
void
gtk_style_context_get_valist (GtkStyleContext *context,
1418
                              GtkStateFlags    state,
1419 1420
                              va_list          args)
{
1421
  const gchar *property_name;
1422 1423 1424

  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));

1425
  property_name = va_arg (args, const gchar *);
1426

1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448
  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 *);
    }
1449 1450
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461
/**
 * 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
 **/
1462 1463
void
gtk_style_context_get (GtkStyleContext *context,
1464
                       GtkStateFlags    state,
1465 1466 1467 1468 1469 1470 1471
                       ...)
{
  va_list args;

  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));

  va_start (args, state);
1472
  gtk_style_context_get_valist (context, state, args);
1473 1474 1475
  va_end (args);
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1476 1477 1478 1479 1480
/**
 * gtk_style_context_set_state:
 * @context: a #GtkStyleContext
 * @flags: state to represent
 *
1481 1482
 * Sets the state to be used when rendering with any
 * of the gtk_render_*() functions.
Carlos Garnacho's avatar
Carlos Garnacho committed
1483 1484 1485
 *
 * Since: 3.0
 **/
1486 1487 1488 1489 1490 1491
void
gtk_style_context_set_state (GtkStyleContext *context,
                             GtkStateFlags    flags)
{
  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));

1492
  context->priv->info->state_flags = flags;
1493 1494
  
  gtk_style_context_queue_invalidate_internal (context, GTK_CSS_CHANGE_STATE);
1495 1496
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1497 1498 1499 1500
/**
 * gtk_style_context_get_state:
 * @context: a #GtkStyleContext
 *
1501
 * Returns the state used when rendering.
Carlos Garnacho's avatar
Carlos Garnacho committed
1502 1503 1504 1505 1506
 *
 * Returns: the state flags
 *
 * Since: 3.0
 **/
1507 1508 1509 1510 1511
GtkStateFlags
gtk_style_context_get_state (GtkStyleContext *context)
{
  g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), 0);

1512
  return context->priv->info->state_flags;
1513 1514
}

1515 1516 1517 1518 1519 1520 1521 1522 1523 1524
/**
 * 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
1525 1526 1527 1528
 * 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.
1529 1530 1531 1532
 *
 * Returns: %TRUE if there is a running transition animation for @state.
 *
 * Since: 3.0
1533 1534
 *
 * Deprecated: 3.6: This function always returns %FALSE
1535
 **/
1536
gboolean
1537 1538 1539
gtk_style_context_state_is_running (GtkStyleContext *context,
                                    GtkStateType     state,
                                    gdouble         *progress)
1540 1541 1542
{
  g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), FALSE);

1543
  return FALSE;
1544 1545
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1546 1547 1548 1549 1550 1551 1552
/**
 * 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
1553 1554 1555 1556 1557
 * 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
1558 1559 1560
 *
 * Since: 3.0
 **/
1561 1562 1563 1564 1565 1566 1567 1568 1569
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);

1570
  priv = context->priv;
1571
  g_return_if_fail (priv->widget == NULL);
1572 1573 1574 1575 1576 1577 1578 1579

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

  if (path)
1580 1581
    priv->widget_path = gtk_widget_path_copy (path);

1582
  _gtk_style_context_queue_invalidate (context, GTK_CSS_CHANGE_ANY);
1583 1584
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1585 1586 1587 1588 1589 1590 1591 1592 1593 1594
/**
 * gtk_style_context_get_path:
 * @context: a #GtkStyleContext
 *
 * Returns the widget path used for style matching.
 *
 * Returns: (transfer none): A #GtkWidgetPath
 *
 * Since: 3.0
 **/
1595
const GtkWidgetPath *
1596 1597 1598 1599
gtk_style_context_get_path (GtkStyleContext *context)
{
  GtkStyleContextPrivate *priv;

1600
  priv = context->priv;
1601 1602 1603 1604
  if (priv->widget)
    return gtk_widget_get_path (priv->widget);
  else
    return priv->widget_path;
1605 1606
}

1607 1608 1609 1610 1611 1612 1613
/**
 * 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
1614 1615
 * <ulink url="http://www.w3.org/TR/css3-cascade/#inheritance">inheritance</ulink>
 * of properties.
1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636
 *
 * 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)
1637 1638 1639
    {
      parent->priv->children = g_slist_prepend (parent->priv->children, context);
      g_object_ref (parent);
1640 1641
      if (priv->invalid)
        gtk_style_context_set_invalid (parent, TRUE);
1642
    }
1643 1644

  if (priv->parent)
1645 1646 1647 1648
    {
      priv->parent->priv->children = g_slist_remove (priv->parent->priv->children, context);
      g_object_unref (priv->parent);
    }
1649 1650 1651 1652

  priv->parent = parent;

  g_object_notify (G_OBJECT (context), "parent");
1653
  _gtk_style_context_queue_invalidate (context, GTK_CSS_CHANGE_ANY_PARENT | GTK_CSS_CHANGE_ANY_SIBLING);
1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674
}

/**
 * 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
1675 1676 1677 1678 1679
/**
 * gtk_style_context_save:
 * @context: a #GtkStyleContext
 *
 * Saves the @context state, so all modifications done through
1680 1681
 * 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
1682 1683 1684 1685 1686
 * or gtk_style_context_set_junction_sides() can be reverted in one
 * go through gtk_style_context_restore().
 *
 * Since: 3.0
 **/
1687
void
1688
gtk_style_context_save (GtkStyleContext *context)
1689 1690 1691 1692 1693
{
  GtkStyleContextPrivate *priv;

  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));

1694
  priv = context->priv;
1695

1696
  priv->info = style_info_copy (priv->info);
1697 1698 1699 1700 1701
  /* 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);
1702 1703
}

Carlos Garnacho's avatar
Carlos Garnacho committed
1704 1705 1706 1707
/**
 * gtk_style_context_restore:
 * @context: a #GtkStyleContext
 *
1708 1709
 * Restores @context state to a previous stage.
 * See gtk_style_context_save().
Carlos Garnacho's avatar
Carlos Garnacho committed
1710 1711 1712
 *
 * Since: 3.0
 **/
1713 1714 1715 1716 1717 1718 1719 1720 1721
void
gtk_style_context_restore (GtkStyleContext *context)
{
  GtkStyleContextPrivate *priv;

  g_return_if_fail (GTK_IS_STYLE_CONTEXT (context));

  priv = context->priv;

1722
  priv->info = style_info_pop (priv->info);
1723

1724
  if (!priv->info)
1725
    {
1726
      g_warning ("Unpaired gtk_style_context_restore() call");
1727

1728
      /* Create default region */
1729
      priv->info = style_info_new ();
1730 1731
    }
}
1732

1733
static gboolean
1734 1735 1736
style_class_find (GArray *array,
                  GQuark  class_quark,
                  guint  *position)
1737
{
1738
  gint min, max, mid;
1739
  gboolean found = FALSE;
1740
  guint pos;