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" 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); 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, 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; 87 gboolean active; 88 gboolean null; 89 }; 90 91 enum { 92 PROP_0, 93 PROP_VALUE, 94 PROP_VALUE_ATTRIBUTES, 95 PROP_EDITABLE, 96 PROP_TO_BE_DELETED 97 }; 98 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