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 }