1/* GTK - The GIMP Toolkit
2 * Copyright © 2016 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 "gtkcsscalcvalueprivate.h"
21
22#include <string.h>
23
24GtkCssValue * gtk_css_calc_value_parse_sum (GtkCssParser *parser,
25 GtkCssNumberParseFlags flags);
26
27static GtkCssValue *
28gtk_css_calc_value_parse_value (GtkCssParser *parser,
29 GtkCssNumberParseFlags flags)
30{
31 if (gtk_css_parser_has_token (self: parser, token_type: GTK_CSS_TOKEN_OPEN_PARENS))
32 {
33 GtkCssValue *result;
34
35 gtk_css_parser_start_block (self: parser);
36
37 result = gtk_css_calc_value_parse_sum (parser, flags);
38 if (result == NULL)
39 {
40 gtk_css_parser_end_block (self: parser);
41 return NULL;
42 }
43
44 if (!gtk_css_parser_has_token (self: parser, token_type: GTK_CSS_TOKEN_EOF))
45 {
46 GtkCssLocation start = *gtk_css_parser_get_start_location (self: parser);
47 gtk_css_parser_skip_until (self: parser, token_type: GTK_CSS_TOKEN_EOF);
48 gtk_css_parser_error (self: parser,
49 code: GTK_CSS_PARSER_ERROR_SYNTAX,
50 start: &start,
51 end: gtk_css_parser_get_start_location (self: parser),
52 format: "Expected closing ')' in calc() subterm");
53 gtk_css_value_unref (value: result);
54 gtk_css_parser_end_block (self: parser);
55 return NULL;
56 }
57
58 gtk_css_parser_end_block (self: parser);
59
60 return result;
61 }
62
63 return _gtk_css_number_value_parse (parser, flags);
64}
65
66static gboolean
67is_number (GtkCssValue *value)
68{
69 return gtk_css_number_value_get_dimension (value) == GTK_CSS_DIMENSION_NUMBER
70 && !gtk_css_number_value_has_percent (value);
71}
72
73static GtkCssValue *
74gtk_css_calc_value_parse_product (GtkCssParser *parser,
75 GtkCssNumberParseFlags flags)
76{
77 GtkCssValue *result, *value, *temp;
78 GtkCssNumberParseFlags actual_flags;
79 GtkCssLocation start;
80
81 actual_flags = flags | GTK_CSS_PARSE_NUMBER;
82 gtk_css_parser_get_token (self: parser);
83 start = *gtk_css_parser_get_start_location (self: parser);
84 result = gtk_css_calc_value_parse_value (parser, flags: actual_flags);
85 if (result == NULL)
86 return NULL;
87
88 while (TRUE)
89 {
90 if (actual_flags != GTK_CSS_PARSE_NUMBER && !is_number (value: result))
91 actual_flags = GTK_CSS_PARSE_NUMBER;
92
93 if (gtk_css_parser_try_delim (self: parser, codepoint: '*'))
94 {
95 value = gtk_css_calc_value_parse_product (parser, flags: actual_flags);
96 if (value == NULL)
97 goto fail;
98 if (is_number (value))
99 temp = gtk_css_number_value_multiply (value: result, factor: _gtk_css_number_value_get (number: value, one_hundred_percent: 100));
100 else
101 temp = gtk_css_number_value_multiply (value, factor: _gtk_css_number_value_get (number: result, one_hundred_percent: 100));
102 _gtk_css_value_unref (value);
103 _gtk_css_value_unref (value: result);
104 result = temp;
105 }
106 else if (gtk_css_parser_try_delim (self: parser, codepoint: '/'))
107 {
108 value = gtk_css_calc_value_parse_product (parser, flags: GTK_CSS_PARSE_NUMBER);
109 if (value == NULL)
110 goto fail;
111 temp = gtk_css_number_value_multiply (value: result, factor: 1.0 / _gtk_css_number_value_get (number: value, one_hundred_percent: 100));
112 _gtk_css_value_unref (value);
113 _gtk_css_value_unref (value: result);
114 result = temp;
115 }
116 else
117 {
118 break;
119 }
120 }
121
122 if (is_number (value: result) && !(flags & GTK_CSS_PARSE_NUMBER))
123 {
124 gtk_css_parser_error (self: parser,
125 code: GTK_CSS_PARSER_ERROR_SYNTAX,
126 start: &start,
127 end: gtk_css_parser_get_start_location (self: parser),
128 format: "calc() product term has no units");
129 goto fail;
130 }
131
132 return result;
133
134fail:
135 _gtk_css_value_unref (value: result);
136 return NULL;
137}
138
139GtkCssValue *
140gtk_css_calc_value_parse_sum (GtkCssParser *parser,
141 GtkCssNumberParseFlags flags)
142{
143 GtkCssValue *result;
144
145 result = gtk_css_calc_value_parse_product (parser, flags);
146 if (result == NULL)
147 return NULL;
148
149 while (TRUE)
150 {
151 GtkCssValue *next, *temp;
152
153 if (gtk_css_parser_try_delim (self: parser, codepoint: '+'))
154 {
155 next = gtk_css_calc_value_parse_product (parser, flags);
156 if (next == NULL)
157 goto fail;
158 }
159 else if (gtk_css_parser_try_delim (self: parser, codepoint: '-'))
160 {
161 temp = gtk_css_calc_value_parse_product (parser, flags);
162 if (temp == NULL)
163 goto fail;
164 next = gtk_css_number_value_multiply (value: temp, factor: -1);
165 _gtk_css_value_unref (value: temp);
166 }
167 else
168 {
169 if (gtk_css_parser_has_token (self: parser, token_type: GTK_CSS_TOKEN_SIGNED_INTEGER) ||
170 gtk_css_parser_has_token (self: parser, token_type: GTK_CSS_TOKEN_SIGNED_NUMBER) ||
171 gtk_css_parser_has_token (self: parser, token_type: GTK_CSS_TOKEN_SIGNED_INTEGER_DIMENSION) ||
172 gtk_css_parser_has_token (self: parser, token_type: GTK_CSS_TOKEN_SIGNED_DIMENSION))
173 {
174 gtk_css_parser_error_syntax (self: parser, format: "Unexpected signed number, did you forget a space between sign and number?");
175 gtk_css_parser_consume_token (self: parser);
176 }
177 break;
178 }
179
180 temp = gtk_css_number_value_add (value1: result, value2: next);
181 _gtk_css_value_unref (value: result);
182 _gtk_css_value_unref (value: next);
183 result = temp;
184 }
185
186 return result;
187
188fail:
189 _gtk_css_value_unref (value: result);
190 return NULL;
191}
192
193typedef struct
194{
195 GtkCssNumberParseFlags flags;
196 GtkCssValue *value;
197} ParseCalcData;
198
199static guint
200gtk_css_calc_value_parse_arg (GtkCssParser *parser,
201 guint arg,
202 gpointer data_)
203{
204 ParseCalcData *data = data_;
205
206 data->value = gtk_css_calc_value_parse_sum (parser, flags: data->flags);
207 if (data->value == NULL)
208 return 0;
209
210 return 1;
211}
212
213GtkCssValue *
214gtk_css_calc_value_parse (GtkCssParser *parser,
215 GtkCssNumberParseFlags flags)
216{
217 ParseCalcData data;
218
219 /* This can only be handled at compute time, we allow '-' after all */
220 data.flags = flags & ~GTK_CSS_POSITIVE_ONLY;
221 data.value = NULL;
222
223 if (!gtk_css_parser_has_function (self: parser, name: "calc"))
224 {
225 gtk_css_parser_error_syntax (self: parser, format: "Expected 'calc('");
226 return NULL;
227 }
228
229 if (!gtk_css_parser_consume_function (self: parser, min_args: 1, max_args: 1, parse_func: gtk_css_calc_value_parse_arg, data: &data))
230 return NULL;
231
232 return data.value;
233}
234
235

source code of gtk/gtk/gtkcsscalcvalue.c