1 /*
2  * Copyright (C) 2009 - 2011 Vivien Malerba <malerba@gnome-db.org>
3  * Copyright (C) 2010 David King <davidk@openismus.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA  02110-1301, USA.
19  */
20 
21 #include <string.h>
22 #include <glib/gi18n-lib.h>
23 #include <gtk/gtk.h>
24 #include "gdaui-combo.h"
25 #include "gdaui-data-store.h"
26 #include "gdaui-data-selector.h"
27 
28 struct _GdauiComboPrivate {
29 	GdaDataModel     *model; /* proxied model (the one when _set_model() is called) */
30 	GdaDataModelIter *iter; /* for @model, may be NULL */
31 	GdauiDataStore   *store; /* model proxy */
32 
33 	/* columns of the model to display */
34 	gint              n_cols;
35 	gint             *cols_index;
36 
37 	/* columns' chars width if computed, for all the model's columns */
38 	gint             *cols_width;
39 
40 	gulong            changed_id; /* signal handler ID for the "changed" signal */
41 };
42 
43 static void gdaui_combo_class_init   (GdauiComboClass *klass);
44 static void gdaui_combo_init         (GdauiCombo *combo,
45 				      GdauiComboClass *klass);
46 static void gdaui_combo_set_property (GObject *object,
47 				      guint paramid,
48 				      const GValue *value,
49 				      GParamSpec *pspec);
50 static void gdaui_combo_get_property (GObject *object,
51 				      guint param_id,
52 				      GValue *value,
53 				      GParamSpec *pspec);
54 static void gdaui_combo_dispose      (GObject *object);
55 static void gdaui_combo_finalize     (GObject *object);
56 
57 static void gdaui_combo_get_preferred_width (GtkWidget *widget,
58 					     gint *minimum_size,
59 					     gint *natural_size);
60 
61 /* GdauiDataSelector interface */
62 static void              gdaui_combo_selector_init (GdauiDataSelectorIface *iface);
63 static GdaDataModel     *combo_selector_get_model (GdauiDataSelector *iface);
64 static void              combo_selector_set_model (GdauiDataSelector *iface, GdaDataModel *model);
65 static GArray           *combo_selector_get_selected_rows (GdauiDataSelector *iface);
66 static GdaDataModelIter *combo_selector_get_data_set (GdauiDataSelector *iface);
67 static gboolean          combo_selector_select_row (GdauiDataSelector *iface, gint row);
68 static void              combo_selector_unselect_row (GdauiDataSelector *iface, gint row);
69 static void              combo_selector_set_column_visible (GdauiDataSelector *iface, gint column, gboolean visible);
70 
71 enum {
72 	PROP_0,
73 	PROP_MODEL,
74 	PROP_AS_LIST
75 };
76 
77 /* get a pointer to the parents to be able to call their destructor */
78 static GObjectClass *parent_class = NULL;
79 
80 /*
81  * GdauiCombo class implementation
82  */
83 
84 GType
gdaui_combo_get_type(void)85 gdaui_combo_get_type (void)
86 {
87 	static GType type = 0;
88 
89 	if (G_UNLIKELY (type == 0)) {
90 		static const GTypeInfo info = {
91 			sizeof (GdauiComboClass),
92 			(GBaseInitFunc) NULL,
93 			(GBaseFinalizeFunc) NULL,
94 			(GClassInitFunc) gdaui_combo_class_init,
95 			NULL,
96 			NULL,
97 			sizeof (GdauiCombo),
98 			0,
99 			(GInstanceInitFunc) gdaui_combo_init,
100 			0
101 		};
102 
103 		static const GInterfaceInfo selector_info = {
104                         (GInterfaceInitFunc) gdaui_combo_selector_init,
105                         NULL,
106                         NULL
107                 };
108 
109 		type = g_type_register_static (GTK_TYPE_COMBO_BOX, "GdauiCombo", &info, 0);
110 		g_type_add_interface_static (type, GDAUI_TYPE_DATA_SELECTOR, &selector_info);
111 	}
112 	return type;
113 }
114 
115 static void
gdaui_combo_class_init(GdauiComboClass * klass)116 gdaui_combo_class_init (GdauiComboClass *klass)
117 {
118 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
119 
120 	parent_class = g_type_class_peek_parent (klass);
121 
122 	object_class->set_property = gdaui_combo_set_property;
123 	object_class->get_property = gdaui_combo_get_property;
124 	object_class->dispose = gdaui_combo_dispose;
125 	object_class->finalize = gdaui_combo_finalize;
126 
127 	GTK_WIDGET_CLASS (klass)->get_preferred_width = gdaui_combo_get_preferred_width;
128 
129 	/* add class properties */
130 	g_object_class_install_property (object_class, PROP_MODEL,
131 					 g_param_spec_object ("model", _("The data model to display"), NULL,
132 							      GDA_TYPE_DATA_MODEL,
133 							      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
134 	g_object_class_install_property (object_class, PROP_AS_LIST,
135 					 g_param_spec_boolean ("as-list", _("Display popup as list"), NULL,
136 							       FALSE,
137 							      (G_PARAM_READABLE | G_PARAM_WRITABLE)));
138 }
139 
140 static void
gdaui_combo_selector_init(GdauiDataSelectorIface * iface)141 gdaui_combo_selector_init (GdauiDataSelectorIface *iface)
142 {
143 	iface->get_model = combo_selector_get_model;
144 	iface->set_model = combo_selector_set_model;
145 	iface->get_selected_rows = combo_selector_get_selected_rows;
146 	iface->get_data_set = combo_selector_get_data_set;
147 	iface->select_row = combo_selector_select_row;
148 	iface->unselect_row = combo_selector_unselect_row;
149 	iface->set_column_visible = combo_selector_set_column_visible;
150 }
151 
152 static void selection_changed_cb (GtkComboBox *widget, gpointer data);
153 
154 static void
gdaui_combo_init(GdauiCombo * combo,G_GNUC_UNUSED GdauiComboClass * klass)155 gdaui_combo_init (GdauiCombo *combo, G_GNUC_UNUSED GdauiComboClass *klass)
156 {
157 	g_return_if_fail (GDAUI_IS_COMBO (combo));
158 
159 	/* allocate private structure */
160 	combo->priv = g_new0 (GdauiComboPrivate, 1);
161 	combo->priv->model = NULL;
162 	combo->priv->store = NULL;
163 	combo->priv->iter = NULL;
164 
165 	gtk_combo_box_set_wrap_width (GTK_COMBO_BOX (combo), 0);
166 	combo->priv->changed_id = g_signal_connect (combo, "changed",
167 						    G_CALLBACK (selection_changed_cb), NULL);
168 }
169 
170 static void
sync_iter_with_selection(GdauiCombo * combo)171 sync_iter_with_selection (GdauiCombo *combo)
172 {
173 	gint selrow = -1;
174 	GtkTreeIter iter;
175 
176 	if (! combo->priv->iter)
177 		return;
178 
179 	/* there is at most one selected row */
180 	if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter))
181 		selrow = gdaui_data_store_get_row_from_iter (combo->priv->store, &iter);
182 
183 	/* update iter */
184 	if ((selrow == -1) || !gda_data_model_iter_move_to_row (combo->priv->iter, selrow)) {
185 		gda_data_model_iter_invalidate_contents (combo->priv->iter);
186 		g_object_set (G_OBJECT (combo->priv->iter), "current-row", -1, NULL);
187 	}
188 }
189 
190 static void
selection_changed_cb(GtkComboBox * widget,G_GNUC_UNUSED gpointer data)191 selection_changed_cb (GtkComboBox *widget, G_GNUC_UNUSED gpointer data)
192 {
193 	sync_iter_with_selection ((GdauiCombo *)widget);
194 	g_signal_emit_by_name (widget, "selection-changed");
195 }
196 
197 static void
gdaui_combo_set_property(GObject * object,guint param_id,const GValue * value,GParamSpec * pspec)198 gdaui_combo_set_property (GObject *object,
199 			  guint param_id,
200 			  const GValue *value,
201 			  GParamSpec *pspec)
202 {
203 	GdauiCombo *combo = (GdauiCombo *) object;
204 
205 	g_return_if_fail (GDAUI_IS_COMBO (combo));
206 
207 	switch (param_id) {
208 	case PROP_MODEL :
209 		gdaui_combo_set_model (combo,
210 				       GDA_DATA_MODEL (g_value_get_object (value)),
211 				       0, NULL);
212 		break;
213 	case PROP_AS_LIST: {
214 		GtkStyleContext *style;
215 		style = gtk_widget_get_style_context (GTK_WIDGET (combo));
216 		if (g_value_get_boolean (value)) {
217 			gtk_style_context_remove_class (style, "gdaui-combo-normal");
218 			gtk_style_context_add_class (style, "gdaui-combo-as-list");
219 		}
220 		else {
221 			gtk_style_context_remove_class (style, "gdaui-combo-as-list");
222 			gtk_style_context_add_class (style, "gdaui-combo-normal");
223 		}
224 		break;
225 	}
226 	default :
227 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
228 		break;
229 	}
230 }
231 
232 static void
gdaui_combo_get_property(GObject * object,guint param_id,GValue * value,GParamSpec * pspec)233 gdaui_combo_get_property (GObject *object,
234 			  guint param_id,
235 			  GValue *value,
236 			  GParamSpec *pspec)
237 {
238 	GdauiCombo *combo = (GdauiCombo *) object;
239 
240 	g_return_if_fail (GDAUI_IS_COMBO (combo));
241 
242 	switch (param_id) {
243 	case PROP_MODEL :
244 		g_value_set_object (value, G_OBJECT (combo->priv->model));
245 		break;
246 	case PROP_AS_LIST: {
247 		const gchar *name;
248 		name = gtk_widget_get_name ((GtkWidget*) combo);
249 		g_value_set_boolean (value, name && !strcmp (name, "gdaui-combo-as-list") ? TRUE : FALSE);
250 		break;
251 	}
252 	default :
253 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
254 		break;
255 	}
256 }
257 
258 static void
gdaui_combo_get_preferred_width(GtkWidget * widget,gint * minimum_size,gint * natural_size)259 gdaui_combo_get_preferred_width (GtkWidget *widget,
260 				 gint *minimum_size,
261 				 gint *natural_size)
262 {
263 #define MINSIZE 50
264 	GTK_WIDGET_CLASS (parent_class)->get_preferred_width (widget, minimum_size, natural_size);
265 	if (minimum_size && (*minimum_size > MINSIZE))
266 		*minimum_size = MINSIZE;
267 	if (natural_size && (*natural_size > MINSIZE))
268 		*natural_size = MINSIZE;
269 }
270 
271 static void
gdaui_combo_dispose(GObject * object)272 gdaui_combo_dispose (GObject *object)
273 {
274 	GdauiCombo *combo = (GdauiCombo *) object;
275 
276 	g_return_if_fail (GDAUI_IS_COMBO (combo));
277 
278 	/* free objects references */
279 	if (combo->priv->store) {
280 		g_signal_handler_disconnect (combo, combo->priv->changed_id);
281 		if (combo->priv->iter)
282 			g_object_unref (combo->priv->iter);
283 		g_object_unref (G_OBJECT (combo->priv->store));
284 		combo->priv->store = NULL;
285 	}
286 
287 	/* chain to parent class */
288 	parent_class->dispose (object);
289 }
290 
291 static void
gdaui_combo_finalize(GObject * object)292 gdaui_combo_finalize (GObject *object)
293 {
294 	GdauiCombo *combo = (GdauiCombo *) object;
295 
296 	g_return_if_fail (GDAUI_IS_COMBO (combo));
297 
298 	/* free memory */
299 	if (combo->priv->cols_index)
300 		g_free (combo->priv->cols_index);
301 	if (combo->priv->cols_width)
302 		g_free (combo->priv->cols_width);
303 
304 	g_free (combo->priv);
305 	combo->priv = NULL;
306 
307 	/* chain to parent class */
308 	parent_class->finalize (object);
309 }
310 
311 /**
312  * gdaui_combo_new:
313  *
314  * Create a new GdauiCombo widget.
315  *
316  * Returns: (transfer full): the newly-created widget.
317  *
318  * Since: 4.2
319  */
320 GtkWidget *
gdaui_combo_new()321 gdaui_combo_new ()
322 {
323 	GdauiCombo *combo;
324 
325 	combo = g_object_new (GDAUI_TYPE_COMBO, NULL);
326 
327 	return GTK_WIDGET (combo);
328 }
329 
330 /**
331  * gdaui_combo_new_with_model:
332  * @model: a #GdaDataModel object.
333  * @n_cols: number of columns in the model to be shown
334  * @cols_index: an array of columns to be shown, its size must be @n_cols
335  *
336  * Create a new GdauiCombo widget with a model. See gdaui_combo_set_model() for
337  * more information about the @n_cols and @cols_index usage.
338  *
339  * Returns: (transfer full): the newly-created widget.
340  *
341  * Since: 4.2
342  */
343 GtkWidget *
gdaui_combo_new_with_model(GdaDataModel * model,gint n_cols,gint * cols_index)344 gdaui_combo_new_with_model (GdaDataModel *model, gint n_cols, gint *cols_index)
345 {
346 	GdauiCombo *combo;
347 
348 	g_return_val_if_fail (GDA_IS_DATA_MODEL (model), NULL);
349 
350 	combo = GDAUI_COMBO (gdaui_combo_new ());
351 	gdaui_combo_set_model (GDAUI_COMBO (combo), GDA_DATA_MODEL (model), n_cols, cols_index);
352 
353 	return GTK_WIDGET (combo);
354 }
355 
356 static void cell_layout_data_func (GtkCellLayout *cell_layout, GtkCellRenderer *cell,
357 				   GtkTreeModel *tree_model, GtkTreeIter *iter, GdauiCombo *combo);
358 
359 /**
360  * gdaui_combo_set_model: (skip)
361  * @combo: a #GdauiCombo widget.
362  * @model: a #GdaDataModel object.
363  * @n_cols: number of columns in the model to be shown
364  * @cols_index: (array length=n_cols): an array of columns to be shown, its size must be @n_cols
365  *
366  * Makes @combo display data stored in @model (makes the
367  * combo widget refresh its list of values and display the values contained
368  * in the model). A NULL @model will make the combo empty
369  * and disassociate the previous model, if any.
370  *
371  * if @n_cols is %0, then all the columns of @model will be displayed in @combo.
372  *
373  * Since: 4.2
374  *
375  * Deprecated: 5.2
376  */
377 void
gdaui_combo_set_model(GdauiCombo * combo,GdaDataModel * model,gint n_cols,gint * cols_index)378 gdaui_combo_set_model (GdauiCombo *combo, GdaDataModel *model, gint n_cols, gint *cols_index)
379 {
380 	gdaui_combo_set_data (combo, model, n_cols, cols_index);
381 }
382 
383 /**
384  * gdaui_combo_set_data:
385  * @combo: a #GdauiCombo widget.
386  * @model: a #GdaDataModel object to get data from.
387  * @n_cols: number of columns in the model to be shown
388  * @cols_index: (array length=n_cols): an array of columns to be shown, its size must be @n_cols
389  *
390  * Makes @combo display data stored in @model (makes the
391  * combo widget refresh its list of values and display the values contained
392  * in the model). A NULL @model will make the combo empty
393  * and disassociate the previous model, if any.
394  *
395  * if @n_cols is %0, then all the columns of @model will be displayed in @combo.
396  *
397  * Since: 5.2
398  */
399 void
gdaui_combo_set_data(GdauiCombo * combo,GdaDataModel * model,gint n_cols,gint * cols_index)400 gdaui_combo_set_data (GdauiCombo *combo, GdaDataModel *model, gint n_cols, gint *cols_index)
401 {
402 	gint ln_cols, model_ncols = -1;
403 	gint *lcols_index;
404 
405 	g_return_if_fail (GDAUI_IS_COMBO (combo));
406 	g_return_if_fail (model == NULL || GDA_IS_DATA_MODEL (model));
407 
408 	/* reset all */
409 	if (combo->priv->store) {
410 		g_object_unref (G_OBJECT (combo->priv->store));
411 		combo->priv->store = NULL;
412 		gtk_combo_box_set_model (GTK_COMBO_BOX (combo), NULL);
413 		gtk_combo_box_set_active (GTK_COMBO_BOX (combo), -1);
414 	}
415 	if (combo->priv->model) {
416 		g_object_unref (combo->priv->model);
417 		combo->priv->model = NULL;
418 	}
419 	if (combo->priv->cols_index) {
420 		g_free (combo->priv->cols_index);
421 		combo->priv->cols_index = NULL;
422 	}
423 	combo->priv->n_cols = 0;
424 	gtk_cell_layout_clear (GTK_CELL_LAYOUT (combo));
425 
426 	if (combo->priv->cols_width) {
427 		g_free (combo->priv->cols_width);
428 		combo->priv->cols_width = NULL;
429 	}
430 
431 	/* set model */
432 	if (model) {
433 		combo->priv->model = model;
434 		g_object_ref (model);
435 
436 		combo->priv->store = GDAUI_DATA_STORE (gdaui_data_store_new (combo->priv->model));
437 		gtk_combo_box_set_model (GTK_COMBO_BOX (combo), GTK_TREE_MODEL (combo->priv->store));
438 		model_ncols = gda_data_model_get_n_columns (model);
439 		combo->priv->cols_width = g_new (gint, model_ncols);
440 		gint i;
441 		for (i = 0; i < model_ncols; i++)
442 			combo->priv->cols_width [i] = -1;
443 	}
444 
445 	if (!n_cols && model) {
446 		gint i;
447 		ln_cols = model_ncols;
448 		lcols_index = g_new (gint, ln_cols);
449 		for (i = 0; i < ln_cols; i++)
450 			lcols_index [i] = i;
451 	}
452 	else {
453 		ln_cols = n_cols;
454 		lcols_index = cols_index;
455 	}
456 
457 	/* set columns with cell renderers*/
458 	if (ln_cols && model) {
459 		gint i, index;
460 		GtkCellRenderer *renderer;
461 		GdaDataHandler *dh;
462 
463 		/* local copy */
464 		combo->priv->cols_index = g_new0 (gint, ln_cols);
465 		combo->priv->n_cols = ln_cols;
466 		memcpy (combo->priv->cols_index, lcols_index, sizeof (gint) * ln_cols);
467 
468 		/* compute cell renderers' widths in chars */
469 		gint j, nrows;
470 		const GValue *cvalue;
471 		nrows = gda_data_model_get_n_rows (model);
472 		for (j = 0; j < nrows; j++) {
473 			for (i = 0; i < ln_cols; i++) {
474 				cvalue = gda_data_model_get_value_at (model, combo->priv->cols_index [i], j, NULL);
475 				if (cvalue && (G_VALUE_TYPE (cvalue) != GDA_TYPE_NULL)) {
476 					gchar *str;
477 					gint len;
478 					dh = gda_data_handler_get_default (G_VALUE_TYPE (cvalue));
479 					str = gda_data_handler_get_str_from_value (dh, cvalue);
480 					len = strlen (str);
481 					g_free (str);
482 					if (len > combo->priv->cols_width [combo->priv->cols_index [i]])
483 						combo->priv->cols_width [combo->priv->cols_index [i]] = len;
484 				}
485 			}
486 		}
487 
488 		/* create cell renderers */
489 		for (i = 0; i < ln_cols; i++) {
490 			GdaColumn *column;
491 			GType type;
492 
493 			index = combo->priv->cols_index [i];
494 
495 			column = gda_data_model_describe_column (model, index);
496 			type = gda_column_get_g_type (column);
497 			dh = gda_data_handler_get_default (type);
498 
499 			renderer = gtk_cell_renderer_text_new ();
500 			g_object_set_data (G_OBJECT (renderer), "data-handler", dh);
501 			g_object_set_data (G_OBJECT (renderer), "colnum", GINT_TO_POINTER (index));
502 
503 			gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, FALSE);
504 			gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo), renderer,
505 							    (GtkCellLayoutDataFunc) cell_layout_data_func, combo, NULL);
506 			/* Don't unref the renderer! */
507 		}
508 	}
509 
510 	if (!n_cols && model)
511 		g_free (lcols_index);
512 }
513 
514 static void
cell_layout_data_func(G_GNUC_UNUSED GtkCellLayout * cell_layout,GtkCellRenderer * cell,GtkTreeModel * tree_model,GtkTreeIter * iter,G_GNUC_UNUSED GdauiCombo * combo)515 cell_layout_data_func (G_GNUC_UNUSED GtkCellLayout *cell_layout, GtkCellRenderer *cell,
516 		       GtkTreeModel *tree_model, GtkTreeIter *iter, G_GNUC_UNUSED GdauiCombo *combo)
517 {
518 	GdaDataHandler *dh;
519 	gint colnum;
520 	const GValue *value;
521 	gchar *str;
522 
523 	dh = g_object_get_data (G_OBJECT (cell), "data-handler");
524 	colnum = GPOINTER_TO_INT (g_object_get_data  (G_OBJECT (cell), "colnum"));
525 
526 	gtk_tree_model_get (tree_model, iter, colnum, &value, -1);
527 	str = gda_data_handler_get_str_from_value (dh, value);
528 	g_object_set (G_OBJECT (cell), "text", str, NULL);
529 	g_free (str);
530 }
531 
532 /*
533  * _gdaui_combo_set_selected
534  * @combo: a #GdauiCombo widget
535  * @values: a list of #GValue
536  *
537  * Sets the currently selected row of @combo from the values stored in @values.
538  *
539  * WARNING: @values must contain one value for each column set to be displayed when the
540  * data model was associated to @combo.
541  *
542  * Returns: TRUE if a row in the model was found to match the list of values.
543  */
544 gboolean
_gdaui_combo_set_selected(GdauiCombo * combo,const GSList * values)545 _gdaui_combo_set_selected (GdauiCombo *combo, const GSList *values)
546 {
547 	g_return_val_if_fail (GDAUI_IS_COMBO (combo), FALSE);
548 	g_return_val_if_fail (combo->priv->cols_index, FALSE);
549 	g_return_val_if_fail (g_slist_length ((GSList *) values) == (guint)combo->priv->n_cols, FALSE);
550 
551 	return _gdaui_combo_set_selected_ext (combo, values, combo->priv->cols_index);
552 }
553 
554 /*
555  * _gdaui_combo_get_selected
556  * @combo: a #GdauiCombo widget
557  *
558  * Get a list of the currently selected values in @combo. The list itself must be free'd using g_slist_free(),
559  * but not the values it contains.
560  *
561  * WARNING: @values will contain one value for each column set to be displayed when the
562  * data model was associated to @combo.
563  *
564  * Returns: a new list of values, or %NULL if there is no displayed data in @combo.
565  *
566  * Since: 4.2
567  */
568 GSList *
_gdaui_combo_get_selected(GdauiCombo * combo)569 _gdaui_combo_get_selected (GdauiCombo *combo)
570 {
571 	g_return_val_if_fail (GDAUI_IS_COMBO (combo), NULL);
572 	if (!combo->priv->store)
573 		return NULL;
574 	g_return_val_if_fail (combo->priv->n_cols, NULL);
575 	g_return_val_if_fail (combo->priv->cols_index, NULL);
576 
577 	return _gdaui_combo_get_selected_ext (combo, combo->priv->n_cols, combo->priv->cols_index);
578 }
579 
580 /*
581  * _gdaui_combo_set_selected_ext
582  * @combo: a #GdauiCombo widget
583  * @values: (element-type GObject.Value): a list of #GValue objects
584  * @cols_index: (allow-none) (array): array of gint, index of column to which each value in @values corresponds, or %NULL
585  *
586  * Sets the currently selected row of @combo from the values stored in @values, assuming that
587  * these values correspond to the columns listed in @cols_index. @cols_index must contain at least as
588  * many #gint as there are elements in @values;
589  *
590  * if @cols_index is %NULL, then it is assumed that @values has the same number of columns
591  * than @combo's data
592  * model and that the values in @values are ordered in the same way as the columns of
593  * @combo's data model.
594  *
595  * Returns: TRUE if a row in the model was found to match the list of values.
596  */
597 gboolean
_gdaui_combo_set_selected_ext(GdauiCombo * combo,const GSList * values,gint * cols_index)598 _gdaui_combo_set_selected_ext (GdauiCombo *combo, const GSList *values, gint *cols_index)
599 {
600 	gint row;
601 	GdaDataProxy *proxy;
602 
603 	g_return_val_if_fail (GDAUI_IS_COMBO (combo), FALSE);
604 	g_return_val_if_fail (combo->priv->store, FALSE);
605 	g_return_val_if_fail (values, FALSE);
606 
607 	proxy = gdaui_data_store_get_proxy (combo->priv->store);
608 	row = gda_data_model_get_row_from_values (GDA_DATA_MODEL (proxy), (GSList *) values, cols_index);
609 	gtk_combo_box_set_active (GTK_COMBO_BOX (combo), row);
610 
611 	return (row >= 0) ? TRUE : FALSE;
612 }
613 
614 /*
615  * _gdaui_combo_get_selected_ext
616  * @combo: a #GdauiCombo widget
617  * @n_cols: the number of columns for which values are requested
618  * @cols_index: (array) (allow-none) (array length=n_cols): an array of @n_cols #gint indicating which column to get a value for, or %NULL
619  *
620  * Get a list of the currently selected values in @combo. The list itself must be free'd using g_slist_free(),
621  * but not the values it contains. If there is no selected value in @combo, then %NULL is returned.
622  *
623  * if n_cols equals 0 and @cols_index is %NULL, then a #GValue will be returned for each column of @combo's data model.
624  *
625  * Returns: a new list of values, or %NULL if there is no displayed data in @combo.
626  *
627  * Since: 4.2
628  */
629 GSList *
_gdaui_combo_get_selected_ext(GdauiCombo * combo,gint n_cols,gint * cols_index)630 _gdaui_combo_get_selected_ext (GdauiCombo *combo, gint n_cols, gint *cols_index)
631 {
632 	GtkTreeIter iter;
633 	GSList *retval = NULL;
634 	gint index, nbcols;
635 	GValue *value;
636 
637 	g_return_val_if_fail (GDAUI_IS_COMBO (combo), NULL);
638 	if (! combo->priv->store)
639 		return NULL;
640 	if (!n_cols) {
641 		GdaDataProxy *proxy;
642 
643 		g_return_val_if_fail (!cols_index, NULL);
644 		proxy = gdaui_data_store_get_proxy (combo->priv->store);
645 		nbcols = gda_data_model_get_n_columns ((GdaDataModel *) proxy);
646 	}
647 	else {
648 		g_return_val_if_fail (n_cols > 0, NULL);
649 		nbcols = n_cols;
650 	}
651 
652 	if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter)) {
653 		for (index = 0;	index < nbcols; index++) {
654 			gtk_tree_model_get (GTK_TREE_MODEL (combo->priv->store), &iter,
655 					    cols_index ? cols_index[index] : index, &value, -1);
656 			retval = g_slist_append (retval, value);
657 		}
658 	}
659 
660 	return retval;
661 }
662 
663 /**
664  * gdaui_combo_add_null:
665  * @combo: a #GdauiCombo widget
666  * @add_null: set to %TRUE to add a NULL value to the combo box
667  *
668  * Tells if @combo should add a special entry representing an "undefined choice", as a %NULL entry. The default is
669  * that only the available choices in @combo's model are presented.
670  *
671  * Since: 4.2
672  */
673 void
gdaui_combo_add_null(GdauiCombo * combo,gboolean add_null)674 gdaui_combo_add_null (GdauiCombo *combo, gboolean add_null)
675 {
676 	g_return_if_fail (GDAUI_IS_COMBO (combo));
677 
678 	g_object_set (G_OBJECT (combo->priv->store), "prepend-null-entry", add_null, NULL);
679 }
680 
681 /**
682  * gdaui_combo_is_null_selected:
683  * @combo: a #GdauiCombo widget
684  *
685  * Tell if the currently selected entry represents the "undefined choice" entry.
686  *
687  * Returns: %TRUE if the %NULL value is selected
688  *
689  * Since: 4.2
690  */
691 gboolean
gdaui_combo_is_null_selected(GdauiCombo * combo)692 gdaui_combo_is_null_selected (GdauiCombo *combo)
693 {
694 	gint active_row;
695 	gboolean has_undef_choice;
696 
697 	g_return_val_if_fail (GDAUI_IS_COMBO (combo), FALSE);
698 
699 	active_row = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
700 	if (active_row == -1)
701 		return TRUE;
702 
703 	g_object_get (G_OBJECT (combo->priv->store), "prepend-null-entry", &has_undef_choice, NULL);
704 	if (has_undef_choice && (active_row == 0))
705 		return TRUE;
706 
707 	return FALSE;
708 }
709 
710 /* GdauiDataSelector interface */
711 static GdaDataModel *
combo_selector_get_model(GdauiDataSelector * iface)712 combo_selector_get_model (GdauiDataSelector *iface)
713 {
714 	GdauiCombo *combo;
715 	combo = GDAUI_COMBO (iface);
716 	return combo->priv->model;
717 }
718 
719 static void
combo_selector_set_model(GdauiDataSelector * iface,GdaDataModel * model)720 combo_selector_set_model (GdauiDataSelector *iface, GdaDataModel *model)
721 {
722 	g_object_set (G_OBJECT (iface), "model", model, NULL);
723 }
724 
725 static GArray *
combo_selector_get_selected_rows(GdauiDataSelector * iface)726 combo_selector_get_selected_rows (GdauiDataSelector *iface)
727 {
728 	GtkTreeIter iter;
729 	GArray *retval = NULL;
730 	GdauiCombo *combo;
731 
732 	combo = GDAUI_COMBO (iface);
733 
734 	/* there is at most one selected row */
735 	if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter)) {
736 		gint row;
737 		row = gdaui_data_store_get_row_from_iter (combo->priv->store, &iter);
738 		if (row >=0) {
739 			if (!retval)
740 				retval = g_array_new (FALSE, FALSE, sizeof (gint));
741 			g_array_append_val (retval, row);
742 		}
743 	}
744 
745 	return retval;
746 }
747 
748 static GdaDataModelIter *
combo_selector_get_data_set(GdauiDataSelector * iface)749 combo_selector_get_data_set (GdauiDataSelector *iface)
750 {
751 	GdauiCombo *combo;
752 
753 	combo = GDAUI_COMBO (iface);
754 	if (! combo->priv->iter && combo->priv->model) {
755 		GdaDataModel *proxy;
756 		proxy = (GdaDataModel*) gdaui_data_store_get_proxy (combo->priv->store);
757 		combo->priv->iter = gda_data_model_create_iter (proxy);
758 		sync_iter_with_selection (combo);
759 	}
760 
761 	return combo->priv->iter;
762 }
763 
764 static gboolean
combo_selector_select_row(GdauiDataSelector * iface,gint row)765 combo_selector_select_row (GdauiDataSelector *iface, gint row)
766 {
767 	GdauiCombo *combo;
768 	GtkTreeIter iter;
769 
770 	combo = GDAUI_COMBO (iface);
771 	gtk_combo_box_set_active (GTK_COMBO_BOX (combo), row);
772 	if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter)) {
773 		gint srow;
774 		srow = gdaui_data_store_get_row_from_iter (combo->priv->store, &iter);
775 		if (srow == row)
776 			return TRUE;
777 	}
778 	return FALSE;
779 }
780 
781 static void
combo_selector_unselect_row(GdauiDataSelector * iface,gint row)782 combo_selector_unselect_row (GdauiDataSelector *iface, gint row)
783 {
784 	GdauiCombo *combo;
785 	GtkTreeIter iter;
786 
787 	combo = GDAUI_COMBO (iface);
788 	if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter)) {
789 		gint srow;
790 		srow = gdaui_data_store_get_row_from_iter (combo->priv->store, &iter);
791 		if (srow == row)
792 			gtk_combo_box_set_active (GTK_COMBO_BOX (combo), -1);
793 	}
794 }
795 
796 static void
combo_selector_set_column_visible(GdauiDataSelector * iface,gint column,gboolean visible)797 combo_selector_set_column_visible (GdauiDataSelector *iface, gint column, gboolean visible)
798 {
799 	GdauiCombo *combo;
800 	combo = GDAUI_COMBO (iface);
801 
802 	/* analyse existing columns */
803 	GList *cells, *list;
804 	gint cellpos;
805 	cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (iface));
806 	for (cellpos = 0, list = cells;
807 	     list;
808 	     cellpos ++, list = list->next) {
809 		gint col;
810 		col = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (list->data), "colnum"));
811 		if (col == column) {
812 			g_object_set (G_OBJECT (list->data), "visible", visible, NULL);
813 			g_list_free (cells);
814 			return;
815 		}
816 		else if (col > column)
817 			break;
818 	}
819 	g_list_free (cells);
820 
821 	/* column does not exist at this point */
822 	if (!visible || !combo->priv->model)
823 		return;
824 	if ((column < 0) || (column >= gda_data_model_get_n_columns (combo->priv->model))) {
825 		g_warning (_("Column %d out of range (0-%d)"), column, gda_data_model_get_n_columns (combo->priv->model)-1);
826 		return;
827 	}
828 
829 	GdaDataHandler *dh;
830 	if (combo->priv->cols_width[column] == -1) {
831 		/* compute column width in chars */
832 		gint j, nrows;
833 		const GValue *cvalue;
834 		nrows = gda_data_model_get_n_rows (combo->priv->model);
835 		for (j = 0; j < nrows; j++) {
836 			cvalue = gda_data_model_get_value_at (combo->priv->model, column, j, NULL);
837 			if (cvalue && (G_VALUE_TYPE (cvalue) != GDA_TYPE_NULL)) {
838 				gchar *str;
839 				gint len;
840 				dh = gda_data_handler_get_default (G_VALUE_TYPE (cvalue));
841 				str = gda_data_handler_get_str_from_value (dh, cvalue);
842 				len = strlen (str);
843 				g_free (str);
844 				if (len > combo->priv->cols_width [column])
845 					combo->priv->cols_width [column] = len;
846 			}
847 		}
848 	}
849 
850 	/* create cell renderer */
851 	GdaColumn *mcolumn;
852 	GType type;
853 	GtkCellRenderer *renderer;
854 
855 	mcolumn = gda_data_model_describe_column (combo->priv->model, column);
856 	type = gda_column_get_g_type (mcolumn);
857 	dh = gda_data_handler_get_default (type);
858 
859 	renderer = gtk_cell_renderer_text_new ();
860 	g_object_set_data (G_OBJECT (renderer), "data-handler", dh);
861 	g_object_set_data (G_OBJECT (renderer), "colnum", GINT_TO_POINTER (column));
862 
863 	gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, FALSE);
864 	gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo), renderer,
865 					    (GtkCellLayoutDataFunc) cell_layout_data_func, combo, NULL);
866 	gtk_cell_layout_reorder (GTK_CELL_LAYOUT (combo), renderer, cellpos);
867 	/* Don't unref the renderer! */
868 
869 	gint ww;
870 	g_object_get ((GObject*) iface, "wrap-width", &ww, NULL);
871 	g_object_set ((GObject*) iface, "wrap-width", 1, NULL);
872 	g_object_set ((GObject*) iface, "wrap-width", ww, NULL);
873 }
874