1/*
2 * Copyright © 2014 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 "gtkcssnodedeclarationprivate.h"
21
22#include "gtkprivate.h"
23
24#include <string.h>
25
26struct _GtkCssNodeDeclaration {
27 guint refcount;
28 GQuark name;
29 GQuark id;
30 GtkStateFlags state;
31 guint n_classes;
32 GQuark classes[0];
33};
34
35static inline gsize
36sizeof_node (guint n_classes)
37{
38 return sizeof (GtkCssNodeDeclaration)
39 + sizeof (GQuark) * n_classes;
40}
41
42static inline gsize
43sizeof_this_node (GtkCssNodeDeclaration *decl)
44{
45 return sizeof_node (n_classes: decl->n_classes);
46}
47
48static void
49gtk_css_node_declaration_make_writable (GtkCssNodeDeclaration **decl)
50{
51 if ((*decl)->refcount == 1)
52 return;
53
54 (*decl)->refcount--;
55
56 *decl = g_memdup2 (mem: *decl, byte_size: sizeof_this_node (decl: *decl));
57 (*decl)->refcount = 1;
58}
59
60static void
61gtk_css_node_declaration_make_writable_resize (GtkCssNodeDeclaration **decl,
62 gsize offset,
63 gsize bytes_added,
64 gsize bytes_removed)
65{
66 gsize old_size = sizeof_this_node (decl: *decl);
67 gsize new_size = old_size + bytes_added - bytes_removed;
68
69 if ((*decl)->refcount == 1)
70 {
71 if (bytes_removed > 0 && old_size - offset - bytes_removed > 0)
72 memmove (dest: ((char *) *decl) + offset, src: ((char *) *decl) + offset + bytes_removed, n: old_size - offset - bytes_removed);
73 *decl = g_realloc (mem: *decl, n_bytes: new_size);
74 if (bytes_added > 0 && old_size - offset > 0)
75 memmove (dest: ((char *) *decl) + offset + bytes_added, src: ((char *) *decl) + offset, n: old_size - offset);
76 }
77 else
78 {
79 GtkCssNodeDeclaration *old = *decl;
80
81 old->refcount--;
82
83 *decl = g_malloc (n_bytes: new_size);
84 memcpy (dest: *decl, src: old, n: offset);
85 if (old_size - offset - bytes_removed > 0)
86 memcpy (dest: ((char *) *decl) + offset + bytes_added, src: ((char *) old) + offset + bytes_removed, n: old_size - offset - bytes_removed);
87 (*decl)->refcount = 1;
88 }
89}
90
91GtkCssNodeDeclaration *
92gtk_css_node_declaration_new (void)
93{
94 static GtkCssNodeDeclaration empty = {
95 1, /* need to own a ref ourselves so the copy-on-write path kicks in when people change things */
96 0,
97 0,
98 0,
99 0
100 };
101
102 return gtk_css_node_declaration_ref (decl: &empty);
103}
104
105GtkCssNodeDeclaration *
106gtk_css_node_declaration_ref (GtkCssNodeDeclaration *decl)
107{
108 decl->refcount++;
109
110 return decl;
111}
112
113void
114gtk_css_node_declaration_unref (GtkCssNodeDeclaration *decl)
115{
116 decl->refcount--;
117 if (decl->refcount > 0)
118 return;
119
120 g_free (mem: decl);
121}
122
123gboolean
124gtk_css_node_declaration_set_name (GtkCssNodeDeclaration **decl,
125 GQuark name)
126{
127 if ((*decl)->name == name)
128 return FALSE;
129
130 gtk_css_node_declaration_make_writable (decl);
131 (*decl)->name = name;
132
133 return TRUE;
134}
135
136GQuark
137gtk_css_node_declaration_get_name (const GtkCssNodeDeclaration *decl)
138{
139 return decl->name;
140}
141
142gboolean
143gtk_css_node_declaration_set_id (GtkCssNodeDeclaration **decl,
144 GQuark id)
145{
146 if ((*decl)->id == id)
147 return FALSE;
148
149 gtk_css_node_declaration_make_writable (decl);
150 (*decl)->id = id;
151
152 return TRUE;
153}
154
155GQuark
156gtk_css_node_declaration_get_id (const GtkCssNodeDeclaration *decl)
157{
158 return decl->id;
159}
160
161gboolean
162gtk_css_node_declaration_set_state (GtkCssNodeDeclaration **decl,
163 GtkStateFlags state)
164{
165 if ((*decl)->state == state)
166 return FALSE;
167
168 gtk_css_node_declaration_make_writable (decl);
169 (*decl)->state = state;
170
171 return TRUE;
172}
173
174GtkStateFlags
175gtk_css_node_declaration_get_state (const GtkCssNodeDeclaration *decl)
176{
177 return decl->state;
178}
179
180static gboolean
181find_class (const GtkCssNodeDeclaration *decl,
182 GQuark class_quark,
183 guint *position)
184{
185 int min, max, mid;
186 gboolean found = FALSE;
187 guint pos;
188
189 *position = 0;
190
191 if (decl->n_classes == 0)
192 return FALSE;
193
194 min = 0;
195 max = decl->n_classes - 1;
196
197 do
198 {
199 GQuark item;
200
201 mid = (min + max) / 2;
202 item = decl->classes[mid];
203
204 if (class_quark == item)
205 {
206 found = TRUE;
207 pos = mid;
208 break;
209 }
210 else if (class_quark > item)
211 min = pos = mid + 1;
212 else
213 {
214 max = mid - 1;
215 pos = mid;
216 }
217 }
218 while (min <= max);
219
220 *position = pos;
221
222 return found;
223}
224
225gboolean
226gtk_css_node_declaration_add_class (GtkCssNodeDeclaration **decl,
227 GQuark class_quark)
228{
229 guint pos;
230
231 if (find_class (decl: *decl, class_quark, position: &pos))
232 return FALSE;
233
234 gtk_css_node_declaration_make_writable_resize (decl,
235 offset: (char *) &(*decl)->classes[pos] - (char *) *decl,
236 bytes_added: sizeof (GQuark),
237 bytes_removed: 0);
238 (*decl)->n_classes++;
239 (*decl)->classes[pos] = class_quark;
240
241 return TRUE;
242}
243
244gboolean
245gtk_css_node_declaration_remove_class (GtkCssNodeDeclaration **decl,
246 GQuark class_quark)
247{
248 guint pos;
249
250 if (!find_class (decl: *decl, class_quark, position: &pos))
251 return FALSE;
252
253 gtk_css_node_declaration_make_writable_resize (decl,
254 offset: (char *) &(*decl)->classes[pos] - (char *) *decl,
255 bytes_added: 0,
256 bytes_removed: sizeof (GQuark));
257 (*decl)->n_classes--;
258
259 return TRUE;
260}
261
262gboolean
263gtk_css_node_declaration_clear_classes (GtkCssNodeDeclaration **decl)
264{
265 if ((*decl)->n_classes == 0)
266 return FALSE;
267
268 gtk_css_node_declaration_make_writable_resize (decl,
269 offset: (char *) (*decl)->classes - (char *) *decl,
270 bytes_added: 0,
271 bytes_removed: sizeof (GQuark) * (*decl)->n_classes);
272 (*decl)->n_classes = 0;
273
274 return TRUE;
275}
276
277gboolean
278gtk_css_node_declaration_has_class (const GtkCssNodeDeclaration *decl,
279 GQuark class_quark)
280{
281 guint pos;
282
283 switch (decl->n_classes)
284 {
285 case 3:
286 if (decl->classes[2] == class_quark)
287 return TRUE;
288 G_GNUC_FALLTHROUGH;
289
290 case 2:
291 if (decl->classes[1] == class_quark)
292 return TRUE;
293 G_GNUC_FALLTHROUGH;
294
295 case 1:
296 if (decl->classes[0] == class_quark)
297 return TRUE;
298 G_GNUC_FALLTHROUGH;
299
300 case 0:
301 return FALSE;
302
303 default:
304 return find_class (decl, class_quark, position: &pos);
305 }
306}
307
308const GQuark *
309gtk_css_node_declaration_get_classes (const GtkCssNodeDeclaration *decl,
310 guint *n_classes)
311{
312 *n_classes = decl->n_classes;
313
314 return decl->classes;
315}
316
317void
318gtk_css_node_declaration_add_bloom_hashes (const GtkCssNodeDeclaration *decl,
319 GtkCountingBloomFilter *filter)
320{
321 guint i;
322
323 if (decl->name)
324 gtk_counting_bloom_filter_add (self: filter, hash: gtk_css_hash_name (name: decl->name));
325 if (decl->id)
326 gtk_counting_bloom_filter_add (self: filter, hash: gtk_css_hash_id (id: decl->id));
327
328 for (i = 0; i < decl->n_classes; i++)
329 {
330 gtk_counting_bloom_filter_add (self: filter, hash: gtk_css_hash_class (klass: decl->classes[i]));
331 }
332}
333
334void
335gtk_css_node_declaration_remove_bloom_hashes (const GtkCssNodeDeclaration *decl,
336 GtkCountingBloomFilter *filter)
337{
338 guint i;
339
340 if (decl->name)
341 gtk_counting_bloom_filter_remove (self: filter, hash: gtk_css_hash_name (name: decl->name));
342 if (decl->id)
343 gtk_counting_bloom_filter_remove (self: filter, hash: gtk_css_hash_id (id: decl->id));
344
345 for (i = 0; i < decl->n_classes; i++)
346 {
347 gtk_counting_bloom_filter_remove (self: filter, hash: gtk_css_hash_class (klass: decl->classes[i]));
348 }
349}
350
351guint
352gtk_css_node_declaration_hash (gconstpointer elem)
353{
354 const GtkCssNodeDeclaration *decl = elem;
355 guint hash, i;
356
357 hash = GPOINTER_TO_UINT (decl->name);
358 hash <<= 5;
359 hash ^= GPOINTER_TO_UINT (decl->id);
360
361 for (i = 0; i < decl->n_classes; i++)
362 {
363 hash <<= 5;
364 hash += decl->classes[i];
365 }
366
367 hash ^= decl->state;
368
369 return hash;
370}
371
372gboolean
373gtk_css_node_declaration_equal (gconstpointer elem1,
374 gconstpointer elem2)
375{
376 const GtkCssNodeDeclaration *decl1 = elem1;
377 const GtkCssNodeDeclaration *decl2 = elem2;
378 guint i;
379
380 if (decl1 == decl2)
381 return TRUE;
382
383 if (decl1->name != decl2->name)
384 return FALSE;
385
386 if (decl1->state != decl2->state)
387 return FALSE;
388
389 if (decl1->id != decl2->id)
390 return FALSE;
391
392 if (decl1->n_classes != decl2->n_classes)
393 return FALSE;
394
395 for (i = 0; i < decl1->n_classes; i++)
396 {
397 if (decl1->classes[i] != decl2->classes[i])
398 return FALSE;
399 }
400
401 return TRUE;
402}
403
404static int
405cmpstr (gconstpointer a,
406 gconstpointer b,
407 gpointer data)
408{
409 char **ap = (char **) a;
410 char **bp = (char **) b;
411
412 return g_ascii_strcasecmp (s1: *ap, s2: *bp);
413}
414
415/* Append the declaration to the string, in selector format */
416void
417gtk_css_node_declaration_print (const GtkCssNodeDeclaration *decl,
418 GString *string)
419{
420 guint i;
421 char **classnames;
422
423 if (decl->name)
424 g_string_append (string, val: g_quark_to_string (quark: decl->name));
425 else
426 g_string_append (string, val: "*");
427
428 if (decl->id)
429 {
430 g_string_append_c (string, '#');
431 g_string_append (string, val: g_quark_to_string (quark: decl->id));
432 }
433
434 classnames = g_new (char *, decl->n_classes);
435 for (i = 0; i < decl->n_classes; i++)
436 classnames[i] = (char *)g_quark_to_string (quark: decl->classes[i]);
437
438 g_qsort_with_data (pbase: classnames, total_elems: decl->n_classes, size: sizeof (char *), compare_func: cmpstr, NULL);
439
440 for (i = 0; i < decl->n_classes; i++)
441 {
442 g_string_append_c (string, '.');
443 g_string_append (string, val: classnames[i]);
444 }
445 g_free (mem: classnames);
446
447 for (i = 0; i < sizeof (GtkStateFlags) * 8; i++)
448 {
449 if (decl->state & (1 << i))
450 {
451 const char *name = gtk_css_pseudoclass_name (flags: 1 << i);
452 g_assert (name);
453 g_string_append_c (string, ':');
454 g_string_append (string, val: name);
455 }
456 }
457}
458
459char *
460gtk_css_node_declaration_to_string (const GtkCssNodeDeclaration *decl)
461{
462 GString *s = g_string_new (NULL);
463
464 gtk_css_node_declaration_print (decl, string: s);
465
466 return g_string_free (string: s, FALSE);
467}
468

source code of gtk/gtk/gtkcssnodedeclaration.c