1/* GTK - The GIMP Toolkit
2 * Copyright (C) 2017 Red Hat, Inc.
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 * Author: Matthias Clasen
18 */
19
20#include "config.h"
21
22#include <gtk/css/gtkcss.h>
23#include "gtk/css/gtkcsstokenizerprivate.h"
24#include "gtk/css/gtkcssparserprivate.h"
25#include "gtkcssnumbervalueprivate.h"
26#include "gtkcssfontvariationsvalueprivate.h"
27
28struct _GtkCssValue {
29 GTK_CSS_VALUE_BASE
30 GHashTable *axes;
31};
32
33static GtkCssValue *default_font_variations;
34
35static GtkCssValue *gtk_css_font_variations_value_new_empty (void);
36
37static void
38gtk_css_font_variations_value_add_axis (GtkCssValue *value,
39 const char *name,
40 GtkCssValue *coord)
41{
42 g_hash_table_insert (hash_table: value->axes, key: g_strdup (str: name), value: coord);
43}
44
45
46static void
47gtk_css_value_font_variations_free (GtkCssValue *value)
48{
49 g_hash_table_unref (hash_table: value->axes);
50
51 g_slice_free (GtkCssValue, value);
52}
53
54static GtkCssValue *
55gtk_css_value_font_variations_compute (GtkCssValue *specified,
56 guint property_id,
57 GtkStyleProvider *provider,
58 GtkCssStyle *style,
59 GtkCssStyle *parent_style)
60{
61 return _gtk_css_value_ref (value: specified);
62}
63
64static gboolean
65gtk_css_value_font_variations_equal (const GtkCssValue *value1,
66 const GtkCssValue *value2)
67{
68 gpointer name, coord1, coord2;
69 GHashTableIter iter;
70
71 if (g_hash_table_size (hash_table: value1->axes) != g_hash_table_size (hash_table: value2->axes))
72 return FALSE;
73
74 g_hash_table_iter_init (iter: &iter, hash_table: value1->axes);
75 while (g_hash_table_iter_next (iter: &iter, key: &name, value: &coord1))
76 {
77 coord2 = g_hash_table_lookup (hash_table: value2->axes, key: name);
78 if (coord2 == NULL)
79 return FALSE;
80
81 if (!_gtk_css_value_equal (value1: coord1, value2: coord2))
82 return FALSE;
83 }
84
85 return TRUE;
86}
87
88static GtkCssValue *
89gtk_css_value_font_variations_transition (GtkCssValue *start,
90 GtkCssValue *end,
91 guint property_id,
92 double progress)
93{
94 const char *name;
95 GtkCssValue *start_coord, *end_coord;
96 GHashTableIter iter;
97 GtkCssValue *result, *transition;
98
99 /* XXX: For value that are only in start or end but not both,
100 * we don't transition but just keep the value.
101 * That causes an abrupt transition to the new value at the end.
102 */
103
104 result = gtk_css_font_variations_value_new_empty ();
105
106 g_hash_table_iter_init (iter: &iter, hash_table: start->axes);
107 while (g_hash_table_iter_next (iter: &iter, key: (gpointer *)&name, value: (gpointer *)&start_coord))
108 {
109 end_coord = g_hash_table_lookup (hash_table: end->axes, key: name);
110 if (end_coord == NULL)
111 transition = _gtk_css_value_ref (value: start_coord);
112 else
113 transition = _gtk_css_value_transition (start: start_coord, end: end_coord, property_id, progress);
114
115 gtk_css_font_variations_value_add_axis (value: result, name, coord: transition);
116 }
117
118 g_hash_table_iter_init (iter: &iter, hash_table: end->axes);
119 while (g_hash_table_iter_next (iter: &iter, key: (gpointer *)&name, value: (gpointer *)&end_coord))
120 {
121 start_coord = g_hash_table_lookup (hash_table: start->axes, key: name);
122 if (start_coord != NULL)
123 continue;
124
125 gtk_css_font_variations_value_add_axis (value: result, name, _gtk_css_value_ref (value: end_coord));
126 }
127
128 return result;
129}
130
131static void
132gtk_css_value_font_variations_print (const GtkCssValue *value,
133 GString *string)
134{
135 GHashTableIter iter;
136 const char *name;
137 GtkCssValue *coord;
138 gboolean first = TRUE;
139
140 if (value == default_font_variations)
141 {
142 g_string_append (string, val: "normal");
143 return;
144 }
145
146 g_hash_table_iter_init (iter: &iter, hash_table: value->axes);
147 while (g_hash_table_iter_next (iter: &iter, key: (gpointer *)&name, value: (gpointer *)&coord))
148 {
149 if (first)
150 first = FALSE;
151 else
152 g_string_append (string, val: ", ");
153 g_string_append_printf (string, format: "\"%s\" ", name);
154 _gtk_css_value_print (value: coord, string);
155 }
156}
157
158static const GtkCssValueClass GTK_CSS_VALUE_FONT_VARIATIONS = {
159 "GtkCssFontVariationsValue",
160 gtk_css_value_font_variations_free,
161 gtk_css_value_font_variations_compute,
162 gtk_css_value_font_variations_equal,
163 gtk_css_value_font_variations_transition,
164 NULL,
165 NULL,
166 gtk_css_value_font_variations_print
167};
168
169static GtkCssValue *
170gtk_css_font_variations_value_new_empty (void)
171{
172 GtkCssValue *result;
173
174 result = _gtk_css_value_new (GtkCssValue, &GTK_CSS_VALUE_FONT_VARIATIONS);
175 result->axes = g_hash_table_new_full (hash_func: g_str_hash, key_equal_func: g_str_equal,
176 key_destroy_func: g_free,
177 value_destroy_func: (GDestroyNotify) _gtk_css_value_unref);
178 result->is_computed = TRUE;
179
180 return result;
181}
182
183GtkCssValue *
184gtk_css_font_variations_value_new_default (void)
185{
186 if (default_font_variations == NULL)
187 default_font_variations = gtk_css_font_variations_value_new_empty ();
188
189 return _gtk_css_value_ref (value: default_font_variations);
190}
191
192static gboolean
193is_valid_opentype_tag (const char *s)
194{
195 if (strlen (s: s) != 4)
196 return FALSE;
197
198 if (s[0] < 0x20 || s[0] > 0x7e ||
199 s[1] < 0x20 || s[1] > 0x7e ||
200 s[2] < 0x20 || s[2] > 0x7e ||
201 s[3] < 0x20 || s[3] > 0x7e)
202 return FALSE;
203
204 return TRUE;
205}
206
207GtkCssValue *
208gtk_css_font_variations_value_parse (GtkCssParser *parser)
209{
210 GtkCssValue *result, *coord;
211 char *name;
212
213 if (gtk_css_parser_try_ident (self: parser, ident: "normal"))
214 return gtk_css_font_variations_value_new_default ();
215
216 result = gtk_css_font_variations_value_new_empty ();
217
218 do {
219 name = gtk_css_parser_consume_string (self: parser);
220 if (name == NULL)
221 {
222 _gtk_css_value_unref (value: result);
223 return NULL;
224 }
225
226 if (!is_valid_opentype_tag (s: name))
227 {
228 gtk_css_parser_error_value (self: parser, format: "Not a valid OpenType tag.");
229 g_free (mem: name);
230 _gtk_css_value_unref (value: result);
231 return NULL;
232 }
233
234 coord = _gtk_css_number_value_parse (parser, flags: GTK_CSS_PARSE_NUMBER);
235 if (coord == NULL)
236 {
237 g_free (mem: name);
238 _gtk_css_value_unref (value: result);
239 return NULL;
240 }
241
242 gtk_css_font_variations_value_add_axis (value: result, name, coord);
243 g_free (mem: name);
244 } while (gtk_css_parser_try_token (self: parser, token_type: GTK_CSS_TOKEN_COMMA));
245
246 return result;
247}
248
249char *
250gtk_css_font_variations_value_get_variations (GtkCssValue *value)
251{
252 GtkCssValue *coord;
253 GHashTableIter iter;
254 gboolean first = TRUE;
255 const char *name;
256 GString *string;
257
258 g_return_val_if_fail (value->class == &GTK_CSS_VALUE_FONT_VARIATIONS, NULL);
259
260 if (value == default_font_variations)
261 return NULL;
262
263 string = g_string_new (init: "");
264
265 g_hash_table_iter_init (iter: &iter, hash_table: value->axes);
266 while (g_hash_table_iter_next (iter: &iter, key: (gpointer *)&name, value: (gpointer *)&coord))
267 {
268 if (first)
269 first = FALSE;
270 else
271 g_string_append (string, val: ",");
272 g_string_append_printf (string, format: "%s=%g", name,
273 _gtk_css_number_value_get (number: coord, one_hundred_percent: 100));
274 }
275
276 return g_string_free (string, FALSE);
277}
278

source code of gtk/gtk/gtkcssfontvariationsvalue.c