summaryrefslogtreecommitdiffstats
path: root/tdegtk/tdegtk-cairo-support.c
diff options
context:
space:
mode:
Diffstat (limited to 'tdegtk/tdegtk-cairo-support.c')
-rw-r--r--tdegtk/tdegtk-cairo-support.c1418
1 files changed, 1418 insertions, 0 deletions
diff --git a/tdegtk/tdegtk-cairo-support.c b/tdegtk/tdegtk-cairo-support.c
new file mode 100644
index 0000000..8b3a1d5
--- /dev/null
+++ b/tdegtk/tdegtk-cairo-support.c
@@ -0,0 +1,1418 @@
+/* The TdeGtk Theming Engine for Gtk+.
+ * Copyright (C) 2011 Canonical Ltd
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * Authored by Andrea Cimitan <[email protected]>
+ *
+ */
+
+#include <cairo.h>
+#include <gtk/gtk.h>
+
+#include "gtkroundedboxprivate.h"
+#include "raico-blur.h"
+#include "tdegtk-cairo-support.h"
+#include "tdegtk-support.h"
+#include "tdegtk-types.h"
+
+/* apply default color */
+static void
+apply_default_color (GdkRGBA *colors[4],
+ GdkRGBA *default_color)
+{
+ gint i;
+
+ for (i = 0; i < 4; i++)
+ if (tdegtk_gdk_rgba_is_default (colors[i]))
+ *colors[i] = *default_color;
+}
+
+/* set the border sides to 0 using hidden_side */
+static void
+hide_border_sides (GtkBorder *border,
+ guint hidden_side)
+{
+ if (hidden_side & SIDE_TOP)
+ border->top = 0;
+ if (hidden_side & SIDE_RIGHT)
+ border->right = 0;
+ if (hidden_side & SIDE_BOTTOM)
+ border->bottom = 0;
+ if (hidden_side & SIDE_LEFT)
+ border->left = 0;
+}
+
+/* shrink coordinate using GtkBorder */
+static void
+shrink_with_border (GtkBorder *border,
+ gdouble *x,
+ gdouble *y,
+ gdouble *width,
+ gdouble *height)
+{
+ *x += border->left;
+ *y += border->top;
+ *width -= border->left + border->right;
+ *height -= border->top + border->bottom;
+}
+
+/* draw the background */
+static void
+draw_background (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ guint hidden_side,
+ GtkJunctionSides junction)
+{
+ GdkRGBA bg_color;
+ GtkBorder border;
+ GtkRoundedBox border_box;
+ GtkStateFlags state;
+ cairo_pattern_t *bg_pat;
+ gdouble progress;
+ gboolean running;
+
+ state = gtk_theming_engine_get_state (engine);
+
+ gtk_theming_engine_get (engine, state,
+ "background-image", &bg_pat,
+ NULL);
+ gtk_theming_engine_get_background_color (engine, state, &bg_color);
+ gtk_theming_engine_get_border (engine, state, &border);
+
+ hide_border_sides (&border, hidden_side);
+
+ running = gtk_theming_engine_state_is_running (engine, GTK_STATE_PRELIGHT, &progress);
+
+ cairo_save (cr);
+
+ cairo_translate (cr, x, y);
+
+ /* clear cr if we can draw directly on the background */
+ if (gtk_theming_engine_has_class (engine, "background"))
+ {
+ cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.0); /* transparent */
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ cairo_paint (cr);
+ }
+
+ if (running)
+ {
+ GdkRGBA other_bg;
+ GtkStateFlags other_state;
+ cairo_pattern_t *new_pat = NULL;
+ cairo_pattern_t *other_pat;
+
+ if (state & GTK_STATE_FLAG_PRELIGHT)
+ {
+ other_state = state & ~(GTK_STATE_FLAG_PRELIGHT);
+
+ /* useful math function to use for a pulse loop animation could be
+ * progress = 1 - MAX (1 - fabs ((progress - 0.5)*2), 0);
+ * but we need to handle the not-running case (once animation finished),
+ * otherwise the last frame will be the PRELIGHT at full opacity. */
+ progress = 1 - progress;
+ }
+ else
+ other_state = state | GTK_STATE_FLAG_PRELIGHT;
+
+ gtk_theming_engine_get (engine, other_state,
+ "background-image", &other_pat,
+ NULL);
+ gtk_theming_engine_get_background_color (engine, other_state, &other_bg);
+
+ if (bg_pat && other_pat)
+ {
+ /* two patterns */
+ cairo_pattern_type_t type, other_type;
+ gint n0, n1;
+
+ cairo_pattern_get_color_stop_count (bg_pat, &n0);
+ cairo_pattern_get_color_stop_count (other_pat, &n1);
+ type = cairo_pattern_get_type (bg_pat);
+ other_type = cairo_pattern_get_type (other_pat);
+
+ if (type == other_type && n0 == n1)
+ {
+ /* two similar patterns, blend them point by point */
+ gdouble offset0, red0, green0, blue0, alpha0;
+ gdouble offset1, red1, green1, blue1, alpha1;
+ gdouble x00, x01, y00, y01, x10, x11, y10, y11;
+ gdouble r00, r01, r10, r11;
+ gint i;
+
+ if (type == CAIRO_PATTERN_TYPE_LINEAR)
+ {
+ cairo_pattern_get_linear_points (bg_pat, &x00, &y00, &x01, &y01);
+ cairo_pattern_get_linear_points (other_pat, &x10, &y10, &x11, &y11);
+
+ new_pat = cairo_pattern_create_linear (x00 + (x10 - x00) * progress,
+ y00 + (y10 - y00) * progress,
+ x01 + (x11 - x01) * progress,
+ y01 + (y11 - y01) * progress);
+ }
+ else
+ {
+ cairo_pattern_get_radial_circles (bg_pat, &x00, &y00, &r00, &x01, &y01, &r01);
+ cairo_pattern_get_radial_circles (other_pat, &x10, &y10, &r10, &x11, &y11, &r11);
+
+ new_pat = cairo_pattern_create_radial (x00 + (x10 - x00) * progress,
+ y00 + (y10 - y00) * progress,
+ r00 + (r10 - r00) * progress,
+ x01 + (x11 - x01) * progress,
+ y01 + (y11 - y01) * progress,
+ r01 + (r11 - r01) * progress);
+ }
+
+ cairo_pattern_set_filter (new_pat, CAIRO_FILTER_FAST);
+ i = 0;
+
+ while (i < n0 && i < n1)
+ {
+ cairo_pattern_get_color_stop_rgba (bg_pat, i,
+ &offset0,
+ &red0, &green0, &blue0,
+ &alpha0);
+ cairo_pattern_get_color_stop_rgba (other_pat, i,
+ &offset1,
+ &red1, &green1, &blue1,
+ &alpha1);
+ cairo_pattern_add_color_stop_rgba (new_pat,
+ offset0 + ((offset1 - offset0) * progress),
+ red0 + ((red1 - red0) * progress),
+ green0 + ((green1 - green0) * progress),
+ blue0 + ((blue1 - blue0) * progress),
+ alpha0 + ((alpha1 - alpha0) * progress));
+ i++;
+ }
+ }
+ else
+ {
+ /* two different patterns, paint them with alpha */
+ cairo_save (cr);
+
+ cairo_reset_clip (cr);
+ cairo_rectangle (cr, 0, 0, width, height);
+ cairo_clip (cr);
+
+ cairo_push_group (cr);
+
+ cairo_scale (cr, width, height);
+
+ cairo_set_source (cr, other_pat);
+ cairo_paint_with_alpha (cr, progress);
+
+ cairo_set_source (cr, bg_pat);
+ cairo_paint_with_alpha (cr, 1.0 - progress);
+
+ new_pat = cairo_pop_group (cr);
+
+ cairo_restore (cr);
+ }
+ }
+ else if (bg_pat || other_pat)
+ {
+ /* only one pattern, blend it with a color */
+ const GdkRGBA *c;
+ cairo_pattern_t *p;
+ gdouble x0, y0, x1, y1, r0, r1;
+ gint n, i;
+
+ if (bg_pat)
+ {
+ p = bg_pat;
+ c = &other_bg;
+ progress = 1 - progress;
+ }
+ else
+ {
+ p = other_pat;
+ c = &bg_color;
+ }
+
+ if (cairo_pattern_get_type (p) == CAIRO_PATTERN_TYPE_LINEAR)
+ {
+ cairo_pattern_get_linear_points (p, &x0, &y0, &x1, &y1);
+ new_pat = cairo_pattern_create_linear (x0, y0, x1, y1);
+ }
+ else
+ {
+ cairo_pattern_get_radial_circles (p, &x0, &y0, &r0, &x1, &y1, &r1);
+ new_pat = cairo_pattern_create_radial (x0, y0, r0, x1, y1, r1);
+ }
+
+ cairo_pattern_get_color_stop_count (p, &n);
+
+ for (i = 0; i < n; i++)
+ {
+ gdouble red1, green1, blue1, alpha1;
+ gdouble offset;
+
+ cairo_pattern_get_color_stop_rgba (p, i,
+ &offset,
+ &red1, &green1, &blue1,
+ &alpha1);
+ cairo_pattern_add_color_stop_rgba (new_pat, offset,
+ c->red + ((red1 - c->red) * progress),
+ c->green + ((green1 - c->green) * progress),
+ c->blue + ((blue1 - c->blue) * progress),
+ c->alpha + ((alpha1 - c->alpha) * progress));
+ }
+ }
+ else
+ {
+ /* just colors, create a new pattern blending them */
+ new_pat = cairo_pattern_create_rgba (CLAMP (bg_color.red + ((other_bg.red - bg_color.red) * progress), 0, 1),
+ CLAMP (bg_color.green + ((other_bg.green - bg_color.green) * progress), 0, 1),
+ CLAMP (bg_color.blue + ((other_bg.blue - bg_color.blue) * progress), 0, 1),
+ CLAMP (bg_color.alpha + ((other_bg.alpha - bg_color.alpha) * progress), 0, 1));
+ }
+
+ if (new_pat)
+ {
+ /* replace pattern to use */
+ cairo_pattern_destroy (bg_pat);
+ bg_pat = new_pat;
+ }
+
+ if (other_pat)
+ cairo_pattern_destroy (other_pat);
+ }
+
+ /* create the path to fill */
+ _gtk_rounded_box_init_rect (&border_box, 0, 0, width, height);
+ _gtk_rounded_box_apply_border_radius (&border_box, engine, state, junction);
+ _gtk_rounded_box_shrink (&border_box, border.top, border.right, border.bottom, border.left);
+ _gtk_rounded_box_path (&border_box, cr);
+
+ if (bg_pat)
+ {
+ /* pattern */
+ cairo_scale (cr, width, height);
+ cairo_set_source (cr, bg_pat);
+ cairo_scale (cr, 1.0 / width, 1.0 / height);
+ }
+ else
+ /* one color */
+ gdk_cairo_set_source_rgba (cr, &bg_color);
+
+ cairo_fill (cr);
+
+ if (bg_pat)
+ cairo_pattern_destroy (bg_pat);
+
+ cairo_restore (cr);
+}
+
+/* draw the inner glow */
+static void
+draw_glow (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ guint hidden_side,
+ GtkJunctionSides junction)
+{
+ GdkRGBA *glow_color;
+ GtkBorder border;
+ GtkRoundedBox border_box, padding_box;
+ GtkStateFlags state;
+ cairo_t *cr_surface;
+ cairo_surface_t *surface;
+ gint bradius = 0;
+ raico_blur_t* blur = NULL;
+
+ state = gtk_theming_engine_get_state (engine);
+
+ gtk_theming_engine_get (engine, state,
+ "-tdegtk-glow-radius", &bradius,
+ "-tdegtk-glow-color", &glow_color,
+ NULL);
+
+ if (bradius <= 0)
+ goto end_draw_glow;
+
+ gtk_theming_engine_get_border (engine, state, &border);
+
+ hide_border_sides (&border, hidden_side);
+
+ cairo_save (cr);
+
+ cairo_translate (cr, x, y);
+
+ /* create the path to clip */
+ _gtk_rounded_box_init_rect (&border_box, 0, 0, width, height);
+ _gtk_rounded_box_apply_border_radius (&border_box, engine, state, junction);
+ _gtk_rounded_box_shrink (&border_box, border.top, border.right, border.bottom, border.left);
+ _gtk_rounded_box_path (&border_box, cr);
+
+ cairo_clip (cr);
+
+ /* create the surface to blur */
+ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+ width + bradius * 2,
+ height + bradius * 2);
+ cr_surface = cairo_create (surface);
+
+ /* create the path on the surface to blur */
+ _gtk_rounded_box_move (&border_box, bradius, bradius);
+ padding_box = border_box;
+ _gtk_rounded_box_shrink (&padding_box, border.top * 2, border.right * 2, border.bottom * 2, border.left * 2);
+
+ cairo_set_fill_rule (cr_surface, CAIRO_FILL_RULE_EVEN_ODD);
+
+ gdk_cairo_set_source_rgba (cr_surface, glow_color);
+
+ _gtk_rounded_box_path (&border_box, cr_surface);
+ _gtk_rounded_box_path (&padding_box, cr_surface);
+ cairo_fill (cr_surface);
+
+ /* create and apply raico blur */
+ blur = raico_blur_create ();
+ raico_blur_set_radius (blur, bradius);
+ raico_blur_apply (blur, surface);
+
+ /* paint the blurred surface to cr */
+ cairo_set_source_surface (cr, surface, - bradius, - bradius);
+ cairo_paint (cr);
+
+ cairo_restore (cr);
+
+ cairo_surface_destroy (surface);
+ cairo_destroy (cr_surface);
+
+end_draw_glow:
+ gdk_rgba_free (glow_color);
+}
+
+/* draw a repeated texture */
+static void
+draw_texture (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ guint hidden_side,
+ GtkJunctionSides junction)
+{
+ GtkStateFlags state;
+ GValue value = { 0, };
+ cairo_pattern_t *texture = NULL;
+ cairo_surface_t *surface = NULL;
+
+ state = gtk_theming_engine_get_state (engine);
+
+ gtk_theming_engine_get_property (engine, "-tdegtk-background-texture", state, &value);
+
+ if (!G_VALUE_HOLDS_BOXED (&value))
+ return;
+
+ texture = g_value_dup_boxed (&value);
+ g_value_unset (&value);
+
+ if (texture != NULL)
+ cairo_pattern_get_surface (texture, &surface);
+
+ if (surface != NULL)
+ {
+ GtkBorder border;
+ GtkRoundedBox border_box;
+ cairo_pattern_t *pat;
+
+ gtk_theming_engine_get_border (engine, state, &border);
+
+ hide_border_sides (&border, hidden_side);
+
+ cairo_save (cr);
+
+ cairo_translate (cr, x, y);
+
+ /* create the path to fill */
+ _gtk_rounded_box_init_rect (&border_box, 0, 0, width, height);
+ _gtk_rounded_box_apply_border_radius (&border_box, engine, state, junction);
+ _gtk_rounded_box_shrink (&border_box, border.top, border.right, border.bottom, border.left);
+ _gtk_rounded_box_path (&border_box, cr);
+
+ pat = cairo_pattern_create_for_surface (surface);
+ cairo_pattern_set_extend (pat, CAIRO_EXTEND_REPEAT);
+ cairo_set_source (cr, pat);
+
+ cairo_fill (cr);
+
+ cairo_restore (cr);
+
+ cairo_pattern_destroy (pat);
+ }
+
+ if (texture != NULL)
+ cairo_pattern_destroy (texture);
+}
+
+void
+tdegtk_cairo_draw_background (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ guint hidden_side,
+ GtkJunctionSides junction)
+{
+ GtkBorder *outer_border;
+ GtkStateFlags state;
+
+ state = gtk_theming_engine_get_state (engine);
+
+ gtk_theming_engine_get (engine, state,
+ "-tdegtk-outer-stroke-width", &outer_border,
+ NULL);
+
+ hide_border_sides (outer_border, hidden_side);
+
+ shrink_with_border (outer_border, &x, &y, &width, &height);
+
+ /* first layer, background */
+ draw_background (engine, cr,
+ x, y,
+ width, height,
+ hidden_side, junction);
+
+ /* second layer, glow */
+ draw_glow (engine, cr,
+ x, y,
+ width, height,
+ hidden_side, junction);
+
+ /* third layer, texture */
+ draw_texture (engine, cr,
+ x, y,
+ width, height,
+ hidden_side, junction);
+
+ gtk_border_free (outer_border);
+}
+
+/* shade a color */
+static void
+color_shade (const GdkRGBA *color,
+ gdouble factor,
+ GdkRGBA *color_return)
+{
+ GtkSymbolicColor *literal, *shade;
+
+ literal = gtk_symbolic_color_new_literal (color);
+ shade = gtk_symbolic_color_new_shade (literal, factor);
+ gtk_symbolic_color_unref (literal);
+
+ gtk_symbolic_color_resolve (shade, NULL, color_return);
+ gtk_symbolic_color_unref (shade);
+}
+
+/* draw the border */
+static void
+draw_border (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ guint hidden_side,
+ GtkJunctionSides junction)
+{
+ GdkRGBA *colors[4];
+ GtkBorder border;
+ GtkBorderStyle border_style;
+ GtkRoundedBox border_box, padding_box;
+ GtkStateFlags state;
+ cairo_pattern_t *border_pat;
+ gboolean running;
+ gdouble progress;
+ static const guint current_side[4] = { SIDE_TOP, SIDE_RIGHT, SIDE_BOTTOM, SIDE_LEFT };
+ guint i, j;
+
+ state = gtk_theming_engine_get_state (engine);
+
+ gtk_theming_engine_get (engine, state,
+ "border-style", &border_style,
+ "border-top-color", &colors[0],
+ "border-right-color", &colors[1],
+ "border-bottom-color", &colors[2],
+ "border-left-color", &colors[3],
+ "-tdegtk-border-gradient", &border_pat,
+ NULL);
+ gtk_theming_engine_get_border (engine, state, &border);
+
+ hide_border_sides (&border, hidden_side);
+
+ running = gtk_theming_engine_state_is_running (engine, GTK_STATE_PRELIGHT, &progress);
+
+ cairo_save (cr);
+
+ cairo_translate (cr, x, y);
+
+ if (running)
+ {
+ GtkStateFlags other_state;
+ cairo_pattern_t *new_pat = NULL;
+ cairo_pattern_t *other_pat;
+
+ if (state & GTK_STATE_FLAG_PRELIGHT)
+ {
+ other_state = state & ~(GTK_STATE_FLAG_PRELIGHT);
+ progress = 1 - progress;
+ }
+ else
+ other_state = state | GTK_STATE_FLAG_PRELIGHT;
+
+ gtk_theming_engine_get (engine, other_state,
+ "-tdegtk-border-gradient", &other_pat,
+ NULL);
+
+ if (border_pat && other_pat)
+ {
+ /* two patterns */
+ cairo_pattern_type_t type, other_type;
+ gint n0, n1;
+
+ cairo_pattern_get_color_stop_count (border_pat, &n0);
+ cairo_pattern_get_color_stop_count (other_pat, &n1);
+ type = cairo_pattern_get_type (border_pat);
+ other_type = cairo_pattern_get_type (other_pat);
+
+ if (type == other_type && n0 == n1)
+ {
+ /* two similar patterns, blend them point by point */
+ gdouble offset0, red0, green0, blue0, alpha0;
+ gdouble offset1, red1, green1, blue1, alpha1;
+ gdouble x00, x01, y00, y01, x10, x11, y10, y11;
+ gdouble r00, r01, r10, r11;
+ gint i;
+
+ if (type == CAIRO_PATTERN_TYPE_LINEAR)
+ {
+ cairo_pattern_get_linear_points (border_pat, &x00, &y00, &x01, &y01);
+ cairo_pattern_get_linear_points (other_pat, &x10, &y10, &x11, &y11);
+
+ new_pat = cairo_pattern_create_linear (x00 + (x10 - x00) * progress,
+ y00 + (y10 - y00) * progress,
+ x01 + (x11 - x01) * progress,
+ y01 + (y11 - y01) * progress);
+ }
+ else
+ {
+ cairo_pattern_get_radial_circles (border_pat, &x00, &y00, &r00, &x01, &y01, &r01);
+ cairo_pattern_get_radial_circles (other_pat, &x10, &y10, &r10, &x11, &y11, &r11);
+
+ new_pat = cairo_pattern_create_radial (x00 + (x10 - x00) * progress,
+ y00 + (y10 - y00) * progress,
+ r00 + (r10 - r00) * progress,
+ x01 + (x11 - x01) * progress,
+ y01 + (y11 - y01) * progress,
+ r01 + (r11 - r01) * progress);
+ }
+
+ cairo_pattern_set_filter (new_pat, CAIRO_FILTER_FAST);
+ i = 0;
+
+ while (i < n0 && i < n1)
+ {
+ cairo_pattern_get_color_stop_rgba (border_pat, i,
+ &offset0,
+ &red0, &green0, &blue0,
+ &alpha0);
+ cairo_pattern_get_color_stop_rgba (other_pat, i,
+ &offset1,
+ &red1, &green1, &blue1,
+ &alpha1);
+ cairo_pattern_add_color_stop_rgba (new_pat,
+ offset0 + ((offset1 - offset0) * progress),
+ red0 + ((red1 - red0) * progress),
+ green0 + ((green1 - green0) * progress),
+ blue0 + ((blue1 - blue0) * progress),
+ alpha0 + ((alpha1 - alpha0) * progress));
+ i++;
+ }
+ }
+ else
+ {
+ /* two different patterns, paint them with alpha */
+ cairo_save (cr);
+
+ cairo_reset_clip (cr);
+ cairo_rectangle (cr, 0, 0, width, height);
+ cairo_clip (cr);
+
+ cairo_push_group (cr);
+
+ cairo_scale (cr, width, height);
+
+ cairo_set_source (cr, other_pat);
+ cairo_paint_with_alpha (cr, progress);
+
+ cairo_set_source (cr, border_pat);
+ cairo_paint_with_alpha (cr, 1.0 - progress);
+
+ new_pat = cairo_pop_group (cr);
+
+ cairo_restore (cr);
+ }
+ }
+ else if (border_pat || other_pat)
+ {
+ /* one pattern, blend it with a color */
+ const GdkRGBA *c;
+ cairo_pattern_t *p;
+ gdouble x0, y0, x1, y1, r0, r1;
+ gint n, i;
+
+ if (border_pat)
+ {
+ GdkRGBA other_color;
+
+ gtk_theming_engine_get_border_color (engine, other_state, &other_color);
+
+ p = border_pat;
+ c = &other_color;
+ progress = 1 - progress;
+ }
+ else
+ {
+ GdkRGBA border_color;
+
+ gtk_theming_engine_get_border_color (engine, state, &border_color);
+
+ p = other_pat;
+ c = &border_color;
+ }
+
+ if (cairo_pattern_get_type (p) == CAIRO_PATTERN_TYPE_LINEAR)
+ {
+ cairo_pattern_get_linear_points (p, &x0, &y0, &x1, &y1);
+ new_pat = cairo_pattern_create_linear (x0, y0, x1, y1);
+ }
+ else
+ {
+ cairo_pattern_get_radial_circles (p, &x0, &y0, &r0, &x1, &y1, &r1);
+ new_pat = cairo_pattern_create_radial (x0, y0, r0, x1, y1, r1);
+ }
+
+ cairo_pattern_get_color_stop_count (p, &n);
+
+ for (i = 0; i < n; i++)
+ {
+ gdouble red1, green1, blue1, alpha1;
+ gdouble offset;
+
+ cairo_pattern_get_color_stop_rgba (p, i,
+ &offset,
+ &red1, &green1, &blue1,
+ &alpha1);
+ cairo_pattern_add_color_stop_rgba (new_pat, offset,
+ c->red + ((red1 - c->red) * progress),
+ c->green + ((green1 - c->green) * progress),
+ c->blue + ((blue1 - c->blue) * progress),
+ c->alpha + ((alpha1 - c->alpha) * progress));
+ }
+ }
+ else
+ {
+ /* just colors, create new colors blending them */
+ GdkRGBA *other_colors[4];
+
+ gtk_theming_engine_get (engine, other_state,
+ "border-top-color", &other_colors[0],
+ "border-right-color", &other_colors[1],
+ "border-bottom-color", &other_colors[2],
+ "border-left-color", &other_colors[3],
+ NULL);
+
+ for (i = 0; i < 4; i++)
+ {
+ colors[i]->red = CLAMP (colors[i]->red + ((other_colors[i]->red - colors[i]->red) * progress), 0, 1);
+ colors[i]->green = CLAMP (colors[i]->green + ((other_colors[i]->green - colors[i]->green) * progress), 0, 1);
+ colors[i]->blue = CLAMP (colors[i]->blue + ((other_colors[i]->blue - colors[i]->blue) * progress), 0, 1);
+ colors[i]->alpha = CLAMP (colors[i]->alpha + ((other_colors[i]->alpha - colors[i]->alpha) * progress), 0, 1);
+ gdk_rgba_free (other_colors[i]);
+ }
+ }
+
+ if (new_pat)
+ {
+ /* replace pattern to use */
+ cairo_pattern_destroy (border_pat);
+ border_pat = new_pat;
+ }
+
+ if (other_pat)
+ cairo_pattern_destroy (other_pat);
+ }
+
+ switch (border_style)
+ {
+ default:
+ g_assert_not_reached ();
+ case GTK_BORDER_STYLE_NONE:
+ case GTK_BORDER_STYLE_SOLID:
+ break;
+ case GTK_BORDER_STYLE_INSET:
+ color_shade (colors[1], 1.8, colors[1]);
+ color_shade (colors[2], 1.8, colors[2]);
+ break;
+ case GTK_BORDER_STYLE_OUTSET:
+ color_shade (colors[0], 1.8, colors[0]);
+ color_shade (colors[3], 1.8, colors[3]);
+ break;
+ }
+
+ /* create the path to fill */
+ _gtk_rounded_box_init_rect (&border_box, 0, 0, width, height);
+ _gtk_rounded_box_apply_border_radius (&border_box, engine, state, junction);
+ padding_box = border_box;
+ _gtk_rounded_box_shrink (&padding_box, border.top, border.right, border.bottom, border.left);
+
+ cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+
+ switch (border_style)
+ {
+ default:
+ g_assert_not_reached ();
+ case GTK_BORDER_STYLE_NONE:
+ break;
+ case GTK_BORDER_STYLE_SOLID:
+ case GTK_BORDER_STYLE_INSET:
+ case GTK_BORDER_STYLE_OUTSET:
+
+ if (border_pat)
+ {
+ /* pattern */
+ cairo_scale (cr, width, height);
+ cairo_set_source (cr, border_pat);
+ cairo_scale (cr, 1.0 / width, 1.0 / height);
+
+ _gtk_rounded_box_path (&border_box, cr);
+ _gtk_rounded_box_path (&padding_box, cr);
+ cairo_fill (cr);
+ }
+ else if (gdk_rgba_equal (colors[0], colors[1]) &&
+ gdk_rgba_equal (colors[0], colors[2]) &&
+ gdk_rgba_equal (colors[0], colors[3]))
+ {
+ /* one color */
+ gdk_cairo_set_source_rgba (cr, colors[0]);
+
+ _gtk_rounded_box_path (&border_box, cr);
+ _gtk_rounded_box_path (&padding_box, cr);
+ cairo_fill (cr);
+ }
+ else
+ {
+ for (i = 0; i < 4; i++)
+ {
+ /* different colors */
+ if (hidden_side & current_side[i])
+ continue;
+
+ for (j = 0; j < 4; j++)
+ {
+ if (hidden_side & current_side[j])
+ continue;
+
+ if (i == j ||
+ gdk_rgba_equal (colors[i], colors[j]))
+ {
+ /* we were already painted when i == j */
+ if (i > j)
+ break;
+
+ if (j == 0)
+ _gtk_rounded_box_path_top (&border_box, &padding_box, cr);
+ else if (j == 1)
+ _gtk_rounded_box_path_right (&border_box, &padding_box, cr);
+ else if (j == 2)
+ _gtk_rounded_box_path_bottom (&border_box, &padding_box, cr);
+ else if (j == 3)
+ _gtk_rounded_box_path_left (&border_box, &padding_box, cr);
+ }
+ }
+ /* we were already painted when i == j */
+ if (i > j)
+ continue;
+
+ gdk_cairo_set_source_rgba (cr, colors[i]);
+
+ cairo_fill (cr);
+ }
+ }
+ break;
+ }
+
+ cairo_restore (cr);
+
+ if (border_pat)
+ cairo_pattern_destroy (border_pat);
+
+ for (i = 0; i < 4; i++)
+ gdk_rgba_free (colors[i]);
+}
+
+/* draw the inner stroke */
+static void
+draw_inner_stroke (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ guint hidden_side,
+ GtkJunctionSides junction)
+{
+ GdkRGBA *colors[4];
+ GdkRGBA *inner_stroke_color;
+ GtkBorder *inner_border;
+ GtkRoundedBox border_box, padding_box;
+ GtkStateFlags state;
+ cairo_pattern_t *inner_stroke_pat;
+ static const guint current_side[4] = { SIDE_TOP, SIDE_RIGHT, SIDE_BOTTOM, SIDE_LEFT };
+ guint i, j;
+
+ state = gtk_theming_engine_get_state (engine);
+
+ gtk_theming_engine_get (engine, state,
+ "-tdegtk-inner-stroke-color", &inner_stroke_color,
+ "-tdegtk-inner-stroke-top-color", &colors[0],
+ "-tdegtk-inner-stroke-right-color", &colors[1],
+ "-tdegtk-inner-stroke-bottom-color", &colors[2],
+ "-tdegtk-inner-stroke-left-color", &colors[3],
+ "-tdegtk-inner-stroke-gradient", &inner_stroke_pat,
+ "-tdegtk-inner-stroke-width", &inner_border,
+ NULL);
+
+ hide_border_sides (inner_border, hidden_side);
+
+ if (tdegtk_gtk_border_is_zero (inner_border))
+ goto end_draw_inner_stroke;
+
+ apply_default_color (colors, inner_stroke_color);
+
+ cairo_save (cr);
+
+ cairo_translate (cr, x, y);
+
+ /* create the path to fill */
+ _gtk_rounded_box_init_rect (&border_box, 0, 0, width, height);
+ _gtk_rounded_box_apply_border_radius (&border_box, engine, state, junction);
+ padding_box = border_box;
+ _gtk_rounded_box_shrink (&padding_box, inner_border->top,
+ inner_border->right,
+ inner_border->bottom,
+ inner_border->left);
+
+ cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+
+ if (inner_stroke_pat)
+ {
+ /* pattern */
+ cairo_scale (cr, width, height);
+ cairo_set_source (cr, inner_stroke_pat);
+ cairo_scale (cr, 1.0 / width, 1.0 / height);
+
+ _gtk_rounded_box_path (&border_box, cr);
+ _gtk_rounded_box_path (&padding_box, cr);
+ cairo_fill (cr);
+ }
+ else if (gdk_rgba_equal (colors[0], colors[1]) &&
+ gdk_rgba_equal (colors[0], colors[2]) &&
+ gdk_rgba_equal (colors[0], colors[3]))
+ {
+ /* one color */
+ gdk_cairo_set_source_rgba (cr, colors[0]);
+
+ _gtk_rounded_box_path (&border_box, cr);
+ _gtk_rounded_box_path (&padding_box, cr);
+ cairo_fill (cr);
+ }
+ else
+ {
+ /* different colors */
+ for (i = 0; i < 4; i++)
+ {
+ if (hidden_side & current_side[i])
+ continue;
+
+ for (j = 0; j < 4; j++)
+ {
+ if (hidden_side & current_side[j])
+ continue;
+
+ if (i == j ||
+ gdk_rgba_equal (colors[i], colors[j]))
+ {
+ /* we were already painted when i == j */
+ if (i > j)
+ break;
+
+ if (j == 0)
+ _gtk_rounded_box_path_top (&border_box, &padding_box, cr);
+ else if (j == 1)
+ _gtk_rounded_box_path_right (&border_box, &padding_box, cr);
+ else if (j == 2)
+ _gtk_rounded_box_path_bottom (&border_box, &padding_box, cr);
+ else if (j == 3)
+ _gtk_rounded_box_path_left (&border_box, &padding_box, cr);
+ }
+ }
+ /* we were already painted when i == j */
+ if (i > j)
+ continue;
+
+ gdk_cairo_set_source_rgba (cr, colors[i]);
+
+ cairo_fill (cr);
+ }
+ }
+
+ cairo_restore (cr);
+
+end_draw_inner_stroke:
+ gtk_border_free (inner_border);
+
+ if (inner_stroke_pat != NULL)
+ cairo_pattern_destroy (inner_stroke_pat);
+
+ gdk_rgba_free (inner_stroke_color);
+
+ for (i = 0; i < 4; i++)
+ gdk_rgba_free (colors[i]);
+}
+
+/* draw the outer stroke */
+static void
+draw_outer_stroke (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ guint hidden_side,
+ GtkJunctionSides junction)
+{
+ GdkRGBA *outer_stroke_color;
+ GdkRGBA *colors[4];
+ GtkBorder *outer_border;
+ GtkRoundedBox border_box, padding_box;
+ GtkStateFlags state;
+ cairo_pattern_t *outer_stroke_pat;
+ static const guint current_side[4] = { SIDE_TOP, SIDE_RIGHT, SIDE_BOTTOM, SIDE_LEFT };
+ guint i, j;
+
+ state = gtk_theming_engine_get_state (engine);
+
+ gtk_theming_engine_get (engine, state,
+ "-tdegtk-outer-stroke-color", &outer_stroke_color,
+ "-tdegtk-outer-stroke-top-color", &colors[0],
+ "-tdegtk-outer-stroke-right-color", &colors[1],
+ "-tdegtk-outer-stroke-bottom-color", &colors[2],
+ "-tdegtk-outer-stroke-left-color", &colors[3],
+ "-tdegtk-outer-stroke-gradient", &outer_stroke_pat,
+ "-tdegtk-outer-stroke-width", &outer_border,
+ NULL);
+
+ hide_border_sides (outer_border, hidden_side);
+
+ if (tdegtk_gtk_border_is_zero (outer_border))
+ goto end_draw_outer_stroke;
+
+ apply_default_color (colors, outer_stroke_color);
+
+ cairo_save (cr);
+
+ cairo_translate (cr, x, y);
+
+ /* create the path to fill */
+ _gtk_rounded_box_init_rect (&border_box, 0, 0, width, height);
+ _gtk_rounded_box_apply_border_radius (&border_box, engine, state, junction);
+ padding_box = border_box;
+ _gtk_rounded_box_shrink (&padding_box, outer_border->top,
+ outer_border->right,
+ outer_border->bottom,
+ outer_border->left);
+
+ cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+
+ if (outer_stroke_pat)
+ {
+ /* pattern */
+ cairo_scale (cr, width, height);
+ cairo_set_source (cr, outer_stroke_pat);
+ cairo_scale (cr, 1.0 / width, 1.0 / height);
+
+ _gtk_rounded_box_path (&border_box, cr);
+ _gtk_rounded_box_path (&padding_box, cr);
+ cairo_fill (cr);
+ }
+ else if (gdk_rgba_equal (colors[0], colors[1]) &&
+ gdk_rgba_equal (colors[0], colors[2]) &&
+ gdk_rgba_equal (colors[0], colors[3]))
+ {
+ /* one color */
+ gdk_cairo_set_source_rgba (cr, colors[0]);
+
+ _gtk_rounded_box_path (&border_box, cr);
+ _gtk_rounded_box_path (&padding_box, cr);
+ cairo_fill (cr);
+ }
+ else
+ {
+ /* different colors */
+ for (i = 0; i < 4; i++)
+ {
+ if (hidden_side & current_side[i])
+ continue;
+
+ for (j = 0; j < 4; j++)
+ {
+ if (hidden_side & current_side[j])
+ continue;
+
+ if (i == j ||
+ gdk_rgba_equal (colors[i], colors[j]))
+ {
+ /* we were already painted when i == j */
+ if (i > j)
+ break;
+
+ if (j == 0)
+ _gtk_rounded_box_path_top (&border_box, &padding_box, cr);
+ else if (j == 1)
+ _gtk_rounded_box_path_right (&border_box, &padding_box, cr);
+ else if (j == 2)
+ _gtk_rounded_box_path_bottom (&border_box, &padding_box, cr);
+ else if (j == 3)
+ _gtk_rounded_box_path_left (&border_box, &padding_box, cr);
+ }
+ }
+ /* we were already painted when i == j */
+ if (i > j)
+ continue;
+
+ gdk_cairo_set_source_rgba (cr, colors[i]);
+
+ cairo_fill (cr);
+ }
+ }
+
+ cairo_restore (cr);
+
+end_draw_outer_stroke:
+ gtk_border_free (outer_border);
+
+ if (outer_stroke_pat != NULL)
+ cairo_pattern_destroy (outer_stroke_pat);
+
+ gdk_rgba_free (outer_stroke_color);
+
+ for (i = 0; i < 4; i++)
+ gdk_rgba_free (colors[i]);
+}
+
+void
+tdegtk_cairo_draw_frame (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ guint hidden_side,
+ GtkJunctionSides junction)
+{
+ GtkBorder border;
+ GtkBorder *outer_border;
+ GtkStateFlags state;
+
+ state = gtk_theming_engine_get_state (engine);
+
+ gtk_theming_engine_get (engine, state,
+ "-tdegtk-outer-stroke-width", &outer_border,
+ NULL);
+ gtk_theming_engine_get_border (engine, state, &border);
+
+ hide_border_sides (&border, hidden_side);
+ hide_border_sides (outer_border, hidden_side);
+
+ if (!tdegtk_gtk_border_is_zero (outer_border))
+ {
+ /* first layer, outer stroke */
+ draw_outer_stroke (engine, cr,
+ x, y,
+ width, height,
+ hidden_side, junction);
+
+ shrink_with_border (outer_border, &x, &y, &width, &height);
+ }
+
+ /* second layer, inner stroke */
+ draw_inner_stroke (engine, cr,
+ x + border.left, y + border.top,
+ width - (border.left + border.right), height - (border.top + border.bottom),
+ hidden_side, junction);
+
+ /* third layer, border */
+ draw_border (engine, cr,
+ x, y,
+ width, height,
+ hidden_side, junction);
+
+ gtk_border_free (outer_border);
+}
+
+gboolean
+tdegtk_cairo_draw_from_texture (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height)
+{
+ GtkStateFlags state;
+ GValue value = { 0, };
+ cairo_pattern_t *texture = NULL;
+ cairo_surface_t *surface = NULL;
+ gboolean retval = FALSE;
+
+ state = gtk_theming_engine_get_state (engine);
+
+ gtk_theming_engine_get_property (engine, "background-image", state, &value);
+
+ if (!G_VALUE_HOLDS_BOXED (&value))
+ return FALSE;
+
+ texture = g_value_dup_boxed (&value);
+ g_value_unset (&value);
+
+ if (texture != NULL)
+ cairo_pattern_get_surface (texture, &surface);
+
+ if (surface != NULL)
+ {
+ cairo_save (cr);
+
+ cairo_scale (cr, width / cairo_image_surface_get_width (surface),
+ height / cairo_image_surface_get_height (surface));
+ cairo_set_source_surface (cr, surface, x, y);
+
+ cairo_paint (cr);
+
+ cairo_restore (cr);
+
+ retval = TRUE;
+ }
+
+ if (texture != NULL)
+ cairo_pattern_destroy (texture);
+
+ return retval;
+}
+
+void
+tdegtk_cairo_round_rect (cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ gint radius,
+ guint sides,
+ GtkJunctionSides junction)
+{
+ radius = CLAMP (radius, 0, MIN (width / 2, height / 2));
+
+ if (sides & SIDE_RIGHT)
+ {
+ if (radius == 0 ||
+ (junction & GTK_JUNCTION_CORNER_TOPRIGHT))
+ cairo_move_to (cr, x + width, y);
+ else
+ {
+ cairo_new_sub_path (cr);
+
+ cairo_arc (cr, x + width - radius, y + radius, radius, - G_PI / 4, 0);
+ }
+
+ if (radius == 0 ||
+ (junction & GTK_JUNCTION_CORNER_BOTTOMRIGHT))
+ cairo_line_to (cr, x + width, y + height);
+ else
+ cairo_arc (cr, x + width - radius, y + height - radius, radius, 0, G_PI / 4);
+ }
+
+ if (sides & SIDE_BOTTOM)
+ {
+ if (radius != 0 &&
+ ! (junction & GTK_JUNCTION_CORNER_BOTTOMRIGHT))
+ {
+ if ((sides & SIDE_RIGHT) == 0)
+ cairo_new_sub_path (cr);
+
+ cairo_arc (cr, x + width - radius, y + height - radius, radius, G_PI / 4, G_PI / 2);
+ }
+ else if ((sides & SIDE_RIGHT) == 0)
+ cairo_move_to (cr, x + width, y + height);
+
+ if (radius == 0 ||
+ (junction & GTK_JUNCTION_CORNER_BOTTOMLEFT))
+ cairo_line_to (cr, x, y + height);
+ else
+ cairo_arc (cr, x + radius, y + height - radius, radius, G_PI / 2, 3 * (G_PI / 4));
+ }
+ else
+ cairo_move_to (cr, x, y + height);
+
+ if (sides & SIDE_LEFT)
+ {
+ if (radius != 0 &&
+ ! (junction & GTK_JUNCTION_CORNER_BOTTOMLEFT))
+ {
+ if ((sides & SIDE_BOTTOM) == 0)
+ cairo_new_sub_path (cr);
+
+ cairo_arc (cr, x + radius, y + height - radius, radius, 3 * (G_PI / 4), G_PI);
+ }
+ else if ((sides & SIDE_BOTTOM) == 0)
+ cairo_move_to (cr, x, y + height);
+
+ if (radius == 0 ||
+ (junction & GTK_JUNCTION_CORNER_TOPLEFT))
+ cairo_line_to (cr, x, y);
+ else
+ cairo_arc (cr, x + radius, y + radius, radius, G_PI, G_PI + G_PI / 4);
+ }
+
+ if (sides & SIDE_TOP)
+ {
+ if (radius != 0 &&
+ ! (junction & GTK_JUNCTION_CORNER_TOPLEFT))
+ {
+ if ((sides & SIDE_LEFT) == 0)
+ cairo_new_sub_path (cr);
+
+ cairo_arc (cr, x + radius, y + radius, radius, 5 * (G_PI / 4), 3 * (G_PI / 2));
+ }
+ else if ((sides & SIDE_LEFT) == 0)
+ cairo_move_to (cr, x, y);
+
+ if (radius == 0 ||
+ (junction & GTK_JUNCTION_CORNER_TOPRIGHT))
+ cairo_line_to (cr, x + width, y);
+ else
+ cairo_arc (cr, x + width - radius, y + radius, radius, 3 * (G_PI / 2), - G_PI / 4);
+ }
+}
+
+void
+tdegtk_cairo_round_rect_inner (cairo_t *cr,
+ gdouble x,
+ gdouble y,
+ gdouble width,
+ gdouble height,
+ gint radius,
+ guint sides,
+ GtkJunctionSides junction)
+{
+ gdouble line_width;
+
+ line_width = cairo_get_line_width (cr);
+
+ /* center the rounded rectangle using line_width */
+ tdegtk_cairo_round_rect (cr, x + line_width / 2.0,
+ y + line_width / 2.0,
+ width - line_width,
+ height - line_width,
+ radius, sides, junction);
+}
+
+void
+tdegtk_cairo_set_source_border (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble width,
+ gdouble height)
+{
+ GdkRGBA border_color;
+ GtkBorderStyle border_style;
+ GtkStateFlags state;
+ cairo_pattern_t *border_pat;
+
+ state = gtk_theming_engine_get_state (engine);
+
+ gtk_theming_engine_get (engine, state,
+ "border-style", &border_style,
+ "-tdegtk-border-gradient", &border_pat,
+ NULL);
+ gtk_theming_engine_get_border_color (engine, state, &border_color);
+
+ if (border_pat)
+ {
+ /* pattern */
+ cairo_scale (cr, width, height);
+ cairo_set_source (cr, border_pat);
+ cairo_scale (cr, 1.0 / width, 1.0 / height);
+ }
+ else
+ /* one color */
+ gdk_cairo_set_source_rgba (cr, &border_color);
+
+ if (border_pat != NULL)
+ cairo_pattern_destroy (border_pat);
+}
+
+void
+tdegtk_cairo_set_source_inner_stroke (GtkThemingEngine *engine,
+ cairo_t *cr,
+ gdouble width,
+ gdouble height)
+{
+ GdkRGBA *inner_stroke_color;
+ GtkStateFlags state;
+ cairo_pattern_t *inner_stroke_pat;
+
+ state = gtk_theming_engine_get_state (engine);
+
+ gtk_theming_engine_get (engine, state,
+ "-tdegtk-inner-stroke-color", &inner_stroke_color,
+ "-tdegtk-inner-stroke-gradient", &inner_stroke_pat,
+ NULL);
+
+ if (inner_stroke_pat)
+ {
+ /* pattern */
+ cairo_scale (cr, width, height);
+ cairo_set_source (cr, inner_stroke_pat);
+ cairo_scale (cr, 1.0 / width, 1.0 / height);
+ }
+ else
+ /* one color */
+ gdk_cairo_set_source_rgba (cr, inner_stroke_color);
+
+ if (inner_stroke_pat != NULL)
+ cairo_pattern_destroy (inner_stroke_pat);
+
+ gdk_rgba_free (inner_stroke_color);
+}