1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2011 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 
18 #include "config.h"
19 
20 #include "gtkcssdimensionvalueprivate.h"
21 
22 #include "gtkcssenumvalueprivate.h"
23 #include "gtkstylepropertyprivate.h"
24 
25 #include "fallback-c89.c"
26 
27 struct _GtkCssValue {
28   GTK_CSS_VALUE_BASE
29   GtkCssUnit unit;
30   double value;
31 };
32 
33 static void
gtk_css_value_dimension_free(GtkCssValue * value)34 gtk_css_value_dimension_free (GtkCssValue *value)
35 {
36   g_slice_free (GtkCssValue, value);
37 }
38 
39 static double
get_base_font_size_px(guint property_id,GtkStyleProviderPrivate * provider,GtkCssStyle * style,GtkCssStyle * parent_style)40 get_base_font_size_px (guint                    property_id,
41                        GtkStyleProviderPrivate *provider,
42                        GtkCssStyle             *style,
43                        GtkCssStyle             *parent_style)
44 {
45   if (property_id == GTK_CSS_PROPERTY_FONT_SIZE)
46     {
47       if (parent_style)
48         return _gtk_css_number_value_get (gtk_css_style_get_value (parent_style, GTK_CSS_PROPERTY_FONT_SIZE), 100);
49       else
50         return gtk_css_font_size_get_default_px (provider, style);
51     }
52 
53   return _gtk_css_number_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_FONT_SIZE), 100);
54 }
55 
56 static double
get_dpi(GtkCssStyle * style)57 get_dpi (GtkCssStyle *style)
58 {
59   return _gtk_css_number_value_get (gtk_css_style_get_value (style, GTK_CSS_PROPERTY_DPI), 96);
60 }
61 
62 static GtkCssValue *
gtk_css_value_dimension_compute(GtkCssValue * number,guint property_id,GtkStyleProviderPrivate * provider,GtkCssStyle * style,GtkCssStyle * parent_style)63 gtk_css_value_dimension_compute (GtkCssValue             *number,
64                               guint                    property_id,
65                               GtkStyleProviderPrivate *provider,
66                               GtkCssStyle             *style,
67                               GtkCssStyle             *parent_style)
68 {
69   GtkBorderStyle border_style;
70 
71   /* special case according to http://dev.w3.org/csswg/css-backgrounds/#the-border-width */
72   switch (property_id)
73     {
74       case GTK_CSS_PROPERTY_BORDER_TOP_WIDTH:
75         border_style = _gtk_css_border_style_value_get(gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_TOP_STYLE));
76         if (border_style == GTK_BORDER_STYLE_NONE || border_style == GTK_BORDER_STYLE_HIDDEN)
77           return gtk_css_dimension_value_new (0, GTK_CSS_NUMBER);
78         break;
79       case GTK_CSS_PROPERTY_BORDER_RIGHT_WIDTH:
80         border_style = _gtk_css_border_style_value_get(gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_RIGHT_STYLE));
81         if (border_style == GTK_BORDER_STYLE_NONE || border_style == GTK_BORDER_STYLE_HIDDEN)
82           return gtk_css_dimension_value_new (0, GTK_CSS_NUMBER);
83         break;
84       case GTK_CSS_PROPERTY_BORDER_BOTTOM_WIDTH:
85         border_style = _gtk_css_border_style_value_get(gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_BOTTOM_STYLE));
86         if (border_style == GTK_BORDER_STYLE_NONE || border_style == GTK_BORDER_STYLE_HIDDEN)
87           return gtk_css_dimension_value_new (0, GTK_CSS_NUMBER);
88         break;
89       case GTK_CSS_PROPERTY_BORDER_LEFT_WIDTH:
90         border_style = _gtk_css_border_style_value_get(gtk_css_style_get_value (style, GTK_CSS_PROPERTY_BORDER_LEFT_STYLE));
91         if (border_style == GTK_BORDER_STYLE_NONE || border_style == GTK_BORDER_STYLE_HIDDEN)
92           return gtk_css_dimension_value_new (0, GTK_CSS_NUMBER);
93         break;
94       case GTK_CSS_PROPERTY_OUTLINE_WIDTH:
95         border_style = _gtk_css_border_style_value_get(gtk_css_style_get_value (style, GTK_CSS_PROPERTY_OUTLINE_STYLE));
96         if (border_style == GTK_BORDER_STYLE_NONE || border_style == GTK_BORDER_STYLE_HIDDEN)
97           return gtk_css_dimension_value_new (0, GTK_CSS_NUMBER);
98         break;
99       default:
100         break;
101     }
102 
103   switch (number->unit)
104     {
105     default:
106       g_assert_not_reached();
107       /* fall through */
108     case GTK_CSS_PERCENT:
109       /* percentages for font sizes are computed, other percentages aren't */
110       if (property_id == GTK_CSS_PROPERTY_FONT_SIZE)
111         return gtk_css_dimension_value_new (number->value / 100.0 *
112                                             get_base_font_size_px (property_id, provider, style, parent_style),
113                                             GTK_CSS_PX);
114     case GTK_CSS_NUMBER:
115     case GTK_CSS_PX:
116     case GTK_CSS_DEG:
117     case GTK_CSS_S:
118       return _gtk_css_value_ref (number);
119     case GTK_CSS_PT:
120       return gtk_css_dimension_value_new (number->value * get_dpi (style) / 72.0,
121                                           GTK_CSS_PX);
122     case GTK_CSS_PC:
123       return gtk_css_dimension_value_new (number->value * get_dpi (style) / 72.0 * 12.0,
124                                           GTK_CSS_PX);
125     case GTK_CSS_IN:
126       return gtk_css_dimension_value_new (number->value * get_dpi (style),
127                                           GTK_CSS_PX);
128     case GTK_CSS_CM:
129       return gtk_css_dimension_value_new (number->value * get_dpi (style) * 0.39370078740157477,
130                                           GTK_CSS_PX);
131     case GTK_CSS_MM:
132       return gtk_css_dimension_value_new (number->value * get_dpi (style) * 0.039370078740157477,
133                                           GTK_CSS_PX);
134     case GTK_CSS_EM:
135       return gtk_css_dimension_value_new (number->value *
136                                           get_base_font_size_px (property_id, provider, style, parent_style),
137                                           GTK_CSS_PX);
138     case GTK_CSS_EX:
139       /* for now we pretend ex is half of em */
140       return gtk_css_dimension_value_new (number->value * 0.5 *
141                                           get_base_font_size_px (property_id, provider, style, parent_style),
142                                           GTK_CSS_PX);
143     case GTK_CSS_REM:
144       return gtk_css_dimension_value_new (number->value *
145                                           gtk_css_font_size_get_default_px (provider, style),
146                                           GTK_CSS_PX);
147     case GTK_CSS_RAD:
148       return gtk_css_dimension_value_new (number->value * 360.0 / (2 * G_PI),
149                                           GTK_CSS_DEG);
150     case GTK_CSS_GRAD:
151       return gtk_css_dimension_value_new (number->value * 360.0 / 400.0,
152                                           GTK_CSS_DEG);
153     case GTK_CSS_TURN:
154       return gtk_css_dimension_value_new (number->value * 360.0,
155                                           GTK_CSS_DEG);
156     case GTK_CSS_MS:
157       return gtk_css_dimension_value_new (number->value / 1000.0,
158                                           GTK_CSS_S);
159     }
160 }
161 
162 static gboolean
gtk_css_value_dimension_equal(const GtkCssValue * number1,const GtkCssValue * number2)163 gtk_css_value_dimension_equal (const GtkCssValue *number1,
164                             const GtkCssValue *number2)
165 {
166   return number1->unit == number2->unit &&
167          number1->value == number2->value;
168 }
169 
170 static void
gtk_css_value_dimension_print(const GtkCssValue * number,GString * string)171 gtk_css_value_dimension_print (const GtkCssValue *number,
172                             GString           *string)
173 {
174   char buf[G_ASCII_DTOSTR_BUF_SIZE];
175 
176   const char *names[] = {
177     /* [GTK_CSS_NUMBER] = */ "",
178     /* [GTK_CSS_PERCENT] = */ "%",
179     /* [GTK_CSS_PX] = */ "px",
180     /* [GTK_CSS_PT] = */ "pt",
181     /* [GTK_CSS_EM] = */ "em",
182     /* [GTK_CSS_EX] = */ "ex",
183     /* [GTK_CSS_REM] = */ "rem",
184     /* [GTK_CSS_PC] = */ "pc",
185     /* [GTK_CSS_IN] = */ "in",
186     /* [GTK_CSS_CM] = */ "cm",
187     /* [GTK_CSS_MM] = */ "mm",
188     /* [GTK_CSS_RAD] = */ "rad",
189     /* [GTK_CSS_DEG] = */ "deg",
190     /* [GTK_CSS_GRAD] = */ "grad",
191     /* [GTK_CSS_TURN] = */ "turn",
192     /* [GTK_CSS_S] = */ "s",
193     /* [GTK_CSS_MS] = */ "ms",
194   };
195 
196   if (isinf (number->value))
197     g_string_append (string, "infinite");
198   else
199     {
200       g_ascii_dtostr (buf, sizeof (buf), number->value);
201       g_string_append (string, buf);
202       if (number->value != 0.0)
203         g_string_append (string, names[number->unit]);
204     }
205 }
206 
207 static double
gtk_css_value_dimension_get(const GtkCssValue * value,double one_hundred_percent)208 gtk_css_value_dimension_get (const GtkCssValue *value,
209                              double             one_hundred_percent)
210 {
211   if (value->unit == GTK_CSS_PERCENT)
212     return value->value * one_hundred_percent / 100;
213   else
214     return value->value;
215 }
216 
217 static GtkCssDimension
gtk_css_value_dimension_get_dimension(const GtkCssValue * value)218 gtk_css_value_dimension_get_dimension (const GtkCssValue *value)
219 {
220   return gtk_css_unit_get_dimension (value->unit);
221 }
222 
223 static gboolean
gtk_css_value_dimension_has_percent(const GtkCssValue * value)224 gtk_css_value_dimension_has_percent (const GtkCssValue *value)
225 {
226   return gtk_css_unit_get_dimension (value->unit) == GTK_CSS_DIMENSION_PERCENTAGE;
227 }
228 
229 static GtkCssValue *
gtk_css_value_dimension_multiply(const GtkCssValue * value,double factor)230 gtk_css_value_dimension_multiply (const GtkCssValue *value,
231                                   double             factor)
232 {
233   return gtk_css_dimension_value_new (value->value * factor, value->unit);
234 }
235 
236 static GtkCssValue *
gtk_css_value_dimension_try_add(const GtkCssValue * value1,const GtkCssValue * value2)237 gtk_css_value_dimension_try_add (const GtkCssValue *value1,
238                                  const GtkCssValue *value2)
239 {
240   if (value1->unit != value2->unit)
241     return NULL;
242 
243   return gtk_css_dimension_value_new (value1->value + value2->value, value1->unit);
244 }
245 
246 static gint
gtk_css_value_dimension_get_calc_term_order(const GtkCssValue * value)247 gtk_css_value_dimension_get_calc_term_order (const GtkCssValue *value)
248 {
249   /* note: the order is alphabetic */
250   guint order_per_unit[] = {
251     /* [GTK_CSS_NUMBER] = */ 0,
252     /* [GTK_CSS_PERCENT] = */ 16,
253     /* [GTK_CSS_PX] = */ 11,
254     /* [GTK_CSS_PT] = */ 10,
255     /* [GTK_CSS_EM] = */ 3,
256     /* [GTK_CSS_EX] = */ 4,
257     /* [GTK_CSS_REM] = */ 13,
258     /* [GTK_CSS_PC] = */ 9,
259     /* [GTK_CSS_IN] = */ 6,
260     /* [GTK_CSS_CM] = */ 1,
261     /* [GTK_CSS_MM] = */ 7,
262     /* [GTK_CSS_RAD] = */ 12,
263     /* [GTK_CSS_DEG] = */ 2,
264     /* [GTK_CSS_GRAD] = */ 5,
265     /* [GTK_CSS_TURN] = */ 15,
266     /* [GTK_CSS_S] = */ 14,
267     /* [GTK_CSS_MS] = */ 8
268   };
269 
270   return 1000 + order_per_unit[value->unit];
271 }
272 
273 static const GtkCssNumberValueClass GTK_CSS_VALUE_DIMENSION = {
274   {
275     gtk_css_value_dimension_free,
276     gtk_css_value_dimension_compute,
277     gtk_css_value_dimension_equal,
278     gtk_css_number_value_transition,
279     gtk_css_value_dimension_print
280   },
281   gtk_css_value_dimension_get,
282   gtk_css_value_dimension_get_dimension,
283   gtk_css_value_dimension_has_percent,
284   gtk_css_value_dimension_multiply,
285   gtk_css_value_dimension_try_add,
286   gtk_css_value_dimension_get_calc_term_order
287 };
288 
289 GtkCssValue *
gtk_css_dimension_value_new(double value,GtkCssUnit unit)290 gtk_css_dimension_value_new (double     value,
291                              GtkCssUnit unit)
292 {
293   static GtkCssValue number_singletons[] = {
294     { &GTK_CSS_VALUE_DIMENSION.value_class, 1, GTK_CSS_NUMBER, 0 },
295     { &GTK_CSS_VALUE_DIMENSION.value_class, 1, GTK_CSS_NUMBER, 1 },
296   };
297   static GtkCssValue px_singletons[] = {
298     { &GTK_CSS_VALUE_DIMENSION.value_class, 1, GTK_CSS_PX, 0 },
299     { &GTK_CSS_VALUE_DIMENSION.value_class, 1, GTK_CSS_PX, 1 },
300     { &GTK_CSS_VALUE_DIMENSION.value_class, 1, GTK_CSS_PX, 2 },
301     { &GTK_CSS_VALUE_DIMENSION.value_class, 1, GTK_CSS_PX, 3 },
302     { &GTK_CSS_VALUE_DIMENSION.value_class, 1, GTK_CSS_PX, 4 },
303   };
304   GtkCssValue *result;
305 
306   if (unit == GTK_CSS_NUMBER && (value == 0 || value == 1))
307     return _gtk_css_value_ref (&number_singletons[(int) value]);
308 
309   if (unit == GTK_CSS_PX &&
310       (value == 0 ||
311        value == 1 ||
312        value == 2 ||
313        value == 3 ||
314        value == 4))
315     {
316       return _gtk_css_value_ref (&px_singletons[(int) value]);
317     }
318 
319   result = _gtk_css_value_new (GtkCssValue, &GTK_CSS_VALUE_DIMENSION.value_class);
320   result->unit = unit;
321   result->value = value;
322 
323   return result;
324 }
325 
326