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#include "gtkprivate.h"
25
26typedef struct _GtkStyleCascadeIter GtkStyleCascadeIter;
27typedef struct _GtkStyleProviderData GtkStyleProviderData;
28
29struct _GtkStyleCascadeIter {
30 int n_cascades;
31 int *cascade_index; /* each one points at last index that was returned, */
32 /* not next one that should be returned */
33};
34
35struct _GtkStyleProviderData
36{
37 GtkStyleProvider *provider;
38 guint priority;
39 guint changed_signal_id;
40};
41
42static GtkStyleProvider *
43gtk_style_cascade_iter_next (GtkStyleCascade *cascade,
44 GtkStyleCascadeIter *iter)
45{
46 GtkStyleCascade *cas;
47 int ix, highest_priority_index = 0;
48 GtkStyleProviderData *highest_priority_data = NULL;
49
50 for (cas = cascade, ix = 0; ix < iter->n_cascades; cas = cas->parent, ix++)
51 {
52 GtkStyleProviderData *data;
53
54 if (iter->cascade_index[ix] <= 0)
55 continue;
56
57 data = &g_array_index (cas->providers,
58 GtkStyleProviderData,
59 iter->cascade_index[ix] - 1);
60 if (highest_priority_data == NULL || data->priority > highest_priority_data->priority)
61 {
62 highest_priority_index = ix;
63 highest_priority_data = data;
64 }
65 }
66
67 if (highest_priority_data != NULL)
68 {
69 iter->cascade_index[highest_priority_index]--;
70 return highest_priority_data->provider;
71 }
72 return NULL;
73}
74
75static GtkStyleProvider *
76gtk_style_cascade_iter_init (GtkStyleCascade *cascade,
77 GtkStyleCascadeIter *iter)
78{
79 GtkStyleCascade *cas = cascade;
80 int ix;
81
82 iter->n_cascades = 1;
83 while ((cas = cas->parent) != NULL)
84 iter->n_cascades++;
85
86 iter->cascade_index = g_new (int, iter->n_cascades);
87 for (cas = cascade, ix = 0; ix < iter->n_cascades; cas = cas->parent, ix++)
88 iter->cascade_index[ix] = cas->providers->len;
89
90 return gtk_style_cascade_iter_next (cascade, iter);
91}
92
93static void
94gtk_style_cascade_iter_clear (GtkStyleCascadeIter *iter)
95{
96 g_free (mem: iter->cascade_index);
97}
98
99static GtkSettings *
100gtk_style_cascade_get_settings (GtkStyleProvider *provider)
101{
102 GtkStyleCascade *cascade = GTK_STYLE_CASCADE (provider);
103 GtkStyleCascadeIter iter;
104 GtkSettings *settings;
105 GtkStyleProvider *item;
106
107 for (item = gtk_style_cascade_iter_init (cascade, iter: &iter);
108 item;
109 item = gtk_style_cascade_iter_next (cascade, iter: &iter))
110 {
111 settings = gtk_style_provider_get_settings (provider: item);
112 if (settings)
113 {
114 gtk_style_cascade_iter_clear (iter: &iter);
115 return settings;
116 }
117 }
118
119 gtk_style_cascade_iter_clear (iter: &iter);
120 return NULL;
121}
122
123static GtkCssValue *
124gtk_style_cascade_get_color (GtkStyleProvider *provider,
125 const char *name)
126{
127 GtkStyleCascade *cascade = GTK_STYLE_CASCADE (provider);
128 GtkStyleCascadeIter iter;
129 GtkCssValue *color;
130 GtkStyleProvider *item;
131
132 for (item = gtk_style_cascade_iter_init (cascade, iter: &iter);
133 item;
134 item = gtk_style_cascade_iter_next (cascade, iter: &iter))
135 {
136 color = gtk_style_provider_get_color (provider: item, name);
137 if (color)
138 {
139 gtk_style_cascade_iter_clear (iter: &iter);
140 return color;
141 }
142 }
143
144 gtk_style_cascade_iter_clear (iter: &iter);
145 return NULL;
146}
147
148static int
149gtk_style_cascade_get_scale (GtkStyleProvider *provider)
150{
151 GtkStyleCascade *cascade = GTK_STYLE_CASCADE (provider);
152
153 return cascade->scale;
154}
155
156static GtkCssKeyframes *
157gtk_style_cascade_get_keyframes (GtkStyleProvider *provider,
158 const char *name)
159{
160 GtkStyleCascade *cascade = GTK_STYLE_CASCADE (provider);
161 GtkStyleCascadeIter iter;
162 GtkCssKeyframes *keyframes;
163 GtkStyleProvider *item;
164
165 for (item = gtk_style_cascade_iter_init (cascade, iter: &iter);
166 item;
167 item = gtk_style_cascade_iter_next (cascade, iter: &iter))
168 {
169 keyframes = gtk_style_provider_get_keyframes (provider: item, name);
170 if (keyframes)
171 {
172 gtk_style_cascade_iter_clear (iter: &iter);
173 return keyframes;
174 }
175 }
176
177 gtk_style_cascade_iter_clear (iter: &iter);
178 return NULL;
179}
180
181static void
182gtk_style_cascade_lookup (GtkStyleProvider *provider,
183 const GtkCountingBloomFilter *filter,
184 GtkCssNode *node,
185 GtkCssLookup *lookup,
186 GtkCssChange *change)
187{
188 GtkStyleCascade *cascade = GTK_STYLE_CASCADE (provider);
189 GtkStyleCascadeIter iter;
190 GtkStyleProvider *item;
191 GtkCssChange iter_change;
192
193 for (item = gtk_style_cascade_iter_init (cascade, iter: &iter);
194 item;
195 item = gtk_style_cascade_iter_next (cascade, iter: &iter))
196 {
197 gtk_style_provider_lookup (provider: item, filter, node, lookup,
198 out_change: change ? &iter_change : NULL);
199 if (change)
200 *change |= iter_change;
201 }
202 gtk_style_cascade_iter_clear (iter: &iter);
203}
204
205static void
206gtk_style_cascade_provider_iface_init (GtkStyleProviderInterface *iface)
207{
208 iface->get_color = gtk_style_cascade_get_color;
209 iface->get_settings = gtk_style_cascade_get_settings;
210 iface->get_scale = gtk_style_cascade_get_scale;
211 iface->get_keyframes = gtk_style_cascade_get_keyframes;
212 iface->lookup = gtk_style_cascade_lookup;
213}
214
215G_DEFINE_TYPE_EXTENDED (GtkStyleCascade, _gtk_style_cascade, G_TYPE_OBJECT, 0,
216 G_IMPLEMENT_INTERFACE (GTK_TYPE_STYLE_PROVIDER,
217 gtk_style_cascade_provider_iface_init));
218
219static void
220gtk_style_cascade_dispose (GObject *object)
221{
222 GtkStyleCascade *cascade = GTK_STYLE_CASCADE (object);
223
224 _gtk_style_cascade_set_parent (cascade, NULL);
225 g_array_unref (array: cascade->providers);
226
227 G_OBJECT_CLASS (_gtk_style_cascade_parent_class)->dispose (object);
228}
229
230static void
231_gtk_style_cascade_class_init (GtkStyleCascadeClass *klass)
232{
233 GObjectClass *object_class = G_OBJECT_CLASS (klass);
234
235 object_class->dispose = gtk_style_cascade_dispose;
236}
237
238static void
239style_provider_data_clear (gpointer data_)
240{
241 GtkStyleProviderData *data = data_;
242
243 g_signal_handler_disconnect (instance: data->provider, handler_id: data->changed_signal_id);
244 g_object_unref (object: data->provider);
245}
246
247static void
248_gtk_style_cascade_init (GtkStyleCascade *cascade)
249{
250 cascade->scale = 1;
251
252 cascade->providers = g_array_new (FALSE, FALSE, element_size: sizeof (GtkStyleProviderData));
253 g_array_set_clear_func (array: cascade->providers, clear_func: style_provider_data_clear);
254}
255
256GtkStyleCascade *
257_gtk_style_cascade_new (void)
258{
259 return g_object_new (GTK_TYPE_STYLE_CASCADE, NULL);
260}
261
262void
263_gtk_style_cascade_set_parent (GtkStyleCascade *cascade,
264 GtkStyleCascade *parent)
265{
266 gtk_internal_return_if_fail (GTK_IS_STYLE_CASCADE (cascade));
267 gtk_internal_return_if_fail (parent == NULL || GTK_IS_STYLE_CASCADE (parent));
268
269 if (cascade->parent == parent)
270 return;
271
272 if (parent)
273 {
274 g_object_ref (parent);
275 g_signal_connect_swapped (parent,
276 "gtk-private-changed",
277 G_CALLBACK (gtk_style_provider_changed),
278 cascade);
279 }
280
281 if (cascade->parent)
282 {
283 g_signal_handlers_disconnect_by_func (cascade->parent,
284 gtk_style_provider_changed,
285 cascade);
286 g_object_unref (object: cascade->parent);
287 }
288
289 cascade->parent = parent;
290}
291
292void
293_gtk_style_cascade_add_provider (GtkStyleCascade *cascade,
294 GtkStyleProvider *provider,
295 guint priority)
296{
297 GtkStyleProviderData data;
298 guint i;
299
300 gtk_internal_return_if_fail (GTK_IS_STYLE_CASCADE (cascade));
301 gtk_internal_return_if_fail (GTK_IS_STYLE_PROVIDER (provider));
302 gtk_internal_return_if_fail (GTK_STYLE_PROVIDER (cascade) != provider);
303
304 data.provider = g_object_ref (provider);
305 data.priority = priority;
306 data.changed_signal_id = g_signal_connect_swapped (provider,
307 "gtk-private-changed",
308 G_CALLBACK (gtk_style_provider_changed),
309 cascade);
310
311 /* ensure it gets removed first */
312 _gtk_style_cascade_remove_provider (cascade, provider);
313
314 for (i = 0; i < cascade->providers->len; i++)
315 {
316 if (g_array_index (cascade->providers, GtkStyleProviderData, i).priority > priority)
317 break;
318 }
319 g_array_insert_val (cascade->providers, i, data);
320
321 gtk_style_provider_changed (GTK_STYLE_PROVIDER (cascade));
322}
323
324void
325_gtk_style_cascade_remove_provider (GtkStyleCascade *cascade,
326 GtkStyleProvider *provider)
327{
328 guint i;
329
330 gtk_internal_return_if_fail (GTK_IS_STYLE_CASCADE (cascade));
331 gtk_internal_return_if_fail (GTK_IS_STYLE_PROVIDER (provider));
332
333 for (i = 0; i < cascade->providers->len; i++)
334 {
335 GtkStyleProviderData *data = &g_array_index (cascade->providers, GtkStyleProviderData, i);
336
337 if (data->provider == provider)
338 {
339 g_array_remove_index (array: cascade->providers, index_: i);
340
341 gtk_style_provider_changed (GTK_STYLE_PROVIDER (cascade));
342 break;
343 }
344 }
345}
346
347void
348_gtk_style_cascade_set_scale (GtkStyleCascade *cascade,
349 int scale)
350{
351 gtk_internal_return_if_fail (GTK_IS_STYLE_CASCADE (cascade));
352
353 if (cascade->scale == scale)
354 return;
355
356 cascade->scale = scale;
357
358 gtk_style_provider_changed (GTK_STYLE_PROVIDER (cascade));
359}
360
361int
362_gtk_style_cascade_get_scale (GtkStyleCascade *cascade)
363{
364 gtk_internal_return_val_if_fail (GTK_IS_STYLE_CASCADE (cascade), 1);
365
366 return cascade->scale;
367}
368

source code of gtk/gtk/gtkstylecascade.c