1 /*
2  * Copyright © 2015 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.1 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  * Authors: Benjamin Otte <otte@gnome.org>
18  */
19 
20 #include "config.h"
21 
22 #include "gtkbuiltiniconprivate.h"
23 
24 #include "gtkcssnodeprivate.h"
25 #include "gtkcssnumbervalueprivate.h"
26 #include "gtkrendericonprivate.h"
27 
28 /* GtkBuiltinIcon is a gadget implementation that is meant to replace
29  * all calls to gtk_render_ functions to render arrows, expanders, checks
30  * radios, handles, separators, etc. See the GtkCssImageBuiltinType
31  * enumeration for the full set of builtin icons that this gadget can
32  * render.
33  *
34  * Use gtk_builtin_icon_set_image to set which of the builtin icons
35  * is rendered.
36  *
37  * Use gtk_builtin_icon_set_default_size to set a non-zero default
38  * size for the icon. If you need to support a legacy size style property,
39  * use gtk_builtin_icon_set_default_size_property.
40  *
41  * Themes can override the acutal image that is used with the
42  * -gtk-icon-source property. If it is not specified, a builtin
43  * fallback is used.
44  */
45 
46 typedef struct _GtkBuiltinIconPrivate GtkBuiltinIconPrivate;
47 struct _GtkBuiltinIconPrivate {
48   GtkCssImageBuiltinType        image_type;
49   int                           default_size;
50   int                           strikethrough;
51   gboolean                      strikethrough_valid;
52   char *                        default_size_property;
53 };
54 
G_DEFINE_TYPE_WITH_CODE(GtkBuiltinIcon,gtk_builtin_icon,GTK_TYPE_CSS_GADGET,G_ADD_PRIVATE (GtkBuiltinIcon))55 G_DEFINE_TYPE_WITH_CODE (GtkBuiltinIcon, gtk_builtin_icon, GTK_TYPE_CSS_GADGET,
56                          G_ADD_PRIVATE (GtkBuiltinIcon))
57 
58 static void
59 gtk_builtin_icon_get_preferred_size (GtkCssGadget   *gadget,
60                                      GtkOrientation  orientation,
61                                      gint            for_size,
62                                      gint           *minimum,
63                                      gint           *natural,
64                                      gint           *minimum_baseline,
65                                      gint           *natural_baseline)
66 {
67   GtkBuiltinIconPrivate *priv = gtk_builtin_icon_get_instance_private (GTK_BUILTIN_ICON (gadget));
68   double min_size;
69   guint property;
70 
71   if (orientation == GTK_ORIENTATION_HORIZONTAL)
72     property = GTK_CSS_PROPERTY_MIN_WIDTH;
73   else
74     property = GTK_CSS_PROPERTY_MIN_HEIGHT;
75   min_size = _gtk_css_number_value_get (gtk_css_style_get_value (gtk_css_gadget_get_style (gadget), property), 100);
76   if (min_size > 0.0)
77     {
78       *minimum = *natural = min_size;
79     }
80   else if (priv->default_size_property)
81     {
82       GValue value = G_VALUE_INIT;
83 
84       /* Do it a bit more complicated here so we get warnings when
85        * somebody sets a non-int proerty.
86        */
87       g_value_init (&value, G_TYPE_INT);
88       gtk_widget_style_get_property (gtk_css_gadget_get_owner (gadget),
89                                      priv->default_size_property,
90                                      &value);
91       *minimum = *natural = g_value_get_int (&value);
92       g_value_unset (&value);
93     }
94   else
95     {
96       *minimum = *natural = priv->default_size;
97     }
98 
99   if (minimum_baseline)
100     {
101       if (!priv->strikethrough_valid)
102         {
103           GtkWidget *widget;
104           PangoContext *pango_context;
105           const PangoFontDescription *font_desc;
106           PangoFontMetrics *metrics;
107 
108           widget = gtk_css_gadget_get_owner (gadget);
109           pango_context = gtk_widget_get_pango_context (widget);
110           font_desc = pango_context_get_font_description (pango_context);
111 
112           metrics = pango_context_get_metrics (pango_context,
113                                                font_desc,
114                                                pango_context_get_language (pango_context));
115           priv->strikethrough = pango_font_metrics_get_strikethrough_position (metrics);
116           priv->strikethrough_valid = TRUE;
117 
118           pango_font_metrics_unref (metrics);
119         }
120 
121       *minimum_baseline = *minimum * 0.5 + PANGO_PIXELS (priv->strikethrough);
122     }
123 
124   if (natural_baseline)
125     *natural_baseline = *minimum_baseline;
126 }
127 
128 static void
gtk_builtin_icon_allocate(GtkCssGadget * gadget,const GtkAllocation * allocation,int baseline,GtkAllocation * out_clip)129 gtk_builtin_icon_allocate (GtkCssGadget        *gadget,
130                            const GtkAllocation *allocation,
131                            int                  baseline,
132                            GtkAllocation       *out_clip)
133 {
134   GdkRectangle icon_clip;
135 
136   GTK_CSS_GADGET_CLASS (gtk_builtin_icon_parent_class)->allocate (gadget, allocation, baseline, out_clip);
137 
138   gtk_css_style_render_icon_get_extents (gtk_css_gadget_get_style (gadget),
139                                          &icon_clip,
140                                          allocation->x, allocation->y,
141                                          allocation->width, allocation->height);
142   gdk_rectangle_union (out_clip, &icon_clip, out_clip);
143 }
144 
145 static gboolean
gtk_builtin_icon_draw(GtkCssGadget * gadget,cairo_t * cr,int x,int y,int width,int height)146 gtk_builtin_icon_draw (GtkCssGadget *gadget,
147                        cairo_t      *cr,
148                        int           x,
149                        int           y,
150                        int           width,
151                        int           height)
152 {
153   GtkBuiltinIconPrivate *priv = gtk_builtin_icon_get_instance_private (GTK_BUILTIN_ICON (gadget));
154 
155   gtk_css_style_render_icon (gtk_css_gadget_get_style (gadget),
156                              cr,
157                              x, y,
158                              width, height,
159                              priv->image_type);
160 
161   return FALSE;
162 }
163 
164 static void
gtk_builtin_icon_style_changed(GtkCssGadget * gadget,GtkCssStyleChange * change)165 gtk_builtin_icon_style_changed (GtkCssGadget      *gadget,
166                                 GtkCssStyleChange *change)
167 {
168   GtkBuiltinIconPrivate *priv = gtk_builtin_icon_get_instance_private (GTK_BUILTIN_ICON (gadget));
169 
170   if (gtk_css_style_change_affects (change, GTK_CSS_AFFECTS_FONT))
171     priv->strikethrough_valid = FALSE;
172 
173   GTK_CSS_GADGET_CLASS (gtk_builtin_icon_parent_class)->style_changed (gadget, change);
174 }
175 
176 static void
gtk_builtin_icon_finalize(GObject * object)177 gtk_builtin_icon_finalize (GObject *object)
178 {
179   GtkBuiltinIconPrivate *priv = gtk_builtin_icon_get_instance_private (GTK_BUILTIN_ICON (object));
180 
181   g_free (priv->default_size_property);
182 
183   G_OBJECT_CLASS (gtk_builtin_icon_parent_class)->finalize (object);
184 }
185 
186 static void
gtk_builtin_icon_class_init(GtkBuiltinIconClass * klass)187 gtk_builtin_icon_class_init (GtkBuiltinIconClass *klass)
188 {
189   GtkCssGadgetClass *gadget_class = GTK_CSS_GADGET_CLASS (klass);
190   GObjectClass *object_class = G_OBJECT_CLASS (klass);
191 
192   object_class->finalize = gtk_builtin_icon_finalize;
193 
194   gadget_class->get_preferred_size = gtk_builtin_icon_get_preferred_size;
195   gadget_class->allocate = gtk_builtin_icon_allocate;
196   gadget_class->draw = gtk_builtin_icon_draw;
197   gadget_class->style_changed = gtk_builtin_icon_style_changed;
198 }
199 
200 static void
gtk_builtin_icon_init(GtkBuiltinIcon * custom_gadget)201 gtk_builtin_icon_init (GtkBuiltinIcon *custom_gadget)
202 {
203 }
204 
205 GtkCssGadget *
gtk_builtin_icon_new_for_node(GtkCssNode * node,GtkWidget * owner)206 gtk_builtin_icon_new_for_node (GtkCssNode *node,
207                                GtkWidget  *owner)
208 {
209   return g_object_new (GTK_TYPE_BUILTIN_ICON,
210                        "node", node,
211                        "owner", owner,
212                        NULL);
213 }
214 
215 GtkCssGadget *
gtk_builtin_icon_new(const char * name,GtkWidget * owner,GtkCssGadget * parent,GtkCssGadget * next_sibling)216 gtk_builtin_icon_new (const char   *name,
217                       GtkWidget    *owner,
218                       GtkCssGadget *parent,
219                       GtkCssGadget *next_sibling)
220 {
221   GtkCssNode *node;
222   GtkCssGadget *result;
223 
224   node = gtk_css_node_new ();
225   gtk_css_node_set_name (node, g_intern_string (name));
226   if (parent)
227     gtk_css_node_insert_before (gtk_css_gadget_get_node (parent),
228                                 node,
229                                 next_sibling ? gtk_css_gadget_get_node (next_sibling) : NULL);
230 
231   result = gtk_builtin_icon_new_for_node (node, owner);
232 
233   g_object_unref (node);
234 
235   return result;
236 }
237 
238 void
gtk_builtin_icon_set_image(GtkBuiltinIcon * icon,GtkCssImageBuiltinType image)239 gtk_builtin_icon_set_image (GtkBuiltinIcon         *icon,
240                             GtkCssImageBuiltinType  image)
241 {
242   GtkBuiltinIconPrivate *priv;
243 
244   g_return_if_fail (GTK_IS_BUILTIN_ICON (icon));
245 
246   priv = gtk_builtin_icon_get_instance_private (icon);
247 
248   if (priv->image_type != image)
249     {
250       priv->image_type = image;
251       gtk_widget_queue_draw (gtk_css_gadget_get_owner (GTK_CSS_GADGET (icon)));
252     }
253 }
254 
255 GtkCssImageBuiltinType
gtk_builtin_icon_get_image(GtkBuiltinIcon * icon)256 gtk_builtin_icon_get_image (GtkBuiltinIcon *icon)
257 {
258   GtkBuiltinIconPrivate *priv;
259 
260   g_return_val_if_fail (GTK_IS_BUILTIN_ICON (icon), GTK_CSS_IMAGE_BUILTIN_NONE);
261 
262   priv = gtk_builtin_icon_get_instance_private (icon);
263 
264   return priv->image_type;
265 }
266 
267 void
gtk_builtin_icon_set_default_size(GtkBuiltinIcon * icon,int default_size)268 gtk_builtin_icon_set_default_size (GtkBuiltinIcon *icon,
269                                    int             default_size)
270 {
271   GtkBuiltinIconPrivate *priv;
272 
273   g_return_if_fail (GTK_IS_BUILTIN_ICON (icon));
274 
275   priv = gtk_builtin_icon_get_instance_private (icon);
276 
277   if (priv->default_size != default_size)
278     {
279       priv->default_size = default_size;
280       gtk_widget_queue_resize (gtk_css_gadget_get_owner (GTK_CSS_GADGET (icon)));
281     }
282 }
283 
284 int
gtk_builtin_icon_get_default_size(GtkBuiltinIcon * icon)285 gtk_builtin_icon_get_default_size (GtkBuiltinIcon *icon)
286 {
287   GtkBuiltinIconPrivate *priv;
288 
289   g_return_val_if_fail (GTK_IS_BUILTIN_ICON (icon), GTK_CSS_IMAGE_BUILTIN_NONE);
290 
291   priv = gtk_builtin_icon_get_instance_private (icon);
292 
293   return priv->default_size;
294 }
295 
296 /**
297  * gtk_builtin_icon_set_default_size_property:
298  * @icon: icon to set the property for
299  * @property_name: Name of the style property
300  *
301  * Sets the name of a widget style property to use to compute the default size
302  * of the icon. If it is set to no %NULL, it will be used instead of the value
303  * set via gtk_builtin_icon_set_default_size() to set the default size of the
304  * icon.
305  *
306  * @property_name must refer to a style property that is of integer type.
307  *
308  * This function is intended strictly for backwards compatibility reasons.
309  */
310 void
gtk_builtin_icon_set_default_size_property(GtkBuiltinIcon * icon,const char * property_name)311 gtk_builtin_icon_set_default_size_property (GtkBuiltinIcon *icon,
312                                             const char     *property_name)
313 {
314   GtkBuiltinIconPrivate *priv;
315 
316   g_return_if_fail (GTK_IS_BUILTIN_ICON (icon));
317 
318   priv = gtk_builtin_icon_get_instance_private (icon);
319 
320   if (g_strcmp0 (priv->default_size_property, property_name))
321     {
322       priv->default_size_property = g_strdup (property_name);
323       gtk_widget_queue_resize (gtk_css_gadget_get_owner (GTK_CSS_GADGET (icon)));
324     }
325 }
326 
327 const char *
gtk_builtin_icon_get_default_size_property(GtkBuiltinIcon * icon)328 gtk_builtin_icon_get_default_size_property (GtkBuiltinIcon *icon)
329 {
330   GtkBuiltinIconPrivate *priv;
331 
332   g_return_val_if_fail (GTK_IS_BUILTIN_ICON (icon), NULL);
333 
334   priv = gtk_builtin_icon_get_instance_private (icon);
335 
336   return priv->default_size_property;
337 }
338