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