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
gdaui_data_cell_renderer_boolean_get_type(void)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
gdaui_data_cell_renderer_boolean_init(GdauiDataCellRendererBoolean * cell)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
gdaui_data_cell_renderer_boolean_class_init(GdauiDataCellRendererBooleanClass * class)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
gdaui_data_cell_renderer_boolean_dispose(GObject * object)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
gdaui_data_cell_renderer_boolean_finalize(GObject * object)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
gdaui_data_cell_renderer_boolean_get_property(GObject * object,guint param_id,GValue * value,GParamSpec * pspec)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
gdaui_data_cell_renderer_boolean_set_property(GObject * object,guint param_id,const GValue * value,GParamSpec * pspec)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 *
gdaui_data_cell_renderer_boolean_new(GdaDataHandler * dh,GType type)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
gdaui_data_cell_renderer_boolean_get_size(GtkCellRenderer * cell,GtkWidget * widget,const GdkRectangle * cell_area,gint * x_offset,gint * y_offset,gint * width,gint * height)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
gdaui_data_cell_renderer_boolean_render(GtkCellRenderer * cell,cairo_t * cr,GtkWidget * widget,const GdkRectangle * background_area,const GdkRectangle * cell_area,GtkCellRendererState flags)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
gdaui_data_cell_renderer_boolean_activate(GtkCellRenderer * cell,GdkEvent * event,GtkWidget * widget,const gchar * path,const GdkRectangle * background_area,const GdkRectangle * cell_area,GtkCellRendererState flags)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