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