]> Pileus Git - ~andy/gtk/blob - gtk/deprecated/gtkgradient.c
GtkBubbleWindow: use OSD style class
[~andy/gtk] / gtk / deprecated / gtkgradient.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 2010 Carlos Garnacho <carlosg@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 #define GDK_DISABLE_DEPRECATION_WARNINGS
21
22 #include "gtkgradientprivate.h"
23
24 #include "gtkcsscolorvalueprivate.h"
25 #include "gtkcssrgbavalueprivate.h"
26 #include "gtkstylecontextprivate.h"
27 #include "gtkstyleproperties.h"
28 #include "gtksymboliccolorprivate.h"
29
30 /**
31  * SECTION:gtkgradient
32  * @Short_description: Gradients
33  * @Title: GtkGradient
34  *
35  * GtkGradient is a boxed type that represents a gradient.
36  * It is the result of parsing a
37  * <link linkend="gtkcssprovider-gradients">gradient expression</link>.
38  * To obtain the gradient represented by a GtkGradient, it has to
39  * be resolved with gtk_gradient_resolve(), which replaces all
40  * symbolic color references by the colors they refer to (in a given
41  * context) and constructs a #cairo_pattern_t value.
42  *
43  * It is not normally necessary to deal directly with #GtkGradients,
44  * since they are mostly used behind the scenes by #GtkStyleContext and
45  * #GtkCssProvider.
46  *
47  * #GtkGradient is deprecated. It was used internally by GTK's CSS engine
48  * to represent gradients. As its handling is not conforming to modern
49  * web standards, it is not used anymore. If you want to use gradients in
50  * your own code, please use Cairo directly.
51  */
52
53 G_DEFINE_BOXED_TYPE (GtkGradient, gtk_gradient,
54                      gtk_gradient_ref, gtk_gradient_unref)
55
56 typedef struct ColorStop ColorStop;
57
58 struct ColorStop
59 {
60   gdouble offset;
61   GtkSymbolicColor *color;
62 };
63
64 struct _GtkGradient
65 {
66   gdouble x0;
67   gdouble y0;
68   gdouble x1;
69   gdouble y1;
70   gdouble radius0;
71   gdouble radius1;
72
73   GArray *stops;
74
75   guint ref_count;
76 };
77
78 /**
79  * gtk_gradient_new_linear:
80  * @x0: X coordinate of the starting point
81  * @y0: Y coordinate of the starting point
82  * @x1: X coordinate of the end point
83  * @y1: Y coordinate of the end point
84  *
85  * Creates a new linear gradient along the line defined by (x0, y0) and (x1, y1). Before using the gradient
86  * a number of stop colors must be added through gtk_gradient_add_color_stop().
87  *
88  * Returns: A newly created #GtkGradient
89  *
90  * Since: 3.0
91  *
92  * Deprecated: 3.8: #GtkGradient is deprecated.
93  **/
94 GtkGradient *
95 gtk_gradient_new_linear (gdouble x0,
96                          gdouble y0,
97                          gdouble x1,
98                          gdouble y1)
99 {
100   GtkGradient *gradient;
101
102   gradient = g_slice_new (GtkGradient);
103   gradient->stops = g_array_new (FALSE, FALSE, sizeof (ColorStop));
104
105   gradient->x0 = x0;
106   gradient->y0 = y0;
107   gradient->x1 = x1;
108   gradient->y1 = y1;
109   gradient->radius0 = 0;
110   gradient->radius1 = 0;
111
112   gradient->ref_count = 1;
113
114   return gradient;
115 }
116
117 /**
118  * gtk_gradient_new_radial:
119  * @x0: X coordinate of the start circle
120  * @y0: Y coordinate of the start circle
121  * @radius0: radius of the start circle
122  * @x1: X coordinate of the end circle
123  * @y1: Y coordinate of the end circle
124  * @radius1: radius of the end circle
125  *
126  * Creates a new radial gradient along the two circles defined by (x0, y0, radius0) and
127  * (x1, y1, radius1). Before using the gradient a number of stop colors must be added
128  * through gtk_gradient_add_color_stop().
129  *
130  * Returns: A newly created #GtkGradient
131  *
132  * Since: 3.0
133  *
134  * Deprecated: 3.8: #GtkGradient is deprecated.
135  **/
136 GtkGradient *
137 gtk_gradient_new_radial (gdouble x0,
138                          gdouble y0,
139                          gdouble radius0,
140                          gdouble x1,
141                          gdouble y1,
142                          gdouble radius1)
143 {
144   GtkGradient *gradient;
145
146   gradient = g_slice_new (GtkGradient);
147   gradient->stops = g_array_new (FALSE, FALSE, sizeof (ColorStop));
148
149   gradient->x0 = x0;
150   gradient->y0 = y0;
151   gradient->x1 = x1;
152   gradient->y1 = y1;
153   gradient->radius0 = radius0;
154   gradient->radius1 = radius1;
155
156   gradient->ref_count = 1;
157
158   return gradient;
159 }
160
161 /**
162  * gtk_gradient_add_color_stop:
163  * @gradient: a #GtkGradient
164  * @offset: offset for the color stop
165  * @color: color to use
166  *
167  * Adds a stop color to @gradient.
168  *
169  * Since: 3.0
170  *
171  * Deprecated: 3.8: #GtkGradient is deprecated.
172  **/
173 void
174 gtk_gradient_add_color_stop (GtkGradient      *gradient,
175                              gdouble           offset,
176                              GtkSymbolicColor *color)
177 {
178   ColorStop stop;
179
180   g_return_if_fail (gradient != NULL);
181
182   stop.offset = offset;
183   stop.color = gtk_symbolic_color_ref (color);
184
185   g_array_append_val (gradient->stops, stop);
186 }
187
188 /**
189  * gtk_gradient_ref:
190  * @gradient: a #GtkGradient
191  *
192  * Increases the reference count of @gradient.
193  *
194  * Returns: The same @gradient
195  *
196  * Since: 3.0
197  *
198  * Deprecated: 3.8: #GtkGradient is deprecated.
199  **/
200 GtkGradient *
201 gtk_gradient_ref (GtkGradient *gradient)
202 {
203   g_return_val_if_fail (gradient != NULL, NULL);
204
205   gradient->ref_count++;
206
207   return gradient;
208 }
209
210 /**
211  * gtk_gradient_unref:
212  * @gradient: a #GtkGradient
213  *
214  * Decreases the reference count of @gradient, freeing its memory
215  * if the reference count reaches 0.
216  *
217  * Since: 3.0
218  *
219  * Deprecated: 3.8: #GtkGradient is deprecated.
220  **/
221 void
222 gtk_gradient_unref (GtkGradient *gradient)
223 {
224   g_return_if_fail (gradient != NULL);
225
226   gradient->ref_count--;
227
228   if (gradient->ref_count == 0)
229     {
230       guint i;
231
232       for (i = 0; i < gradient->stops->len; i++)
233         {
234           ColorStop *stop;
235
236           stop = &g_array_index (gradient->stops, ColorStop, i);
237           gtk_symbolic_color_unref (stop->color);
238         }
239
240       g_array_free (gradient->stops, TRUE);
241       g_slice_free (GtkGradient, gradient);
242     }
243 }
244
245 /**
246  * gtk_gradient_resolve:
247  * @gradient: a #GtkGradient
248  * @props: #GtkStyleProperties to use when resolving named colors
249  * @resolved_gradient: (out): return location for the resolved pattern
250  *
251  * If @gradient is resolvable, @resolved_gradient will be filled in
252  * with the resolved gradient as a cairo_pattern_t, and %TRUE will
253  * be returned. Generally, if @gradient can't be resolved, it is
254  * due to it being defined on top of a named color that doesn't
255  * exist in @props.
256  *
257  * Returns: %TRUE if the gradient has been resolved
258  *
259  * Since: 3.0
260  *
261  * Deprecated: 3.8: #GtkGradient is deprecated.
262  **/
263 gboolean
264 gtk_gradient_resolve (GtkGradient         *gradient,
265                       GtkStyleProperties  *props,
266                       cairo_pattern_t    **resolved_gradient)
267 {
268   cairo_pattern_t *pattern;
269   guint i;
270
271   g_return_val_if_fail (gradient != NULL, FALSE);
272   g_return_val_if_fail (GTK_IS_STYLE_PROPERTIES (props), FALSE);
273   g_return_val_if_fail (resolved_gradient != NULL, FALSE);
274
275   if (gradient->radius0 == 0 && gradient->radius1 == 0)
276     pattern = cairo_pattern_create_linear (gradient->x0, gradient->y0,
277                                            gradient->x1, gradient->y1);
278   else
279     pattern = cairo_pattern_create_radial (gradient->x0, gradient->y0,
280                                            gradient->radius0,
281                                            gradient->x1, gradient->y1,
282                                            gradient->radius1);
283
284   for (i = 0; i < gradient->stops->len; i++)
285     {
286       ColorStop *stop;
287       GdkRGBA color;
288
289       stop = &g_array_index (gradient->stops, ColorStop, i);
290
291       if (!gtk_symbolic_color_resolve (stop->color, props, &color))
292         {
293           cairo_pattern_destroy (pattern);
294           return FALSE;
295         }
296
297       cairo_pattern_add_color_stop_rgba (pattern, stop->offset,
298                                          color.red, color.green,
299                                          color.blue, color.alpha);
300     }
301
302   *resolved_gradient = pattern;
303   return TRUE;
304 }
305
306 cairo_pattern_t *
307 _gtk_gradient_resolve_full (GtkGradient             *gradient,
308                             GtkStyleProviderPrivate *provider,
309                             GtkCssComputedValues    *values,
310                             GtkCssComputedValues    *parent_values,
311                             GtkCssDependencies      *dependencies)
312 {
313   cairo_pattern_t *pattern;
314   guint i;
315
316   g_return_val_if_fail (gradient != NULL, NULL);
317   g_return_val_if_fail (GTK_IS_STYLE_PROVIDER (provider), NULL);
318   g_return_val_if_fail (GTK_IS_CSS_COMPUTED_VALUES (values), NULL);
319   g_return_val_if_fail (parent_values == NULL || GTK_IS_CSS_COMPUTED_VALUES (parent_values), NULL);
320   g_return_val_if_fail (*dependencies == 0, NULL);
321
322   if (gradient->radius0 == 0 && gradient->radius1 == 0)
323     pattern = cairo_pattern_create_linear (gradient->x0, gradient->y0,
324                                            gradient->x1, gradient->y1);
325   else
326     pattern = cairo_pattern_create_radial (gradient->x0, gradient->y0,
327                                            gradient->radius0,
328                                            gradient->x1, gradient->y1,
329                                            gradient->radius1);
330
331   for (i = 0; i < gradient->stops->len; i++)
332     {
333       ColorStop *stop;
334       GtkCssValue *val;
335       GdkRGBA rgba;
336       GtkCssDependencies stop_deps;
337
338       stop = &g_array_index (gradient->stops, ColorStop, i);
339
340       /* if color resolving fails, assume transparency */
341       val = _gtk_css_color_value_resolve (_gtk_symbolic_color_get_css_value (stop->color),
342                                           provider,
343                                           _gtk_css_computed_values_get_value (values, GTK_CSS_PROPERTY_COLOR),
344                                           GTK_CSS_DEPENDS_ON_COLOR,
345                                           &stop_deps);
346       if (val)
347         {
348           rgba = *_gtk_css_rgba_value_get_rgba (val);
349           *dependencies = _gtk_css_dependencies_union (*dependencies, stop_deps);
350           _gtk_css_value_unref (val);
351         }
352       else
353         {
354           rgba.red = rgba.green = rgba.blue = rgba.alpha = 0.0;
355         }
356
357       cairo_pattern_add_color_stop_rgba (pattern, stop->offset,
358                                          rgba.red, rgba.green,
359                                          rgba.blue, rgba.alpha);
360     }
361
362   return pattern;
363 }
364
365 static void
366 append_number (GString    *str,
367                double      d,
368                const char *zero,
369                const char *half,
370                const char *one)
371 {
372   if (zero && d == 0.0)
373     g_string_append (str, zero);
374   else if (half && d == 0.5)
375     g_string_append (str, half);
376   else if (one && d == 1.0)
377     g_string_append (str, one);
378   else
379     {
380       char buf[G_ASCII_DTOSTR_BUF_SIZE];
381
382       g_ascii_dtostr (buf, sizeof (buf), d);
383       g_string_append (str, buf);
384     }
385 }
386
387 /**
388  * gtk_gradient_to_string:
389  * @gradient: the gradient to print
390  *
391  * Creates a string representation for @gradient that is suitable
392  * for using in GTK CSS files.
393  *
394  * Returns: A string representation for @gradient
395  *
396  * Deprecated: 3.8: #GtkGradient is deprecated.
397  **/
398 char *
399 gtk_gradient_to_string (GtkGradient *gradient)
400 {
401   GString *str;
402   guint i;
403
404   g_return_val_if_fail (gradient != NULL, NULL);
405
406   str = g_string_new ("-gtk-gradient (");
407
408   if (gradient->radius0 == 0 && gradient->radius1 == 0)
409     {
410       g_string_append (str, "linear, ");
411       append_number (str, gradient->x0, "left", "center", "right");
412       g_string_append_c (str, ' ');
413       append_number (str, gradient->y0, "top", "center", "bottom");
414       g_string_append (str, ", ");
415       append_number (str, gradient->x1, "left", "center", "right");
416       g_string_append_c (str, ' ');
417       append_number (str, gradient->y1, "top", "center", "bottom");
418     }
419   else
420     {
421       g_string_append (str, "radial, ");
422       append_number (str, gradient->x0, "left", "center", "right");
423       g_string_append_c (str, ' ');
424       append_number (str, gradient->y0, "top", "center", "bottom");
425       g_string_append (str, ", ");
426       append_number (str, gradient->radius0, NULL, NULL, NULL);
427       g_string_append (str, ", ");
428       append_number (str, gradient->x1, "left", "center", "right");
429       g_string_append_c (str, ' ');
430       append_number (str, gradient->y1, "top", "center", "bottom");
431       g_string_append (str, ", ");
432       append_number (str, gradient->radius1, NULL, NULL, NULL);
433     }
434   
435   for (i = 0; i < gradient->stops->len; i++)
436     {
437       ColorStop *stop;
438       char *s;
439
440       stop = &g_array_index (gradient->stops, ColorStop, i);
441
442       g_string_append (str, ", ");
443
444       if (stop->offset == 0.0)
445         g_string_append (str, "from (");
446       else if (stop->offset == 1.0)
447         g_string_append (str, "to (");
448       else
449         {
450           g_string_append (str, "color-stop (");
451           append_number (str, stop->offset, NULL, NULL, NULL);
452           g_string_append (str, ", ");
453         }
454
455       s = gtk_symbolic_color_to_string (stop->color);
456       g_string_append (str, s);
457       g_free (s);
458
459       g_string_append (str, ")");
460     }
461
462   g_string_append (str, ")");
463
464   return g_string_free (str, FALSE);
465 }
466
467 static GtkGradient *
468 gtk_gradient_fade (GtkGradient *gradient,
469                    double       opacity)
470 {
471   GtkGradient *faded;
472   guint i;
473
474   faded = g_slice_new (GtkGradient);
475   faded->stops = g_array_new (FALSE, FALSE, sizeof (ColorStop));
476
477   faded->x0 = gradient->x0;
478   faded->y0 = gradient->y0;
479   faded->x1 = gradient->x1;
480   faded->y1 = gradient->y1;
481   faded->radius0 = gradient->radius0;
482   faded->radius1 = gradient->radius1;
483
484   faded->ref_count = 1;
485
486   for (i = 0; i < gradient->stops->len; i++)
487     {
488       GtkSymbolicColor *color;
489       ColorStop *stop;
490
491       stop = &g_array_index (gradient->stops, ColorStop, i);
492       color = gtk_symbolic_color_new_alpha (stop->color, opacity);
493       gtk_gradient_add_color_stop (faded, stop->offset, color);
494       gtk_symbolic_color_unref (color);
495     }
496
497   return faded;
498 }
499
500 GtkGradient *
501 _gtk_gradient_transition (GtkGradient *start,
502                           GtkGradient *end,
503                           guint        property_id,
504                           double       progress)
505 {
506   GtkGradient *gradient;
507   guint i;
508
509   g_return_val_if_fail (start != NULL, NULL);
510
511   if (end == NULL)
512     return gtk_gradient_fade (start, 1.0 - CLAMP (progress, 0.0, 1.0));
513
514   if (start->stops->len != end->stops->len)
515     return NULL;
516
517   /* check both are radial/linear */
518   if ((start->radius0 == 0 && start->radius1 == 0) != (end->radius0 == 0 && end->radius1 == 0))
519     return NULL;
520
521   gradient = g_slice_new (GtkGradient);
522   gradient->stops = g_array_new (FALSE, FALSE, sizeof (ColorStop));
523
524   gradient->x0 = (1 - progress) * start->x0 + progress * end->x0;
525   gradient->y0 = (1 - progress) * start->y0 + progress * end->y0;
526   gradient->x1 = (1 - progress) * start->x1 + progress * end->x1;
527   gradient->y1 = (1 - progress) * start->y1 + progress * end->y1;
528   gradient->radius0 = (1 - progress) * start->radius0 + progress * end->radius0;
529   gradient->radius1 = (1 - progress) * start->radius1 + progress * end->radius1;
530
531   gradient->ref_count = 1;
532
533   for (i = 0; i < start->stops->len; i++)
534     {
535       ColorStop *start_stop, *end_stop;
536       GtkSymbolicColor *color;
537       double offset;
538
539       start_stop = &g_array_index (start->stops, ColorStop, i);
540       end_stop = &g_array_index (end->stops, ColorStop, i);
541
542       offset = (1 - progress) * start_stop->offset + progress * end_stop->offset;
543       color = gtk_symbolic_color_new_mix (start_stop->color,
544                                           end_stop->color,
545                                           progress);
546       gtk_gradient_add_color_stop (gradient, offset, color);
547       gtk_symbolic_color_unref (color);
548     }
549
550   return gradient;
551 }