]> Pileus Git - ~andy/gtk/blob - gtk/gtkstylecascade.c
stylecontext: Do invalidation on first resize container
[~andy/gtk] / gtk / gtkstylecascade.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2012 Benjamin Otte <otte@gnome.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include "config.h"
19
20 #include "gtkstylecascadeprivate.h"
21
22 #include "gtkstyleprovider.h"
23 #include "gtkstyleproviderprivate.h"
24
25 typedef struct _GtkStyleCascadeIter GtkStyleCascadeIter;
26 typedef struct _GtkStyleProviderData GtkStyleProviderData;
27
28 struct _GtkStyleCascadeIter {
29   int parent_index; /* pointing at last index that was returned, not next one that should be returned */
30   int index;        /* pointing at last index that was returned, not next one that should be returned */
31 };
32
33 struct _GtkStyleProviderData
34 {
35   GtkStyleProvider *provider;
36   guint priority;
37   guint changed_signal_id;
38 };
39
40 static GtkStyleProvider *
41 gtk_style_cascade_iter_next (GtkStyleCascade     *cascade,
42                              GtkStyleCascadeIter *iter)
43 {
44   if (iter->parent_index > 0)
45     {
46       if (iter->index > 0)
47         {
48           GtkStyleProviderData *data, *parent_data;
49
50           data = &g_array_index (cascade->providers, GtkStyleProviderData, iter->index - 1);
51           parent_data = &g_array_index (cascade->parent->providers, GtkStyleProviderData, iter->parent_index - 1);
52
53           if (data->priority >= parent_data->priority)
54             {
55               iter->index--;
56               return data->provider;
57             }
58           else
59             {
60               iter->parent_index--;
61               return parent_data->provider;
62             }
63         }
64       else
65         {
66           iter->parent_index--;
67           return g_array_index (cascade->parent->providers, GtkStyleProviderData, iter->parent_index).provider;
68         }
69     }
70   else
71     {
72       if (iter->index > 0)
73         {
74           iter->index--;
75           return g_array_index (cascade->providers, GtkStyleProviderData, iter->index).provider;
76         }
77       else
78         {
79           return NULL;
80         }
81     }
82 }
83
84 static GtkStyleProvider *
85 gtk_style_cascade_iter_init (GtkStyleCascade     *cascade,
86                              GtkStyleCascadeIter *iter)
87 {
88   iter->parent_index = cascade->parent ? cascade->parent->providers->len : 0;
89   iter->index = cascade->providers->len;
90
91   return gtk_style_cascade_iter_next (cascade, iter);
92 }
93
94 static gboolean
95 gtk_style_cascade_get_style_property (GtkStyleProvider *provider,
96                                       GtkWidgetPath    *path,
97                                       GtkStateFlags     state,
98                                       GParamSpec       *pspec,
99                                       GValue           *value)
100 {
101   GtkStyleCascade *cascade = GTK_STYLE_CASCADE (provider);
102   GtkStyleCascadeIter iter;
103   GtkStyleProvider *item;
104
105   for (item = gtk_style_cascade_iter_init (cascade, &iter);
106        item;
107        item = gtk_style_cascade_iter_next (cascade, &iter))
108     {
109       if (gtk_style_provider_get_style_property (item,
110                                                  path,
111                                                  state,
112                                                  pspec,
113                                                  value))
114         return TRUE;
115     }
116
117   return FALSE;
118 }
119
120 static void
121 gtk_style_cascade_provider_iface_init (GtkStyleProviderIface *iface)
122 {
123   iface->get_style_property = gtk_style_cascade_get_style_property;
124 }
125
126 static GtkSettings *
127 gtk_style_cascade_get_settings (GtkStyleProviderPrivate *provider)
128 {
129   GtkStyleCascade *cascade = GTK_STYLE_CASCADE (provider);
130   GtkStyleCascadeIter iter;
131   GtkSettings *settings;
132   GtkStyleProvider *item;
133
134   for (item = gtk_style_cascade_iter_init (cascade, &iter);
135        item;
136        item = gtk_style_cascade_iter_next (cascade, &iter))
137     {
138       if (!GTK_IS_STYLE_PROVIDER_PRIVATE (item))
139         continue;
140           
141       settings = _gtk_style_provider_private_get_settings (GTK_STYLE_PROVIDER_PRIVATE (item));
142       if (settings)
143         return settings;
144     }
145
146   return NULL;
147 }
148
149 static GtkCssValue *
150 gtk_style_cascade_get_color (GtkStyleProviderPrivate *provider,
151                              const char              *name)
152 {
153   GtkStyleCascade *cascade = GTK_STYLE_CASCADE (provider);
154   GtkStyleCascadeIter iter;
155   GtkCssValue *color;
156   GtkStyleProvider *item;
157
158   for (item = gtk_style_cascade_iter_init (cascade, &iter);
159        item;
160        item = gtk_style_cascade_iter_next (cascade, &iter))
161     {
162       if (GTK_IS_STYLE_PROVIDER_PRIVATE (item))
163         {
164           color = _gtk_style_provider_private_get_color (GTK_STYLE_PROVIDER_PRIVATE (item), name);
165           if (color)
166             return color;
167         }
168       else
169         {
170           /* If somebody hits this code path, shout at them */
171         }
172     }
173
174   return NULL;
175 }
176
177 static GtkCssKeyframes *
178 gtk_style_cascade_get_keyframes (GtkStyleProviderPrivate *provider,
179                                  const char              *name)
180 {
181   GtkStyleCascade *cascade = GTK_STYLE_CASCADE (provider);
182   GtkStyleCascadeIter iter;
183   GtkCssKeyframes *keyframes;
184   GtkStyleProvider *item;
185
186   for (item = gtk_style_cascade_iter_init (cascade, &iter);
187        item;
188        item = gtk_style_cascade_iter_next (cascade, &iter))
189     {
190       if (!GTK_IS_STYLE_PROVIDER_PRIVATE (item))
191         continue;
192           
193       keyframes = _gtk_style_provider_private_get_keyframes (GTK_STYLE_PROVIDER_PRIVATE (item), name);
194       if (keyframes)
195         return keyframes;
196     }
197
198   return NULL;
199 }
200
201 static void
202 gtk_style_cascade_lookup (GtkStyleProviderPrivate *provider,
203                           const GtkCssMatcher     *matcher,
204                           GtkCssLookup            *lookup)
205 {
206   GtkStyleCascade *cascade = GTK_STYLE_CASCADE (provider);
207   GtkStyleCascadeIter iter;
208   GtkStyleProvider *item;
209
210   for (item = gtk_style_cascade_iter_init (cascade, &iter);
211        item;
212        item = gtk_style_cascade_iter_next (cascade, &iter))
213     {
214       if (GTK_IS_STYLE_PROVIDER_PRIVATE (item))
215         {
216           _gtk_style_provider_private_lookup (GTK_STYLE_PROVIDER_PRIVATE (item),
217                                               matcher,
218                                               lookup);
219         }
220       else
221         {
222           /* you lose */
223           g_warn_if_reached ();
224         }
225     }
226 }
227
228 static GtkCssChange
229 gtk_style_cascade_get_change (GtkStyleProviderPrivate *provider,
230                               const GtkCssMatcher     *matcher)
231 {
232   GtkStyleCascade *cascade = GTK_STYLE_CASCADE (provider);
233   GtkStyleCascadeIter iter;
234   GtkStyleProvider *item;
235   GtkCssChange change = 0;
236
237   for (item = gtk_style_cascade_iter_init (cascade, &iter);
238        item;
239        item = gtk_style_cascade_iter_next (cascade, &iter))
240     {
241       if (GTK_IS_STYLE_PROVIDER_PRIVATE (item))
242         {
243           change |= _gtk_style_provider_private_get_change (GTK_STYLE_PROVIDER_PRIVATE (item),
244                                                             matcher);
245         }
246       else
247         {
248           g_return_val_if_reached (GTK_CSS_CHANGE_ANY);
249         }
250     }
251
252   return change;
253 }
254
255 static void
256 gtk_style_cascade_provider_private_iface_init (GtkStyleProviderPrivateInterface *iface)
257 {
258   iface->get_color = gtk_style_cascade_get_color;
259   iface->get_settings = gtk_style_cascade_get_settings;
260   iface->get_keyframes = gtk_style_cascade_get_keyframes;
261   iface->lookup = gtk_style_cascade_lookup;
262   iface->get_change = gtk_style_cascade_get_change;
263 }
264
265 G_DEFINE_TYPE_EXTENDED (GtkStyleCascade, _gtk_style_cascade, G_TYPE_OBJECT, 0,
266                         G_IMPLEMENT_INTERFACE (GTK_TYPE_STYLE_PROVIDER,
267                                                gtk_style_cascade_provider_iface_init)
268                         G_IMPLEMENT_INTERFACE (GTK_TYPE_STYLE_PROVIDER_PRIVATE,
269                                                gtk_style_cascade_provider_private_iface_init));
270
271 static void
272 gtk_style_cascade_dispose (GObject *object)
273 {
274   GtkStyleCascade *cascade = GTK_STYLE_CASCADE (object);
275
276   _gtk_style_cascade_set_parent (cascade, NULL);
277   g_array_unref (cascade->providers);
278
279   G_OBJECT_CLASS (_gtk_style_cascade_parent_class)->dispose (object);
280 }
281
282 static void
283 _gtk_style_cascade_class_init (GtkStyleCascadeClass *klass)
284 {
285   GObjectClass *object_class = G_OBJECT_CLASS (klass);
286
287   object_class->dispose = gtk_style_cascade_dispose;
288 }
289
290 static void
291 style_provider_data_clear (gpointer data_)
292 {
293   GtkStyleProviderData *data = data_;
294
295   g_signal_handler_disconnect (data->provider, data->changed_signal_id);
296   g_object_unref (data->provider);
297 }
298
299 static void
300 _gtk_style_cascade_init (GtkStyleCascade *cascade)
301 {
302   cascade->providers = g_array_new (FALSE, FALSE, sizeof (GtkStyleProviderData));
303   g_array_set_clear_func (cascade->providers, style_provider_data_clear);
304 }
305
306 GtkStyleCascade *
307 _gtk_style_cascade_new (void)
308 {
309   return g_object_new (GTK_TYPE_STYLE_CASCADE, NULL);
310 }
311
312 GtkStyleCascade *
313 _gtk_style_cascade_get_for_screen (GdkScreen *screen)
314 {
315   GtkStyleCascade *cascade;
316   static GQuark quark = 0;
317
318   g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
319
320   if (G_UNLIKELY (!quark))
321     quark = g_quark_from_static_string ("gtk-style-cascade");
322
323   cascade = g_object_get_qdata (G_OBJECT (screen), quark);
324   if (cascade == NULL)
325     {
326       cascade = _gtk_style_cascade_new ();
327       g_object_set_qdata_full (G_OBJECT (screen), quark, cascade, g_object_unref);
328     }
329
330   return cascade;
331 }
332
333 void
334 _gtk_style_cascade_set_parent (GtkStyleCascade *cascade,
335                                GtkStyleCascade *parent)
336 {
337   g_return_if_fail (GTK_IS_STYLE_CASCADE (cascade));
338   g_return_if_fail (parent == NULL || GTK_IS_STYLE_CASCADE (parent));
339   if (parent)
340     g_return_if_fail (parent->parent == NULL);
341
342   if (cascade->parent == parent)
343     return;
344
345   if (parent)
346     {
347       g_object_ref (parent);
348       g_signal_connect_swapped (parent,
349                                 "-gtk-private-changed",
350                                 G_CALLBACK (_gtk_style_provider_private_changed),
351                                 cascade);
352     }
353
354   if (cascade->parent)
355     {
356       g_signal_handlers_disconnect_by_func (cascade->parent, 
357                                             _gtk_style_provider_private_changed,
358                                             cascade);
359       g_object_unref (cascade->parent);
360     }
361
362   cascade->parent = parent;
363 }
364
365 void
366 _gtk_style_cascade_add_provider (GtkStyleCascade  *cascade,
367                                  GtkStyleProvider *provider,
368                                  guint             priority)
369 {
370   GtkStyleProviderData data;
371   guint i;
372
373   g_return_if_fail (GTK_IS_STYLE_CASCADE (cascade));
374   g_return_if_fail (GTK_IS_STYLE_PROVIDER (provider));
375   g_return_if_fail (GTK_STYLE_PROVIDER (cascade) != provider);
376
377   data.provider = g_object_ref (provider);
378   data.priority = priority;
379   data.changed_signal_id = g_signal_connect_swapped (provider,
380                                                      "-gtk-private-changed",
381                                                      G_CALLBACK (_gtk_style_provider_private_changed),
382                                                      cascade);
383
384   /* ensure it gets removed first */
385   _gtk_style_cascade_remove_provider (cascade, provider);
386
387   for (i = 0; i < cascade->providers->len; i++)
388     {
389       if (g_array_index (cascade->providers, GtkStyleProviderData, i).priority > priority)
390         break;
391     }
392   g_array_insert_val (cascade->providers, i, data);
393
394   _gtk_style_provider_private_changed (GTK_STYLE_PROVIDER_PRIVATE (cascade));
395 }
396
397 void
398 _gtk_style_cascade_remove_provider (GtkStyleCascade  *cascade,
399                                     GtkStyleProvider *provider)
400 {
401   guint i;
402
403   g_return_if_fail (GTK_IS_STYLE_CASCADE (cascade));
404   g_return_if_fail (GTK_IS_STYLE_PROVIDER (provider));
405
406   for (i = 0; i < cascade->providers->len; i++)
407     {
408       GtkStyleProviderData *data = &g_array_index (cascade->providers, GtkStyleProviderData, i);
409
410       if (data->provider == provider)
411         {
412           g_array_remove_index (cascade->providers, i);
413   
414           _gtk_style_provider_private_changed (GTK_STYLE_PROVIDER_PRIVATE (cascade));
415           break;
416         }
417     }
418 }
419