369 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			369 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* GTK - The GIMP Toolkit
 | |
|  * Copyright (C) 2011 Red Hat, Inc.
 | |
|  *
 | |
|  * Author: Cosimo Cecchi <cosimoc@gnome.org>
 | |
|  *
 | |
|  * This library is free software; you can redistribute it and/or
 | |
|  * modify it under the terms of the GNU Lesser General Public
 | |
|  * License as published by the Free Software Foundation; either
 | |
|  * version 2 of the License, or (at your option) any later version.
 | |
|  *
 | |
|  * This library is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
|  * Lesser General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU Lesser General Public
 | |
|  * License along with this library; if not, write to the
 | |
|  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 | |
|  * Boston, MA 02111-1307, USA.
 | |
|  */
 | |
| 
 | |
| #include "config.h"
 | |
| 
 | |
| #include "gtkshadowprivate.h"
 | |
| #include "gtkstylecontext.h"
 | |
| #include "gtkthemingengineprivate.h"
 | |
| #include "gtkthemingengine.h"
 | |
| #include "gtkpango.h"
 | |
| #include "gtkthemingengineprivate.h"
 | |
| 
 | |
| typedef struct _GtkShadowElement GtkShadowElement;
 | |
| 
 | |
| struct _GtkShadowElement {
 | |
|   gint16 hoffset;
 | |
|   gint16 voffset;
 | |
|   gint16 radius;
 | |
|   gint16 spread;
 | |
| 
 | |
|   gboolean inset;
 | |
| 
 | |
|   GdkRGBA color;
 | |
|   GtkSymbolicColor *symbolic_color;
 | |
| };
 | |
| 
 | |
| static void
 | |
| shadow_element_print (GtkShadowElement *element,
 | |
|                       GString          *str)
 | |
| {
 | |
|   gchar *color_str;
 | |
| 
 | |
|   if (element->inset)
 | |
|     g_string_append (str, "inset ");
 | |
| 
 | |
|   g_string_append_printf (str, "%d %d ",
 | |
|                           (gint) element->hoffset,
 | |
|                           (gint) element->voffset);
 | |
| 
 | |
|   if (element->radius != 0)
 | |
|     g_string_append_printf (str, "%d ", (gint) element->radius);
 | |
| 
 | |
|   if (element->spread != 0)
 | |
|     g_string_append_printf (str, "%d ", (gint) element->spread);
 | |
| 
 | |
|   if (element->symbolic_color != NULL)
 | |
|     color_str = gtk_symbolic_color_to_string (element->symbolic_color);
 | |
|   else
 | |
|     color_str = gdk_rgba_to_string (&element->color);
 | |
| 
 | |
|   g_string_append (str, color_str);
 | |
|   g_free (color_str);
 | |
| }
 | |
| 
 | |
| static void
 | |
| shadow_element_free (GtkShadowElement *element)
 | |
| {
 | |
|   if (element->symbolic_color != NULL)
 | |
|     gtk_symbolic_color_unref (element->symbolic_color);
 | |
| 
 | |
|   g_slice_free (GtkShadowElement, element);
 | |
| }
 | |
| 
 | |
| static GtkShadowElement *
 | |
| shadow_element_new (gdouble hoffset,
 | |
|                     gdouble voffset,
 | |
|                     gdouble radius,
 | |
|                     gdouble spread,
 | |
|                     gboolean inset,
 | |
|                     GdkRGBA *color,
 | |
|                     GtkSymbolicColor *symbolic_color)
 | |
| {
 | |
|   GtkShadowElement *retval;
 | |
| 
 | |
|   retval = g_slice_new0 (GtkShadowElement);
 | |
| 
 | |
|   retval->hoffset = hoffset;
 | |
|   retval->voffset = voffset;
 | |
|   retval->radius = radius;
 | |
|   retval->spread = spread;
 | |
|   retval->inset = inset;
 | |
| 
 | |
|   if (symbolic_color != NULL)
 | |
|     retval->symbolic_color = gtk_symbolic_color_ref (symbolic_color);
 | |
| 
 | |
|   if (color != NULL)
 | |
|     retval->color = *color;
 | |
| 
 | |
|   return retval;
 | |
| }                  
 | |
| 
 | |
| /****************
 | |
|  * GtkShadow *
 | |
|  ****************/
 | |
| 
 | |
| G_DEFINE_BOXED_TYPE (GtkShadow, _gtk_shadow,
 | |
|                      _gtk_shadow_ref, _gtk_shadow_unref)
 | |
| 
 | |
| struct _GtkShadow {
 | |
|   GList *elements;
 | |
| 
 | |
|   guint ref_count;
 | |
|   gboolean resolved;
 | |
| };
 | |
| 
 | |
| GtkShadow *
 | |
| _gtk_shadow_new (void)
 | |
| {
 | |
|   GtkShadow *retval;
 | |
| 
 | |
|   retval = g_slice_new0 (GtkShadow);
 | |
|   retval->ref_count = 1;
 | |
|   retval->resolved = FALSE;
 | |
| 
 | |
|   return retval;
 | |
| }
 | |
| 
 | |
| GtkShadow *
 | |
| _gtk_shadow_ref (GtkShadow *shadow)
 | |
| {
 | |
|   g_return_val_if_fail (shadow != NULL, NULL);
 | |
| 
 | |
|   shadow->ref_count++;
 | |
| 
 | |
|   return shadow;
 | |
| }
 | |
| 
 | |
| gboolean
 | |
| _gtk_shadow_get_resolved (GtkShadow *shadow)
 | |
| {
 | |
|   return shadow->resolved;
 | |
| }
 | |
| 
 | |
| void
 | |
| _gtk_shadow_unref (GtkShadow *shadow)
 | |
| {
 | |
|   g_return_if_fail (shadow != NULL);
 | |
| 
 | |
|   shadow->ref_count--;
 | |
| 
 | |
|   if (shadow->ref_count == 0)
 | |
|     {
 | |
|       g_list_free_full (shadow->elements,
 | |
|                         (GDestroyNotify) shadow_element_free);
 | |
|       g_slice_free (GtkShadow, shadow);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void
 | |
| _gtk_shadow_append (GtkShadow        *shadow,
 | |
|                     gdouble           hoffset,
 | |
|                     gdouble           voffset,
 | |
|                     gdouble           radius,
 | |
|                     gdouble           spread,
 | |
|                     gboolean          inset,
 | |
|                     GtkSymbolicColor *color)
 | |
| {
 | |
|   GtkShadowElement *element;
 | |
| 
 | |
|   g_return_if_fail (shadow != NULL);
 | |
|   g_return_if_fail (color != NULL);
 | |
| 
 | |
|   element = shadow_element_new (hoffset, voffset,
 | |
|                                 radius, spread, inset,
 | |
|                                 NULL, color);
 | |
| 
 | |
|   shadow->elements = g_list_append (shadow->elements, element);
 | |
| }
 | |
| 
 | |
| GtkShadow *
 | |
| _gtk_shadow_resolve (GtkShadow          *shadow,
 | |
|                      GtkStyleProperties *props)
 | |
| {
 | |
|   GtkShadow *resolved_shadow;
 | |
|   GtkShadowElement *element, *resolved_element;
 | |
|   GdkRGBA color;
 | |
|   GList *l;
 | |
| 
 | |
|   if (shadow->resolved)
 | |
|     return _gtk_shadow_ref (shadow);
 | |
| 
 | |
|   resolved_shadow = _gtk_shadow_new ();
 | |
| 
 | |
|   for (l = shadow->elements; l != NULL; l = l->next)
 | |
|     {
 | |
|       element = l->data;
 | |
| 
 | |
|       if (!gtk_symbolic_color_resolve (element->symbolic_color,
 | |
|                                        props,
 | |
|                                        &color))
 | |
|         {
 | |
|           _gtk_shadow_unref (resolved_shadow);
 | |
|           return NULL;
 | |
|         }
 | |
| 
 | |
|       resolved_element =
 | |
|         shadow_element_new (element->hoffset, element->voffset,
 | |
|                             element->radius, element->spread, element->inset,
 | |
|                             &color, NULL);
 | |
| 
 | |
|       resolved_shadow->elements =
 | |
|         g_list_append (resolved_shadow->elements, resolved_element);
 | |
|     }
 | |
| 
 | |
|   resolved_shadow->resolved = TRUE;
 | |
| 
 | |
|   return resolved_shadow;
 | |
| }
 | |
| 
 | |
| void
 | |
| _gtk_shadow_print (GtkShadow *shadow,
 | |
|                    GString   *str)
 | |
| {
 | |
|   gint length;
 | |
|   GList *l;
 | |
| 
 | |
|   length = g_list_length (shadow->elements);
 | |
| 
 | |
|   if (length == 0)
 | |
|     return;
 | |
| 
 | |
|   shadow_element_print (shadow->elements->data, str);
 | |
| 
 | |
|   if (length == 1)
 | |
|     return;
 | |
| 
 | |
|   for (l = g_list_next (shadow->elements); l != NULL; l = l->next)
 | |
|     {
 | |
|       g_string_append (str, ", ");
 | |
|       shadow_element_print (l->data, str);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void
 | |
| _gtk_text_shadow_paint_layout (GtkShadow       *shadow,
 | |
|                                cairo_t         *cr,
 | |
|                                PangoLayout     *layout)
 | |
| {
 | |
|   GList *l;
 | |
|   GtkShadowElement *element;
 | |
| 
 | |
|   if (!cairo_has_current_point (cr))
 | |
|     cairo_move_to (cr, 0, 0);
 | |
| 
 | |
|   /* render shadows starting from the last one,
 | |
|    * and the others on top.
 | |
|    */
 | |
|   for (l = g_list_last (shadow->elements); l != NULL; l = l->prev)
 | |
|     {
 | |
|       element = l->data;
 | |
| 
 | |
|       cairo_save (cr);
 | |
| 
 | |
|       cairo_rel_move_to (cr, element->hoffset, element->voffset);
 | |
|       gdk_cairo_set_source_rgba (cr, &element->color);
 | |
|       _gtk_pango_fill_layout (cr, layout);
 | |
| 
 | |
|       cairo_rel_move_to (cr, -element->hoffset, -element->voffset);
 | |
|       cairo_restore (cr);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| _gtk_icon_shadow_paint (GtkShadow *shadow,
 | |
| 			cairo_t *cr)
 | |
| {
 | |
|   GList *l;
 | |
|   GtkShadowElement *element;
 | |
|   cairo_pattern_t *pattern;
 | |
| 
 | |
|   for (l = g_list_last (shadow->elements); l != NULL; l = l->prev)
 | |
|     {
 | |
|       element = l->data;
 | |
| 
 | |
|       cairo_save (cr);
 | |
|       pattern = cairo_pattern_reference (cairo_get_source (cr));
 | |
|       gdk_cairo_set_source_rgba (cr, &element->color);
 | |
| 
 | |
|       cairo_translate (cr, element->hoffset, element->voffset);
 | |
|       cairo_mask (cr, pattern);
 | |
| 
 | |
|       cairo_restore (cr);
 | |
|       cairo_pattern_destroy (pattern);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void
 | |
| _gtk_icon_shadow_paint_spinner (GtkShadow *shadow,
 | |
|                                 cairo_t   *cr,
 | |
|                                 gdouble    radius,
 | |
|                                 gdouble    progress)
 | |
| {
 | |
|   GtkShadowElement *element;
 | |
|   GList *l;
 | |
| 
 | |
|   for (l = g_list_last (shadow->elements); l != NULL; l = l->prev)
 | |
|     {
 | |
|       element = l->data;
 | |
| 
 | |
|       cairo_save (cr);
 | |
| 
 | |
|       cairo_translate (cr, element->hoffset, element->voffset);
 | |
|       _gtk_theming_engine_paint_spinner (cr,
 | |
|                                          radius, progress,
 | |
|                                          &element->color);
 | |
| 
 | |
|       cairo_restore (cr);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void
 | |
| _gtk_box_shadow_render (GtkShadow           *shadow,
 | |
|                         cairo_t             *cr,
 | |
|                         const GtkRoundedBox *padding_box)
 | |
| {
 | |
|   GtkShadowElement *element;
 | |
|   GtkRoundedBox box;
 | |
|   GList *l;
 | |
| 
 | |
|   cairo_save (cr);
 | |
|   cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
 | |
| 
 | |
|   _gtk_rounded_box_path (padding_box, cr);
 | |
|   cairo_clip (cr);
 | |
| 
 | |
|   /* render shadows starting from the last one,
 | |
|    * and the others on top.
 | |
|    */
 | |
|   for (l = g_list_last (shadow->elements); l != NULL; l = l->prev)
 | |
|     {
 | |
|       element = l->data;
 | |
| 
 | |
|       if (!element->inset)
 | |
|         continue;
 | |
| 
 | |
|       box = *padding_box;
 | |
|       _gtk_rounded_box_move (&box, element->hoffset, element->voffset);
 | |
|       _gtk_rounded_box_shrink (&box,
 | |
|                                element->spread, element->spread,
 | |
|                                element->spread, element->spread);
 | |
| 
 | |
|       _gtk_rounded_box_path (&box, cr);
 | |
|       _gtk_rounded_box_clip_path (padding_box, cr);
 | |
| 
 | |
|       gdk_cairo_set_source_rgba (cr, &element->color);
 | |
|       cairo_fill (cr);
 | |
|   }
 | |
| 
 | |
|   cairo_restore (cr);
 | |
| }
 | 
