evolution-3.6.4/widgets/table/e-cell-toggle.c

No issues found

  1 /*
  2  * e-cell-toggle.c - Multi-state image toggle cell object.
  3  *
  4  * This program 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) version 3.
  8  *
  9  * This program 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 the program; if not, see <http://www.gnu.org/licenses/>
 16  *
 17  *
 18  * Authors:
 19  *		Miguel de Icaza <miguel@ximian.com>
 20  *
 21  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 22  *
 23  */
 24 
 25 #ifdef HAVE_CONFIG_H
 26 #include <config.h>
 27 #endif
 28 
 29 #include <gdk/gdkkeysyms.h>
 30 #include <gtk/gtk.h>
 31 #include <libgnomecanvas/libgnomecanvas.h>
 32 
 33 #include "art/empty.xpm"
 34 
 35 #include "gal-a11y-e-cell-toggle.h"
 36 #include "gal-a11y-e-cell-registry.h"
 37 #include "e-util/e-util.h"
 38 
 39 #include "e-cell-toggle.h"
 40 #include "e-table-item.h"
 41 
 42 #define E_CELL_TOGGLE_GET_PRIVATE(obj) \
 43 	(G_TYPE_INSTANCE_GET_PRIVATE \
 44 	((obj), E_TYPE_CELL_TOGGLE, ECellTogglePrivate))
 45 
 46 struct _ECellTogglePrivate {
 47 	gchar **icon_names;
 48 	guint n_icon_names;
 49 
 50 	GdkPixbuf *empty;
 51 	GPtrArray *pixbufs;
 52 	gint height;
 53 };
 54 
 55 G_DEFINE_TYPE (ECellToggle, e_cell_toggle, E_TYPE_CELL)
 56 
 57 typedef struct {
 58 	ECellView cell_view;
 59 	GnomeCanvas *canvas;
 60 } ECellToggleView;
 61 
 62 static void
 63 cell_toggle_load_icons (ECellToggle *cell_toggle)
 64 {
 65 	GtkIconTheme *icon_theme;
 66 	gint width, height;
 67 	gint max_height = 0;
 68 	guint ii;
 69 	GError *error = NULL;
 70 
 71 	icon_theme = gtk_icon_theme_get_default ();
 72 	gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &width, &height);
 73 
 74 	g_ptr_array_set_size (cell_toggle->priv->pixbufs, 0);
 75 
 76 	for (ii = 0; ii < cell_toggle->priv->n_icon_names; ii++) {
 77 		const gchar *icon_name = cell_toggle->priv->icon_names[ii];
 78 		GdkPixbuf *pixbuf = NULL;
 79 
 80 		if (icon_name != NULL)
 81 			pixbuf = gtk_icon_theme_load_icon (
 82 				icon_theme, icon_name, height, 0, &error);
 83 
 84 		if (error != NULL) {
 85 			g_warning ("%s", error->message);
 86 			g_clear_error (&error);
 87 		}
 88 
 89 		if (pixbuf == NULL)
 90 			pixbuf = g_object_ref (cell_toggle->priv->empty);
 91 
 92 		g_ptr_array_add (cell_toggle->priv->pixbufs, pixbuf);
 93 		max_height = MAX (max_height, gdk_pixbuf_get_height (pixbuf));
 94 	}
 95 
 96 	cell_toggle->priv->height = max_height;
 97 }
 98 
 99 static void
100 cell_toggle_dispose (GObject *object)
101 {
102 	ECellTogglePrivate *priv;
103 
104 	priv = E_CELL_TOGGLE_GET_PRIVATE (object);
105 
106 	if (priv->empty != NULL) {
107 		g_object_unref (priv->empty);
108 		priv->empty = NULL;
109 	}
110 
111 	/* This unrefs all the elements. */
112 	g_ptr_array_set_size (priv->pixbufs, 0);
113 
114 	/* Chain up to parent's dispose() method. */
115 	G_OBJECT_CLASS (e_cell_toggle_parent_class)->dispose (object);
116 }
117 
118 static void
119 cell_toggle_finalize (GObject *object)
120 {
121 	ECellTogglePrivate *priv;
122 	guint ii;
123 
124 	priv = E_CELL_TOGGLE_GET_PRIVATE (object);
125 
126 	/* The array is not NULL-terminated,
127 	 * so g_strfreev() will not work. */
128 	for (ii = 0; ii < priv->n_icon_names; ii++)
129 		g_free (priv->icon_names[ii]);
130 	g_free (priv->icon_names);
131 
132 	g_ptr_array_free (priv->pixbufs, TRUE);
133 
134 	/* Chain up to parent's finalize() method. */
135 	G_OBJECT_CLASS (e_cell_toggle_parent_class)->finalize (object);
136 }
137 
138 static ECellView *
139 cell_toggle_new_view (ECell *ecell,
140                       ETableModel *table_model,
141                       gpointer e_table_item_view)
142 {
143 	ECellToggleView *toggle_view = g_new0 (ECellToggleView, 1);
144 	ETableItem *eti = E_TABLE_ITEM (e_table_item_view);
145 	GnomeCanvas *canvas = GNOME_CANVAS_ITEM (eti)->canvas;
146 
147 	toggle_view->cell_view.ecell = ecell;
148 	toggle_view->cell_view.e_table_model = table_model;
149 	toggle_view->cell_view.e_table_item_view = e_table_item_view;
150 	toggle_view->cell_view.kill_view_cb = NULL;
151 	toggle_view->cell_view.kill_view_cb_data = NULL;
152 	toggle_view->canvas = canvas;
153 
154 	return (ECellView *) toggle_view;
155 }
156 
157 static void
158 cell_toggle_kill_view (ECellView *ecell_view)
159 {
160 	ECellToggleView *toggle_view = (ECellToggleView *) ecell_view;
161 
162 	if (toggle_view->cell_view.kill_view_cb)
163 		toggle_view->cell_view.kill_view_cb (
164 			ecell_view, toggle_view->cell_view.kill_view_cb_data);
165 
166 	if (toggle_view->cell_view.kill_view_cb_data)
167 		g_list_free (toggle_view->cell_view.kill_view_cb_data);
168 
169 	g_free (ecell_view);
170 }
171 
172 static void
173 cell_toggle_draw (ECellView *ecell_view,
174                   cairo_t *cr,
175                   gint model_col,
176                   gint view_col,
177                   gint row,
178                   ECellFlags flags,
179                   gint x1,
180                   gint y1,
181                   gint x2,
182                   gint y2)
183 {
184 	ECellTogglePrivate *priv;
185 	GdkPixbuf *image;
186 	gint x, y;
187 
188 	const gint value = GPOINTER_TO_INT (
189 		e_table_model_value_at (ecell_view->e_table_model, model_col, row));
190 
191 	priv = E_CELL_TOGGLE_GET_PRIVATE (ecell_view->ecell);
192 
193 	if (value < 0 || value >= priv->pixbufs->len)
194 		return;
195 
196 	image = g_ptr_array_index (priv->pixbufs, value);
197 
198 	if ((x2 - x1) < gdk_pixbuf_get_width (image))
199 		x = x1;
200 	else
201 		x = x1 + ((x2 - x1) - gdk_pixbuf_get_width (image)) / 2;
202 
203 	if ((y2 - y1) < gdk_pixbuf_get_height (image))
204 		y = y1;
205 	else
206 		y = y1 + ((y2 - y1) - gdk_pixbuf_get_height (image)) / 2;
207 
208 	cairo_save (cr);
209 	gdk_cairo_set_source_pixbuf (cr, image, x, y);
210 	cairo_paint_with_alpha (cr, 1);
211 	cairo_restore (cr);
212 }
213 
214 static void
215 etog_set_value (ECellToggleView *toggle_view,
216                 gint model_col,
217                 gint view_col,
218                 gint row,
219                 gint value)
220 {
221 	ECellTogglePrivate *priv;
222 
223 	priv = E_CELL_TOGGLE_GET_PRIVATE (toggle_view->cell_view.ecell);
224 
225 	if (value >= priv->pixbufs->len)
226 		value = 0;
227 
228 	e_table_model_set_value_at (
229 		toggle_view->cell_view.e_table_model,
230 		model_col, row, GINT_TO_POINTER (value));
231 }
232 
233 static gint
234 cell_toggle_event (ECellView *ecell_view,
235                    GdkEvent *event,
236                    gint model_col,
237                    gint view_col,
238                    gint row,
239                    ECellFlags flags,
240                    ECellActions *actions)
241 {
242 	ECellToggleView *toggle_view = (ECellToggleView *) ecell_view;
243 	gpointer _value = e_table_model_value_at (
244 		ecell_view->e_table_model, model_col, row);
245 	const gint value = GPOINTER_TO_INT (_value);
246 
247 	switch (event->type) {
248 	case GDK_KEY_PRESS:
249 		if (event->key.keyval != GDK_KEY_space)
250 			return FALSE;
251 		/* Fall through */
252 	case GDK_BUTTON_PRESS:
253 		if (!e_table_model_is_cell_editable (
254 			ecell_view->e_table_model, model_col, row))
255 			return FALSE;
256 
257 		etog_set_value (
258 			toggle_view, model_col, view_col, row, value + 1);
259 
260 		return TRUE;
261 
262 	default:
263 		return FALSE;
264 	}
265 }
266 
267 static gint
268 cell_toggle_height (ECellView *ecell_view,
269                     gint model_col,
270                     gint view_col,
271                     gint row)
272 {
273 	ECellTogglePrivate *priv;
274 
275 	priv = E_CELL_TOGGLE_GET_PRIVATE (ecell_view->ecell);
276 
277 	return priv->height;
278 }
279 
280 static void
281 cell_toggle_print (ECellView *ecell_view,
282                    GtkPrintContext *context,
283                    gint model_col,
284                    gint view_col,
285                    gint row,
286                    gdouble width,
287                    gdouble height)
288 {
289 	ECellTogglePrivate *priv;
290 	GdkPixbuf *image;
291 	gdouble image_width, image_height;
292 	const gint value = GPOINTER_TO_INT (
293 			e_table_model_value_at (ecell_view->e_table_model, model_col, row));
294 
295 	cairo_t *cr;
296 
297 	priv = E_CELL_TOGGLE_GET_PRIVATE (ecell_view->ecell);
298 
299 	if (value >= priv->pixbufs->len)
300 		return;
301 
302 	image = g_ptr_array_index (priv->pixbufs, value);
303 	if (image) {
304 		cr = gtk_print_context_get_cairo_context (context);
305 		cairo_save (cr);
306 		cairo_translate (cr, 0 , 0);
307 		image = gdk_pixbuf_add_alpha (image, TRUE, 255, 255, 255);
308 		image_width = (gdouble) gdk_pixbuf_get_width (image);
309 		image_height = (gdouble) gdk_pixbuf_get_height (image);
310 		cairo_rectangle (
311 			cr,
312 			image_width / 7,
313 			image_height / 3,
314 			image_width - image_width / 4,
315 			image_width - image_height / 7);
316 		cairo_clip (cr);
317 		gdk_cairo_set_source_pixbuf (cr, image, 0, image_height / 4);
318 		cairo_paint (cr);
319 		cairo_restore (cr);
320 	}
321 }
322 
323 static gdouble
324 cell_toggle_print_height (ECellView *ecell_view,
325                           GtkPrintContext *context,
326                           gint model_col,
327                           gint view_col,
328                           gint row,
329                           gdouble width)
330 {
331 	ECellTogglePrivate *priv;
332 
333 	priv = E_CELL_TOGGLE_GET_PRIVATE (ecell_view->ecell);
334 
335 	return priv->height;
336 }
337 
338 static gint
339 cell_toggle_max_width (ECellView *ecell_view,
340                        gint model_col,
341                        gint view_col)
342 {
343 	ECellTogglePrivate *priv;
344 	gint max_width = 0;
345 	gint number_of_rows;
346 	gint row;
347 
348 	priv = E_CELL_TOGGLE_GET_PRIVATE (ecell_view->ecell);
349 
350 	number_of_rows = e_table_model_row_count (ecell_view->e_table_model);
351 	for (row = 0; row < number_of_rows; row++) {
352 		GdkPixbuf *pixbuf;
353 		gpointer value;
354 
355 		value = e_table_model_value_at (
356 			ecell_view->e_table_model, model_col, row);
357 		pixbuf = g_ptr_array_index (
358 			priv->pixbufs, GPOINTER_TO_INT (value));
359 
360 		max_width = MAX (max_width, gdk_pixbuf_get_width (pixbuf));
361 	}
362 
363 	return max_width;
364 }
365 
366 static void
367 e_cell_toggle_class_init (ECellToggleClass *class)
368 {
369 	GObjectClass *object_class;
370 	ECellClass *cell_class;
371 
372 	g_type_class_add_private (class, sizeof (ECellTogglePrivate));
373 
374 	object_class = G_OBJECT_CLASS (class);
375 	object_class->dispose = cell_toggle_dispose;
376 	object_class->finalize = cell_toggle_finalize;
377 
378 	cell_class = E_CELL_CLASS (class);
379 	cell_class->new_view = cell_toggle_new_view;
380 	cell_class->kill_view = cell_toggle_kill_view;
381 	cell_class->draw = cell_toggle_draw;
382 	cell_class->event = cell_toggle_event;
383 	cell_class->height = cell_toggle_height;
384 	cell_class->print = cell_toggle_print;
385 	cell_class->print_height = cell_toggle_print_height;
386 	cell_class->max_width = cell_toggle_max_width;
387 
388 	gal_a11y_e_cell_registry_add_cell_type (
389 		NULL, E_TYPE_CELL_TOGGLE, gal_a11y_e_cell_toggle_new);
390 }
391 
392 static void
393 e_cell_toggle_init (ECellToggle *cell_toggle)
394 {
395 	cell_toggle->priv = E_CELL_TOGGLE_GET_PRIVATE (cell_toggle);
396 
397 	cell_toggle->priv->empty =
398 		gdk_pixbuf_new_from_xpm_data (empty_xpm);
399 
400 	cell_toggle->priv->pixbufs =
401 		g_ptr_array_new_with_free_func (g_object_unref);
402 }
403 
404 /**
405  * e_cell_toggle_construct:
406  * @cell_toggle: a fresh ECellToggle object
407  * @icon_names: array of icon names, some of which may be %NULL
408  * @n_icon_names: length of the @icon_names array
409  *
410  * Constructs the @cell_toggle object with the @icon_names and @n_icon_names
411  * arguments.
412  */
413 void
414 e_cell_toggle_construct (ECellToggle *cell_toggle,
415                          const gchar **icon_names,
416                          guint n_icon_names)
417 {
418 	guint ii;
419 
420 	g_return_if_fail (E_IS_CELL_TOGGLE (cell_toggle));
421 	g_return_if_fail (icon_names != NULL);
422 	g_return_if_fail (n_icon_names > 0);
423 
424 	cell_toggle->priv->icon_names = g_new (gchar *, n_icon_names);
425 	cell_toggle->priv->n_icon_names = n_icon_names;
426 
427 	for (ii = 0; ii < n_icon_names; ii++)
428 		cell_toggle->priv->icon_names[ii] = g_strdup (icon_names[ii]);
429 
430 	cell_toggle_load_icons (cell_toggle);
431 }
432 
433 /**
434  * e_cell_toggle_new:
435  * @icon_names: array of icon names, some of which may be %NULL
436  * @n_icon_names: length of the @icon_names array
437  *
438  * Creates a new ECell renderer that can be used to render toggle
439  * buttons with the icons specified in @icon_names.  The value returned
440  * by ETableModel::get_value is typecast into an integer and clamped
441  * to the [0..n_icon_names) range.  That will select the image rendered.
442  *
443  * %NULL elements in @icon_names will show no icon for the corresponding
444  * integer value.
445  *
446  * Returns: an ECell object that can be used to render multi-state
447  * toggle cells.
448  */
449 ECell *
450 e_cell_toggle_new (const gchar **icon_names,
451                    guint n_icon_names)
452 {
453 	ECellToggle *cell_toggle;
454 
455 	g_return_val_if_fail (icon_names != NULL, NULL);
456 	g_return_val_if_fail (n_icon_names > 0, NULL);
457 
458 	cell_toggle = g_object_new (E_TYPE_CELL_TOGGLE, NULL);
459 	e_cell_toggle_construct (cell_toggle, icon_names, n_icon_names);
460 
461 	return (ECell *) cell_toggle;
462 }
463 
464 GPtrArray *
465 e_cell_toggle_get_pixbufs (ECellToggle *cell_toggle)
466 {
467 	g_return_val_if_fail (E_IS_CELL_TOGGLE (cell_toggle), NULL);
468 
469 	return cell_toggle->priv->pixbufs;
470 }