/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * e-cell-toggle.c - Multi-state image toggle cell object. * Copyright 1999, 2000, Ximian, Inc. * * Authors: * Miguel de Icaza * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License, version 2, as published by the Free Software Foundation. * * 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library 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 #include #include #include #include #include #include #include "e-cell-toggle.h" #include "gal/util/e-util.h" #include "gal/widgets/e-hsv-utils.h" #include "e-table-item.h" #define PARENT_TYPE e_cell_get_type () typedef struct { ECellView cell_view; GdkGC *gc; GnomeCanvas *canvas; GdkPixmap **pixmap_cache; } ECellToggleView; static ECellClass *parent_class; #define CACHE_SEQ_COUNT 6 /* * ECell::realize method */ static ECellView * etog_new_view (ECell *ecell, ETableModel *table_model, void *e_table_item_view) { ECellToggleView *toggle_view = g_new0 (ECellToggleView, 1); ETableItem *eti = E_TABLE_ITEM (e_table_item_view); GnomeCanvas *canvas = GNOME_CANVAS_ITEM (eti)->canvas; ECellToggle *etog = E_CELL_TOGGLE (ecell); int i; toggle_view->cell_view.ecell = ecell; toggle_view->cell_view.e_table_model = table_model; toggle_view->cell_view.e_table_item_view = e_table_item_view; toggle_view->canvas = canvas; toggle_view->pixmap_cache = g_new (GdkPixmap *, etog->n_states * CACHE_SEQ_COUNT); for (i = 0; i < etog->n_states * CACHE_SEQ_COUNT; i++) toggle_view->pixmap_cache[i] = NULL; return (ECellView *) toggle_view; } static void etog_kill_view (ECellView *ecell_view) { ECellToggle *etog = E_CELL_TOGGLE (ecell_view->ecell); ECellToggleView *toggle_view = (ECellToggleView *) ecell_view; int i; for (i = 0; i < etog->n_states * CACHE_SEQ_COUNT; i++) if (toggle_view->pixmap_cache[i]) gdk_pixmap_unref (toggle_view->pixmap_cache[i]); g_free (ecell_view); } static void etog_realize (ECellView *ecell_view) { ECellToggleView *toggle_view = (ECellToggleView *) ecell_view; toggle_view->gc = gdk_gc_new (GTK_WIDGET (toggle_view->canvas)->window); } /* * ECell::unrealize method */ static void etog_unrealize (ECellView *ecv) { ECellToggleView *toggle_view = (ECellToggleView *) ecv; gdk_gc_unref (toggle_view->gc); toggle_view->gc = NULL; } #define PIXMAP_CACHE(toggle_view, cache_seq, image_seq) ((toggle_view)->pixmap_cache[(cache_seq) * E_CELL_TOGGLE (((ECellView *) (toggle_view))->ecell)->n_states + (image_seq)]) #define RGB_COLOR(color) (((color).red & 0xff00) << 8 | \ ((color).green & 0xff00) | \ ((color).blue & 0xff00) >> 8) static void check_cache (ECellToggleView *toggle_view, int image_seq, int cache_seq) { ECellView *ecell_view = (ECellView *) toggle_view; ECellToggle *etog = E_CELL_TOGGLE (ecell_view->ecell); if (PIXMAP_CACHE (toggle_view, cache_seq, image_seq) == NULL) { GdkPixbuf *image = etog->images[image_seq]; GdkPixbuf *flat; guint32 color = 0xffffff; int width = gdk_pixbuf_get_width (image); int height = gdk_pixbuf_get_height (image); PIXMAP_CACHE (toggle_view, cache_seq, image_seq) = gdk_pixmap_new (toggle_view->canvas->layout.bin_window, width, height, gtk_widget_get_visual (GTK_WIDGET (toggle_view->canvas))->depth); switch (cache_seq % 3) { case 0: color = RGB_COLOR (GTK_WIDGET (toggle_view->canvas)->style->bg [GTK_STATE_SELECTED]); break; case 1: color = RGB_COLOR (GTK_WIDGET (toggle_view->canvas)->style->bg [GTK_STATE_ACTIVE]); break; case 2: color = RGB_COLOR (GTK_WIDGET (toggle_view->canvas)->style->base [GTK_STATE_NORMAL]); break; } if (cache_seq >= 3) { double r, g, b, h, s, v; r = ((color >> 16) & 0xff) / 255.0f; g = ((color >> 8) & 0xff) / 255.0f; b = (color & 0xff) / 255.0f; e_rgb_to_hsv (r, g, b, &h, &s, &v); if (v - 0.05f < 0) { v += 0.05f; } else { v -= 0.05f; } e_hsv_to_rgb (h, s, v, &r, &g, &b); color = ((((int)(r * 255.0f)) & 0xff) << 16) + ((((int)(g * 255.0f)) & 0xff) << 8) + (((int)(b * 255.0f)) & 0xff); } flat = gdk_pixbuf_composite_color_simple (image, width, height, GDK_INTERP_BILINEAR, 255, 1, color, color); gdk_pixbuf_render_to_drawable (flat, PIXMAP_CACHE (toggle_view, cache_seq, image_seq), toggle_view->gc, 0, 0, 0, 0, width, height, GDK_RGB_DITHER_NORMAL, 0, 0); gdk_pixbuf_unref (flat); } } /* * ECell::draw method */ static void etog_draw (ECellView *ecell_view, GdkDrawable *drawable, int model_col, int view_col, int row, ECellFlags flags, int x1, int y1, int x2, int y2) { ECellToggle *toggle = E_CELL_TOGGLE (ecell_view->ecell); gboolean selected; ECellToggleView *toggle_view = (ECellToggleView *) ecell_view; GdkPixmap *pixmap; GdkPixbuf *image; int x, y, width, height; int cache_seq; const int value = GPOINTER_TO_INT ( e_table_model_value_at (ecell_view->e_table_model, model_col, row)); selected = flags & E_CELL_SELECTED; if (value >= toggle->n_states){ g_warning ("Value from the table model is %d, the states we support are [0..%d)\n", value, toggle->n_states); return; } if (flags & E_CELL_SELECTED) { if (GTK_WIDGET_HAS_FOCUS (toggle_view->canvas)) cache_seq = 0; else cache_seq = 1; } else cache_seq = 2; if (E_TABLE_ITEM (ecell_view->e_table_item_view)->alternating_row_colors && (row % 2) == 0) cache_seq += 3; check_cache (toggle_view, value, cache_seq); pixmap = PIXMAP_CACHE (toggle_view, cache_seq, value); image = toggle->images[value]; if ((x2 - x1) < gdk_pixbuf_get_width (image)){ x = x1; width = x2 - x1; } else { x = x1 + ((x2 - x1) - gdk_pixbuf_get_width (image)) / 2; width = gdk_pixbuf_get_width (image); } if ((y2 - y1) < gdk_pixbuf_get_height (image)){ y = y1; height = y2 - y1; } else { y = y1 + ((y2 - y1) - gdk_pixbuf_get_height (image)) / 2; height = gdk_pixbuf_get_height (image); } gdk_draw_pixmap (drawable, toggle_view->gc, pixmap, 0, 0, x, y, width, height); } static void etog_set_value (ECellToggleView *toggle_view, int model_col, int view_col, int row, int value) { ECell *ecell = toggle_view->cell_view.ecell; ECellToggle *toggle = E_CELL_TOGGLE (ecell); if (value >= toggle->n_states) value = 0; e_table_model_set_value_at (toggle_view->cell_view.e_table_model, model_col, row, GINT_TO_POINTER (value)); } /* * ECell::event method */ static gint etog_event (ECellView *ecell_view, GdkEvent *event, int model_col, int view_col, int row, ECellFlags flags, ECellActions *actions) { ECellToggleView *toggle_view = (ECellToggleView *) ecell_view; void *_value = e_table_model_value_at (ecell_view->e_table_model, model_col, row); const int value = GPOINTER_TO_INT (_value); #if 0 if (!(flags & E_CELL_EDITING)) return FALSE; #endif switch (event->type){ case GDK_KEY_PRESS: if (event->key.keyval != GDK_space) return FALSE; /* Fall through */ case GDK_BUTTON_PRESS: if (!e_table_model_is_cell_editable(ecell_view->e_table_model, model_col, row)) return FALSE; etog_set_value (toggle_view, model_col, view_col, row, value + 1); return TRUE; default: return FALSE; } return TRUE; } /* * ECell::height method */ static int etog_height (ECellView *ecell_view, int model_col, int view_col, int row) { ECellToggle *toggle = E_CELL_TOGGLE (ecell_view->ecell); return toggle->height; } /* * ECell::print method */ static void etog_print (ECellView *ecell_view, GnomePrintContext *context, int model_col, int view_col, int row, double width, double height) { ECellToggle *toggle = E_CELL_TOGGLE(ecell_view->ecell); GdkPixbuf *image; int scale; const int value = GPOINTER_TO_INT ( e_table_model_value_at (ecell_view->e_table_model, model_col, row)); if (value >= toggle->n_states){ g_warning ("Value from the table model is %d, the states we support are [0..%d)\n", value, toggle->n_states); return; } gnome_print_gsave(context); if (gnome_print_moveto(context, 2, 2) == -1) /* FIXME */; if (gnome_print_lineto(context, width - 2, 2) == -1) /* FIXME */; if (gnome_print_lineto(context, width - 2, height - 2) == -1) /* FIXME */; if (gnome_print_lineto(context, 2, height - 2) == -1) /* FIXME */; if (gnome_print_lineto(context, 2, 2) == -1) /* FIXME */; if (gnome_print_clip(context) == -1) /* FIXME */; image = toggle->images[value]; scale = MIN (width - 4, height - 4); gnome_print_translate (context, 2, (height - scale) / 2); gnome_print_scale (context, scale, scale); gnome_print_pixbuf (context, image); gnome_print_grestore(context); } static gdouble etog_print_height (ECellView *ecell_view, GnomePrintContext *context, int model_col, int view_col, int row, double width) { ECellToggle *toggle = E_CELL_TOGGLE (ecell_view->ecell); return toggle->height; } /* * ECell::max_width method */ static int etog_max_width (ECellView *ecell_view, int model_col, int view_col) { ECellToggle *toggle = E_CELL_TOGGLE (ecell_view->ecell); int max_width = 0; int number_of_rows; int row; number_of_rows = e_table_model_row_count (ecell_view->e_table_model); for (row = 0; row < number_of_rows; row++) { void *value = e_table_model_value_at (ecell_view->e_table_model, model_col, row); max_width = MAX (max_width, gdk_pixbuf_get_width (toggle->images[GPOINTER_TO_INT (value)])); } return max_width; } static void etog_style_set (ECellView *ecell_view, GtkStyle *previous_style) { ECellToggle *toggle = E_CELL_TOGGLE (ecell_view->ecell); ECellToggleView *toggle_view = (ECellToggleView *) ecell_view; int i; for (i = 0; i < toggle->n_states * CACHE_SEQ_COUNT; i++) { if (toggle_view->pixmap_cache[i]) { gdk_pixmap_unref (toggle_view->pixmap_cache[i]); toggle_view->pixmap_cache[i] = NULL; } } } static void etog_destroy (GtkObject *object) { ECellToggle *etog = E_CELL_TOGGLE (object); int i; for (i = 0; i < etog->n_states; i++) gdk_pixbuf_unref (etog->images [i]); g_free (etog->images); etog->images = NULL; etog->n_states = 0; GTK_OBJECT_CLASS (parent_class)->destroy (object); } static void e_cell_toggle_class_init (GtkObjectClass *object_class) { ECellClass *ecc = (ECellClass *) object_class; object_class->destroy = etog_destroy; ecc->new_view = etog_new_view; ecc->kill_view = etog_kill_view; ecc->realize = etog_realize; ecc->unrealize = etog_unrealize; ecc->draw = etog_draw; ecc->event = etog_event; ecc->height = etog_height; ecc->print = etog_print; ecc->print_height = etog_print_height; ecc->max_width = etog_max_width; ecc->style_set = etog_style_set; parent_class = gtk_type_class (PARENT_TYPE); } static void e_cell_toggle_init (GtkObject *object) { ECellToggle *etog = (ECellToggle *) object; etog->images = NULL; etog->n_states = 0; } E_MAKE_TYPE(e_cell_toggle, "ECellToggle", ECellToggle, e_cell_toggle_class_init, e_cell_toggle_init, PARENT_TYPE); /** * e_cell_toggle_construct: * @etog: a fresh ECellToggle object * @border: number of pixels used as a border * @n_states: number of states the toggle will have * @images: a collection of @n_states images, one for each state. * * Constructs the @etog object with the @border, @n_staes, and @images * arguments. */ void e_cell_toggle_construct (ECellToggle *etog, int border, int n_states, GdkPixbuf **images) { int max_height = 0; int i; etog->border = border; etog->n_states = n_states; etog->images = g_new (GdkPixbuf *, n_states); for (i = 0; i < n_states; i++){ etog->images [i] = images [i]; gdk_pixbuf_ref (images [i]); if (gdk_pixbuf_get_height (images [i]) > max_height) max_height = gdk_pixbuf_get_height (images [i]); } etog->height = max_height; } /** * e_cell_checkbox_new: * @border: number of pixels used as a border * @n_states: number of states the toggle will have * @images: a collection of @n_states images, one for each state. * * Creates a new ECell renderer that can be used to render toggle * buttons with the images specified in @images. The value returned * by ETableModel::get_value is typecase into an integer and clamped * to the [0..n_states) range. That will select the image rendered. * * Returns: an ECell object that can be used to render multi-state * toggle cells. */ ECell * e_cell_toggle_new (int border, int n_states, GdkPixbuf **images) { ECellToggle *etog = gtk_type_new (e_cell_toggle_get_type ()); e_cell_toggle_construct (etog, border, n_states, images); return (ECell *) etog; }