1 /* 2 * Copyright (C) 2009 - 2011 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 <libgda/libgda.h> 24 #include <glib/gi18n-lib.h> 25 #include <libgda/gda-enum-types.h> 26 #include "gdaui-data-cell-renderer-boolean.h" 27 #include "marshallers/gdaui-custom-marshal.h" 28 #include "gdaui-data-cell-renderer-util.h" 29 30 static void gdaui_data_cell_renderer_boolean_get_property (GObject *object, 31 guint param_id, 32 GValue *value, 33 GParamSpec *pspec); 34 static void gdaui_data_cell_renderer_boolean_set_property (GObject *object, 35 guint param_id, 36 const GValue *value, 37 GParamSpec *pspec); 38 static void gdaui_data_cell_renderer_boolean_init (GdauiDataCellRendererBoolean *celltext); 39 static void gdaui_data_cell_renderer_boolean_class_init (GdauiDataCellRendererBooleanClass *class); 40 static void gdaui_data_cell_renderer_boolean_dispose (GObject *object); 41 static void gdaui_data_cell_renderer_boolean_finalize (GObject *object); 42 static void gdaui_data_cell_renderer_boolean_render (GtkCellRenderer *cell, 43 cairo_t *cr, 44 GtkWidget *widget, 45 const GdkRectangle *background_area, 46 const GdkRectangle *cell_area, 47 GtkCellRendererState flags); 48 static void gdaui_data_cell_renderer_boolean_get_size (GtkCellRenderer *cell, 49 GtkWidget *widget, 50 const GdkRectangle *cell_area, 51 gint *x_offset, 52 gint *y_offset, 53 gint *width, 54 gint *height); 55 static gboolean gdaui_data_cell_renderer_boolean_activate (GtkCellRenderer *cell, 56 GdkEvent *event, 57 GtkWidget *widget, 58 const gchar *path, 59 const GdkRectangle *background_area, 60 const GdkRectangle *cell_area, 61 GtkCellRendererState flags); 62 63 enum { 64 CHANGED, 65 LAST_SIGNAL 66 }; 67 68 69 struct _GdauiDataCellRendererBooleanPrivate 70 { 71 GdaDataHandler *dh; 72 GType type; 73 GValue *value; 74 gboolean to_be_deleted; 75 gboolean invalid; 76 77 gboolean editable; 78 gboolean active; 79 gboolean null; 80 }; 81 82 enum { 83 PROP_0, 84 PROP_VALUE, 85 PROP_VALUE_ATTRIBUTES, 86 PROP_EDITABLE, 87 PROP_TO_BE_DELETED, 88 PROP_DATA_HANDLER, 89 PROP_TYPE 90 }; 91 92 static GObjectClass *parent_class = NULL; 93 static guint toggle_cell_signals[LAST_SIGNAL] = { 0 }; 94 95 96 GType 97 gdaui_data_cell_renderer_boolean_get_type (void) 98 { 99 static GType cell_type = 0; 100 101 if (!cell_type) { 102 static const GTypeInfo cell_info = { 103 sizeof (GdauiDataCellRendererBooleanClass), 104 NULL, /* base_init */ 105 NULL, /* base_finalize */ 106 (GClassInitFunc) gdaui_data_cell_renderer_boolean_class_init, 107 NULL, /* class_finalize */ 108 NULL, /* class_data */ 109 sizeof (GdauiDataCellRendererBoolean), 110 0, /* n_preallocs */ 111 (GInstanceInitFunc) gdaui_data_cell_renderer_boolean_init, 112 0 113 }; 114 115 cell_type = 116 g_type_register_static (GTK_TYPE_CELL_RENDERER_TOGGLE, "GdauiDataCellRendererBoolean", 117 &cell_info, 0); 118 } 119 120 return cell_type; 121 } 122 123 static void 124 gdaui_data_cell_renderer_boolean_init (GdauiDataCellRendererBoolean *cell) 125 { 126 cell->priv = g_new0 (GdauiDataCellRendererBooleanPrivate, 1); 127 cell->priv->dh = NULL; 128 cell->priv->type = G_TYPE_BOOLEAN; 129 cell->priv->editable = FALSE; 130 g_object_set (G_OBJECT (cell), "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, 131 "xpad", 2, "ypad", 2, NULL); 132 } 133 134 static void 135 gdaui_data_cell_renderer_boolean_class_init (GdauiDataCellRendererBooleanClass *class) 136 { 137 GObjectClass *object_class = G_OBJECT_CLASS (class); 138 GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (class); 139 140 parent_class = g_type_class_peek_parent (class); 141 142 object_class->dispose = gdaui_data_cell_renderer_boolean_dispose; 143 object_class->finalize = gdaui_data_cell_renderer_boolean_finalize; 144 145 object_class->get_property = gdaui_data_cell_renderer_boolean_get_property; 146 object_class->set_property = gdaui_data_cell_renderer_boolean_set_property; 147 148 cell_class->get_size = gdaui_data_cell_renderer_boolean_get_size; 149 cell_class->render = gdaui_data_cell_renderer_boolean_render; 150 cell_class->activate = gdaui_data_cell_renderer_boolean_activate; 151 152 g_object_class_install_property (object_class, 153 PROP_VALUE, 154 g_param_spec_boxed ("value", 155 _("Value"), 156 _("GValue to render"), 157 G_TYPE_VALUE, 158 G_PARAM_READWRITE)); 159 160 g_object_class_install_property (object_class, 161 PROP_VALUE_ATTRIBUTES, 162 g_param_spec_flags ("value-attributes", NULL, NULL, GDA_TYPE_VALUE_ATTRIBUTE, 163 GDA_VALUE_ATTR_NONE, G_PARAM_READWRITE)); 164 165 g_object_class_install_property (object_class, 166 PROP_EDITABLE, 167 g_param_spec_boolean ("editable", 168 _("Editable"), 169 _("The toggle button can be activated"), 170 TRUE, 171 G_PARAM_READABLE | 172 G_PARAM_WRITABLE)); 173 174 g_object_class_install_property (object_class, 175 PROP_TO_BE_DELETED, 176 g_param_spec_boolean ("to-be-deleted", NULL, NULL, FALSE, 177 G_PARAM_WRITABLE)); 178 g_object_class_install_property(object_class, 179 PROP_DATA_HANDLER, 180 g_param_spec_object("data-handler", NULL, NULL, GDA_TYPE_DATA_HANDLER, 181 G_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY)); 182 g_object_class_install_property(object_class, 183 PROP_TYPE, 184 g_param_spec_gtype("type", NULL, NULL, G_TYPE_NONE, 185 G_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY)); 186 187 188 toggle_cell_signals[CHANGED] = 189 g_signal_new ("changed", 190 G_OBJECT_CLASS_TYPE (object_class), 191 G_SIGNAL_RUN_LAST, 192 G_STRUCT_OFFSET (GdauiDataCellRendererBooleanClass, changed), 193 NULL, NULL, 194 _gdaui_marshal_VOID__STRING_VALUE, 195 G_TYPE_NONE, 2, 196 G_TYPE_STRING, 197 G_TYPE_VALUE); 198 } 199 200 static void 201 gdaui_data_cell_renderer_boolean_dispose (GObject *object) 202 { 203 GdauiDataCellRendererBoolean *datacell = GDAUI_DATA_CELL_RENDERER_BOOLEAN (object); 204 205 if (datacell->priv->dh) { 206 g_object_unref (G_OBJECT (datacell->priv->dh)); 207 datacell->priv->dh = NULL; 208 } 209 210 /* parent class */ 211 parent_class->dispose (object); 212 } 213 214 static void 215 gdaui_data_cell_renderer_boolean_finalize (GObject *object) 216 { 217 GdauiDataCellRendererBoolean *datacell = GDAUI_DATA_CELL_RENDERER_BOOLEAN (object); 218 219 if (datacell->priv) { 220 g_free (datacell->priv); 221 datacell->priv = NULL; 222 } 223 224 /* parent class */ 225 parent_class->finalize (object); 226 } 227 228 static void 229 gdaui_data_cell_renderer_boolean_get_property (GObject *object, 230 guint param_id, 231 GValue *value, 232 GParamSpec *pspec) 233 { 234 GdauiDataCellRendererBoolean *cell = GDAUI_DATA_CELL_RENDERER_BOOLEAN (object); 235 236 switch (param_id) { 237 case PROP_VALUE: 238 g_value_set_boxed (value, cell->priv->value); 239 break; 240 case PROP_VALUE_ATTRIBUTES: 241 break; 242 case PROP_EDITABLE: 243 g_value_set_boolean (value, cell->priv->editable); 244 break; 245 default: 246 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); 247 break; 248 } 249 } 250 251 252 static void 253 gdaui_data_cell_renderer_boolean_set_property (GObject *object, 254 guint param_id, 255 const GValue *value, 256 GParamSpec *pspec) 257 { 258 GdauiDataCellRendererBoolean *cell = GDAUI_DATA_CELL_RENDERER_BOOLEAN (object); 259 260 switch (param_id) { 261 case PROP_VALUE: 262 /* Because we don't have a copy of the value, we MUST NOT free it! */ 263 cell->priv->value = NULL; 264 if (value) { 265 GValue *gval = g_value_get_boxed (value); 266 if (gval && !gda_value_is_null (gval)) { 267 g_return_if_fail (G_VALUE_TYPE (gval) == cell->priv->type); 268 if (! gda_value_isa (gval, G_TYPE_BOOLEAN)) 269 g_warning ("GdauiDataCellRendererBoolean can only handle boolean values"); 270 else 271 g_object_set (G_OBJECT (object), 272 "inconsistent", FALSE, 273 "active", g_value_get_boolean (gval), NULL); 274 } 275 else if (gval) 276 g_object_set (G_OBJECT (object), 277 "inconsistent", TRUE, 278 "active", FALSE, NULL); 279 else { 280 cell->priv->invalid = TRUE; 281 g_object_set (G_OBJECT (object), 282 "inconsistent", TRUE, 283 "active", FALSE, NULL); 284 } 285 286 cell->priv->value = gval; 287 } 288 else { 289 cell->priv->invalid = TRUE; 290 g_object_set (G_OBJECT (object), 291 "inconsistent", TRUE, 292 "active", FALSE, NULL); 293 } 294 295 g_object_notify (object, "value"); 296 break; 297 case PROP_VALUE_ATTRIBUTES: 298 cell->priv->invalid = g_value_get_flags (value) & GDA_VALUE_ATTR_DATA_NON_VALID ? TRUE : FALSE; 299 break; 300 case PROP_EDITABLE: 301 cell->priv->editable = g_value_get_boolean (value); 302 g_object_set (G_OBJECT (object), "activatable", cell->priv->editable, NULL); 303 g_object_notify (G_OBJECT(object), "editable"); 304 break; 305 case PROP_TO_BE_DELETED: 306 cell->priv->to_be_deleted = g_value_get_boolean (value); 307 break; 308 case PROP_DATA_HANDLER: 309 if(cell->priv->dh) 310 g_object_unref (G_OBJECT(cell->priv->dh)); 311 312 cell->priv->dh = GDA_DATA_HANDLER(g_value_get_object(value)); 313 if(cell->priv->dh) 314 g_object_ref (G_OBJECT (cell->priv->dh)); 315 break; 316 case PROP_TYPE: 317 cell->priv->type = g_value_get_gtype(value); 318 break; 319 default: 320 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); 321 break; 322 } 323 } 324 325 /** 326 * gdaui_data_cell_renderer_boolean_new: 327 * @dh: a #GdaDataHandler object 328 * @type: the #GType of the data to be displayed 329 * 330 * Creates a new #GdauiDataCellRendererBoolean. Adjust rendering 331 * parameters using object properties. Object properties can be set 332 * globally (with g_object_set()). Also, with #GtkTreeViewColumn, you 333 * can bind a property to a value in a #GtkTreeModel. For example, you 334 * can bind the "active" property on the cell renderer to a boolean value 335 * in the model, thus causing the check button to reflect the state of 336 * the model. 337 * 338 * Returns: (transfer full): the new cell renderer 339 */ 340 GtkCellRenderer * 341 gdaui_data_cell_renderer_boolean_new (GdaDataHandler *dh, GType type) 342 { 343 GObject *obj; 344 345 g_return_val_if_fail (dh && GDA_IS_DATA_HANDLER (dh), NULL); 346 obj = g_object_new (GDAUI_TYPE_DATA_CELL_RENDERER_BOOLEAN, "type", type, 347 "data-handler", dh, NULL); 348 349 return GTK_CELL_RENDERER (obj); 350 } 351 352 static void 353 gdaui_data_cell_renderer_boolean_get_size (GtkCellRenderer *cell, 354 GtkWidget *widget, 355 const GdkRectangle *cell_area, 356 gint *x_offset, 357 gint *y_offset, 358 gint *width, 359 gint *height) 360 { 361 GtkCellRendererClass *toggle_class = g_type_class_peek (GTK_TYPE_CELL_RENDERER_TOGGLE); 362 363 (toggle_class->get_size) (cell, widget, cell_area, x_offset, y_offset, width, height); 364 } 365 366 static void 367 gdaui_data_cell_renderer_boolean_render (GtkCellRenderer *cell, 368 cairo_t *cr, 369 GtkWidget *widget, 370 const GdkRectangle *background_area, 371 const GdkRectangle *cell_area, 372 GtkCellRendererState flags) 373 { 374 GdauiDataCellRendererBoolean *datacell = GDAUI_DATA_CELL_RENDERER_BOOLEAN (cell); 375 GtkCellRendererClass *toggle_class = g_type_class_peek (GTK_TYPE_CELL_RENDERER_TOGGLE); 376 377 (toggle_class->render) (cell, cr, widget, background_area, cell_area, flags); 378 379 if (datacell->priv->to_be_deleted) { 380 GtkStyleContext *style_context = gtk_widget_get_style_context (widget); 381 guint xpad; 382 g_object_get (G_OBJECT(widget), "xpad", &xpad, NULL); 383 gdouble y = cell_area->y + cell_area->height / 2.; 384 gtk_render_line (style_context, 385 cr, 386 cell_area->x + xpad, cell_area->x + cell_area->width - xpad, 387 y, y); 388 389 } 390 if (datacell->priv->invalid) 391 gdaui_data_cell_renderer_draw_invalid_area (cr, cell_area); 392 } 393 394 static gboolean 395 gdaui_data_cell_renderer_boolean_activate (GtkCellRenderer *cell, 396 GdkEvent *event, 397 GtkWidget *widget, 398 const gchar *path, 399 const GdkRectangle *background_area, 400 const GdkRectangle *cell_area, 401 GtkCellRendererState flags) 402 { 403 gboolean editable; 404 g_object_get (G_OBJECT(cell), "editable", &editable, NULL); 405 if (editable) { 406 gboolean retval, active; 407 GValue *value; 408 409 retval = GTK_CELL_RENDERER_CLASS (parent_class)->activate (cell, event, 410 widget, path, 411 background_area, 412 cell_area, flags); 413 active = gtk_cell_renderer_toggle_get_active (GTK_CELL_RENDERER_TOGGLE (cell)); 414 415 value = gda_value_new (G_TYPE_BOOLEAN); 416 g_value_set_boolean (value, ! active); 417 g_signal_emit (G_OBJECT (cell), toggle_cell_signals[CHANGED], 0, path, value); 418 gda_value_free (value); 419 return retval; 420 } 421 422 return FALSE; 423 } 424 425 426