/* * sato-draw.c * This file is part of sato-engine * * Copyright (C) 2006,2007 - OpenedHand 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., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "config.h" #ifndef ENABLE_CAIRO #include "sato-draw.h" #include "sato-style.h" #include #include #include #define DEBUG(func) // g_printf ("%s: detail = '%s'; state = %d; x:%d; y:%d; w:%d; h:%d;\n", func, detail, state_type, x, y, width, height); #define DETAIL(foo) (detail && strcmp (foo, detail) == 0) GtkStyleClass *parent_style_class; typedef enum { SATO_CORNER_TOP_LEFT, SATO_CORNER_TOP_RIGHT, SATO_CORNER_BOTTOM_LEFT, SATO_CORNER_BOTTOM_RIGHT } SatoCorner; /** * Prepare a new GC with the additional Sato style values */ static GdkGC* sato_gc_new (GdkGC* old_gc, GdkDrawable *d) { GdkGC *new_gc; new_gc = gdk_gc_new (d); gdk_gc_copy (new_gc, old_gc); gdk_gc_set_line_attributes (new_gc, 2, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER); return new_gc; } static void sato_corner (GdkWindow *window, GdkGC *gc, gint x, gint y, SatoCorner corner) { GdkColor aa; GdkGCValues gc_values; gdk_gc_get_values (gc, &gc_values); gdk_colormap_query_color (gdk_drawable_get_colormap (window), gc_values.foreground.pixel, &gc_values.foreground); /* Brighten the colour for fake AA. Need a better way to do this */ aa.red = MIN (65535, gc_values.foreground.red * 1.8); aa.blue = MIN (65535, gc_values.foreground.blue * 1.8); aa.green = MIN (65535, gc_values.foreground.green * 1.8); switch (corner) { case SATO_CORNER_TOP_LEFT: gdk_draw_point (window, gc, x + 1, y + 1); gdk_draw_point (window, gc, x + 1, y + 2); gdk_draw_point (window, gc, x + 2, y + 1); gdk_draw_point (window, gc, x + 2, y + 2); gdk_gc_set_rgb_fg_color (gc, &aa); gdk_draw_point (window, gc, x + 2, y); gdk_draw_point (window, gc, x, y + 2); break; case SATO_CORNER_BOTTOM_LEFT: gdk_gc_set_rgb_fg_color (gc, &gc_values.foreground); gdk_draw_point (window, gc, x + 1, y - 3); gdk_draw_point (window, gc, x + 1, y - 2); gdk_draw_point (window, gc, x + 2, y - 2); gdk_draw_point (window, gc, x + 2, y - 3); gdk_gc_set_rgb_fg_color (gc, &aa); gdk_draw_point (window, gc, x + 2, y - 1); gdk_draw_point (window, gc, x, y - 3); break; case SATO_CORNER_TOP_RIGHT: gdk_gc_set_rgb_fg_color (gc, &gc_values.foreground); gdk_draw_point (window, gc, x - 3, y + 1); gdk_draw_point (window, gc, x - 2, y + 1); gdk_draw_point (window, gc, x - 2, y + 2); gdk_draw_point (window, gc, x - 3, y + 2); gdk_gc_set_rgb_fg_color (gc, &aa); gdk_draw_point (window, gc, x - 3, y); gdk_draw_point (window, gc, x - 1, y + 2); break; case SATO_CORNER_BOTTOM_RIGHT: gdk_gc_set_rgb_fg_color (gc, &gc_values.foreground); gdk_draw_point (window, gc, x - 2, y - 3); gdk_draw_point (window, gc, x - 2, y - 2); gdk_draw_point (window, gc, x - 3, y - 2); gdk_draw_point (window, gc, x - 3, y - 3); gdk_gc_set_rgb_fg_color (gc, &aa); gdk_draw_point (window, gc, x - 3, y - 1); gdk_draw_point (window, gc, x- 1, y - 3); break; } /* reset colour */ gdk_gc_set_rgb_fg_color (gc, &gc_values.foreground); } static void sato_rounded_rectangle (GdkWindow * window, GdkGC * gc, gint x, gint y, gint width, gint height) { /** draw corners **/ sato_corner (window, gc, x, y, SATO_CORNER_TOP_LEFT); sato_corner (window, gc, x, y + height, SATO_CORNER_BOTTOM_LEFT); sato_corner (window, gc, x + width, y, SATO_CORNER_TOP_RIGHT); sato_corner (window, gc, x+ width, y + height, SATO_CORNER_BOTTOM_RIGHT); /** draw lines **/ /* top and bottom */ y++; height++; gdk_draw_line (window, gc, x + 3, y, x + width - 3, y); gdk_draw_line (window, gc, x + 3, y + height - 3, x + width - 3, y + height - 3); y--; height--; /* left and right */ x++; width++; gdk_draw_line (window, gc, x, y + 3, x, y + height - 3); gdk_draw_line (window, gc, x + width - 3, y + 3, x + width - 3, y + height - 3); } static void sato_gradient (GtkStyle * style, GdkWindow * window, GtkStateType state_type, gint x, gint y, gint width, gint height) { gint i, rd, gd, bd; /* rd, gd, bd - change in r g and b for gradient */ GdkColor start_color, end_color; GdkGC *gc; gc = gdk_gc_new (window); /* get the start and end colours */ if (state_type == GTK_STATE_ACTIVE) { start_color = style->bg[state_type]; sato_shade_colour (&style->bg[state_type], &end_color, 1.15); } else { sato_shade_colour (&style->bg[state_type], &start_color, 1.15); end_color = style->bg[state_type]; } /* set line for 1px */ gdk_gc_set_line_attributes (gc, 1, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_MITER); /* calculate the delta values */ rd = (start_color.red - end_color.red) / MAX (height, 1); gd = (start_color.green - end_color.green) / MAX (height, 1); bd = (start_color.blue - end_color.blue) / MAX (height, 1); i = -1; while (++i < height) { gdk_gc_set_rgb_fg_color (gc, &start_color); gdk_draw_line (window, gc, x, y + i, x + width, y + i); start_color.red -= rd; start_color.blue -= bd; start_color.green -= gd; } /* round off the gradient corners */ gdk_draw_point (window, style->bg_gc[state_type], x, y); gdk_draw_point (window, style->bg_gc[state_type], x, y + height - 1); gdk_draw_point (window, style->bg_gc[state_type], x + width - 1, y + height - 1); gdk_draw_point (window, style->bg_gc[state_type], x + width - 1, y); } static void sato_draw_box (DRAW_ARGS) { GdkGC *gc; gboolean has_focus; DEBUG ("draw_box"); SANITIZE_SIZE; if (DETAIL ("paned") || DETAIL ("menubar") || DETAIL ("toolbar")) return; has_focus = (widget && GTK_WIDGET_HAS_FOCUS (widget)); /*** spin buttons ***/ if (DETAIL ("spinbutton_down") || DETAIL ("spinbutton_up")) return; if (DETAIL ("spinbutton")) { /* FIXME: for RTL */ width += 10; x -= 10; } /*** combo boxes ***/ if (DETAIL ("button") && widget && GTK_IS_COMBO_BOX_ENTRY (widget->parent)) { GtkWidget *entry; entry = g_object_get_data (G_OBJECT (widget->parent), "sato-combo-entry"); if (GTK_IS_ENTRY (entry)) { gtk_widget_queue_draw_area (entry, entry->allocation.x, entry->allocation.y, entry->allocation.width,entry->allocation.height); if (!has_focus) has_focus = GTK_WIDGET_HAS_FOCUS (entry); } g_object_set_data (G_OBJECT (widget->parent), "sato-combo-button", widget); /* FIXME: RTL */ width += 10; x -= 10; } if (has_focus) gc = sato_gc_new (style->bg_gc [GTK_STATE_SELECTED], window); else gc = sato_gc_new (style->text_gc[state_type], window); /*** draw the gradient ***/ if (!DETAIL ("menu") && !DETAIL ("trough")) sato_gradient (style, window, state_type, x+1, y+1, width-2, height-2); /*** treeview headers ***/ if (widget && GTK_IS_TREE_VIEW (widget->parent)) { goto exit; } if (DETAIL ("trough")) { if (widget && GTK_IS_HSCALE (widget)) { gdk_draw_line (window, gc, x, y + height / 2, x + width, y + height / 2); goto exit; } else if (widget && GTK_IS_VSCALE (widget)) { gdk_draw_line (window, gc, x + width / 2, y, x + width / 2, y + height); goto exit; } else gdk_draw_rectangle (window, style->base_gc[state_type], TRUE, x, y, width, height); } /*** draw the border ***/ if (!DETAIL ("bar")) { sato_rounded_rectangle (window, gc, x, y, width, height); } exit: g_object_unref (gc); } static void sato_draw_shadow (DRAW_ARGS) { GdkGC *gc; DEBUG ("draw_shadow"); if (shadow_type == GTK_SHADOW_NONE) return; SANITIZE_SIZE; /* FIXME: for RTL */ if (widget && (GTK_IS_SPIN_BUTTON (widget) || GTK_IS_COMBO_BOX_ENTRY (widget->parent))) width += 10; if (widget && GTK_IS_COMBO_BOX_ENTRY (widget->parent)) { GtkWidget *button; g_object_set_data (G_OBJECT (widget->parent), "sato-combo-entry", widget); button = g_object_get_data (G_OBJECT (widget->parent), "sato-combo-button"); if (GTK_IS_BUTTON (button)) gtk_widget_queue_draw_area (button, button->allocation.x, button->allocation.y, button->allocation.width,button->allocation.height); } /* draw a hilight shadow on focused widgets (i.e. entry widgets) */ if (widget && GTK_WIDGET_HAS_FOCUS (widget)) gc = sato_gc_new (style->base_gc[GTK_STATE_SELECTED], window); else gc = sato_gc_new (style->text_gc[state_type], window); sato_rounded_rectangle (window, gc, x, y, width, height); g_object_unref (gc); } static void sato_draw_focus (GtkStyle *style, GdkWindow *window, GtkStateType state_type, GdkRectangle *area, GtkWidget *widget, const gchar *detail, gint x, gint y, gint width, gint height) { GdkGC *gc; DEBUG ("draw_focus"); gc = sato_gc_new (style->fg_gc[state_type], window); sato_rounded_rectangle (window, gc, x, y, width, height); g_object_unref (gc); } static void sato_draw_check (GtkStyle * style, GdkWindow * window, GtkStateType state_type, GtkShadowType shadow_type, GdkRectangle * area, GtkWidget * widget, const gchar * detail, gint x, gint y, gint width, gint height) { GdkGC *gc; DEBUG ("draw_check"); gc = sato_gc_new (style->text_gc[state_type], window); /* clear the background */ gdk_draw_rectangle (window, style->base_gc[GTK_STATE_NORMAL], TRUE, x+1, y+1, width-2, height-2); if (shadow_type == GTK_SHADOW_IN) { GdkGC *mark_gc; mark_gc = sato_gc_new (style->base_gc[GTK_STATE_SELECTED], window); gdk_draw_rectangle (window, mark_gc, TRUE, x + 5, y + 5, width - 10, height - 10); g_object_unref (mark_gc); } sato_rounded_rectangle (window, gc, x, y, width, height); g_object_unref (gc); } static void sato_draw_option (GtkStyle * style, GdkWindow * window, GtkStateType state_type, GtkShadowType shadow_type, GdkRectangle * area, GtkWidget * widget, const gchar * detail, gint x, gint y, gint width, gint height) { GdkGC *gc; DEBUG ("draw_option"); gc = sato_gc_new (style->text_gc[state_type], window); /* clear the background */ gdk_draw_arc (window, style->base_gc[GTK_STATE_NORMAL], TRUE, x+1, y+1, width-2, height-2, 0, 360 * 64); if (shadow_type == GTK_SHADOW_IN) { GdkGC *mark_gc; mark_gc = sato_gc_new (style->base_gc[GTK_STATE_SELECTED], window); gdk_draw_arc (window, mark_gc, TRUE, x + 5, y + 5, width - 10, height - 10, 0, 360 * 64); g_object_unref (mark_gc); } gdk_draw_arc (window, gc, FALSE, x, y, width, height, 0, 360 * 64); g_object_unref (gc); } static void sato_draw_box_gap (GtkStyle * style, GdkWindow * window, GtkStateType state_type, GtkShadowType shadow_type, GdkRectangle * area, GtkWidget * widget, const gchar * detail, gint x, gint y, gint width, gint height, GtkPositionType gap_side, gint gap_x, gint gap_width) { GdkGC *gc; GdkRectangle rect; if (shadow_type == GTK_SHADOW_NONE) return; gc = sato_gc_new (style->fg_gc [state_type], window); /* start off with a rectangle... */ gdk_draw_rectangle (window, gc, FALSE, x + 1, y + 1, width - 2, height - 2); /* this stuff draws the corners as long as they don't overlap with a gap */ if (!((gap_side == GTK_POS_TOP || gap_side == GTK_POS_LEFT) && gap_x < 4)) { gtk_style_apply_default_background (style, window, TRUE, state_type, area, x, y, 3, 3); sato_corner (window, gc, x, y, SATO_CORNER_TOP_LEFT); } if (!((gap_side == GTK_POS_TOP && gap_x > width - 4) || (gap_side == GTK_POS_RIGHT && gap_x < 4))) { gtk_style_apply_default_background (style, window, TRUE, state_type, area, x + width - 3, y, 3, 3); sato_corner (window, gc, x + width, y, SATO_CORNER_TOP_RIGHT); } if (!((gap_side == GTK_POS_BOTTOM && gap_x > width - 4) || (gap_side == GTK_POS_RIGHT && gap_x > height - 4))) { gtk_style_apply_default_background (style, window, TRUE, state_type, area, x + width - 3, y + height - 3, 3, 3); sato_corner (window, gc, x + width, y + height, SATO_CORNER_BOTTOM_RIGHT); } if (!((gap_side == GTK_POS_BOTTOM && gap_x < 4) || (gap_side == GTK_POS_LEFT && gap_x > height - 4))) { gtk_style_apply_default_background (style, window, TRUE, state_type, area, x, y + height - 3, 3, 3); sato_corner (window, gc, x, y + height, SATO_CORNER_BOTTOM_LEFT); } switch (gap_side) { case GTK_POS_TOP: rect.x = x + gap_x; rect.y = y; rect.width = gap_width; rect.height = 2; break; case GTK_POS_BOTTOM: rect.x = x + gap_x; rect.y = y + height - 2; rect.width = gap_width; rect.height = 2; break; case GTK_POS_LEFT: rect.x = x; rect.y = y + gap_x; rect.width = 2; rect.height = gap_width; break; case GTK_POS_RIGHT: rect.x = x + width - 2; rect.y = y + gap_x; rect.width = 2; rect.height = gap_width; break; } /* and finally blank out the gap */ gtk_style_apply_default_background (style, window, TRUE, state_type, area, rect.x, rect.y, rect.width, rect.height); g_object_unref (gc); } static void sato_draw_extension (GtkStyle * style, GdkWindow * window, GtkStateType state_type, GtkShadowType shadow_type, GdkRectangle * area, GtkWidget * widget,const gchar * detail, gint x, gint y, gint width, gint height, GtkPositionType gap_side) { GdkGC *gc; gc = sato_gc_new (style->text_gc[state_type], window); switch (gap_side) { case GTK_POS_TOP: /* bottom tab */ sato_corner (window, gc, x, y + height, SATO_CORNER_BOTTOM_LEFT); sato_corner (window, gc, x + width, y + height, SATO_CORNER_BOTTOM_RIGHT); y -= 5; height += 5; break; case GTK_POS_BOTTOM: /* top tab */ sato_corner (window, gc, x, y, SATO_CORNER_TOP_LEFT); sato_corner (window, gc, x + width, y, SATO_CORNER_TOP_RIGHT); height += 5; break; case GTK_POS_LEFT: /* right tab */ sato_corner (window, gc, x + width, y, SATO_CORNER_TOP_RIGHT); sato_corner (window, gc, x + width, y + height, SATO_CORNER_BOTTOM_RIGHT); x -= 5; width += 5; break; case GTK_POS_RIGHT: /* left tab */ sato_corner (window, gc, x, y, SATO_CORNER_TOP_LEFT); sato_corner (window, gc, x, y + height, SATO_CORNER_BOTTOM_LEFT); width += 5; break; } if (state_type == GTK_STATE_NORMAL) sato_gradient (style, window, state_type, x + 4, y + 4, width - 8, height - 8); /* top and bottom */ y++; height++; if (gap_side != GTK_POS_TOP) gdk_draw_line (window, gc, x + 3, y, x + width - 3, y); if (gap_side != GTK_POS_BOTTOM) gdk_draw_line (window, gc, x + 3, y + height - 3, x + width - 3, y + height - 3); y--; height--; /* left and right */ x++; width++; if (gap_side != GTK_POS_LEFT) gdk_draw_line (window, gc, x, y + 3, x, y + height - 3); if (gap_side != GTK_POS_RIGHT) gdk_draw_line (window, gc, x + width - 3, y + 3, x + width - 3, y + height - 3); g_object_unref (gc); } static void sato_draw_vline (GtkStyle *style, GdkWindow *window, GtkStateType state_type, GdkRectangle *area, GtkWidget *widget, const gchar *detail, gint y1, gint y2, gint x) { GdkGC *line_gc; line_gc = sato_gc_new (style->text_gc[state_type], window); if (DETAIL ("vscale") || DETAIL ("hscale")) return; gdk_draw_line (window, line_gc, x, y1, x, y2); g_object_unref (line_gc); } static void sato_draw_hline (GtkStyle *style, GdkWindow *window, GtkStateType state_type, GdkRectangle *area,GtkWidget *widget, const gchar *detail, gint x1, gint x2, gint y) { GdkGC *line_gc; line_gc = sato_gc_new (style->text_gc[state_type], window); if (DETAIL ("vscale") || DETAIL ("hscale")) return; gdk_draw_line (window, line_gc, x1, y, x2, y); g_object_unref (line_gc); } void sato_draw_style_class_init (GtkStyleClass * style_class) { parent_style_class = g_type_class_peek_parent (style_class); style_class->draw_shadow = sato_draw_shadow; style_class->draw_box = sato_draw_box; style_class->draw_check = sato_draw_check; style_class->draw_option = sato_draw_option; style_class->draw_box_gap = sato_draw_box_gap; style_class->draw_shadow_gap = sato_draw_box_gap; style_class->draw_extension = sato_draw_extension; style_class->draw_hline = sato_draw_hline; style_class->draw_vline = sato_draw_vline; style_class->draw_focus = sato_draw_focus; /* style_class->draw_arrow = draw_arrow; style_class->draw_check = draw_check; style_class->draw_option = draw_option; style_class->draw_tab = draw_tab; style_class->draw_shadow_gap = draw_shadow_gap; style_class->draw_box_gap = draw_box_gap; style_class->draw_extension = draw_extension; style_class->draw_focus = draw_focus; style_class->draw_slider = draw_slider; style_class->draw_handle = draw_handle; style_class->draw_layout = draw_layout; */ } #endif /* not ENABLE_CAIRO */