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 <libgda/libgda.h>
24 #include <glib/gi18n-lib.h>
25 #include "gdaui-entry-combo.h"
26 #include "gdaui-data-cell-renderer-combo.h"
27 #include "marshallers/gdaui-custom-marshal.h"
28 #include <libgda-ui/gdaui-combo.h>
29 #include <libgda-ui/gdaui-data-entry.h>
30 #include <libgda/gda-enum-types.h>
31 #include "gdaui-data-cell-renderer-util.h"
32 
33 #define GDAUI_DATA_CELL_RENDERER_COMBO_PATH "gdaui-data-cell-renderer-combo-path"
34 
35 static void gdaui_data_cell_renderer_combo_init       (GdauiDataCellRendererCombo      *celltext);
36 static void gdaui_data_cell_renderer_combo_class_init (GdauiDataCellRendererComboClass *class);
37 static void gdaui_data_cell_renderer_combo_dispose    (GObject *object);
38 static void gdaui_data_cell_renderer_combo_finalize   (GObject *object);
39 
40 static void gdaui_data_cell_renderer_combo_get_property  (GObject *object,
41 							  guint param_id,
42 							  GValue *value,
43 							  GParamSpec *pspec);
44 static void gdaui_data_cell_renderer_combo_set_property  (GObject *object,
45 							  guint param_id,
46 							  const GValue *value,
47 							  GParamSpec *pspec);
48 static void gdaui_data_cell_renderer_combo_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 void gdaui_data_cell_renderer_combo_render     (GtkCellRenderer          *cell,
56 						       cairo_t                  *cr,
57 						       GtkWidget                *widget,
58 						       const GdkRectangle       *background_area,
59 						       const GdkRectangle       *cell_area,
60 						       GtkCellRendererState      flags);
61 
62 static GtkCellEditable *gdaui_data_cell_renderer_combo_start_editing (GtkCellRenderer     *cell,
63 								      GdkEvent            *event,
64 								      GtkWidget           *widget,
65 								      const gchar         *path,
66 								      const GdkRectangle  *background_area,
67 								      const GdkRectangle  *cell_area,
68 								      GtkCellRendererState flags);
69 
70 enum {
71 	CHANGED,
72 	LAST_SIGNAL
73 };
74 
75 enum {
76 	PROP_0,
77 	PROP_VALUES,
78 	PROP_VALUES_DISPLAY,
79 	PROP_VALUE_ATTRIBUTES,
80 	PROP_TO_BE_DELETED,
81 	PROP_SET_DEFAULT_IF_INVALID,
82 	PROP_SHOW_EXPANDER,
83 	PROP_PARAMLIST,
84 	PROP_PARAMLISTSOURCE
85 };
86 
87 struct _GdauiDataCellRendererComboPrivate
88 {
89 	GdauiSet       *paramlist;
90 	GdauiSetSource *source;
91 	guint         focus_out_id;
92 	guint         attributes;
93 	gboolean      to_be_deleted;
94 	gboolean      set_default_if_invalid;
95 	gboolean      show_expander;
96 	gboolean      invalid;
97 };
98 
99 
100 static GObjectClass *parent_class = NULL;
101 static guint text_cell_renderer_combo_signals [LAST_SIGNAL] = { 0 };
102 
103 GType
104 gdaui_data_cell_renderer_combo_get_type (void)
105 {
106 	static GType cell_text_type = 0;
107 
108 	if (!cell_text_type) {
109 		static const GTypeInfo cell_text_info =	{
110 			sizeof (GdauiDataCellRendererComboClass),
111 			NULL,		/* base_init */
112 			NULL,		/* base_finalize */
113 			(GClassInitFunc) gdaui_data_cell_renderer_combo_class_init,
114 			NULL,		/* class_finalize */
115 			NULL,		/* class_data */
116 			sizeof (GdauiDataCellRendererCombo),
117 			0,              /* n_preallocs */
118 			(GInstanceInitFunc) gdaui_data_cell_renderer_combo_init,
119 			0
120 		};
121 
122 		cell_text_type =
123 			g_type_register_static (GTK_TYPE_CELL_RENDERER_TEXT, "GdauiDataCellRendererCombo",
124 						&cell_text_info, 0);
125 	}
126 
127 	return cell_text_type;
128 }
129 
130 static void
131 gdaui_data_cell_renderer_combo_init (GdauiDataCellRendererCombo *datacell)
132 {
133 	g_object_set ((GObject*) datacell, "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
134 	datacell->priv = g_new0 (GdauiDataCellRendererComboPrivate, 1);
135 	datacell->priv->attributes = 0;
136 	datacell->priv->set_default_if_invalid = FALSE;
137 	datacell->priv->show_expander = TRUE;
138 }
139 
140 static void
141 gdaui_data_cell_renderer_combo_class_init (GdauiDataCellRendererComboClass *class)
142 {
143 	GObjectClass *object_class = G_OBJECT_CLASS (class);
144 	GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (class);
145 
146 	parent_class = g_type_class_peek_parent (class);
147 
148     	object_class->dispose = gdaui_data_cell_renderer_combo_dispose;
149 	object_class->finalize = gdaui_data_cell_renderer_combo_finalize;
150 
151 	object_class->get_property = gdaui_data_cell_renderer_combo_get_property;
152 	object_class->set_property = gdaui_data_cell_renderer_combo_set_property;
153 
154 	cell_class->get_size = gdaui_data_cell_renderer_combo_get_size;
155 	cell_class->render = gdaui_data_cell_renderer_combo_render;
156 	cell_class->start_editing = gdaui_data_cell_renderer_combo_start_editing;
157 
158 	g_object_class_install_property (object_class,
159 					 PROP_VALUES,
160 					 g_param_spec_pointer ("values",
161 							       _("Values limited to PK fields"),
162 							       _("GList of GValue to render, limited to PK fields"),
163 							       G_PARAM_WRITABLE));
164 
165 	g_object_class_install_property (object_class,
166 					 PROP_VALUES_DISPLAY,
167 					 g_param_spec_pointer ("values-display",
168 							       _("Values"),
169 							       _("GList of GValue to render, not limited to PK fields "),
170 							       G_PARAM_WRITABLE));
171 
172 	g_object_class_install_property (object_class,
173 					 PROP_VALUE_ATTRIBUTES,
174 					 g_param_spec_flags ("value-attributes", NULL, NULL, GDA_TYPE_VALUE_ATTRIBUTE,
175 							     GDA_VALUE_ATTR_NONE, G_PARAM_READWRITE));
176 
177 	g_object_class_install_property (object_class,
178 					 PROP_TO_BE_DELETED,
179 					 g_param_spec_boolean ("to-be-deleted", NULL, NULL, FALSE,
180                                                                G_PARAM_WRITABLE));
181 	g_object_class_install_property (object_class,
182 					 PROP_SHOW_EXPANDER,
183 					 g_param_spec_boolean ("show-expander", NULL, NULL, FALSE,
184                                                                G_PARAM_WRITABLE));
185 
186 	g_object_class_install_property (object_class, PROP_SET_DEFAULT_IF_INVALID,
187 					 g_param_spec_boolean ("set-default-if-invalid", NULL, NULL, FALSE,
188                                                                (G_PARAM_READABLE | G_PARAM_WRITABLE)));
189 
190 	g_object_class_install_property (object_class, PROP_PARAMLIST,
191 					 g_param_spec_object ("data-set", NULL, NULL, GDAUI_TYPE_SET,
192 							      (G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE)));
193 
194 	/* Ideally, GdaSetSource would be a boxed type, but it is not yet, so we use g_param_spec_pointer(). */
195 	g_object_class_install_property (object_class, PROP_PARAMLISTSOURCE,
196 					 g_param_spec_pointer ("data-set-source", NULL, NULL,
197                                                                (G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE)));
198 
199 	text_cell_renderer_combo_signals [CHANGED] =
200 		g_signal_new ("changed",
201 			      G_OBJECT_CLASS_TYPE (object_class),
202 			      G_SIGNAL_RUN_LAST,
203 			      G_STRUCT_OFFSET (GdauiDataCellRendererComboClass, changed),
204 			      NULL, NULL,
205 			      _gdaui_marshal_VOID__STRING_SLIST_SLIST,
206 			      G_TYPE_NONE, 3,
207 			      G_TYPE_STRING,
208 			      G_TYPE_POINTER,
209 			      G_TYPE_POINTER);
210 
211 }
212 
213 static void
214 gdaui_data_cell_renderer_combo_dispose (GObject *object)
215 {
216 	GdauiDataCellRendererCombo *datacell = GDAUI_DATA_CELL_RENDERER_COMBO (object);
217 
218 	if (datacell->priv->paramlist) {
219 		g_object_unref (datacell->priv->paramlist);
220 		datacell->priv->paramlist = NULL;
221 	}
222 
223 	/* parent class */
224 	parent_class->dispose (object);
225 }
226 
227 static void
228 gdaui_data_cell_renderer_combo_finalize (GObject *object)
229 {
230 	GdauiDataCellRendererCombo *datacell = GDAUI_DATA_CELL_RENDERER_COMBO (object);
231 
232 	if (datacell->priv) {
233 		g_free (datacell->priv);
234 		datacell->priv = NULL;
235 	}
236 
237 	/* parent class */
238 	parent_class->finalize (object);
239 }
240 
241 
242 static void
243 gdaui_data_cell_renderer_combo_get_property (GObject *object,
244 					     guint param_id,
245 					     GValue *value,
246 					     GParamSpec *pspec)
247 {
248 	GdauiDataCellRendererCombo *datacell = GDAUI_DATA_CELL_RENDERER_COMBO (object);
249 
250 	switch (param_id) {
251 	case PROP_VALUE_ATTRIBUTES:
252 		g_value_set_flags (value, datacell->priv->attributes);
253 		break;
254 	case PROP_SET_DEFAULT_IF_INVALID:
255 		g_value_set_boolean (value, datacell->priv->set_default_if_invalid);
256 		break;
257 	default:
258 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
259 		break;
260 	}
261 }
262 
263 static gchar *render_text_to_display_from_values (GList *values);
264 
265 static void
266 gdaui_data_cell_renderer_combo_set_property (GObject *object,
267 					     guint param_id,
268 					     const GValue *value,
269 					     GParamSpec *pspec)
270 {
271 	GdauiDataCellRendererCombo *datacell = GDAUI_DATA_CELL_RENDERER_COMBO (object);
272 
273 	switch (param_id) {
274 	case PROP_VALUES:
275 		datacell->priv->invalid = FALSE;
276 		if (value) {
277 			GList *gvalues = g_value_get_pointer (value);
278 			if (gvalues) {
279 				GSList *values = NULL;
280 				gint length = 0, row;
281 				gboolean allnull = TRUE;
282 
283 				/* copy gvalues into the values GSList */
284 				while (gvalues) {
285 					values = g_slist_append (values, gvalues->data);
286 
287 					if (!gvalues->data ||
288 					    (gvalues->data && !gda_value_is_null ((GValue *)(gvalues->data))))
289 						allnull = FALSE;
290 
291 					length ++;
292 					gvalues = g_list_next (gvalues);
293 				}
294 
295 				g_return_if_fail (length == gdaui_set_source_get_ref_n_cols (datacell->priv->source));
296 
297 				if (allnull)
298 					g_object_set (G_OBJECT (object), "text", "", NULL);
299 				else {
300 					/* find the data model row for the values */
301 					/* if (gdaui_data_model_get_status (datacell->priv->data_model) &  */
302 					/* 					    GDAUI_DATA_MODEL_NEEDS_INIT_REFRESH) */
303 					/* 						gdaui_data_model_refresh (datacell->priv->data_model, NULL); */
304 					GdaDataModel *source_model;
305 					source_model = gda_set_source_get_data_model (gdaui_set_source_get_source (datacell->priv->source));
306 					row = gda_data_model_get_row_from_values (source_model,
307 										  values,
308 										  gdaui_set_source_get_ref_columns (datacell->priv->source));
309 					if (row >= 0) {
310 						GList *dsplay_values = NULL;
311 						gint i;
312 						gchar *str;
313 
314 						for (i = 0; i < gdaui_set_source_get_shown_n_cols (datacell->priv->source); i++) {
315 							const GValue *value;
316 							gint* cols;
317 							cols = gdaui_set_source_get_shown_columns (datacell->priv->source);
318 							value = gda_data_model_get_value_at (source_model, cols [i], row, NULL);
319 							dsplay_values = g_list_append (dsplay_values, (GValue *) value);
320 						}
321 						str = render_text_to_display_from_values (dsplay_values);
322 						g_list_free (dsplay_values);
323 						g_object_set (G_OBJECT (object), "text", str, NULL);
324 						g_free (str);
325 					}
326 					else {
327 						if (datacell->priv->attributes & GDA_VALUE_ATTR_CAN_BE_NULL)
328 							g_object_set (G_OBJECT (object), "text", "", NULL);
329 						else
330 							g_object_set (G_OBJECT (object), "text", "???", NULL);
331 					}
332 				}
333 
334 				g_slist_free (values);
335 			}
336 			else {
337 				datacell->priv->invalid = TRUE;
338 				g_object_set (G_OBJECT (object), "text", "", NULL);
339 			}
340 		}
341 		else {
342 			datacell->priv->invalid = TRUE;
343 			g_object_set (G_OBJECT (object), "text", "", NULL);
344 		}
345 
346 		g_object_notify (object, "values");
347 		break;
348 	case PROP_VALUES_DISPLAY:
349 		if (value) {
350 			GList *gvalues = g_value_get_pointer (value);
351 			gchar *str;
352 
353 			g_assert (g_list_length (gvalues) == (guint) gdaui_set_source_get_shown_n_cols (datacell->priv->source));
354 			str = render_text_to_display_from_values (gvalues);
355 			g_object_set (G_OBJECT (object), "text", str, NULL);
356 			g_free (str);
357 		}
358 		else
359 			g_object_set (G_OBJECT (object), "text", "", NULL);
360 
361 		g_object_notify (object, "values-display");
362 		break;
363 	case PROP_VALUE_ATTRIBUTES:
364 		datacell->priv->attributes = g_value_get_flags (value);
365 		break;
366 	case PROP_TO_BE_DELETED:
367 		datacell->priv->to_be_deleted = g_value_get_boolean (value);
368 		break;
369 	case PROP_SHOW_EXPANDER:
370 		datacell->priv->show_expander = g_value_get_boolean (value);
371 		break;
372 	case PROP_SET_DEFAULT_IF_INVALID:
373 		datacell->priv->set_default_if_invalid = g_value_get_boolean (value);
374 		break;
375 	case PROP_PARAMLIST:
376 		if (datacell->priv->paramlist)
377 			g_object_unref (datacell->priv->paramlist);
378 
379 		datacell->priv->paramlist = GDAUI_SET (g_value_get_object(value));
380 		if(datacell->priv->paramlist)
381 			g_object_ref(datacell->priv->paramlist);
382 
383 		g_object_ref (G_OBJECT (datacell->priv->paramlist));
384 		break;
385 	case PROP_PARAMLISTSOURCE:
386 		datacell->priv->source = GDAUI_SET_SOURCE (g_value_get_pointer(value));
387 		break;
388 	default:
389 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
390 		break;
391 	}
392 }
393 
394 static gchar *
395 render_text_to_display_from_values (GList *values)
396 {
397 	GList *list = values;
398 	gboolean allnull = TRUE;
399 	GString *string = g_string_new ("");
400 	gchar *retval;
401 	gchar *str;
402 
403 	while (list) {
404 		if (list->data && !gda_value_is_null ((GValue *)(list->data)))
405 			allnull = FALSE;
406 
407 		if (list != values)
408 			g_string_append (string, " / ");
409 		if (list->data) {
410 			str = gda_value_stringify ((GValue *)(list->data));
411 			/* TODO: use GdaDataHandler */
412 			g_string_append (string, str);
413 			g_free (str);
414 		}
415 		else
416 			g_string_append (string, " ? ");
417 
418 		list = g_list_next (list);
419 	}
420 
421 	if (!allnull) {
422 		retval = string->str;
423 		g_string_free (string, FALSE);
424 	}
425 	else {
426 		retval = g_strdup ("");
427 		g_string_free (string, TRUE);
428 	}
429 
430 	return retval;
431 }
432 
433 /**
434  * gdaui_data_cell_renderer_combo_new:
435  * @paramlist: a #GdaSet object
436  * @source: a #GdauiSetSource structure listed in @paramlist->sources_list
437  *
438  * Creates a new #GdauiDataCellRendererCombo which will fill the parameters listed in
439  * @source->nodes with values available from @source->data_model.
440  *
441  * Returns: (transfer full): the new cell renderer
442  **/
443 GtkCellRenderer *
444 gdaui_data_cell_renderer_combo_new (GdauiSet *paramlist, GdauiSetSource *source)
445 {
446 	GObject *obj;
447 
448 	g_return_val_if_fail (GDAUI_IS_SET (paramlist), NULL);
449 	g_return_val_if_fail (source, NULL);
450 	g_return_val_if_fail (g_slist_find (paramlist->sources_list, source), NULL);
451 
452 	obj = g_object_new (GDAUI_TYPE_DATA_CELL_RENDERER_COMBO, "data-set", paramlist,
453 			    "data-set-source", source, NULL);
454 
455 	return GTK_CELL_RENDERER (obj);
456 }
457 
458 static void
459 gdaui_data_cell_renderer_combo_get_size (GtkCellRenderer *cell,
460 					 GtkWidget       *widget,
461 					 const GdkRectangle *cell_area,
462 					 gint            *x_offset,
463 					 gint            *y_offset,
464 					 gint            *width,
465 					 gint            *height)
466 {
467 	gint calc_width = 0;
468 	gint calc_height = 0;
469 
470 	/* get the size as calculated by the GtkCellRendererText */
471 	GtkCellRendererClass *text_class = g_type_class_peek (GTK_TYPE_CELL_RENDERER_TEXT);
472 	(text_class->get_size) (cell, widget, cell_area, x_offset, y_offset, width, height);
473 
474 	/* Add more space for the popdown menu symbol */
475 	if (GDAUI_DATA_CELL_RENDERER_COMBO (cell)->priv->show_expander) {
476 		guint xpad, ypad;
477 		g_object_get ((GObject*) cell, "xpad", &xpad, "ypad", &ypad, NULL);
478 		gint expander_size;
479 		gtk_widget_style_get (widget, "expander-size", &expander_size, NULL);
480 		calc_width = (gint) xpad * 2 + expander_size;
481 		calc_height = (gint) ypad * 2 + expander_size;
482 	}
483 
484 	if (width)
485 		*width += calc_width;
486 
487 	if (height && (*height < calc_height))
488 		*height = calc_height;
489 }
490 
491 static void
492 gdaui_data_cell_renderer_combo_render (GtkCellRenderer      *cell,
493 				       cairo_t              *cr,
494 				       GtkWidget            *widget,
495 				       const GdkRectangle   *background_area,
496 				       const GdkRectangle   *cell_area,
497 				       GtkCellRendererState  flags)
498 
499 {
500 	GdauiDataCellRendererCombo *combocell = GDAUI_DATA_CELL_RENDERER_COMBO (cell);
501 
502 	/* render the text as for the GtkCellRendererText */
503 	GtkCellRendererClass *text_class = g_type_class_peek (GTK_TYPE_CELL_RENDERER_TEXT);
504 	(text_class->render) (cell, cr, widget, background_area, cell_area, flags);
505 
506 	/* render the popdown menu symbol */
507 	if (combocell->priv->show_expander) {
508 		gint expander_size;
509 		GtkStyleContext *style_context = gtk_widget_get_style_context (widget);
510 		guint xpad, ypad;
511 
512 		gtk_widget_style_get (widget, "expander-size", &expander_size, NULL);
513 		g_object_get ((GObject*) cell, "xpad", &xpad, "ypad", &ypad, NULL);
514 
515 
516 		gtk_render_expander (style_context,
517 				    cr,
518 				    cell_area->x + cell_area->width - xpad - expander_size/2.,
519 				    cell_area->y + cell_area->height - ypad - expander_size/2.,
520                                     expander_size, expander_size);
521 	}
522 
523 	if (combocell->priv->to_be_deleted) {
524 		GtkStyleContext *style_context = gtk_widget_get_style_context (widget);
525 		guint xpad;
526 		g_object_get ((GObject*) cell, "xpad", &xpad, NULL);
527 
528 		gdouble y = cell_area->y + cell_area->height / 2.;
529 		gtk_render_line (style_context,
530 				 cr,
531 				 cell_area->x + xpad, cell_area->x + cell_area->width - xpad,
532 				 y, y);
533 	}
534 
535 	if (combocell->priv->invalid)
536 		gdaui_data_cell_renderer_draw_invalid_area (cr, cell_area);
537 }
538 
539 static void gdaui_data_cell_renderer_combo_editing_done (GtkCellEditable *combo, GdauiDataCellRendererCombo *datacell);
540 static gboolean gdaui_data_cell_renderer_combo_focus_out_event (GtkWidget *widget, GdkEvent *event,
541 								GdauiDataCellRendererCombo *datacell);
542 
543 static GtkCellEditable *
544 gdaui_data_cell_renderer_combo_start_editing (GtkCellRenderer     *cell,
545 					      G_GNUC_UNUSED GdkEvent            *event,
546 					      G_GNUC_UNUSED GtkWidget           *widget,
547 					      const gchar         *path,
548 					      G_GNUC_UNUSED const GdkRectangle        *background_area,
549 					      G_GNUC_UNUSED const GdkRectangle        *cell_area,
550 					      G_GNUC_UNUSED GtkCellRendererState flags)
551 {
552 	GdauiDataCellRendererCombo *datacell;
553 	GtkWidget *combo;
554 	gboolean editable;
555 	GdaDataModel *source_model;
556 
557 	g_object_get ((GObject*) cell, "editable", &editable, NULL);
558 	if (editable == FALSE)
559 		return NULL;
560 
561 	datacell = GDAUI_DATA_CELL_RENDERER_COMBO (cell);
562 	source_model = gda_set_source_get_data_model (gdaui_set_source_get_source (datacell->priv->source));
563 	combo = gdaui_combo_new_with_model (source_model,
564 					    gdaui_set_source_get_shown_n_cols (datacell->priv->source),
565 					    gdaui_set_source_get_shown_columns (datacell->priv->source));
566 
567 	g_object_set (combo, "has-frame", FALSE, NULL);
568 	g_object_set_data_full (G_OBJECT (combo),
569 				GDAUI_DATA_CELL_RENDERER_COMBO_PATH,
570 				g_strdup (path), g_free);
571 	gdaui_combo_add_null (GDAUI_COMBO (combo),
572 				      (datacell->priv->attributes & GDA_VALUE_ATTR_CAN_BE_NULL) ? TRUE : FALSE);
573 	gtk_widget_show (combo);
574 
575 	g_signal_connect (GTK_CELL_EDITABLE (combo), "editing-done",
576 			  G_CALLBACK (gdaui_data_cell_renderer_combo_editing_done), datacell);
577 	datacell->priv->focus_out_id = g_signal_connect (combo, "focus-out-event",
578 							 G_CALLBACK (gdaui_data_cell_renderer_combo_focus_out_event),
579 							 datacell);
580 
581 	return GTK_CELL_EDITABLE (combo);
582 }
583 
584 
585 static void
586 gdaui_data_cell_renderer_combo_editing_done (GtkCellEditable *combo, GdauiDataCellRendererCombo *datacell)
587 {
588 	const gchar *path;
589 	gboolean canceled;
590 	GSList *list, *list_all;
591 
592 	if (datacell->priv->focus_out_id > 0) {
593 		g_signal_handler_disconnect (combo, datacell->priv->focus_out_id);
594 		datacell->priv->focus_out_id = 0;
595 	}
596 
597 	/*canceled = _gtk_combo_box_editing_canceled (GTK_COMBO_BOX (combo));*/
598 	canceled = FALSE; /* FIXME */
599 	gtk_cell_renderer_stop_editing (GTK_CELL_RENDERER (datacell), canceled);
600 	if (canceled)
601 		return;
602 
603 	list = _gdaui_combo_get_selected_ext (GDAUI_COMBO (combo),
604 					      gdaui_set_source_get_ref_n_cols (datacell->priv->source),
605 					      gdaui_set_source_get_ref_columns (datacell->priv->source));
606 	list_all = _gdaui_combo_get_selected_ext (GDAUI_COMBO (combo), 0, NULL);
607 
608 	path = g_object_get_data (G_OBJECT (combo), GDAUI_DATA_CELL_RENDERER_COMBO_PATH);
609 	g_signal_emit (datacell, text_cell_renderer_combo_signals [CHANGED], 0, path, list, list_all);
610 	g_slist_free (list);
611 	g_slist_free (list_all);
612 }
613 
614 
615 static gboolean
616 gdaui_data_cell_renderer_combo_focus_out_event (GtkWidget *widget, G_GNUC_UNUSED GdkEvent  *event,
617 						GdauiDataCellRendererCombo *datacell)
618 {
619 
620 	gdaui_data_cell_renderer_combo_editing_done (GTK_CELL_EDITABLE (widget), datacell);
621 
622 	return FALSE;
623 }
624