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 | |
26 | typedef struct _GtkStyleCascadeIter GtkStyleCascadeIter; |
27 | typedef struct _GtkStyleProviderData GtkStyleProviderData; |
28 | |
29 | struct _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 | |
35 | struct _GtkStyleProviderData |
36 | { |
37 | GtkStyleProvider *provider; |
38 | guint priority; |
39 | guint changed_signal_id; |
40 | }; |
41 | |
42 | static GtkStyleProvider * |
43 | gtk_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 | |
75 | static GtkStyleProvider * |
76 | gtk_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 | |
93 | static void |
94 | gtk_style_cascade_iter_clear (GtkStyleCascadeIter *iter) |
95 | { |
96 | g_free (mem: iter->cascade_index); |
97 | } |
98 | |
99 | static GtkSettings * |
100 | gtk_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 | |
123 | static GtkCssValue * |
124 | gtk_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 | |
148 | static int |
149 | gtk_style_cascade_get_scale (GtkStyleProvider *provider) |
150 | { |
151 | GtkStyleCascade *cascade = GTK_STYLE_CASCADE (provider); |
152 | |
153 | return cascade->scale; |
154 | } |
155 | |
156 | static GtkCssKeyframes * |
157 | gtk_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 | |
181 | static void |
182 | gtk_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 | |
205 | static void |
206 | gtk_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 | |
215 | G_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 | |
219 | static void |
220 | gtk_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 | |
230 | static 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 | |
238 | static void |
239 | style_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 | |
247 | static 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 | |
256 | GtkStyleCascade * |
257 | _gtk_style_cascade_new (void) |
258 | { |
259 | return g_object_new (GTK_TYPE_STYLE_CASCADE, NULL); |
260 | } |
261 | |
262 | void |
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 | |
292 | void |
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 | |
324 | void |
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 | |
347 | void |
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 | |
361 | int |
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 | |