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 "gtkcssbordervalueprivate.h"
21 
22 #include "gtkcssnumbervalueprivate.h"
23 
24 struct _GtkCssValue {
25   GTK_CSS_VALUE_BASE
26   guint fill :1;
27   GtkCssValue *values[4];
28 };
29 
30 static void
gtk_css_value_border_free(GtkCssValue * value)31 gtk_css_value_border_free (GtkCssValue *value)
32 {
33   guint i;
34 
35   for (i = 0; i < 4; i++)
36     {
37       if (value->values[i])
38         _gtk_css_value_unref (value->values[i]);
39     }
40 
41   g_slice_free (GtkCssValue, value);
42 }
43 
44 static GtkCssValue *
gtk_css_value_border_compute(GtkCssValue * value,guint property_id,GtkStyleProviderPrivate * provider,GtkCssStyle * style,GtkCssStyle * parent_style)45 gtk_css_value_border_compute (GtkCssValue             *value,
46                               guint                    property_id,
47                               GtkStyleProviderPrivate *provider,
48                               GtkCssStyle             *style,
49                               GtkCssStyle             *parent_style)
50 {
51   GtkCssValue *values[4];
52   GtkCssValue *computed;
53   gboolean changed = FALSE;
54   guint i;
55 
56   for (i = 0; i < 4; i++)
57     {
58       if (value->values[i])
59         {
60           values[i] = _gtk_css_value_compute (value->values[i], property_id, provider, style, parent_style);
61           changed |= (values[i] != value->values[i]);
62         }
63       else
64         {
65           values[i] = NULL;
66         }
67     }
68 
69   if (!changed)
70     {
71       for (i = 0; i < 4; i++)
72         {
73           if (values[i] != NULL)
74             _gtk_css_value_unref (values[i]);
75         }
76       return _gtk_css_value_ref (value);
77     }
78 
79   computed = _gtk_css_border_value_new (values[0], values[1], values[2], values[3]);
80   computed->fill = value->fill;
81 
82   return computed;
83 }
84 
85 static gboolean
gtk_css_value_border_equal(const GtkCssValue * value1,const GtkCssValue * value2)86 gtk_css_value_border_equal (const GtkCssValue *value1,
87                             const GtkCssValue *value2)
88 {
89   guint i;
90 
91   if (value1->fill != value2->fill)
92     return FALSE;
93 
94   for (i = 0; i < 4; i++)
95     {
96       if (!_gtk_css_value_equal0 (value1->values[i], value2->values[i]))
97         return FALSE;
98     }
99 
100   return TRUE;
101 }
102 
103 static GtkCssValue *
gtk_css_value_border_transition(GtkCssValue * start,GtkCssValue * end,guint property_id,double progress)104 gtk_css_value_border_transition (GtkCssValue *start,
105                                  GtkCssValue *end,
106                                  guint        property_id,
107                                  double       progress)
108 {
109   return NULL;
110 }
111 
112 static void
gtk_css_value_border_print(const GtkCssValue * value,GString * string)113 gtk_css_value_border_print (const GtkCssValue *value,
114                             GString           *string)
115 {
116   guint i, n;
117 
118   if (!_gtk_css_value_equal0 (value->values[GTK_CSS_RIGHT], value->values[GTK_CSS_LEFT]))
119     n = 4;
120   else if (!_gtk_css_value_equal0 (value->values[GTK_CSS_TOP], value->values[GTK_CSS_BOTTOM]))
121     n = 3;
122   else if (!_gtk_css_value_equal0 (value->values[GTK_CSS_TOP], value->values[GTK_CSS_RIGHT]))
123     n = 2;
124   else
125     n = 1;
126 
127   for (i = 0; i < n; i++)
128     {
129       if (i > 0)
130         g_string_append_c (string, ' ');
131 
132       if (value->values[i] == NULL)
133         g_string_append (string, "auto");
134       else
135         _gtk_css_value_print (value->values[i], string);
136     }
137 
138   if (value->fill)
139     g_string_append (string, " fill");
140 }
141 
142 static const GtkCssValueClass GTK_CSS_VALUE_BORDER = {
143   gtk_css_value_border_free,
144   gtk_css_value_border_compute,
145   gtk_css_value_border_equal,
146   gtk_css_value_border_transition,
147   gtk_css_value_border_print
148 };
149 
150 GtkCssValue *
_gtk_css_border_value_new(GtkCssValue * top,GtkCssValue * right,GtkCssValue * bottom,GtkCssValue * left)151 _gtk_css_border_value_new (GtkCssValue *top,
152                            GtkCssValue *right,
153                            GtkCssValue *bottom,
154                            GtkCssValue *left)
155 {
156   GtkCssValue *result;
157 
158   result = _gtk_css_value_new (GtkCssValue, &GTK_CSS_VALUE_BORDER);
159   result->values[GTK_CSS_TOP] = top;
160   result->values[GTK_CSS_RIGHT] = right;
161   result->values[GTK_CSS_BOTTOM] = bottom;
162   result->values[GTK_CSS_LEFT] = left;
163 
164   return result;
165 }
166 
167 GtkCssValue *
_gtk_css_border_value_parse(GtkCssParser * parser,GtkCssNumberParseFlags flags,gboolean allow_auto,gboolean allow_fill)168 _gtk_css_border_value_parse (GtkCssParser           *parser,
169                              GtkCssNumberParseFlags  flags,
170                              gboolean                allow_auto,
171                              gboolean                allow_fill)
172 {
173   GtkCssValue *result;
174   guint i;
175 
176   result = _gtk_css_border_value_new (NULL, NULL, NULL, NULL);
177 
178   if (allow_fill)
179     result->fill = _gtk_css_parser_try (parser, "fill", TRUE);
180 
181   for (i = 0; i < 4; i++)
182     {
183       if (allow_auto && _gtk_css_parser_try (parser, "auto", TRUE))
184         continue;
185 
186       if (!gtk_css_number_value_can_parse (parser))
187         break;
188 
189       result->values[i] = _gtk_css_number_value_parse (parser, flags);
190       if (result->values[i] == NULL)
191         {
192           _gtk_css_value_unref (result);
193           return NULL;
194         }
195     }
196 
197   if (i == 0)
198     {
199       _gtk_css_parser_error (parser, "Expected a number");
200       _gtk_css_value_unref (result);
201       return NULL;
202     }
203 
204   if (allow_fill && !result->fill)
205     result->fill = _gtk_css_parser_try (parser, "fill", TRUE);
206 
207   for (; i < 4; i++)
208     {
209       if (result->values[(i - 1) >> 1])
210         result->values[i] = _gtk_css_value_ref (result->values[(i - 1) >> 1]);
211     }
212 
213   return result;
214 }
215 
216 GtkCssValue *
_gtk_css_border_value_get_top(const GtkCssValue * value)217 _gtk_css_border_value_get_top (const GtkCssValue *value)
218 {
219   g_return_val_if_fail (value->class == &GTK_CSS_VALUE_BORDER, NULL);
220 
221   return value->values[GTK_CSS_TOP];
222 }
223 
224 GtkCssValue *
_gtk_css_border_value_get_right(const GtkCssValue * value)225 _gtk_css_border_value_get_right (const GtkCssValue *value)
226 {
227   g_return_val_if_fail (value->class == &GTK_CSS_VALUE_BORDER, NULL);
228 
229   return value->values[GTK_CSS_RIGHT];
230 }
231 
232 GtkCssValue *
_gtk_css_border_value_get_bottom(const GtkCssValue * value)233 _gtk_css_border_value_get_bottom (const GtkCssValue *value)
234 {
235   g_return_val_if_fail (value->class == &GTK_CSS_VALUE_BORDER, NULL);
236 
237   return value->values[GTK_CSS_BOTTOM];
238 }
239 
240 GtkCssValue *
_gtk_css_border_value_get_left(const GtkCssValue * value)241 _gtk_css_border_value_get_left (const GtkCssValue *value)
242 {
243   g_return_val_if_fail (value->class == &GTK_CSS_VALUE_BORDER, NULL);
244 
245   return value->values[GTK_CSS_LEFT];
246 }
247 
248