1 /*
2 * Copyright (C) 2009 - 2012 Vivien Malerba <malerba@gnome-db.org>
3 * Copyright (C) 2010 David King <davidk@openismus.com>
4 * Copyright (C) 2011 Murray Cumming <murrayc@murrayc.com>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22 #include <stdlib.h>
23 #include <string.h>
24 #include <libgda/libgda.h>
25 #include <glib/gi18n-lib.h>
26 #include "gdaui-data-cell-renderer-pict.h"
27 #include "custom-marshal.h"
test(Iter first,Iter last)28 #include "common-pict.h"
29 #include <libgda/gda-enum-types.h>
30 #include "gdaui-data-cell-renderer-util.h"
31
32 static void gdaui_data_cell_renderer_pict_get_property (GObject *object,
33 guint param_id,
34 GValue *value,
35 GParamSpec *pspec);
36 static void gdaui_data_cell_renderer_pict_set_property (GObject *object,
37 guint param_id,
38 const GValue *value,
39 GParamSpec *pspec);
40 static void gdaui_data_cell_renderer_pict_dispose (GObject *object);
41
42 static void gdaui_data_cell_renderer_pict_init (GdauiDataCellRendererPict *celltext);
43 static void gdaui_data_cell_renderer_pict_class_init (GdauiDataCellRendererPictClass *class);
44 static void gdaui_data_cell_renderer_pict_render (GtkCellRenderer *cell,
45 cairo_t *cr,
46 GtkWidget *widget,
47 const GdkRectangle *background_area,
48 const GdkRectangle *cell_area,
49 GtkCellRendererState flags);
test(int N)50 static void gdaui_data_cell_renderer_pict_get_size (GtkCellRenderer *cell,
51 GtkWidget *widget,
52 const GdkRectangle *cell_area,
53 gint *x_offset,
54 gint *y_offset,
55 gint *width,
56 gint *height);
57 static gboolean gdaui_data_cell_renderer_pict_activate (GtkCellRenderer *cell,
58 GdkEvent *event,
59 GtkWidget *widget,
60 const gchar *path,
61 const GdkRectangle *background_area,
test()62 const GdkRectangle *cell_area,
63 GtkCellRendererState flags);
64
65 /* get a pointer to the parents to be able to call their destructor */
66 static GObjectClass *parent_class = NULL;
67
68 enum {
69 CHANGED,
70 LAST_SIGNAL
71 };
72
73
74 struct _GdauiDataCellRendererPictPrivate
75 {
76 GdaDataHandler *dh;
77 GType type;
78 GValue *value;
79 PictBinData bindata;
80 PictOptions options;
81 PictAllocation size;
82 PictMenu popup_menu;
83 gboolean to_be_deleted;
84 gboolean invalid;
85
86 gboolean editable;
operator ()less87 gboolean active;
88 gboolean null;
89 };
constexpr_test()90
91 enum {
92 PROP_0,
93 PROP_VALUE,
94 PROP_VALUE_ATTRIBUTES,
95 PROP_EDITABLE,
96 PROP_TO_BE_DELETED
97 };
98
main(int,char **)99 static guint pixbuf_cell_signals[LAST_SIGNAL] = { 0 };
100
101
102 GType
103 gdaui_data_cell_renderer_pict_get_type (void)
104 {
105 static GType cell_type = 0;
106
107 if (!cell_type) {
108 static const GTypeInfo cell_info = {
109 sizeof (GdauiDataCellRendererPictClass),
110 NULL, /* base_init */
111 NULL, /* base_finalize */
112 (GClassInitFunc) gdaui_data_cell_renderer_pict_class_init,
113 NULL, /* class_finalize */
114 NULL, /* class_data */
115 sizeof (GdauiDataCellRendererPict),
116 0, /* n_preallocs */
117 (GInstanceInitFunc) gdaui_data_cell_renderer_pict_init,
118 0
119 };
120
121 cell_type = g_type_register_static (GTK_TYPE_CELL_RENDERER_PIXBUF, "GdauiDataCellRendererPict",
122 &cell_info, 0);
123 }
124
125 return cell_type;
126 }
127
128 static void
129 notify_property_cb (GtkCellRenderer *cell, GParamSpec *pspec, G_GNUC_UNUSED gpointer data)
130 {
131 if (!strcmp (pspec->name, "stock-size")) {
132 GdauiDataCellRendererPict *pictcell;
133 guint size;
134
135 pictcell = (GdauiDataCellRendererPict *) cell;
136 g_object_get ((GObject *) cell, "stock-size", &size, NULL);
137 gtk_icon_size_lookup (size, &(pictcell->priv->size.width), &(pictcell->priv->size.height));
138 common_pict_clear_pixbuf_cache (&(pictcell->priv->options));
139 }
140 }
141
142 static void
143 gdaui_data_cell_renderer_pict_init (GdauiDataCellRendererPict *cell)
144 {
145 cell->priv = g_new0 (GdauiDataCellRendererPictPrivate, 1);
146 cell->priv->dh = NULL;
147 cell->priv->type = GDA_TYPE_BINARY;
148 cell->priv->editable = FALSE;
149
150 cell->priv->bindata.data = NULL;
151 cell->priv->bindata.data_length = 0;
152 cell->priv->options.encoding = ENCODING_NONE;
153 cell->priv->options.serialize = FALSE;
154 common_pict_init_cache (&(cell->priv->options));
155
156 gtk_icon_size_lookup (GTK_ICON_SIZE_DIALOG, &(cell->priv->size.width), &(cell->priv->size.height));
157
158 g_object_set ((GObject*) cell, "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE,
159 "xpad", 2, "ypad", 2, NULL);
160 g_signal_connect (G_OBJECT (cell), "notify",
161 G_CALLBACK (notify_property_cb), NULL);
162 }
163
164 static void
165 gdaui_data_cell_renderer_pict_class_init (GdauiDataCellRendererPictClass *class)
166 {
167 GObjectClass *object_class = G_OBJECT_CLASS (class);
168 GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (class);
169
170 parent_class = g_type_class_peek_parent (class);
171
172 object_class->get_property = gdaui_data_cell_renderer_pict_get_property;
173 object_class->set_property = gdaui_data_cell_renderer_pict_set_property;
174 object_class->dispose = gdaui_data_cell_renderer_pict_dispose;
175
176 cell_class->get_size = gdaui_data_cell_renderer_pict_get_size;
177 cell_class->render = gdaui_data_cell_renderer_pict_render;
178 cell_class->activate = gdaui_data_cell_renderer_pict_activate;
179
180 g_object_class_install_property (object_class,
181 PROP_VALUE,
182 g_param_spec_boxed ("value",
183 _("Value"),
184 _("GValue to render"),
185 G_TYPE_VALUE,
186 G_PARAM_READWRITE));
187
188 g_object_class_install_property (object_class,
189 PROP_VALUE_ATTRIBUTES,
190 g_param_spec_flags ("value-attributes", NULL, NULL, GDA_TYPE_VALUE_ATTRIBUTE,
191 GDA_VALUE_ATTR_NONE, G_PARAM_READWRITE));
192
193 g_object_class_install_property (object_class,
194 PROP_EDITABLE,
195 g_param_spec_boolean ("editable",
196 _("Editable"),
197 _("The toggle button can be activated"),
198 TRUE,
199 G_PARAM_READABLE |
200 G_PARAM_WRITABLE));
201
202 g_object_class_install_property (object_class,
203 PROP_TO_BE_DELETED,
204 g_param_spec_boolean ("to-be-deleted", NULL, NULL, FALSE,
205 G_PARAM_WRITABLE));
206
207 pixbuf_cell_signals[CHANGED] = g_signal_new ("changed",
208 G_OBJECT_CLASS_TYPE (object_class),
209 G_SIGNAL_RUN_LAST,
210 G_STRUCT_OFFSET (GdauiDataCellRendererPictClass, changed),
211 NULL, NULL,
212 _marshal_VOID__STRING_VALUE,
213 G_TYPE_NONE, 2,
214 G_TYPE_STRING,
215 G_TYPE_VALUE);
216 }
217
218 static void
219 gdaui_data_cell_renderer_pict_dispose (GObject *object)
220 {
221 GdauiDataCellRendererPict *cell;
222
223 g_return_if_fail (object != NULL);
224 g_return_if_fail (GDAUI_IS_DATA_CELL_RENDERER_PICT (object));
225 cell = GDAUI_DATA_CELL_RENDERER_PICT (object);
226
227 if (cell->priv) {
228 g_hash_table_destroy (cell->priv->options.pixbuf_hash);
229
230 /* the private area itself */
231 g_free (cell->priv);
232 cell->priv = NULL;
233 }
234
235 /* for the parent class */
236 parent_class->dispose (object);
237 }
238
239 static void
240 gdaui_data_cell_renderer_pict_get_property (GObject *object,
241 guint param_id,
242 GValue *value,
243 GParamSpec *pspec)
244 {
245 GdauiDataCellRendererPict *cell = GDAUI_DATA_CELL_RENDERER_PICT (object);
246
247 switch (param_id) {
248 case PROP_VALUE:
249 g_value_set_boxed (value, cell->priv->value);
250 break;
251 case PROP_VALUE_ATTRIBUTES:
252 break;
253 case PROP_EDITABLE:
254 g_value_set_boolean (value, cell->priv->editable);
255 break;
256 default:
257 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
258 break;
259 }
260 }
261
262
263 static void
264 gdaui_data_cell_renderer_pict_set_property (GObject *object,
265 guint param_id,
266 const GValue *value,
267 GParamSpec *pspec)
268 {
269 GdauiDataCellRendererPict *cell = GDAUI_DATA_CELL_RENDERER_PICT (object);
270
271 switch (param_id) {
272 case PROP_VALUE:
273 /* Because we don't have a copy of the value, we MUST NOT free it! */
274 cell->priv->value = NULL;
275 g_object_set (G_OBJECT (cell), "pixbuf", NULL, "stock-id", NULL, NULL);
276 if (value) {
277 GValue *gval = g_value_get_boxed (value);
278 GdkPixbuf *pixbuf = NULL;
279 const gchar *stock = NULL;
280 GError *error = NULL;
281
282 if (!gval)
283 cell->priv->invalid = TRUE;
284
285 if (cell->priv->bindata.data) {
286 g_free (cell->priv->bindata.data);
287 cell->priv->bindata.data = NULL;
288 cell->priv->bindata.data_length = 0;
289 }
290
291 /* fill in cell->priv->data */
292 if (common_pict_load_data (&(cell->priv->options), gval, &(cell->priv->bindata), &stock, &error)) {
293 /* try to make a pixbuf */
294 pixbuf = common_pict_fetch_cached_pixbuf (&(cell->priv->options), gval);
295 if (pixbuf)
296 g_object_ref (pixbuf);
297 else {
298 pixbuf = common_pict_make_pixbuf (&(cell->priv->options),
299 &(cell->priv->bindata), &(cell->priv->size),
300 &stock, &error);
301 if (pixbuf)
302 common_pict_add_cached_pixbuf (&(cell->priv->options), gval, pixbuf);
303 }
304
305 if (!pixbuf && !stock)
306 stock = GTK_STOCK_MISSING_IMAGE;
307 }
308
309 /* display something */
310 if (pixbuf) {
311 g_object_set (G_OBJECT (cell), "pixbuf", pixbuf, NULL);
312 g_object_unref (pixbuf);
313 }
314
315 if (stock)
316 g_object_set (G_OBJECT (cell), "stock-id", stock, NULL);
317 if (error)
318 g_error_free (error);
319
320 cell->priv->value = gval;
321 }
322 else
323 cell->priv->invalid = TRUE;
324
325 g_object_notify (object, "value");
326 break;
327 case PROP_VALUE_ATTRIBUTES:
328 cell->priv->invalid = g_value_get_flags (value) & GDA_VALUE_ATTR_DATA_NON_VALID ? TRUE : FALSE;
329 break;
330 case PROP_EDITABLE:
331 cell->priv->editable = g_value_get_boolean (value);
332 /* FIXME */
333 /*g_object_notify (G_OBJECT(object), "editable");*/
334 break;
335 case PROP_TO_BE_DELETED:
336 cell->priv->to_be_deleted = g_value_get_boolean (value);
337 break;
338 default:
339 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
340 break;
341 }
342 }
343
344 /**
345 * gdaui_data_cell_renderer_pict_new:
346 * @dh: a #GdaDataHandler object
347 * @type:
348 * @options: options string
349 *
350 * Creates a new #GdauiDataCellRendererPict. Adjust rendering
351 * parameters using object properties. Object properties can be set
352 * globally (with g_object_set()). Also, with #GtkTreeViewColumn, you
353 * can bind a property to a value in a #GtkTreeModel. For example, you
354 * can bind the "active" property on the cell renderer to a pict value
355 * in the model, thus causing the check button to reflect the state of
356 * the model.
357 *
358 * Returns: the new cell renderer
359 */
360 GtkCellRenderer *
361 gdaui_data_cell_renderer_pict_new (GdaDataHandler *dh, GType type, const gchar *options)
362 {
363 GObject *obj;
364 GdauiDataCellRendererPict *cell;
365
366 g_return_val_if_fail (dh && GDA_IS_DATA_HANDLER (dh), NULL);
367 obj = g_object_new (GDAUI_TYPE_DATA_CELL_RENDERER_PICT, "stock-size", GTK_ICON_SIZE_DIALOG, NULL);
368
369 cell = GDAUI_DATA_CELL_RENDERER_PICT (obj);
370 cell->priv->dh = dh;
371 g_object_ref (G_OBJECT (dh));
372 cell->priv->type = type;
373
374 common_pict_parse_options (&(cell->priv->options), options);
375
376 return GTK_CELL_RENDERER (obj);
377 }
378
379 static void
380 gdaui_data_cell_renderer_pict_get_size (GtkCellRenderer *cell,
381 GtkWidget *widget,
382 const GdkRectangle *cell_area,
383 gint *x_offset,
384 gint *y_offset,
385 gint *width,
386 gint *height)
387 {
388 /* FIXME */
389 /* GtkIconSize */
390 GtkCellRendererClass *pixbuf_class = g_type_class_peek (GTK_TYPE_CELL_RENDERER_PIXBUF);
391
392 (pixbuf_class->get_size) (cell, widget, cell_area, x_offset, y_offset, width, height);
393 }
394
395 static void
396 gdaui_data_cell_renderer_pict_render (GtkCellRenderer *cell,
397 cairo_t *cr,
398 GtkWidget *widget,
399 const GdkRectangle *background_area,
400 const GdkRectangle *cell_area,
401 GtkCellRendererState flags)
402 {
403 GdauiDataCellRendererPict *datacell = GDAUI_DATA_CELL_RENDERER_PICT (cell);
404 GtkCellRendererClass *pixbuf_class = g_type_class_peek (GTK_TYPE_CELL_RENDERER_PIXBUF);
405
406 (pixbuf_class->render) (cell, cr, widget, background_area, cell_area, flags);
407
408 if (datacell->priv->to_be_deleted) {
409 GtkStyleContext *style_context = gtk_widget_get_style_context (widget);
410 guint xpad;
411 g_object_get ((GObject*) cell, "xpad", &xpad, NULL);
412
413 gdouble y = cell_area->y + cell_area->height / 2.;
414 gtk_render_line (style_context,
415 cr,
416 cell_area->x + xpad, y, cell_area->x + cell_area->width - xpad, y);
417 }
418 if (datacell->priv->invalid)
419 gdaui_data_cell_renderer_draw_invalid_area (cr, cell_area);
420 }
421
422 static void
423 pict_data_changed_cb (PictBinData *bindata, GdauiDataCellRendererPict *pictcell)
424 {
425 GValue *value;
426
427 value = common_pict_get_value (bindata, &(pictcell->priv->options),
428 pictcell->priv->type);
429 g_free (bindata->data);
430 g_signal_emit (G_OBJECT (pictcell), pixbuf_cell_signals[CHANGED], 0,
431 g_object_get_data (G_OBJECT (pictcell), "last-path"), value);
432 gda_value_free (value);
433 }
434
435 static gboolean
436 gdaui_data_cell_renderer_pict_activate (GtkCellRenderer *cell,
437 G_GNUC_UNUSED GdkEvent *event,
438 GtkWidget *widget,
439 const gchar *path,
440 G_GNUC_UNUSED const GdkRectangle *background_area,
441 G_GNUC_UNUSED const GdkRectangle *cell_area,
442 G_GNUC_UNUSED GtkCellRendererState flags)
443 {
444 GdauiDataCellRendererPict *pictcell;
445
446 pictcell = GDAUI_DATA_CELL_RENDERER_PICT (cell);
447 if (pictcell->priv->editable) {
448 int event_time;
449
450 g_object_set_data_full (G_OBJECT (pictcell), "last-path", g_strdup (path), g_free);
451 if (pictcell->priv->popup_menu.menu) {
452 gtk_widget_destroy (pictcell->priv->popup_menu.menu);
453 pictcell->priv->popup_menu.menu = NULL;
454 }
455 common_pict_create_menu (&(pictcell->priv->popup_menu), widget, &(pictcell->priv->bindata),
456 &(pictcell->priv->options),
457 (PictCallback) pict_data_changed_cb, pictcell);
458
459 common_pict_adjust_menu_sensitiveness (&(pictcell->priv->popup_menu), pictcell->priv->editable,
460 &(pictcell->priv->bindata));
461 event_time = gtk_get_current_event_time ();
462 gtk_menu_popup (GTK_MENU (pictcell->priv->popup_menu.menu), NULL, NULL, NULL, NULL,
463 0, event_time);
464 }
465
466 return FALSE;
467 }
468
469
470