1 /*
2  * Copyright (C) 2009 Bas Driessen <bas.driessen@xobas.com>
3  * Copyright (C) 2009 - 2012 Vivien Malerba <malerba@gnome-db.org>
4  * Copyright (C) 2010 David King <davidk@openismus.com>
5  * Copyright (C) 2011 Murray Cumming <murrayc@murrayc.com>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20  * Boston, MA  02110-1301, USA.
21  */
22 
23 #include <string.h>
24 #include <gdk/gdkkeysyms.h>
25 #include <glib/gi18n-lib.h>
26 #include <libgda/libgda.h>
27 #include <libgda-ui.h>
28 #include <libgda/gda-blob-op.h>
29 #include "internal/utility.h"
30 #include "marshallers/gdaui-marshal.h"
31 #include "data-entries/gdaui-data-cell-renderer-combo.h"
32 #include "data-entries/gdaui-data-cell-renderer-info.h"
33 #include <libgda/binreloc/gda-binreloc.h>
34 #include <gtk/gtk.h>
35 #include <libgda/gda-debug-macros.h>
36 
37 static void gdaui_raw_grid_class_init (GdauiRawGridClass *klass);
38 static void gdaui_raw_grid_init (GdauiRawGrid *wid);
39 static void gdaui_raw_grid_dispose (GObject *object);
40 
41 static void gdaui_raw_grid_set_property (GObject *object,
42 					 guint param_id,
43 					 const GValue *value,
44 					 GParamSpec *pspec);
45 static void gdaui_raw_grid_get_property (GObject *object,
46 					 guint param_id,
47 					 GValue *value,
48 					 GParamSpec *pspec);
49 
50 static void create_columns_data (GdauiRawGrid *grid);
51 
52 static void proxy_sample_changed_cb (GdaDataProxy *proxy, gint sample_start, gint sample_end, GdauiRawGrid *grid);
53 static void proxy_row_updated_cb (GdaDataProxy *proxy, gint proxy_row, GdauiRawGrid *grid);
54 static void proxy_reset_pre_cb (GdaDataProxy *proxy, GdauiRawGrid *grid);
55 static void proxy_reset_cb (GdaDataProxy *proxy, GdauiRawGrid *grid);
56 static void paramlist_public_data_changed_cb (GdauiSet *paramlist, GdauiRawGrid *grid);
57 static void paramlist_param_attr_changed_cb (GdaSet *paramlist, GdaHolder *param,
58 					     const gchar *att_name, const GValue *att_value, GdauiRawGrid *grid);
59 static GError *iter_validate_set_cb (GdaDataModelIter *iter, GdauiRawGrid *grid);
60 static void iter_row_changed_cb (GdaDataModelIter *iter, gint row, GdauiRawGrid *grid);
61 
62 static void remove_all_columns (GdauiRawGrid *grid);
63 static void reset_columns_default (GdauiRawGrid *grid);
64 static void reset_columns_in_xml_layout (GdauiRawGrid *grid, xmlNodePtr grid_node);
65 
66 
67 /* GdauiDataProxy interface */
68 static void            gdaui_raw_grid_widget_init           (GdauiDataProxyIface *iface);
69 static GdaDataProxy   *gdaui_raw_grid_get_proxy             (GdauiDataProxy *iface);
70 static void            gdaui_raw_grid_set_column_editable   (GdauiDataProxy *iface, gint column, gboolean editable);
71 static void            gdaui_raw_grid_show_column_actions   (GdauiDataProxy *iface, gint column, gboolean show_actions);
72 static GtkActionGroup *gdaui_raw_grid_get_actions_group     (GdauiDataProxy *iface);
73 static gboolean        gdaui_raw_grid_widget_set_write_mode (GdauiDataProxy *iface, GdauiDataProxyWriteMode mode);
74 static GdauiDataProxyWriteMode gdaui_raw_grid_widget_get_write_mode (GdauiDataProxy *iface);
75 
76 /* GdauiDataSelector interface */
77 static void              gdaui_raw_grid_selector_init (GdauiDataSelectorIface *iface);
78 static GdaDataModel     *gdaui_raw_grid_selector_get_model (GdauiDataSelector *iface);
79 static void              gdaui_raw_grid_selector_set_model (GdauiDataSelector *iface, GdaDataModel *model);
80 static GArray           *gdaui_raw_grid_selector_get_selected_rows (GdauiDataSelector *iface);
81 static GdaDataModelIter *gdaui_raw_grid_selector_get_data_set (GdauiDataSelector *iface);
82 static gboolean          gdaui_raw_grid_selector_select_row (GdauiDataSelector *iface, gint row);
83 static void              gdaui_raw_grid_selector_unselect_row (GdauiDataSelector *iface, gint row);
84 static void              gdaui_raw_grid_selector_set_column_visible (GdauiDataSelector *iface, gint column, gboolean visible);
85 
86 typedef struct {
87 	GtkCellRenderer   *data_cell;
88 	GtkCellRenderer   *info_cell;
89 	GtkTreeViewColumn *column; /* no ref held */
90 
91 	gboolean           prog_hidden; /* status as requested by the programmer */
92 	gboolean           hidden; /* real status of the column */
93 	gchar             *title;
94 
95 	GdaHolder         *single_param;
96 	GdauiSetGroup     *group;
97 
98 	gboolean           info_shown;
99 	gboolean           data_locked; /* TRUE if no modification allowed on that column */
100         gchar             *tooltip_text;
101 } ColumnData;
102 
103 #define COLUMN_DATA(x) ((ColumnData *)(x))
104 
105 static void destroy_column_data (GdauiRawGrid *grid);
106 static ColumnData *get_column_data_for_group (GdauiRawGrid *grid, GdauiSetGroup *group);
107 static ColumnData *get_column_data_for_holder (GdauiRawGrid *grid, GdaHolder *holder);
108 static ColumnData *get_column_data_for_id (GdauiRawGrid *grid, const gchar *id);
109 
110 typedef struct {
111 	GdauiRawGridFormatFunc  func;
112 	gpointer                data;
113 	GDestroyNotify          dnotify;
114 } FormattingFuncData;
115 static FormattingFuncData *formatting_func_new (GdauiRawGridFormatFunc func,
116 						gpointer data, GDestroyNotify dnotify);
117 static void                formatting_func_destroy (FormattingFuncData *fd);
118 
119 struct _GdauiRawGridPriv
120 {
121 	GdaDataModel               *data_model;  /* data model provided by set_model() */
122 	GdaDataModelIter           *iter;        /* iterator for @store, used for its structure */
123 	GdauiSet                   *iter_info;
124 	gint                        iter_row;    /* @iter's last row in case of proxy reset */
125 	gboolean                    reset_soft;  /* tells if proxy rest was "soft" */
126 	GdauiDataStore             *store;       /* GtkTreeModel interface, using @proxy */
127 	GdaDataProxy               *proxy;       /* proxy data model, proxying @data_model */
128 
129 	GSList                     *columns_data; /* list of ColumnData */
130 	GHashTable                 *columns_hash; /* key = a GtkCellRenderer, value = a #ColumnData (no ref held) */
131 
132 	GSList                     *reordered_indexes;  /* Indexes of the reordered columns. */
133 
134 	gboolean                    default_show_info_cell;
135 	gboolean                    default_show_global_actions;
136 
137 	GtkActionGroup             *actions_group;
138 
139 	gint                        export_type; /* used by the export dialog */
140 	GdauiDataProxyWriteMode     write_mode;
141 
142 	GtkWidget                  *filter;
143 	GtkWidget                  *filter_window;
144 
145 	/* store the position of the mouse for popup menu on button press event */
146 	gint                        bin_x;
147 	gint                        bin_y;
148 
149 	GSList                     *formatting_funcs; /* list of #FormattingFuncData structures */
150 };
151 
152 /* get a pointer to the parents to be able to call their destructor */
153 static GObjectClass *parent_class = NULL;
154 
155 /* signals */
156 enum {
157 	DOUBLE_CLICKED,
158 	POPULATE_POPUP,
159 	LAST_SIGNAL
160 };
161 
162 static gint gdaui_raw_grid_signals[LAST_SIGNAL] = { 0, 0 };
163 
164 /* properties */
165 enum {
166 	PROP_0,
167 	PROP_MODEL,
168 	PROP_XML_LAYOUT,
169 	PROP_INFO_CELL_VISIBLE,
170 	PROP_GLOBAL_ACTIONS_VISIBLE
171 };
172 
173 /*
174  * Real initialization
175  */
176 
177 static gboolean tree_view_event_cb (GtkWidget *treeview, GdkEvent *event, GdauiRawGrid *grid);
178 static gint     tree_view_popup_button_pressed_cb (GtkWidget *widget, GdkEventButton *event,
179 						   GdauiRawGrid *grid);
180 
181 static void     tree_view_selection_changed_cb (GtkTreeSelection *selection, GdauiRawGrid *grid);
182 static void     tree_view_row_activated_cb     (GtkTreeView *tree_view, GtkTreePath *path,
183 						GtkTreeViewColumn *column, GdauiRawGrid *grid);
184 
185 static void action_new_cb (GtkAction *action, GdauiRawGrid *grid);
186 static void action_delete_cb (GtkToggleAction *action, GdauiRawGrid *grid);
187 static void action_commit_cb (GtkAction *action, GdauiRawGrid *grid);
188 static void action_reset_cb (GtkAction *action, GdauiRawGrid *grid);
189 static void action_first_chunck_cb (GtkAction *action, GdauiRawGrid *grid);
190 static void action_prev_chunck_cb (GtkAction *action, GdauiRawGrid *grid);
191 static void action_next_chunck_cb (GtkAction *action, GdauiRawGrid *grid);
192 static void action_last_chunck_cb (GtkAction *action, GdauiRawGrid *grid);
193 static void action_filter_cb (GtkAction *action, GdauiRawGrid *grid);
194 
195 static GtkToggleActionEntry ui_actions_t[] = {
196 	{ "ActionDelete", GTK_STOCK_REMOVE, "_Delete", NULL, N_("Delete the current record"), G_CALLBACK (action_delete_cb), FALSE},
197 };
198 
199 static GtkActionEntry ui_actions[] = {
200 	{ "ActionNew", GTK_STOCK_ADD, "_New", NULL, N_("Create a new record"), G_CALLBACK (action_new_cb)},
201 	{ "ActionCommit", GTK_STOCK_SAVE, "_Commit", NULL, N_("Commit the modifications"), G_CALLBACK (action_commit_cb)},
202 	{ "ActionReset", GTK_STOCK_CLEAR, "_Clear", NULL, N_("Clear all the modifications"), G_CALLBACK (action_reset_cb)},
203 	{ "ActionFirstChunck", GTK_STOCK_GOTO_FIRST, "_First chunck", NULL, N_("Go to first chunck"), G_CALLBACK (action_first_chunck_cb)},
204 	{ "ActionLastChunck", GTK_STOCK_GOTO_LAST, "_Last chunck", NULL, N_("Go to last chunck"), G_CALLBACK (action_last_chunck_cb)},
205 	{ "ActionPrevChunck", GTK_STOCK_GO_BACK, "_Previous chunck", NULL, N_("Go to previous chunck"), G_CALLBACK (action_prev_chunck_cb)},
206 	{ "ActionNextChunck", GTK_STOCK_GO_FORWARD, "Ne_xt chunck", NULL, N_("Go to next chunck"), G_CALLBACK (action_next_chunck_cb)},
207 	{ "ActionFilter", GTK_STOCK_FIND, "Filter", NULL, N_("Filter records"), G_CALLBACK (action_filter_cb)}
208 };
209 
210 GType
211 gdaui_raw_grid_get_type (void)
212 {
213 	static GType type = 0;
214 
215 	if (G_UNLIKELY (type == 0)) {
216 		static const GTypeInfo info = {
217 			sizeof (GdauiRawGridClass),
218 			(GBaseInitFunc) NULL,
219 			(GBaseFinalizeFunc) NULL,
220 			(GClassInitFunc) gdaui_raw_grid_class_init,
221 			NULL,
222 			NULL,
223 			sizeof (GdauiRawGrid),
224 			0,
225 			(GInstanceInitFunc) gdaui_raw_grid_init,
226 			0
227 		};
228 
229 		static const GInterfaceInfo proxy_info = {
230                         (GInterfaceInitFunc) gdaui_raw_grid_widget_init,
231                         NULL,
232                         NULL
233                 };
234 
235 		static const GInterfaceInfo selector_info = {
236                         (GInterfaceInitFunc) gdaui_raw_grid_selector_init,
237                         NULL,
238                         NULL
239                 };
240 
241 		type = g_type_register_static (GTK_TYPE_TREE_VIEW, "GdauiRawGrid", &info, 0);
242 		g_type_add_interface_static (type, GDAUI_TYPE_DATA_PROXY, &proxy_info);
243 		g_type_add_interface_static (type, GDAUI_TYPE_DATA_SELECTOR, &selector_info);
244 	}
245 
246 	return type;
247 }
248 
249 static void
250 gdaui_raw_grid_widget_init (GdauiDataProxyIface *iface)
251 {
252 	iface->get_proxy = gdaui_raw_grid_get_proxy;
253 	iface->set_column_editable = gdaui_raw_grid_set_column_editable;
254 	iface->show_column_actions = gdaui_raw_grid_show_column_actions;
255 	iface->get_actions_group = gdaui_raw_grid_get_actions_group;
256 	iface->set_write_mode = gdaui_raw_grid_widget_set_write_mode;
257 	iface->get_write_mode = gdaui_raw_grid_widget_get_write_mode;
258 }
259 
260 static void
261 gdaui_raw_grid_selector_init (GdauiDataSelectorIface *iface)
262 {
263 	iface->get_model = gdaui_raw_grid_selector_get_model;
264 	iface->set_model = gdaui_raw_grid_selector_set_model;
265 	iface->get_selected_rows = gdaui_raw_grid_selector_get_selected_rows;
266 	iface->get_data_set = gdaui_raw_grid_selector_get_data_set;
267 	iface->select_row = gdaui_raw_grid_selector_select_row;
268 	iface->unselect_row = gdaui_raw_grid_selector_unselect_row;
269 	iface->set_column_visible = gdaui_raw_grid_selector_set_column_visible;
270 }
271 
272 static void
273 gdaui_raw_grid_class_init (GdauiRawGridClass *klass)
274 {
275 	GObjectClass  *object_class = G_OBJECT_CLASS (klass);
276 	parent_class = g_type_class_peek_parent (klass);
277 
278 	/**
279 	 * GdauiRawGrid::double-clicked:
280 	 * @grid: GdauiRawGrid
281 	 * @row: the row that was double clicked
282 	 *
283 	 * Emitted when the user double clicks on a row
284 	 */
285 	gdaui_raw_grid_signals[DOUBLE_CLICKED] =
286 		g_signal_new ("double-clicked",
287                               G_TYPE_FROM_CLASS (object_class),
288                               G_SIGNAL_RUN_FIRST,
289                               G_STRUCT_OFFSET (GdauiRawGridClass, double_clicked),
290                               NULL, NULL,
291                               _gdaui_marshal_VOID__INT, G_TYPE_NONE,
292                               1, G_TYPE_INT);
293 
294 	/**
295 	 * GdauiRawGrid::populate-popup:
296 	 * @grid: GdauiRawGrid
297 	 * @menu: a #GtkMenu to modify
298 	 *
299 	 * Connect this signal and modify the popup menu.
300 	 */
301 	gdaui_raw_grid_signals[POPULATE_POPUP] =
302 		g_signal_new ("populate-popup",
303                               G_TYPE_FROM_CLASS (object_class),
304                               G_SIGNAL_RUN_FIRST,
305                               G_STRUCT_OFFSET (GdauiRawGridClass, populate_popup),
306                               NULL, NULL,
307                               _gdaui_marshal_VOID__OBJECT, G_TYPE_NONE,
308                               1, GTK_TYPE_MENU);
309 
310 	object_class->dispose = gdaui_raw_grid_dispose;
311 
312 	/* Properties */
313         object_class->set_property = gdaui_raw_grid_set_property;
314         object_class->get_property = gdaui_raw_grid_get_property;
315 	g_object_class_install_property (object_class, PROP_MODEL,
316                                          g_param_spec_object ("model", _("Data to display"), NULL, GDA_TYPE_DATA_MODEL,
317 							      G_PARAM_READABLE | G_PARAM_WRITABLE));
318 
319 	g_object_class_install_property (object_class, PROP_XML_LAYOUT,
320 					 g_param_spec_pointer ("xml-layout",
321 							       _("Pointer to an XML layout specification (as an xmlNodePtr to a <gdaui_grid> node)"), NULL,
322 							       G_PARAM_WRITABLE));
323 	g_object_class_install_property (object_class, PROP_INFO_CELL_VISIBLE,
324                                          g_param_spec_boolean ("info-cell-visible", NULL, _("Info cell visible"), FALSE,
325                                                                G_PARAM_READABLE | G_PARAM_WRITABLE));
326 
327 	g_object_class_install_property (object_class, PROP_GLOBAL_ACTIONS_VISIBLE,
328                                          g_param_spec_boolean ("global-actions-visible", NULL, _("Global Actions visible"), FALSE,
329                                                                G_PARAM_READABLE | G_PARAM_WRITABLE));
330 }
331 
332 static gboolean
333 gdaui_raw_grid_query_tooltip (GtkWidget   *widget,
334 			      gint         x,
335 			      gint         y,
336 			      gboolean     keyboard_tip,
337 			      GtkTooltip  *tooltip,
338 			      G_GNUC_UNUSED gpointer     data)
339 {
340 	GtkTreeView *tree_view = GTK_TREE_VIEW (widget);
341 
342 	if (!gtk_tree_view_get_tooltip_context (tree_view, &x, &y,
343 						keyboard_tip,
344 						NULL, NULL, NULL))
345 		return FALSE;
346 
347 	gint position = 0;
348 	gint col_x = 0;
349 	GList *list, *columns = gtk_tree_view_get_columns (tree_view);
350 	for (list = columns; list; list = list->next) {
351 		GtkTreeViewColumn *column = list->data;
352 		if (x >= col_x && x < (col_x + gtk_tree_view_column_get_width (column)))
353 			break;
354 		else
355 			col_x += gtk_tree_view_column_get_width (column);
356 		++position;
357 	}
358 	if (columns)
359 		g_list_free (columns);
360 	if (!list)
361 		return FALSE;
362 
363 	GdauiRawGrid *grid = GDAUI_RAW_GRID (tree_view);
364 	ColumnData *cdata = COLUMN_DATA (g_slist_nth (grid->priv->columns_data, position)->data);
365 	g_return_val_if_fail (cdata, FALSE);
366 
367 	if (! cdata->tooltip_text)
368 		return FALSE;
369 
370 	gtk_tooltip_set_markup (tooltip, cdata->tooltip_text);
371 
372 	return TRUE;
373 }
374 
375 static void
376 gdaui_raw_grid_init (GdauiRawGrid *grid)
377 {
378 	GtkTreeView *tree_view;
379 	GtkTreeSelection *selection;
380 
381 	grid->priv = g_new0 (GdauiRawGridPriv, 1);
382 	grid->priv->store = NULL;
383 	grid->priv->iter = NULL;
384 	grid->priv->iter_row = -1;
385 	grid->priv->proxy = NULL;
386 	grid->priv->default_show_info_cell = FALSE;
387 	grid->priv->default_show_global_actions = TRUE;
388 	grid->priv->columns_data = NULL;
389 	grid->priv->columns_hash = g_hash_table_new (NULL, NULL);
390 	grid->priv->export_type = 1;
391 	grid->priv->write_mode = GDAUI_DATA_PROXY_WRITE_ON_DEMAND;
392 
393 	tree_view = GTK_TREE_VIEW (grid);
394 	gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (tree_view), TRUE);
395 	gtk_tree_view_set_enable_search (GTK_TREE_VIEW (tree_view), TRUE);
396 	g_signal_connect (G_OBJECT (tree_view), "event",
397 			  G_CALLBACK (tree_view_event_cb), grid);
398 	_gdaui_setup_right_click_selection_on_treeview (tree_view);
399 	g_signal_connect (G_OBJECT (tree_view), "button-press-event",
400                           G_CALLBACK (tree_view_popup_button_pressed_cb), grid);
401 	gtk_tree_view_set_enable_search (tree_view, FALSE);
402 
403 	/* selection and signal handling */
404 	selection = gtk_tree_view_get_selection (tree_view);
405 	gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
406 	g_signal_connect (G_OBJECT (selection), "changed",
407 			  G_CALLBACK (tree_view_selection_changed_cb), grid);
408 	g_signal_connect (G_OBJECT (tree_view), "row-activated",
409 			  G_CALLBACK (tree_view_row_activated_cb), grid);
410 
411 	/* tooltip */
412 	g_object_set (G_OBJECT (grid), "has-tooltip", TRUE, NULL);
413 	g_signal_connect (grid, "query-tooltip",
414 			  G_CALLBACK (gdaui_raw_grid_query_tooltip), NULL);
415 
416 	/* action group */
417 	grid->priv->actions_group = gtk_action_group_new ("Actions");
418 	gtk_action_group_set_translation_domain (grid->priv->actions_group, GETTEXT_PACKAGE);
419 	gtk_action_group_add_actions (grid->priv->actions_group, ui_actions, G_N_ELEMENTS (ui_actions), grid);
420 	gtk_action_group_add_toggle_actions (grid->priv->actions_group, ui_actions_t, G_N_ELEMENTS (ui_actions_t), grid);
421 
422 	grid->priv->filter = NULL;
423 	grid->priv->filter_window = NULL;
424 
425 	grid->priv->formatting_funcs = NULL;
426 }
427 
428 /**
429  * gdaui_raw_grid_new:
430  * @model: a #GdaDataModel
431  *
432  * Creates a new #GdauiRawGrid widget suitable to display the data in @model
433  *
434  * Returns: (transfer full): the new widget
435  *
436  * Since: 4.2
437  */
438 GtkWidget *
439 gdaui_raw_grid_new (GdaDataModel *model)
440 {
441 	GtkWidget *grid;
442 
443 	g_return_val_if_fail (!model || GDA_IS_DATA_MODEL (model), NULL);
444 
445 	grid = (GtkWidget *) g_object_new (GDAUI_TYPE_RAW_GRID, "model", model, NULL);
446 
447 	return grid;
448 }
449 
450 static void gdaui_raw_grid_clean (GdauiRawGrid *grid);
451 static void
452 gdaui_raw_grid_dispose (GObject *object)
453 {
454 	GdauiRawGrid *grid;
455 
456 	g_return_if_fail (GDAUI_IS_RAW_GRID (object));
457 	grid = GDAUI_RAW_GRID (object);
458 
459 	if (grid->priv) {
460 		gdaui_raw_grid_clean (grid);
461 
462 		if (grid->priv->actions_group) {
463 			g_object_unref (G_OBJECT (grid->priv->actions_group));
464 			grid->priv->actions_group = NULL;
465 		}
466 
467 		if (grid->priv->filter)
468 			gtk_widget_destroy (grid->priv->filter);
469 		if (grid->priv->filter_window)
470 			gtk_widget_destroy (grid->priv->filter_window);
471 
472 		if (grid->priv->formatting_funcs) {
473 			g_slist_foreach (grid->priv->formatting_funcs, (GFunc) formatting_func_destroy,
474 					 NULL);
475 			g_slist_free (grid->priv->formatting_funcs);
476 		}
477 
478 		/* the private area itself */
479 		g_free (grid->priv);
480 		grid->priv = NULL;
481 	}
482 
483 	/* for the parent class */
484 	parent_class->dispose (object);
485 }
486 
487 static void
488 gdaui_raw_grid_set_property (GObject *object,
489 			     guint param_id,
490 			     const GValue *value,
491 			     GParamSpec *pspec)
492 {
493 	GdauiRawGrid *grid;
494 
495         grid = GDAUI_RAW_GRID (object);
496         if (grid->priv) {
497                 switch (param_id) {
498 		case PROP_MODEL: {
499 			GdaDataModel *model = (GdaDataModel*) g_value_get_object (value);
500 
501 			if (model)
502 				g_return_if_fail (GDA_IS_DATA_MODEL (model));
503 			else
504 				return;
505 
506 			if (grid->priv->proxy) {
507 				/* data model has been changed */
508 				if (GDA_IS_DATA_PROXY (model)) {
509 					/* clean all */
510 					gdaui_raw_grid_clean (grid);
511 					g_assert (!grid->priv->proxy);
512 				}
513 				else
514 					g_object_set (G_OBJECT (grid->priv->proxy), "model", model, NULL);
515 			}
516 
517 			if (!grid->priv->proxy) {
518 				/* first time setting */
519 				if (GDA_IS_DATA_PROXY (model))
520 					grid->priv->proxy = g_object_ref (G_OBJECT (model));
521 				else
522 					grid->priv->proxy = GDA_DATA_PROXY (gda_data_proxy_new (model));
523 
524 				g_signal_connect (grid->priv->proxy, "reset",
525 						  G_CALLBACK (proxy_reset_pre_cb), grid);
526 				grid->priv->data_model = gda_data_proxy_get_proxied_model (grid->priv->proxy);
527 
528 				g_signal_connect (grid->priv->proxy, "sample-changed",
529 						  G_CALLBACK (proxy_sample_changed_cb), grid);
530 				g_signal_connect (grid->priv->proxy, "row-updated",
531 						  G_CALLBACK (proxy_row_updated_cb), grid);
532 				g_signal_connect (grid->priv->proxy, "reset",
533 						  G_CALLBACK (proxy_reset_cb), grid);
534 
535 				grid->priv->iter = gda_data_model_create_iter (GDA_DATA_MODEL (grid->priv->proxy));
536 				grid->priv->iter_row = -1;
537 				grid->priv->iter_info = gdaui_set_new (GDA_SET (grid->priv->iter));
538 
539 				g_signal_connect (grid->priv->iter_info, "public-data-changed",
540 						  G_CALLBACK (paramlist_public_data_changed_cb), grid);
541 				g_signal_connect (grid->priv->iter, "holder-attr-changed",
542 						  G_CALLBACK (paramlist_param_attr_changed_cb), grid);
543 
544 				g_signal_connect (grid->priv->iter, "row-changed",
545 						  G_CALLBACK (iter_row_changed_cb), grid);
546 				g_signal_connect (grid->priv->iter, "validate-set",
547 						  G_CALLBACK (iter_validate_set_cb), grid);
548 
549 				gda_data_model_iter_invalidate_contents (grid->priv->iter);
550 
551 				grid->priv->store = GDAUI_DATA_STORE (gdaui_data_store_new ((GdaDataModel*) grid->priv->proxy));
552 				gtk_tree_view_set_model ((GtkTreeView *) grid,
553 							 GTK_TREE_MODEL (grid->priv->store));
554 
555 				create_columns_data (grid);
556 				reset_columns_default (grid);
557 
558 				g_signal_emit_by_name (object, "proxy-changed", grid->priv->proxy);
559 			}
560 
561 			break;
562 		}
563 
564 		case PROP_XML_LAYOUT: {
565 			/* node should be a "gdaui_grid" node */
566 			xmlNodePtr node = g_value_get_pointer (value);
567 			if (node) {
568 				g_return_if_fail (node);
569 				g_return_if_fail (!strcmp ((gchar*) node->name, "gdaui_grid"));
570 
571 				reset_columns_in_xml_layout (grid, node);
572 			}
573 			else
574 				reset_columns_default (grid);
575 			break;
576 		}
577 
578 		case PROP_INFO_CELL_VISIBLE: {
579 			GSList *list;
580 			gboolean show = g_value_get_boolean (value);
581 			grid->priv->default_show_info_cell = show;
582 
583 			for (list = grid->priv->columns_data; list; list = list->next) {
584 				COLUMN_DATA (list->data)->info_shown = show;
585 				g_object_set (G_OBJECT (COLUMN_DATA (list->data)->info_cell), "visible",
586 					      show, NULL);
587 			}
588 			break;
589 		}
590 
591 		case PROP_GLOBAL_ACTIONS_VISIBLE:
592 			gtk_action_group_set_visible (grid->priv->actions_group, g_value_get_boolean (value));
593 			break;
594 
595 		default:
596 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
597 			break;
598                 }
599         }
600 }
601 
602 static void
603 gdaui_raw_grid_get_property (GObject *object,
604 			     guint param_id,
605 			     GValue *value,
606 			     GParamSpec *pspec)
607 {
608 	GdauiRawGrid *grid;
609 
610         grid = GDAUI_RAW_GRID (object);
611         if (grid->priv) {
612                 switch (param_id) {
613 		case PROP_MODEL:
614 			g_value_set_object (value, grid->priv->data_model);
615 			break;
616 		case PROP_INFO_CELL_VISIBLE:
617 			g_value_set_boolean(value, grid->priv->default_show_info_cell);
618 			break;
619 		case PROP_GLOBAL_ACTIONS_VISIBLE:
620 			g_value_set_boolean(value, grid->priv->default_show_global_actions);
621 			break;
622 
623 		default:
624 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
625 			break;
626                 }
627         }
628 }
629 
630 /*
631  * _gdaui_raw_grid_get_selection
632  * @grid: a #GdauiRawGrid widget
633  *
634  * Returns the list of the currently selected rows in a #GdauiRawGrid widget.
635  * The returned value is a list of integers, which represent each of the selected rows.
636  *
637  * If new rows have been inserted, then those new rows will have a row number equal to -1.
638  *
639  * Returns: a new list, should be freed (by calling g_list_free) when no longer needed.
640  */
641 GList *
642 _gdaui_raw_grid_get_selection (GdauiRawGrid *grid)
643 {
644 	GtkTreeSelection *selection;
645 	GList *selected_rows;
646 
647 	g_return_val_if_fail (grid && GDAUI_IS_RAW_GRID (grid), NULL);
648 	g_return_val_if_fail (grid->priv, NULL);
649 
650 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (grid));
651 	selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
652 	if (selected_rows) {
653 		GList *list, *retlist = NULL;
654 		GtkTreeIter iter;
655 		gint row;
656 
657 		list = selected_rows;
658 		for (list = selected_rows; list; list = list->next) {
659 			if (gtk_tree_model_get_iter (GTK_TREE_MODEL (grid->priv->store), &iter,
660 						     (GtkTreePath *)(list->data))) {
661 				gtk_tree_model_get (GTK_TREE_MODEL (grid->priv->store), &iter,
662 						    GDAUI_DATA_STORE_COL_MODEL_ROW, &row, -1);
663 				retlist = g_list_prepend (retlist, GINT_TO_POINTER (row));
664 			}
665 		}
666 		g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
667 		g_list_free (selected_rows);
668 		return g_list_reverse (retlist);
669 	}
670 	else
671 		return NULL;
672 }
673 
674 
675 
676 /*
677  * creates a new string where underscores '_' are replaced by double underscores '__'
678  * WARNING: the old string is free'ed so it is possible to do "str=double_underscores(str);"
679  */
680 static gchar *
681 add_double_underscores (gchar *str)
682 {
683         gchar **arr;
684         gchar *ret;
685 
686         arr = g_strsplit (str, "_", 0);
687         ret = g_strjoinv ("__", arr);
688         g_strfreev (arr);
689 	g_free (str);
690 
691         return ret;
692 }
693 
694 /*
695  * Creates a NEW string
696  */
697 static gchar *
698 remove_double_underscores (gchar *str)
699 {
700         gchar *ptr1, *ptr2, *ret;
701 
702 	ret = g_new (gchar, strlen (str) + 1);
703 	for (ptr1 = str, ptr2 = ret; *ptr1; ptr1++, ptr2++) {
704 		*ptr2 = *ptr1;
705 		if ((ptr1[0] == '_') && (ptr1[1] == '_'))
706 			ptr1++;
707 	}
708 	*ptr2 = 0;
709 
710         return ret;
711 }
712 
713 static void     cell_value_set_attributes (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
714 					   GtkTreeModel *tree_model, GtkTreeIter *iter, GdauiRawGrid *grid);
715 static void     cell_info_set_attributes  (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
716 					   GtkTreeModel *tree_model, GtkTreeIter *iter, GdauiRawGrid *grid);
717 
718 static void     data_cell_value_changed (GtkCellRenderer *renderer, const gchar *path,
719 					 const GValue *new_value, GdauiRawGrid *grid);
720 static void     data_cell_values_changed (GtkCellRenderer *renderer, const gchar *path,
721 					  GSList *new_values, GSList *all_new_values, GdauiRawGrid *grid);
722 static void     data_cell_status_changed (GtkCellRenderer *renderer, const gchar *path,
723 					  GdaValueAttribute requested_action, GdauiRawGrid *grid);
724 static void     treeview_column_clicked_cb (GtkTreeViewColumn *tree_column, GdauiRawGrid *grid);
725 
726 /*
727  * Creates the GtkTreeView's columns, from the grid->priv->store GtkTreeModel
728  */
729 static void
730 create_columns_data (GdauiRawGrid *grid)
731 {
732 	gint i;
733 	GSList *list;
734 
735 	/* Creation of the columns in the treeview, to fit the parameters in grid->priv->iter param list */
736 	for (i = 0, list = grid->priv->iter_info->groups_list;
737 	     list;
738 	     i++, list = list->next) {
739 		GdaHolder *param;
740 		GdauiSetGroup *group;
741 		GtkCellRenderer *renderer;
742 		ColumnData *cdata;
743 		GdaSetGroup *sg;
744 
745 		group = GDAUI_SET_GROUP (list->data);
746 		sg = gdaui_set_group_get_group (group);
747 		/* update the list of columns data */
748 		cdata = get_column_data_for_group (grid, group);
749 		if (!cdata) {
750 			cdata = g_new0 (ColumnData, 1);
751 			cdata->group = group;
752 			cdata->info_shown = grid->priv->default_show_info_cell;
753 			cdata->data_locked = FALSE;
754 			cdata->tooltip_text = NULL;
755 			grid->priv->columns_data = g_slist_append (grid->priv->columns_data, cdata);
756 		}
757 
758 		/* create renderers */
759 		if (gdaui_set_group_get_source (group)) {
760 			/* parameters depending on a GdaDataModel */
761 			gchar *title;
762 			/*
763 			GSList *nodes;
764 
765 			for (nodes = group->group->nodes; nodes; nodes = nodes->next) {
766 				if (gda_holder_get_not_null (GDA_HOLDER (GDA_SET_NODE (nodes->data)->holder))) {
767 					nullok = FALSE;
768 					break;
769 				}
770 			}
771 			*/
772 
773 			/* determine title */
774 			if (gda_set_group_get_n_nodes (sg) == 1)
775 				title = (gchar *) g_object_get_data (G_OBJECT (gda_set_node_get_holder (gda_set_group_get_node (sg))),
776 								     "name");
777 			else
778 				title = (gchar *) g_object_get_data (G_OBJECT (gda_set_source_get_data_model (gda_set_group_get_source (sg))),
779 								     "name");
780 
781 			if (title)
782 				title = add_double_underscores (g_strdup (title));
783 			else
784 				/* FIXME: find a better label */
785 				title = g_strdup (_("Value"));
786 
787 			/* FIXME: if nullok is FALSE, then set the column title in bold */
788 			cdata->tooltip_text = g_strdup (_("Can't be NULL"));
789 			renderer = gdaui_data_cell_renderer_combo_new (grid->priv->iter_info, gdaui_set_group_get_source (group));
790 			cdata->data_cell = g_object_ref_sink ((GObject*) renderer);
791 			g_hash_table_insert (grid->priv->columns_hash, renderer, cdata);
792 			g_free (cdata->title);
793 			cdata->title = title;
794 
795 			g_signal_connect (G_OBJECT (renderer), "changed",
796 					  G_CALLBACK (data_cell_values_changed), grid);
797 		}
798 		else {
799 			/* single direct parameter */
800 			GType g_type;
801 			const gchar *plugin = NULL;
802 			const GValue *plugin_val;
803 			gchar *title;
804 			gint model_col;
805 
806 			param = gda_set_node_get_holder (gda_set_group_get_node (sg));
807 			cdata->single_param = param;
808 			g_type = gda_holder_get_g_type (param);
809 
810 			if (gda_holder_get_not_null (param))
811 				cdata->tooltip_text = g_strdup (_("Can't be NULL"));
812 
813 			g_object_get (G_OBJECT (param), "name", &title, NULL);
814 			if (title && *title)
815 				title = add_double_underscores (title);
816 			else
817 				title = NULL;
818 			if (!title)
819 				title = g_strdup (_("No title"));
820 
821 			plugin_val = gda_holder_get_attribute (param, GDAUI_ATTRIBUTE_PLUGIN);
822 			if (plugin_val) {
823 				if (G_VALUE_TYPE (plugin_val) == G_TYPE_STRING)
824 					plugin = g_value_get_string (plugin_val);
825 				else
826 					g_warning (_("The '%s' attribute should be a G_TYPE_STRING value"),
827 						   GDAUI_ATTRIBUTE_PLUGIN);
828 			}
829 			renderer = _gdaui_new_cell_renderer (g_type, plugin);
830 			cdata->data_cell = g_object_ref_sink ((GObject*) renderer);
831 			g_hash_table_insert (grid->priv->columns_hash, renderer, cdata);
832 			g_free (cdata->title);
833 			cdata->title = title;
834 
835 			model_col = g_slist_index (((GdaSet *)grid->priv->iter)->holders, param);
836 			g_object_set_data (G_OBJECT (renderer), "model_col", GINT_TO_POINTER (model_col));
837 
838 			g_signal_connect (G_OBJECT (renderer), "changed",
839 					  G_CALLBACK (data_cell_value_changed), grid);
840 		}
841 
842 		/* warning: when making modifications to renderer's attributes or signal connect,
843 		 * also make the same modifications to paramlist_param_attr_changed_cb() */
844 
845 		/* settings and signals */
846 		g_object_set (G_OBJECT (renderer), "editable", !cdata->data_locked, NULL);
847 		if (g_object_class_find_property (G_OBJECT_GET_CLASS (renderer), "set-default-if-invalid"))
848 			g_object_set (G_OBJECT (renderer), "set-default-if-invalid", TRUE, NULL);
849 
850 		/* Adding the GValue's information cell as another GtkCellRenderer */
851 		renderer = gdaui_data_cell_renderer_info_new (grid->priv->store, grid->priv->iter, group);
852 		cdata->info_cell = g_object_ref_sink ((GObject*) renderer);
853 		g_hash_table_insert (grid->priv->columns_hash, renderer, cdata);
854 		g_signal_connect (G_OBJECT (renderer), "status-changed",
855 				  G_CALLBACK (data_cell_status_changed), grid);
856 		g_object_set (G_OBJECT (renderer), "visible", cdata->info_shown, NULL);
857 	}
858 }
859 
860 /* keep track of the columns shown and hidden */
861 static void
862 column_visibility_changed (GtkTreeViewColumn *column, G_GNUC_UNUSED GParamSpec *pspec, ColumnData *cdata)
863 {
864 	cdata->hidden = !gtk_tree_view_column_get_visible (column);
865 }
866 
867 /*
868  * Remove all columns from @grid, does not modify ColumnData
869  */
870 static void
871 remove_all_columns (GdauiRawGrid *grid)
872 {
873 	GList *columns, *list;
874 	columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (grid));
875 	for (list = columns; list; list = list->next)
876 		gtk_tree_view_remove_column (GTK_TREE_VIEW (grid),
877 					     (GtkTreeViewColumn*) (list->data));
878 	if (columns)
879 		g_list_free (columns);
880 }
881 
882 /*
883  * Create a treeview column and places it at posision @pos, or las last position
884  * if pos < 0
885  */
886 static void
887 create_tree_view_column (GdauiRawGrid *grid, ColumnData *cdata, gint pos)
888 {
889 	GtkTreeViewColumn *column;
890 	gint n;
891 	n = gtk_tree_view_insert_column_with_data_func (GTK_TREE_VIEW (grid), pos,
892 							cdata->title,
893 							cdata->data_cell,
894 							(GtkTreeCellDataFunc) cell_value_set_attributes,
895 							grid, NULL);
896 	column = gtk_tree_view_get_column (GTK_TREE_VIEW (grid), pos >= 0 ? pos : n-1);
897 	cdata->column = column;
898 	g_object_set_data (G_OBJECT (column), "data_renderer", cdata->data_cell);
899 	g_object_set_data (G_OBJECT (column), "__gdaui_group", cdata->group);
900 
901 	gtk_tree_view_column_pack_end (column, cdata->info_cell, FALSE);
902 	gtk_tree_view_column_set_cell_data_func (column, cdata->info_cell,
903 						 (GtkTreeCellDataFunc) cell_info_set_attributes,
904 						 grid, NULL);
905 
906 	g_signal_connect (column, "notify::visible",
907 			  G_CALLBACK (column_visibility_changed), cdata);
908 
909 	/* Sorting data */
910 	gtk_tree_view_column_set_clickable (column, TRUE);
911 	g_signal_connect (G_OBJECT (column), "clicked",
912 			  G_CALLBACK (treeview_column_clicked_cb), grid);
913 }
914 
915 static void
916 reset_columns_default (GdauiRawGrid *grid)
917 {
918 	GSList *list;
919 
920 	remove_all_columns (grid);
921 	for (list = grid->priv->columns_data; list; list = list->next) {
922 		ColumnData *cdata = COLUMN_DATA (list->data);
923 		if (cdata->prog_hidden)
924 			continue;
925 
926 		/* create treeview column */
927 		create_tree_view_column (grid, cdata, -1);
928 	}
929 }
930 
931 static void
932 reset_columns_in_xml_layout (GdauiRawGrid *grid, xmlNodePtr grid_node)
933 {
934 	remove_all_columns (grid);
935 
936 	xmlNodePtr child;
937 	for (child = grid_node->children; child; child = child->next) {
938 		if (child->type != XML_ELEMENT_NODE)
939 			continue;
940 
941 		if (xmlStrEqual (child->name, BAD_CAST "gdaui_entry")) {
942 			xmlChar *name;
943 			name = xmlGetProp (child, BAD_CAST "name");
944 			if (!name)
945 				continue;
946 
947 			/* find column data */
948 			ColumnData *cdata;
949 			cdata = get_column_data_for_id (grid, (gchar*) name);
950 			if (! cdata) {
951 				g_warning ("Could not find column named '%s', ignoring",
952 					   (gchar*) name);
953 				xmlFree (name);
954 				continue;
955 			}
956 			xmlFree (name);
957 
958 			/* create treeview column */
959 			cdata->prog_hidden = FALSE;
960 			cdata->hidden = FALSE;
961 			create_tree_view_column (grid, cdata, -1);
962 
963 			/* plugin */
964 			xmlChar *plugin;
965 			plugin = xmlGetProp (child, BAD_CAST "plugin");
966 			if (plugin && cdata->single_param) {
967 				GValue *value;
968 				value = gda_value_new_from_string ((gchar*) plugin, G_TYPE_STRING);
969 				gda_holder_set_attribute_static (cdata->single_param,
970 								 GDAUI_ATTRIBUTE_PLUGIN, value);
971 				gda_value_free (value);
972 			}
973 			if (plugin)
974 				xmlFree (plugin);
975 
976 			/* column label */
977 			xmlChar *prop;
978 			prop = xmlGetProp (child, BAD_CAST "label");
979 			if (prop) {
980 				g_free (cdata->title);
981 				cdata->title = g_strdup ((gchar*) prop);
982 				xmlFree (prop);
983 				gtk_tree_view_column_set_title (cdata->column, cdata->title);
984 			}
985 		}
986 	}
987 }
988 
989 static gboolean
990 remove_formatting_function (GdauiRawGrid *grid, GdauiRawGridFormatFunc func)
991 {
992 	if (grid->priv->formatting_funcs) {
993 		GSList *list;
994 		for (list = grid->priv->formatting_funcs; list; list = list->next) {
995 			FormattingFuncData *fd = (FormattingFuncData*) list->data;
996 			if (fd->func == func) {
997 				grid->priv->formatting_funcs = g_slist_remove (grid->priv->formatting_funcs, fd);
998 				formatting_func_destroy (fd);
999 				return TRUE;
1000 			}
1001 		}
1002 	}
1003 	return FALSE;
1004 }
1005 
1006 /**
1007  * gdaui_raw_grid_add_formatting_function:
1008  * @grid: a #GdauiRawGrid widget
1009  * @func: a #GdauiRawGridFormatFunc function pointer
1010  * @data: (allow-none): a pointer to pass to the @func function when called
1011  * @dnotify: (allow-none): destroy notifier for @data
1012  *
1013  * This function allows you to specify that the @func function needs to be called
1014  * whenever the rendering of a cell in @grid needs to be done. It is similar in purpose
1015  * to the gtk_tree_view_column_set_cell_data_func() function.
1016  *
1017  * Since: 5.0.3
1018  */
1019 void
1020 gdaui_raw_grid_add_formatting_function (GdauiRawGrid *grid, GdauiRawGridFormatFunc func,
1021 					gpointer data, GDestroyNotify dnotify)
1022 {
1023 	g_return_if_fail (GDAUI_IS_RAW_GRID (grid));
1024 	g_return_if_fail (func);
1025 
1026 	/* remove existing function */
1027 	remove_formatting_function (grid, func);
1028 
1029 	/* add new one */
1030 	FormattingFuncData *fd;
1031 	fd = formatting_func_new (func, data, dnotify);
1032 	grid->priv->formatting_funcs = g_slist_append (grid->priv->formatting_funcs, fd);
1033 
1034 	/* redraw grid to take new function into account */
1035 	TO_IMPLEMENT;
1036 }
1037 
1038 /**
1039  * gdaui_raw_grid_remove_formatting_function:
1040  * @grid: a #GdauiRawGrid widget
1041  * @func: (scope notified): a #GdauiRawGridFormatFunc function pointer
1042  *
1043  * This function undoes what has been specified before by gdaui_raw_grid_add_formatting_function()
1044  *
1045  * Since: 5.0.3
1046  */
1047 void
1048 gdaui_raw_grid_remove_formatting_function (GdauiRawGrid *grid, GdauiRawGridFormatFunc func)
1049 {
1050 	g_return_if_fail (GDAUI_IS_RAW_GRID (grid));
1051 	g_return_if_fail (func);
1052 
1053 	remove_formatting_function (grid, func);
1054 
1055 	/* redraw grid to take new function into account */
1056 	TO_IMPLEMENT;
1057 }
1058 
1059 static FormattingFuncData *
1060 formatting_func_new (GdauiRawGridFormatFunc func, gpointer data, GDestroyNotify dnotify)
1061 {
1062 	FormattingFuncData *fd;
1063 	fd = g_new0 (FormattingFuncData, 1);
1064 	fd->func = func;
1065 	fd->data = data;
1066 	fd->dnotify = dnotify;
1067 	return fd;
1068 }
1069 
1070 static void
1071 formatting_func_destroy (FormattingFuncData *fd)
1072 {
1073 	if (fd->dnotify)
1074 		fd->dnotify (fd->data);
1075 	g_free (fd);
1076 }
1077 
1078 /*
1079  * Set the attributes for each cell renderer which is not the information cell renderer,
1080  * called by each cell renderer before actually displaying anything.
1081  */
1082 static void
1083 cell_value_set_attributes (G_GNUC_UNUSED GtkTreeViewColumn *tree_column,
1084 			   GtkCellRenderer *cell,
1085 			   G_GNUC_UNUSED GtkTreeModel *tree_model,
1086 			   GtkTreeIter *iter, GdauiRawGrid *grid)
1087 {
1088 	GdauiSetGroup *group;
1089 	GdaSetGroup *sg;
1090 		guint attributes;
1091 	gboolean to_be_deleted = FALSE;
1092 	ColumnData *cdata;
1093 
1094 	cdata = g_hash_table_lookup (grid->priv->columns_hash, cell);
1095 	if (!cdata) {
1096 		g_warning ("Internal error: missing column data");
1097 		return;
1098 	}
1099 	group = cdata->group;
1100 	sg = gdaui_set_group_get_group (group);
1101 
1102 	if (gda_set_group_get_source (sg)) {
1103 		/* parameters depending on a GdaDataModel */
1104 		GList *values = NULL;
1105 
1106 		/* NOTE:
1107 		 * For performances reasons we want to provide, if possible, all the values required by the combo cell
1108 		 * renderer to draw whatever it wants, without further making it search for the values it wants in
1109 		 * source->data_model.
1110 		 *
1111 		 * GdaSetSource *source;
1112 		 * source = group->group->nodes_source;
1113 		 *
1114 		 * For this purpose, we try to get a complete list of values making one row of the node->data_for_params
1115 		 * data model, so that the combo cell renderer has all the values it wants.
1116 		 *
1117 		 * NOTE2:
1118 		 * This optimization is required anyway when source->data_model can be changed depending on
1119 		 * external events and we can't know when it has changed.
1120 		 */
1121 		attributes = _gdaui_utility_proxy_compute_attributes_for_group (group,
1122 										grid->priv->store,
1123 										grid->priv->iter,
1124 										iter, &to_be_deleted);
1125 		values = _gdaui_utility_proxy_compute_values_for_group (group, grid->priv->store,
1126 									grid->priv->iter, iter,
1127 									TRUE);
1128 		if (values) {
1129 
1130 			g_object_set (G_OBJECT (cell),
1131 				      "values-display", values,
1132 				      "value-attributes", attributes,
1133 				      "editable",
1134 				      !cdata->data_locked && !(attributes & GDA_VALUE_ATTR_NO_MODIF),
1135 				      "show-expander",
1136 				      !cdata->data_locked && !(attributes & GDA_VALUE_ATTR_NO_MODIF),
1137 				      "cell-background", GDAUI_COLOR_NORMAL_MODIF,
1138 				      "cell_background-set",
1139 				      ! (attributes & GDA_VALUE_ATTR_IS_UNCHANGED) || to_be_deleted,
1140 				      "to-be-deleted", to_be_deleted,
1141 				      "visible", !(attributes & GDA_VALUE_ATTR_UNUSED),
1142 				      NULL);
1143 		}
1144 		else {
1145 			values = _gdaui_utility_proxy_compute_values_for_group (group, grid->priv->store,
1146 										grid->priv->iter, iter, FALSE);
1147 			g_object_set (G_OBJECT (cell),
1148 				      "values-display", values,
1149 				      "value-attributes", attributes,
1150 				      "editable",
1151 				      !cdata->data_locked && !(attributes & GDA_VALUE_ATTR_NO_MODIF),
1152 				      "show-expander",
1153 				      !cdata->data_locked && !(attributes & GDA_VALUE_ATTR_NO_MODIF),
1154 				      "cell-background", GDAUI_COLOR_NORMAL_MODIF,
1155 				      "cell_background-set",
1156 				      ! (attributes & GDA_VALUE_ATTR_IS_UNCHANGED) || to_be_deleted,
1157 				      "to-be-deleted", to_be_deleted,
1158 				      "visible", !(attributes & GDA_VALUE_ATTR_UNUSED),
1159 				      NULL);
1160 		}
1161 
1162 		if (values)
1163 			g_list_free (values);
1164 	}
1165 	else {
1166 		/* single direct parameter */
1167 		gint col;
1168 		gint offset;
1169 		GValue *value;
1170 
1171 		offset = gda_data_model_get_n_columns (gda_data_proxy_get_proxied_model (grid->priv->proxy));
1172 
1173 		g_assert (gda_set_group_get_n_nodes (sg) == 1);
1174 		col = g_slist_index (((GdaSet *)grid->priv->iter)->holders,
1175 				     gda_set_node_get_holder (gda_set_group_get_node (sg)));
1176 		gtk_tree_model_get (GTK_TREE_MODEL (grid->priv->store), iter,
1177 				    GDAUI_DATA_STORE_COL_TO_DELETE, &to_be_deleted,
1178 				    col, &value,
1179 				    offset + col, &attributes, -1);
1180 		g_object_set (G_OBJECT (cell),
1181 			      "value-attributes", attributes,
1182 			      "value", value,
1183 			      "editable",
1184 			      !cdata->data_locked && !(attributes & GDA_VALUE_ATTR_NO_MODIF),
1185 			      "cell-background", GDAUI_COLOR_NORMAL_MODIF,
1186 			      "cell_background-set",
1187 			      ! (attributes & GDA_VALUE_ATTR_IS_UNCHANGED) || to_be_deleted,
1188 			      "to-be-deleted", to_be_deleted,
1189 			      "visible", !(attributes & GDA_VALUE_ATTR_UNUSED),
1190 			      NULL);
1191 	}
1192 
1193 	if (grid->priv->formatting_funcs) {
1194 		gint row, col_index;
1195 		GSList *list;
1196 
1197 		col_index = g_slist_index (grid->priv->columns_data, cdata);
1198 		gtk_tree_model_get (GTK_TREE_MODEL (grid->priv->store), iter,
1199 				    GDAUI_DATA_STORE_COL_MODEL_ROW, &row, -1);
1200 		for (list = grid->priv->formatting_funcs; list; list = list->next) {
1201 			FormattingFuncData *fd = (FormattingFuncData*) list->data;
1202 			fd->func (cell, cdata->column, col_index, (GdaDataModel*) grid->priv->proxy, row, fd->data);
1203 		}
1204 	}
1205 }
1206 
1207 /*
1208  * Set the attributes for each information cell renderer,
1209  * called by each cell renderer before actually displaying anything.
1210  */
1211 static void
1212 cell_info_set_attributes (GtkTreeViewColumn *tree_column,
1213 			  GtkCellRenderer *cell,
1214 			  G_GNUC_UNUSED GtkTreeModel *tree_model,
1215 			  GtkTreeIter *iter, GdauiRawGrid *grid)
1216 {
1217 	GdauiSetGroup *group;
1218 	GdaSetGroup *sg;
1219 	guint attributes;
1220 	gboolean to_be_deleted = FALSE;
1221 	ColumnData *cdata;
1222 
1223 	cdata = g_hash_table_lookup (grid->priv->columns_hash, cell);
1224 	if (!cdata) {
1225 		g_warning ("Missing column data");
1226 		return;
1227 	}
1228 	group = cdata->group;
1229 	sg = gdaui_set_group_get_group (group);
1230 
1231 	if (gda_set_group_get_source (sg)) {
1232 		/* parameters depending on a GdaDataModel */
1233 		attributes = _gdaui_utility_proxy_compute_attributes_for_group (group, grid->priv->store,
1234 										grid->priv->iter,
1235 										iter, &to_be_deleted);
1236 		g_object_set (G_OBJECT (cell),
1237 			      "editable", !cdata->data_locked && !(attributes & GDA_VALUE_ATTR_NO_MODIF),
1238 			      "value-attributes", attributes,
1239 			      "cell-background", GDAUI_COLOR_NORMAL_MODIF,
1240 			      "cell_background-set", ! (attributes & GDA_VALUE_ATTR_IS_UNCHANGED) || to_be_deleted,
1241 			      "to-be-deleted", to_be_deleted,
1242 			      "visible", cdata->info_shown && !(attributes & GDA_VALUE_ATTR_UNUSED),
1243 			      NULL);
1244 	}
1245 	else {
1246 		/* single direct parameter */
1247 		gint col;
1248 		gint offset;
1249 		gint row;
1250 
1251 		offset = gda_data_model_get_n_columns (gda_data_proxy_get_proxied_model (grid->priv->proxy));
1252 
1253 		g_assert (gda_set_group_get_n_nodes (sg) == 1);
1254 		col = g_slist_index (((GdaSet *)grid->priv->iter)->holders,
1255 				     gda_set_node_get_holder (gda_set_group_get_node (sg)));
1256 		gtk_tree_model_get (GTK_TREE_MODEL (grid->priv->store), iter,
1257 				    GDAUI_DATA_STORE_COL_TO_DELETE, &to_be_deleted,
1258 				    GDAUI_DATA_STORE_COL_MODEL_ROW, &row,
1259 				    offset + col, &attributes, -1);
1260 		g_object_set (G_OBJECT (cell),
1261 			      "editable", !cdata->data_locked && !(attributes & GDA_VALUE_ATTR_NO_MODIF),
1262 			      "value-attributes", attributes,
1263 			      "cell-background", GDAUI_COLOR_NORMAL_MODIF,
1264 			      "cell_background-set", ! (attributes & GDA_VALUE_ATTR_IS_UNCHANGED) || to_be_deleted,
1265 			      "to-be-deleted", to_be_deleted,
1266 			      "visible", cdata->info_shown && !(attributes & GDA_VALUE_ATTR_UNUSED),
1267 			      NULL);
1268 	}
1269 }
1270 
1271 static gboolean set_iter_from_path (GdauiRawGrid *grid, const gchar *path, GtkTreeIter *iter);
1272 
1273 /*
1274  * Callback when a value displayed as a GtkCellRenderer has been changed, for single parameter cell renderers
1275  *
1276  * Apply the new_value to the GTkTreeModel (grid->priv->store)
1277  */
1278 static void
1279 data_cell_value_changed (GtkCellRenderer *renderer, const gchar *path, const GValue *new_value, GdauiRawGrid *grid)
1280 {
1281 	GtkTreeIter iter;
1282 	GdaSetGroup *sg;
1283 	ColumnData *cdata;
1284 
1285 	cdata = g_hash_table_lookup (grid->priv->columns_hash, renderer);
1286 	g_assert (cdata);
1287 	sg = gdaui_set_group_get_group (cdata->group);
1288 	g_assert (gda_set_group_get_n_nodes (sg) == 1);
1289 
1290 	if (set_iter_from_path (grid, path, &iter)) {
1291 		gint col;
1292 		col = g_slist_index (((GdaSet *)grid->priv->iter)->holders,
1293 				     gda_set_node_get_holder (gda_set_group_get_node (sg)));
1294 		gdaui_data_store_set_value (grid->priv->store, &iter, col, new_value);
1295 	}
1296 }
1297 
1298 
1299 /*
1300  * Callback when a value displayed as a GdauiDataCellRendererCombo has been changed.
1301  *
1302  * Apply the new_value to the GTkTreeModel (grid->priv->store)
1303  */
1304 static void
1305 data_cell_values_changed (GtkCellRenderer *renderer, const gchar *path,
1306 			  GSList *new_values, G_GNUC_UNUSED GSList *all_new_values, GdauiRawGrid *grid)
1307 {
1308 	GtkTreeIter iter;
1309 	GdauiSetGroup *group;
1310 	GdaSetGroup *sg;
1311 	ColumnData *cdata;
1312 
1313 	cdata = g_hash_table_lookup (grid->priv->columns_hash, renderer);
1314 	g_assert (cdata);
1315 	group = cdata->group;
1316 	sg = gdaui_set_group_get_group (group);
1317 	g_assert (gda_set_group_get_source (sg));
1318 
1319 	if (new_values)
1320 		g_return_if_fail (gda_set_group_get_n_nodes (sg) == (gint) g_slist_length (new_values));
1321 	else
1322 		/* the reason for not having any value is that the GdauiDataCellRendererCombo had no selected item */
1323 		return;
1324 
1325 	if (set_iter_from_path (grid, path, &iter)) {
1326 		GSList *list, *params;
1327 		gint col;
1328 
1329 		/* update the GdauiDataStore */
1330 		for (params = gda_set_group_get_nodes (sg), list = new_values;
1331 		     list;
1332 		     params = params->next, list = list->next) {
1333 			col = g_slist_index (((GdaSet *)grid->priv->iter)->holders,
1334 					     gda_set_node_get_holder (GDA_SET_NODE (params->data)));
1335 			gdaui_data_store_set_value (grid->priv->store, &iter, col, (GValue *)(list->data));
1336 		}
1337 
1338 #ifdef PROXY_STORE_EXTRA_VALUES
1339 		/* call gda_data_proxy_set_model_row_value() */
1340 		gint proxy_row;
1341 		proxy_row = gdaui_data_store_get_row_from_iter (grid->priv->store, &iter);
1342 
1343 		gint i;
1344 		for (i = 0; i < gdaui_set_source_get_shown_n_cols (gdaui_set_group_get_source (group)); i++) {
1345 			GValue *value;
1346 
1347 			col = group->nodes_source->shown_cols_index[i];
1348 			value = (GValue *) g_slist_nth_data (all_new_values, col);
1349 			gda_data_proxy_set_model_row_value (grid->priv->proxy,
1350 							    gda_set_source_get_data_model (gda_set_group_get_source (sg)),
1351 							    proxy_row, col, value);
1352 		}
1353 #endif
1354 	}
1355 }
1356 
1357 static gboolean
1358 set_iter_from_path (GdauiRawGrid *grid, const gchar *path, GtkTreeIter *iter)
1359 {
1360 	GtkTreePath *treepath;
1361 
1362 	g_assert (path);
1363 
1364 	treepath = gtk_tree_path_new_from_string (path);
1365 	if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (grid->priv->store), iter, treepath)) {
1366 		gtk_tree_path_free (treepath);
1367 		g_warning ("Can't get iter for path %s", path);
1368 		return FALSE;
1369 	}
1370 	gtk_tree_path_free (treepath);
1371 
1372 	return TRUE;
1373 }
1374 
1375 static void
1376 treeview_column_clicked_cb (GtkTreeViewColumn *tree_column, GdauiRawGrid *grid)
1377 {
1378 	GdauiSetGroup *group;
1379 	GdaSetGroup *sg;
1380 	GSList *nodes;
1381 
1382 	group = g_object_get_data (G_OBJECT (tree_column), "__gdaui_group");
1383 	g_assert (group);
1384 	sg = gdaui_set_group_get_group (group);
1385 
1386 	for (nodes = gda_set_group_get_nodes (sg); nodes; nodes = nodes->next) {
1387 		GdaHolder *param = gda_set_node_get_holder (GDA_SET_NODE (nodes->data));
1388 		gint pos;
1389 		g_assert (param);
1390 
1391 		pos = g_slist_index (GDA_SET (grid->priv->iter)->holders, param);
1392 		if (pos >= 0) {
1393 			gda_data_proxy_set_ordering_column (grid->priv->proxy, pos, NULL);
1394 			break;
1395 		}
1396 	}
1397 }
1398 
1399 /*
1400  * Actions
1401  */
1402 static void
1403 data_cell_status_changed (GtkCellRenderer *renderer, const gchar *path, GdaValueAttribute requested_action,
1404 			  GdauiRawGrid *grid)
1405 {
1406 	GtkTreePath *treepath;
1407 	GtkTreeIter iter;
1408 	GdauiSetGroup *group;
1409 	GdaSetGroup *sg;
1410 	GtkTreeModel *tree_model;
1411 	gint col;
1412 	gint offset;
1413 	GValue *attribute;
1414 	ColumnData *cdata;
1415 
1416 	cdata = g_hash_table_lookup (grid->priv->columns_hash, renderer);
1417 	g_assert (cdata);
1418 	group = cdata->group;
1419 	sg = gdaui_set_group_get_group (group);
1420 
1421 	offset = gda_data_model_get_n_columns (gda_data_proxy_get_proxied_model (grid->priv->proxy));
1422 
1423 	tree_model = GTK_TREE_MODEL (grid->priv->store);
1424 
1425 	treepath = gtk_tree_path_new_from_string (path);
1426 	if (! gtk_tree_model_get_iter (tree_model, &iter, treepath)) {
1427 		gtk_tree_path_free (treepath);
1428 		g_warning ("Can't get iter for path %s", path);
1429 		return;
1430 	}
1431 	gtk_tree_path_free (treepath);
1432 
1433 	g_value_set_uint (attribute = gda_value_new (G_TYPE_UINT), requested_action);
1434 	if (gda_set_group_get_source (sg)) {
1435 		/* parameters depending on a GdaDataModel */
1436 		GSList *list;
1437 
1438 		for (list = gda_set_group_get_nodes (sg); list; list = list->next) {
1439 			col = g_slist_index (((GdaSet *)grid->priv->iter)->holders,
1440 					     gda_set_node_get_holder (GDA_SET_NODE (list->data)));
1441 			gdaui_data_store_set_value (grid->priv->store, &iter, offset + col, attribute);
1442 		}
1443 
1444 #ifdef PROXY_STORE_EXTRA_VALUES
1445 		gint proxy_row;
1446 		proxy_row = gdaui_data_store_get_row_from_iter (grid->priv->store, &iter);
1447 
1448 		/* call gda_data_proxy_set_model_row_value() */
1449 		gint i;
1450 		GdauiSetSource gs;
1451 		gs = gdaui_set_group_get_source (group);
1452 		for (i = 0; i < gdaui_set_source_get_shown_n_cols (gs); i++) {
1453 			col = (gdaui_set_source_get_shown_columns (gs))[i];
1454 
1455 			if (requested_action & GDA_VALUE_ATTR_IS_NULL)
1456 				gda_data_proxy_set_model_row_value (grid->priv->proxy,
1457 								    group->nodes_source->data_model,
1458 								    proxy_row, col, NULL);
1459 			else {
1460 				if (requested_action & GDA_VALUE_ATTR_IS_UNCHANGED)
1461 					gda_data_proxy_clear_model_row_value (grid->priv->proxy,
1462 									      group->nodes_source->data_model,
1463 									      proxy_row, col);
1464 				else {
1465 					if (requested_action & GDA_VALUE_ATTR_IS_DEFAULT) {
1466 						TO_IMPLEMENT;
1467 					}
1468 					else
1469 						TO_IMPLEMENT;
1470 				}
1471 			}
1472 		}
1473 #endif
1474 	}
1475 	else {
1476 		/* single direct parameter */
1477 		gint col;
1478 
1479 		g_assert (gda_set_group_get_n_nodes (sg) == 1);
1480 		col = g_slist_index (((GdaSet *)grid->priv->iter)->holders,
1481 				     gda_set_node_get_holder (gda_set_group_get_node (sg)));
1482 		gdaui_data_store_set_value (grid->priv->store, &iter, offset + col, attribute);
1483 	}
1484 	gda_value_free (attribute);
1485 }
1486 
1487 static void
1488 action_new_cb (G_GNUC_UNUSED GtkAction *action, GdauiRawGrid *grid)
1489 {
1490 	GtkTreeIter iter;
1491 	GtkTreePath *path;
1492 
1493 	if (gdaui_data_store_append (grid->priv->store, &iter)) {
1494 		path = gtk_tree_model_get_path (GTK_TREE_MODEL (grid->priv->store), &iter);
1495 		gtk_tree_view_set_cursor (GTK_TREE_VIEW (grid), path, NULL, FALSE);
1496 		gtk_tree_path_free (path);
1497 	}
1498 }
1499 
1500 static void
1501 action_delete_cb (GtkToggleAction *action, GdauiRawGrid *grid)
1502 {
1503 	if (gtk_toggle_action_get_active (action)) {
1504 		GtkTreeIter iter;
1505 		GtkTreeSelection *select;
1506 		GtkTreeModel *model;
1507 		GList *sel_rows;
1508 		GdaDataProxy *proxy;
1509 
1510 		select = gtk_tree_view_get_selection (GTK_TREE_VIEW (grid));
1511 		sel_rows = gtk_tree_selection_get_selected_rows (select, &model);
1512 		proxy = gdaui_data_store_get_proxy (GDAUI_DATA_STORE (model));
1513 
1514 		/* rem: get the list of selected rows after each row deletion because the data model might have changed and
1515 		 * row numbers might also have changed */
1516 		while (sel_rows) {
1517 			gtk_tree_model_get_iter (model, &iter, (GtkTreePath *) (sel_rows->data));
1518 			if (!gda_data_proxy_row_is_deleted (proxy,
1519 							    gdaui_data_store_get_row_from_iter (GDAUI_DATA_STORE (model),
1520 												&iter))) {
1521 				gdaui_data_store_delete (grid->priv->store, &iter);
1522 				g_list_foreach (sel_rows, (GFunc) gtk_tree_path_free, NULL);
1523 				g_list_free (sel_rows);
1524 				sel_rows = gtk_tree_selection_get_selected_rows (select, &model);
1525 			}
1526 			else
1527 				sel_rows = sel_rows->next;
1528 		}
1529 	}
1530 	else {
1531 		GtkTreeIter iter;
1532 		GtkTreeSelection *select;
1533 		GtkTreeModel *model;
1534 		GList *sel_rows, *cur_row;
1535 
1536 		select = gtk_tree_view_get_selection (GTK_TREE_VIEW (grid));
1537 		sel_rows = gtk_tree_selection_get_selected_rows (select, &model);
1538 		cur_row = sel_rows;
1539 		while (cur_row) {
1540 			gtk_tree_model_get_iter (model, &iter, (GtkTreePath *) (cur_row->data));
1541 			gdaui_data_store_undelete (grid->priv->store, &iter);
1542 			cur_row = g_list_next (cur_row);
1543 		}
1544 		g_list_foreach (sel_rows, (GFunc) gtk_tree_path_free, NULL);
1545 		g_list_free (sel_rows);
1546 	}
1547 }
1548 
1549 static void
1550 action_commit_cb (G_GNUC_UNUSED GtkAction *action, GdauiRawGrid *grid)
1551 {
1552 	gint row;
1553 	GError *error = NULL;
1554 	gboolean allok = TRUE;
1555 	gint mod1, mod2;
1556 
1557 	mod1 = gda_data_proxy_get_n_modified_rows (grid->priv->proxy);
1558 	row = gda_data_model_iter_get_row (grid->priv->iter);
1559 	if (grid->priv->write_mode >= GDAUI_DATA_PROXY_WRITE_ON_ROW_CHANGE) {
1560 		gint newrow;
1561 
1562 		allok = gda_data_proxy_apply_row_changes (grid->priv->proxy, row, &error);
1563 		if (allok) {
1564 			newrow = gda_data_model_iter_get_row (grid->priv->iter);
1565 			if (row != newrow) /* => current row has changed because the
1566 					         proxy had to emit a "row_removed" when
1567 						 actually succeeded the commit
1568 					      => we need to come back to that row
1569 					   */
1570 				gda_data_model_iter_move_to_row (grid->priv->iter, row);
1571 		}
1572 	}
1573 	else
1574 		allok = gda_data_proxy_apply_all_changes (grid->priv->proxy, &error);
1575 
1576 	mod2 = gda_data_proxy_get_n_modified_rows (grid->priv->proxy);
1577 	if (!allok) {
1578 		if (mod1 != mod2)
1579 			/* the data model has changed while doing the writing */
1580 			_gdaui_utility_display_error ((GdauiDataProxy *) grid, FALSE, error);
1581 		else
1582 			_gdaui_utility_display_error ((GdauiDataProxy *) grid, TRUE, error);
1583 		g_error_free (error);
1584 	}
1585 }
1586 
1587 static void
1588 action_reset_cb (G_GNUC_UNUSED GtkAction *action, GdauiRawGrid *grid)
1589 {
1590 	gda_data_proxy_cancel_all_changes (grid->priv->proxy);
1591 	gda_data_model_send_hint (GDA_DATA_MODEL (grid->priv->proxy), GDA_DATA_MODEL_HINT_REFRESH, NULL);
1592 }
1593 
1594 static void
1595 action_first_chunck_cb (G_GNUC_UNUSED GtkAction *action, GdauiRawGrid *grid)
1596 {
1597 	gda_data_proxy_set_sample_start (grid->priv->proxy, 0);
1598 }
1599 
1600 static void
1601 action_prev_chunck_cb (G_GNUC_UNUSED GtkAction *action, GdauiRawGrid *grid)
1602 {
1603 	gint sample_size, sample_start;
1604 
1605 	sample_size = gda_data_proxy_get_sample_size (grid->priv->proxy);
1606 	if (sample_size > 0) {
1607 		sample_start = gda_data_proxy_get_sample_start (grid->priv->proxy);
1608 		sample_start -= sample_size;
1609 		gda_data_proxy_set_sample_start (grid->priv->proxy, sample_start);
1610 	}
1611 }
1612 
1613 static void
1614 action_next_chunck_cb (G_GNUC_UNUSED GtkAction *action, GdauiRawGrid *grid)
1615 {
1616 	gint sample_size, sample_start;
1617 
1618 	sample_size = gda_data_proxy_get_sample_size (grid->priv->proxy);
1619 	if (sample_size > 0) {
1620 		sample_start = gda_data_proxy_get_sample_start (grid->priv->proxy);
1621 		sample_start += sample_size;
1622 		gda_data_proxy_set_sample_start (grid->priv->proxy, sample_start);
1623 	}
1624 }
1625 
1626 static void
1627 action_last_chunck_cb (G_GNUC_UNUSED GtkAction *action, GdauiRawGrid *grid)
1628 {
1629 	gda_data_proxy_set_sample_start (grid->priv->proxy, G_MAXINT);
1630 }
1631 
1632 static void
1633 hide_filter_window (GdauiRawGrid *grid)
1634 {
1635 	gtk_widget_hide (grid->priv->filter_window);
1636 	gtk_grab_remove (grid->priv->filter_window);
1637 }
1638 
1639 static gboolean
1640 filter_event (G_GNUC_UNUSED GtkWidget *widget, G_GNUC_UNUSED GdkEventAny *event, GdauiRawGrid *grid)
1641 {
1642 	hide_filter_window (grid);
1643 	return TRUE;
1644 }
1645 
1646 static gboolean
1647 key_press_filter_event (G_GNUC_UNUSED GtkWidget *widget, GdkEventKey *event, GdauiRawGrid *grid)
1648 {
1649 	if (event->keyval == GDK_KEY_Escape ||
1650 	    event->keyval == GDK_KEY_Tab ||
1651             event->keyval == GDK_KEY_KP_Tab ||
1652             event->keyval == GDK_KEY_ISO_Left_Tab) {
1653 		hide_filter_window (grid);
1654 		return TRUE;
1655 	}
1656 	return FALSE;
1657 }
1658 
1659 static void
1660 filter_position_func (GtkWidget *widget,
1661 		      GtkWidget *search_dialog,
1662 		      G_GNUC_UNUSED gpointer   user_data)
1663 {
1664 	gint x, y;
1665 	gint tree_x, tree_y;
1666 	gint tree_width, tree_height;
1667 	GdkWindow *window;
1668 	GdkScreen *screen;
1669 	GtkRequisition requisition;
1670 	gint monitor_num;
1671 	GdkRectangle monitor;
1672 
1673 	window = gtk_widget_get_window (widget);
1674 	screen = gdk_window_get_screen (window);
1675 
1676 	monitor_num = gdk_screen_get_monitor_at_window (screen, window);
1677 	gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1678 
1679 	gtk_widget_realize (search_dialog);
1680 
1681 	gdk_window_get_origin (window, &tree_x, &tree_y);
1682 	tree_width = gdk_window_get_width (window);
1683 	tree_height = gdk_window_get_height (window);
1684 	gtk_widget_get_preferred_size (search_dialog, NULL, &requisition);
1685 
1686 	if (tree_x + tree_width > gdk_screen_get_width (screen))
1687 		x = gdk_screen_get_width (screen) - requisition.width;
1688 	else if (tree_x + tree_width - requisition.width < 0)
1689 		x = 0;
1690 	else
1691 		x = tree_x + tree_width - requisition.width;
1692 
1693 	if (tree_y + tree_height + requisition.height > gdk_screen_get_height (screen))
1694 		y = gdk_screen_get_height (screen) - requisition.height;
1695 	else if (tree_y + tree_height < 0) /* isn't really possible ... */
1696 		y = 0;
1697 	else
1698 		y = tree_y + tree_height;
1699 
1700 	gtk_window_move (GTK_WINDOW (search_dialog), x, y);
1701 }
1702 
1703 static gboolean
1704 popup_grab_on_window (GtkWidget *widget, guint32 activate_time)
1705 {
1706 	GdkDeviceManager *manager;
1707 	GdkDevice *pointer;
1708 	GdkWindow *window;
1709 	window = gtk_widget_get_window (widget);
1710 	manager = gdk_display_get_device_manager (gtk_widget_get_display (widget));
1711 	pointer = gdk_device_manager_get_client_pointer (manager);
1712         if (gdk_device_grab (pointer, window, GDK_OWNERSHIP_WINDOW, TRUE,
1713                               GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK,
1714                               NULL, activate_time) == GDK_GRAB_SUCCESS) {
1715 		GdkDevice *keyb;
1716 		keyb = gdk_device_get_associated_device (pointer);
1717                 if (gdk_device_grab (keyb, window, GDK_OWNERSHIP_WINDOW, TRUE,
1718 				     GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK, NULL, activate_time) == 0)
1719                         return TRUE;
1720 		else {
1721                         gdk_device_ungrab (pointer, activate_time);
1722                         return FALSE;
1723                 }
1724         }
1725         return FALSE;
1726 }
1727 
1728 static void
1729 action_filter_cb (G_GNUC_UNUSED GtkAction *action, GdauiRawGrid *grid)
1730 {
1731 	GtkWidget *toplevel;
1732 	toplevel = gtk_widget_get_toplevel (GTK_WIDGET (grid));
1733 
1734 	if (!grid->priv->filter_window) {
1735 		/* create filter window */
1736 		GtkWidget *frame, *vbox;
1737 
1738 		grid->priv->filter_window = gtk_window_new (GTK_WINDOW_POPUP);
1739 
1740 		gtk_widget_set_events (grid->priv->filter_window,
1741 				       gtk_widget_get_events (grid->priv->filter_window) | GDK_KEY_PRESS_MASK);
1742 
1743 		if (gtk_widget_is_toplevel (toplevel) && gtk_window_get_group (GTK_WINDOW (toplevel)))
1744 			gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
1745 						     GTK_WINDOW (grid->priv->filter_window));
1746 
1747 		g_signal_connect (grid->priv->filter_window, "delete-event",
1748 				  G_CALLBACK (filter_event), grid);
1749 		g_signal_connect (grid->priv->filter_window, "button-press-event",
1750 				  G_CALLBACK (filter_event), grid);
1751 		g_signal_connect (grid->priv->filter_window, "key-press-event",
1752 				  G_CALLBACK (key_press_filter_event), grid);
1753 		frame = gtk_frame_new (NULL);
1754 		gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
1755 		gtk_widget_show (frame);
1756 		gtk_container_add (GTK_CONTAINER (grid->priv->filter_window), frame);
1757 
1758 		vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1759 		gtk_widget_show (vbox);
1760 		gtk_container_add (GTK_CONTAINER (frame), vbox);
1761 		gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
1762 
1763 		/* add real filter widget */
1764 		if (! grid->priv->filter) {
1765 			grid->priv->filter = gdaui_data_filter_new (GDAUI_DATA_PROXY (grid));
1766 			gtk_widget_show (grid->priv->filter);
1767 		}
1768 		gtk_container_add (GTK_CONTAINER (vbox), grid->priv->filter);
1769 	}
1770 	else if (gtk_widget_is_toplevel (toplevel)) {
1771 		if (gtk_window_get_group ((GtkWindow*) toplevel))
1772 			gtk_window_group_add_window (gtk_window_get_group ((GtkWindow*) toplevel),
1773 						     GTK_WINDOW (grid->priv->filter_window));
1774 		else if (gtk_window_get_group (GTK_WINDOW (grid->priv->filter_window)))
1775 			gtk_window_group_remove_window (gtk_window_get_group (GTK_WINDOW (grid->priv->filter_window)),
1776 							GTK_WINDOW (grid->priv->filter_window));
1777 	}
1778 
1779 	/* move the filter window to a correct location */
1780 	/* FIXME: let the user specify the position function like GtkTreeView -> search_position_func() */
1781 	gtk_widget_show (grid->priv->filter_window);
1782 	gtk_grab_add (grid->priv->filter_window);
1783 	filter_position_func (GTK_WIDGET (grid), grid->priv->filter_window, NULL);
1784 	popup_grab_on_window (grid->priv->filter_window,
1785 			      gtk_get_current_event_time ());
1786 }
1787 
1788 /*
1789  * Catch any event in the GtkTreeView widget
1790  */
1791 static gboolean
1792 tree_view_event_cb (GtkWidget *treeview, GdkEvent *event, GdauiRawGrid *grid)
1793 {
1794 	gboolean done = FALSE;
1795 
1796 	if (event->type == GDK_KEY_PRESS) {
1797 		GdkEventKey *ekey = (GdkEventKey *) event;
1798 		guint modifiers = gtk_accelerator_get_default_mod_mask ();
1799 
1800 		/* Tab to move one column left or right */
1801 		if (ekey->keyval == GDK_KEY_Tab) {
1802 			GtkTreeViewColumn *column;
1803 			GtkTreePath *path;
1804 
1805 			/* FIXME: if a column is currently edited, then make sure the editing of that cell is not canceled */
1806 			gtk_tree_view_get_cursor (GTK_TREE_VIEW (treeview), &path, &column);
1807 			if (column && path) {
1808 				GList *columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (treeview));
1809 				GList *col;
1810 				GtkCellRenderer *renderer;
1811 
1812 				/* change column */
1813 				col = g_list_find (columns, column);
1814 				g_return_val_if_fail (col, FALSE);
1815 
1816 				if (((ekey->state & modifiers) == GDK_SHIFT_MASK) || ((ekey->state & modifiers) == GDK_CONTROL_MASK))
1817 					col = g_list_previous (col); /* going to previous column */
1818 				else
1819 					col = g_list_next (col); /* going to next column */
1820 
1821 				if (col) {
1822 					renderer = g_object_get_data (G_OBJECT (col->data), "data_renderer");
1823 					gtk_tree_view_set_cursor_on_cell (GTK_TREE_VIEW (treeview), path,
1824 									  GTK_TREE_VIEW_COLUMN (col->data),
1825 									  renderer, FALSE);
1826 					gtk_widget_grab_focus (treeview);
1827 					done = TRUE;
1828 				}
1829 				g_list_free (columns);
1830 			}
1831 			if (path)
1832 				gtk_tree_path_free (path);
1833 		}
1834 
1835 		/* DELETE to delete the selected row */
1836 		if (ekey->keyval == GDK_KEY_Delete) {
1837 			GtkTreeIter iter;
1838 			GtkTreeSelection *selection;
1839 			GtkTreeModel *model;
1840 			GList *sel_rows, *cur_row;
1841 
1842 			selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (grid));
1843 			sel_rows = gtk_tree_selection_get_selected_rows (selection, &model);
1844 			cur_row = sel_rows;
1845 			while (cur_row) {
1846 				gtk_tree_model_get_iter (model, &iter, (GtkTreePath *) (cur_row->data));
1847 				if (((ekey->state & modifiers) == GDK_SHIFT_MASK) ||
1848 				    ((ekey->state & modifiers) == GDK_CONTROL_MASK))
1849 					gdaui_data_store_undelete (grid->priv->store, &iter);
1850 				else
1851 					gdaui_data_store_delete (grid->priv->store, &iter);
1852 				cur_row = g_list_next (cur_row);
1853 			}
1854 			g_list_foreach (sel_rows, (GFunc) gtk_tree_path_free, NULL);
1855 			g_list_free (sel_rows);
1856 
1857 			done = TRUE;
1858 		}
1859 	}
1860 
1861 	return done;
1862 }
1863 
1864 static void menu_select_all_cb (GtkWidget *widget, GdauiRawGrid *grid);
1865 static void menu_unselect_all_cb (GtkWidget *widget, GdauiRawGrid *grid);
1866 static void menu_show_columns_cb (GtkWidget *widget, GdauiRawGrid *grid);
1867 static void menu_save_as_cb (GtkWidget *widget, GdauiRawGrid *grid);
1868 static void menu_set_filter_cb (GtkWidget *widget, GdauiRawGrid *grid);
1869 static void menu_unset_filter_cb (GtkWidget *widget, GdauiRawGrid *grid);
1870 static void menu_copy_row_cb (GtkWidget *widget, GdauiRawGrid *grid);
1871 static GtkWidget *new_menu_item (const gchar *label,
1872 				 gboolean pixmap,
1873 				 GCallback cb_func,
1874 				 gpointer user_data);
1875 static GtkWidget *new_check_menu_item (const gchar *label,
1876 				       gboolean active,
1877 				       GCallback cb_func,
1878 				       gpointer user_data);
1879 
1880 static void
1881 hidden_column_mitem_toggled_cb (GtkCheckMenuItem *check, G_GNUC_UNUSED GdauiRawGrid *grid)
1882 {
1883 	ColumnData *cdata;
1884 	gboolean act;
1885 	cdata = g_object_get_data (G_OBJECT (check), "c");
1886 	g_assert (cdata);
1887 	act = gtk_check_menu_item_get_active (check);
1888 	gtk_tree_view_column_set_visible (cdata->column, act);
1889 }
1890 
1891 static gboolean
1892 tree_view_popup_button_pressed_cb (G_GNUC_UNUSED GtkWidget *widget, GdkEventButton *event, GdauiRawGrid *grid)
1893 {
1894 	GtkWidget *menu, *submenu;
1895 	GtkTreeView *tree_view;
1896 	GtkTreeSelection *selection;
1897 	GtkSelectionMode sel_mode;
1898 	GSList *list;
1899 	GtkWidget *mitem;
1900 
1901 	if (event->button != 3)
1902 		return FALSE;
1903 
1904 	tree_view = GTK_TREE_VIEW (grid);
1905 	if (event->window != gtk_tree_view_get_bin_window (tree_view))
1906 		return FALSE;
1907 
1908 	selection = gtk_tree_view_get_selection (tree_view);
1909 	sel_mode = gtk_tree_selection_get_mode (selection);
1910 
1911 	grid->priv->bin_x = (gint) event->x;
1912 	grid->priv->bin_y = (gint) event->y;
1913 
1914 	/* create the menu */
1915 	menu = gtk_menu_new ();
1916 	mitem = gtk_menu_item_new_with_label (_("Shown columns"));
1917 	gtk_menu_shell_append (GTK_MENU_SHELL (menu), mitem);
1918 	gtk_widget_show (mitem);
1919 
1920 	submenu = gtk_menu_new ();
1921 	gtk_widget_show (submenu);
1922 	gtk_menu_item_set_submenu (GTK_MENU_ITEM (mitem), submenu);
1923 	for (list = grid->priv->columns_data; list; list = list->next) {
1924 		ColumnData *cdata = (ColumnData*) list->data;
1925 		gchar *tmp;
1926 
1927 		if (cdata->prog_hidden)
1928 			continue;
1929 		tmp = remove_double_underscores (cdata->title);
1930 		mitem = gtk_check_menu_item_new_with_label (tmp);
1931 		g_free (tmp);
1932 		gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (mitem), !cdata->hidden);
1933 		gtk_menu_shell_append (GTK_MENU_SHELL (submenu), mitem);
1934 		gtk_widget_show (mitem);
1935 
1936 		g_object_set_data (G_OBJECT (mitem), "c", cdata);
1937 		g_signal_connect (mitem, "toggled",
1938 				  G_CALLBACK (hidden_column_mitem_toggled_cb), grid);
1939 	}
1940 
1941 	gtk_menu_shell_append (GTK_MENU_SHELL (menu),
1942 			       new_menu_item (GTK_STOCK_COPY, TRUE,
1943 					      G_CALLBACK (menu_copy_row_cb), grid));
1944 
1945 
1946 	if (sel_mode == GTK_SELECTION_MULTIPLE)
1947 		gtk_menu_shell_append (GTK_MENU_SHELL (menu),
1948 				       new_menu_item (_("Select _All"), FALSE,
1949 						      G_CALLBACK (menu_select_all_cb), grid));
1950 
1951 	if ((sel_mode == GTK_SELECTION_SINGLE) || (sel_mode == GTK_SELECTION_MULTIPLE))
1952 		gtk_menu_shell_append (GTK_MENU_SHELL (menu),
1953 				       new_menu_item (_("_Clear Selection"), FALSE,
1954 						      G_CALLBACK (menu_unselect_all_cb), grid));
1955 	gtk_menu_shell_append (GTK_MENU_SHELL (menu),
1956 			       new_check_menu_item (_("Show Column _Titles"),
1957 						    gtk_tree_view_get_headers_visible (tree_view),
1958 						    G_CALLBACK (menu_show_columns_cb), grid));
1959 
1960 	gtk_menu_shell_append (GTK_MENU_SHELL (menu),
1961 			       new_menu_item (_("_Set filter"), FALSE,
1962 					      G_CALLBACK (menu_set_filter_cb), grid));
1963 
1964 	mitem = new_menu_item (_("_Unset filter"), FALSE, G_CALLBACK (menu_unset_filter_cb), grid);
1965 	gtk_menu_shell_append (GTK_MENU_SHELL (menu), mitem);
1966 	if (!gda_data_proxy_get_filter_expr (grid->priv->proxy))
1967 		gtk_widget_set_sensitive (mitem, FALSE);
1968 
1969 	if (sel_mode != GTK_SELECTION_NONE) {
1970 		gtk_menu_shell_append (GTK_MENU_SHELL (menu), gtk_separator_menu_item_new ());
1971 		gtk_menu_shell_append (GTK_MENU_SHELL (menu), new_menu_item (GTK_STOCK_SAVE_AS, TRUE,
1972 									     G_CALLBACK (menu_save_as_cb),
1973 									     grid));
1974 	}
1975 
1976 	/* allow listeners to add their custom menu items */
1977 	g_signal_emit (G_OBJECT (grid), gdaui_raw_grid_signals [POPULATE_POPUP], 0, GTK_MENU (menu));
1978 	gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, event->button, event->time);
1979 	gtk_widget_show_all (menu);
1980 
1981 	return TRUE;
1982 }
1983 
1984 static GtkWidget *
1985 new_menu_item (const gchar *label,
1986 	       gboolean pixmap,
1987 	       GCallback cb_func,
1988 	       gpointer user_data)
1989 {
1990 	GtkWidget *item;
1991 
1992 	if (pixmap)
1993 		item = gtk_image_menu_item_new_from_stock (label, NULL);
1994 	else
1995 		item = gtk_menu_item_new_with_mnemonic (label);
1996 
1997 	g_signal_connect (G_OBJECT (item), "activate",
1998 			  G_CALLBACK (cb_func), user_data);
1999 
2000 	return item;
2001 }
2002 
2003 static GtkWidget *
2004 new_check_menu_item (const gchar *label,
2005 		     gboolean active,
2006 		     GCallback cb_func,
2007 		     gpointer user_data)
2008 {
2009 	GtkWidget *item;
2010 
2011 	item = gtk_check_menu_item_new_with_mnemonic (label);
2012 	gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), active);
2013 
2014 	g_signal_connect (G_OBJECT (item), "toggled",
2015 			  G_CALLBACK (cb_func), user_data);
2016 
2017 	return item;
2018 }
2019 
2020 static void
2021 menu_select_all_cb (G_GNUC_UNUSED GtkWidget *widget, GdauiRawGrid *grid)
2022 {
2023 	GtkTreeSelection *selection;
2024 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (grid));
2025 
2026 	gtk_tree_selection_select_all (selection);
2027 }
2028 
2029 static void
2030 menu_unselect_all_cb (G_GNUC_UNUSED GtkWidget *widget, GdauiRawGrid *grid)
2031 {
2032 	GtkTreeSelection *selection;
2033 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (grid));
2034 
2035 	gtk_tree_selection_unselect_all (selection);
2036 }
2037 
2038 static void
2039 menu_copy_row_cb (GtkWidget *widget, GdauiRawGrid *grid)
2040 {
2041 	GtkTreeModel *model;
2042 	GtkTreeIter iter;
2043 	GtkTreePath *path;
2044 	GtkClipboard *cp;
2045 	GtkTreeViewColumn *column;
2046 	GList *columns;
2047 	cp = gtk_clipboard_get (gdk_atom_intern_static_string ("CLIPBOARD"));
2048 	if (!cp)
2049 		return;
2050 
2051 	if (! gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (grid),
2052 					     grid->priv->bin_x, grid->priv->bin_y, &path,
2053 					     &column, NULL, NULL))
2054 		return;
2055 
2056 	model = GTK_TREE_MODEL (grid->priv->store);
2057 	if (! gtk_tree_model_get_iter (model, &iter, path)) {
2058 		gtk_tree_path_free (path);
2059 		return;
2060 	}
2061 	gtk_tree_path_free (path);
2062 
2063 	columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (grid));
2064 	const GValue *cvalue;
2065 	gboolean cpset = TRUE;
2066 	gtk_tree_model_get (model, &iter, g_list_index (columns, column), &cvalue, -1);
2067 	g_list_free (columns);
2068 	if (G_VALUE_TYPE (cvalue) == GDA_TYPE_NULL)
2069 		gtk_clipboard_set_text (cp, "", -1);
2070 	else if ((G_VALUE_TYPE (cvalue) == GDA_TYPE_BINARY) ||
2071 		 (G_VALUE_TYPE (cvalue) == GDA_TYPE_BLOB)) {
2072 		const GdaBinary *bin;
2073 
2074 		cpset = FALSE;
2075 		if (G_VALUE_TYPE (cvalue) == GDA_TYPE_BINARY)
2076 			bin = gda_value_get_binary (cvalue);
2077 		else {
2078 			GdaBlob *blob;
2079 			blob = (GdaBlob *) gda_value_get_blob ((GValue *) cvalue);
2080 			g_assert (blob);
2081 			bin = (GdaBinary *) blob;
2082 			if (blob->op &&
2083 			    (bin->binary_length != gda_blob_op_get_length (blob->op)))
2084 				gda_blob_op_read_all (blob->op, blob);
2085 		}
2086 		if (bin) {
2087 			GdkPixbufLoader *loader;
2088 			GdkPixbuf *pixbuf = NULL;
2089 			loader = gdk_pixbuf_loader_new ();
2090 			if (gdk_pixbuf_loader_write (loader, bin->data, bin->binary_length, NULL)) {
2091 				if (gdk_pixbuf_loader_close (loader, NULL)) {
2092 					pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
2093 					g_object_ref (pixbuf);
2094 				}
2095 				else
2096 					gdk_pixbuf_loader_close (loader, NULL);
2097 			}
2098 			else
2099 				gdk_pixbuf_loader_close (loader, NULL);
2100 			g_object_unref (loader);
2101 
2102 			if (pixbuf) {
2103 				gtk_clipboard_set_image (cp, pixbuf);
2104 				g_object_unref (pixbuf);
2105 				cpset = TRUE;
2106 			}
2107 		}
2108 	}
2109 	else
2110 		cpset = FALSE;
2111 
2112 	if (!cpset) {
2113 		gchar *str;
2114 		GdaDataHandler *dh;
2115 		dh = gda_data_handler_get_default (G_VALUE_TYPE (cvalue));
2116 		if (dh)
2117 			str = gda_data_handler_get_str_from_value (dh, cvalue);
2118 		else
2119 			str = gda_value_stringify (cvalue);
2120 		gtk_clipboard_set_text (cp, str, -1);
2121 		g_free (str);
2122 	}
2123 }
2124 
2125 static void
2126 menu_show_columns_cb (GtkWidget *widget, GdauiRawGrid *grid)
2127 {
2128 	GtkCheckMenuItem *item;
2129 
2130 	item = (GtkCheckMenuItem *) widget;
2131 
2132 	g_return_if_fail (GTK_IS_CHECK_MENU_ITEM (item));
2133 	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (grid),
2134 					   gtk_check_menu_item_get_active (item));
2135 }
2136 
2137 static void export_type_changed_cb (GtkComboBox *types, GtkWidget *dialog);
2138 static void save_as_response_cb (GtkDialog *dialog, gint response_id, GdauiRawGrid *grid);
2139 static void
2140 menu_save_as_cb (G_GNUC_UNUSED GtkWidget *widget, GdauiRawGrid *grid)
2141 {
2142 	GtkWidget *dialog;
2143 	GtkWidget *label;
2144 	GtkWidget *filename;
2145 	GtkWidget *types, *scope;
2146 	GtkWidget *hbox, *grid1, *check, *dbox;
2147 	char *str;
2148 	GtkTreeSelection *sel;
2149 	gint selrows;
2150 
2151 	/* create dialog box */
2152 	dialog = gtk_dialog_new_with_buttons (_("Saving Data"),
2153 					      GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (grid))), 0,
2154 					      GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2155 					      GTK_STOCK_SAVE, GTK_RESPONSE_OK,
2156 					      NULL);
2157 	gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE);
2158 	gtk_container_set_border_width (GTK_CONTAINER (dialog), 6);
2159 
2160 	str = g_strdup_printf ("<big><b>%s:</b></big>\n%s", _("Saving data to a file"),
2161 			       _("The data will be exported to the selected file."));
2162 	label = gtk_label_new ("");
2163 	gtk_label_set_markup (GTK_LABEL (label), str);
2164 	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
2165 	gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
2166 	g_free (str);
2167 
2168 	dbox = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
2169 	gtk_box_pack_start (GTK_BOX (dbox), label, FALSE, TRUE, 2);
2170 
2171 	str = g_strdup_printf ("<b>%s:</b>", _("File name"));
2172 	label = gtk_label_new ("");
2173 	gtk_label_set_markup (GTK_LABEL (label), str);
2174 	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
2175 	g_free (str);
2176 	gtk_box_pack_start (GTK_BOX (dbox), label, FALSE, TRUE, 2);
2177 
2178 	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); /* HIG */
2179 	gtk_box_pack_start (GTK_BOX (dbox), hbox, TRUE, TRUE, 5);
2180 	gtk_widget_show (hbox);
2181 	label = gtk_label_new ("    ");
2182 	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
2183 	gtk_widget_show (label);
2184 
2185 	filename = gtk_file_chooser_widget_new (GTK_FILE_CHOOSER_ACTION_SAVE);
2186 	g_object_set_data (G_OBJECT (dialog), "filename", filename);
2187 	gtk_box_pack_start (GTK_BOX (hbox), filename, TRUE, TRUE, 0);
2188 	gtk_widget_show (filename);
2189 	gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (filename),
2190 					     gdaui_get_default_path ());
2191 
2192 	str = g_strdup_printf ("<b>%s:</b>", _("Details"));
2193 	label = gtk_label_new ("");
2194 	gtk_label_set_markup (GTK_LABEL (label), str);
2195 	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
2196 	g_free (str);
2197 	gtk_box_pack_start (GTK_BOX (dbox), label, FALSE, TRUE, 2);
2198 
2199 	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); /* HIG */
2200 	gtk_box_pack_start (GTK_BOX (dbox), hbox, FALSE, FALSE, 5);
2201 	gtk_widget_show (hbox);
2202 	label = gtk_label_new ("    ");
2203 	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
2204 	gtk_widget_show (label);
2205 
2206 	grid1 = gtk_grid_new ();
2207 	gtk_grid_set_row_spacing (GTK_GRID (grid1), 5);
2208 	gtk_grid_set_column_spacing (GTK_GRID (grid1), 5);
2209 	gtk_box_pack_start (GTK_BOX (hbox), grid1, TRUE, TRUE, 0);
2210 	gtk_widget_show (grid1);
2211 
2212 	/* file type */
2213 	label = gtk_label_new (_("File type:"));
2214 	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
2215 	gtk_grid_attach (GTK_GRID (grid1), label, 0, 0, 1, 1);
2216 	gtk_widget_show (label);
2217 
2218 	types = gtk_combo_box_text_new ();
2219 	gtk_grid_attach (GTK_GRID (grid1), types, 1, 0, 1, 1);
2220 	gtk_widget_show (types);
2221 	g_object_set_data (G_OBJECT (dialog), "types", types);
2222 
2223 	gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (types), _("Tab-delimited"));
2224 	gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (types), _("Comma-delimited"));
2225 	gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (types), _("XML"));
2226 	gtk_combo_box_set_active (GTK_COMBO_BOX (types), grid->priv->export_type);
2227 
2228 	g_signal_connect (types, "changed",
2229 			  G_CALLBACK (export_type_changed_cb), dialog);
2230 
2231 	/* data scope */
2232 	label = gtk_label_new (_("Data to save:"));
2233 	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
2234 	gtk_grid_attach (GTK_GRID (grid1), label, 0, 1, 1, 1);
2235 	gtk_widget_show (label);
2236 
2237 	sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (grid));
2238 	selrows = gtk_tree_selection_count_selected_rows (sel);
2239 	if (selrows <= 0)
2240 		gtk_widget_set_sensitive (label, FALSE);
2241 
2242 	scope = gtk_combo_box_text_new ();
2243 	gtk_grid_attach (GTK_GRID (grid1), scope, 1, 1, 1, 1);
2244 	gtk_widget_show (scope);
2245 	g_object_set_data (G_OBJECT (dialog), "scope", scope);
2246 
2247 	gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (scope), _("All data (without any local modification)"));
2248 	gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (scope), _("Only displayed data"));
2249 	if (selrows > 0)
2250 		gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (scope), _("Only selected data"));
2251 	gtk_combo_box_set_active (GTK_COMBO_BOX (scope), 0);
2252 
2253 	/* other options */
2254 	GtkWidget *exp;
2255 	exp = gtk_expander_new (_("Other options"));
2256 	gtk_grid_attach (GTK_GRID (grid1), exp, 0, 2, 2, 1);
2257 
2258 	GtkWidget *grid2;
2259 	grid2 = gtk_grid_new ();
2260 	gtk_container_add (GTK_CONTAINER (exp), grid2);
2261 
2262 	label = gtk_label_new (_("Empty string when NULL?"));
2263 	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
2264 	gtk_grid_attach (GTK_GRID (grid2), label, 0, 0, 1, 1);
2265 	gtk_widget_set_tooltip_text (label, _("Export NULL values as an empty \"\" string"));
2266 
2267 	check = gtk_check_button_new ();
2268 	gtk_grid_attach (GTK_GRID (grid2), check, 1, 0, 1, 1);
2269 	g_object_set_data (G_OBJECT (dialog), "null_as_empty", check);
2270 	gtk_widget_set_tooltip_text (check, _("Export NULL values as an empty \"\" string"));
2271 
2272 	label = gtk_label_new (_("Invalid data as NULL?"));
2273 	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
2274 	gtk_grid_attach (GTK_GRID (grid2), label, 2, 0, 1, 1);
2275 	gtk_widget_set_tooltip_text (label, _("Don't export invalid data,\nbut export a NULL value instead"));
2276 
2277 	check = gtk_check_button_new ();
2278 	gtk_grid_attach (GTK_GRID (grid2), check, 3, 0, 1, 1);
2279 	g_object_set_data (G_OBJECT (dialog), "invalid_as_null", check);
2280 	gtk_widget_set_tooltip_text (check, _("Don't export invalid data,\nbut export a NULL value instead"));
2281 
2282 	label = gtk_label_new (_("Field names on first row?"));
2283 	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
2284 	gtk_grid_attach (GTK_GRID (grid2), label, 0, 1, 1, 1);
2285 	gtk_widget_set_tooltip_text (label, _("Add a row at beginning with columns names"));
2286 
2287 	check = gtk_check_button_new ();
2288 	gtk_grid_attach (GTK_GRID (grid2), check, 1, 1, 1, 1);
2289 	g_object_set_data (G_OBJECT (dialog), "first_row", check);
2290 	gtk_widget_set_tooltip_text (check, _("Add a row at beginning with columns names"));
2291 
2292 	export_type_changed_cb (GTK_COMBO_BOX (types), dialog);
2293 
2294 	/* run the dialog */
2295 	g_signal_connect (G_OBJECT (dialog), "response", G_CALLBACK (save_as_response_cb), grid);
2296 	gtk_widget_show_all (dialog);
2297 }
2298 
2299 static void
2300 export_type_changed_cb (GtkComboBox *types, GtkWidget *dialog)
2301 {
2302 	gboolean is_cvs = TRUE;
2303 	GtkWidget *wid;
2304 	if (gtk_combo_box_get_active (types) == 2) /* XML */
2305 		is_cvs = FALSE;
2306 	wid = g_object_get_data (G_OBJECT (dialog), "first_row");
2307 	gtk_widget_set_sensitive (wid, is_cvs);
2308 	wid = g_object_get_data (G_OBJECT (dialog), "invalid_as_null");
2309 	gtk_widget_set_sensitive (wid, is_cvs);
2310 	wid = g_object_get_data (G_OBJECT (dialog), "null_as_empty");
2311 	gtk_widget_set_sensitive (wid, is_cvs);
2312 }
2313 
2314 static gboolean confirm_file_overwrite (GtkWindow *parent, const gchar *path);
2315 
2316 static void
2317 save_as_response_cb (GtkDialog *dialog, gint response_id, GdauiRawGrid *grid)
2318 {
2319 	if (response_id == GTK_RESPONSE_OK) {
2320 		GtkWidget *types;
2321 		gint export_type;
2322 		GtkWidget *filename;
2323 		gboolean selection_only = FALSE;
2324 		gboolean null_as_empty = FALSE;
2325 		gboolean invalid_as_null = FALSE;
2326 		gboolean first_row = FALSE;
2327 		gchar *body;
2328 		gchar *path;
2329 		GList *columns, *list;
2330 		gint *cols, nb_cols;
2331 		gint *rows = NULL, nb_rows = 0;
2332 		GdaHolder *param;
2333 		GdaSet *paramlist;
2334 		GdaDataModel *model_to_use = (GdaDataModel*) grid->priv->proxy;
2335 		GtkWidget *scope;
2336 		gint scope_v;
2337 
2338 		model_to_use = model_to_use;
2339 		scope = g_object_get_data (G_OBJECT (dialog), "scope");
2340 		scope_v = gtk_combo_box_get_active (GTK_COMBO_BOX (scope));
2341 		if (scope_v == 0)
2342 			model_to_use = grid->priv->data_model;
2343 		else if (scope_v == 2)
2344 			selection_only = TRUE;
2345 
2346 		types = g_object_get_data (G_OBJECT (dialog), "types");
2347 		filename = g_object_get_data (G_OBJECT (dialog), "filename");
2348 		gdaui_set_default_path (gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (filename)));
2349 		null_as_empty = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
2350 							      (g_object_get_data (G_OBJECT (dialog), "null_as_empty")));
2351 		invalid_as_null = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
2352 							      (g_object_get_data (G_OBJECT (dialog), "invalid_as_null")));
2353 		first_row = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
2354 							  (g_object_get_data (G_OBJECT (dialog), "first_row")));
2355 
2356 		/* output columns computation */
2357 		columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (grid));
2358 		cols = g_new (gint, gda_data_model_get_n_columns (GDA_DATA_MODEL (model_to_use)));
2359 		nb_cols = 0;
2360 		for (list = columns; list; list = list->next) {
2361 			if (gtk_tree_view_column_get_visible (GTK_TREE_VIEW_COLUMN (list->data))) {
2362 				GdauiSetGroup *group;
2363 				GSList *params;
2364 
2365 				group = g_object_get_data (G_OBJECT (list->data), "__gdaui_group");
2366 				for (params = gda_set_group_get_nodes (gdaui_set_group_get_group (group))
2367 				     ; params; nb_cols ++, params = params->next)
2368 					cols [nb_cols] = g_slist_index (((GdaSet *)grid->priv->iter)->holders,
2369 									gda_set_node_get_holder (GDA_SET_NODE (params->data)));
2370 			}
2371 		}
2372 		g_list_free (columns);
2373 
2374 		/* output rows computation */
2375 		if (selection_only) {
2376 			GtkTreeSelection *selection;
2377 			GList *sel_rows, *list;
2378 			gint pos;
2379 
2380 			selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (grid));
2381 			sel_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
2382 
2383 			nb_rows = g_list_length (sel_rows);
2384 			rows = g_new0 (gint, nb_rows);
2385 
2386 			for (pos = 0, list = sel_rows; list; list = list->next, pos++) {
2387 				gint *ind = gtk_tree_path_get_indices ((GtkTreePath*) list->data);
2388 				rows [pos] = *ind;
2389 				gtk_tree_path_free ((GtkTreePath*) list->data);
2390 			}
2391 			g_list_free (sel_rows);
2392 		}
2393 
2394 		/* Actual ouput computations */
2395 		export_type = gtk_combo_box_get_active (GTK_COMBO_BOX (types));
2396 		grid->priv->export_type = export_type;
2397 		paramlist = gda_set_new (NULL);
2398 
2399 		if (null_as_empty) {
2400 			param = gda_holder_new_boolean ("NULL_AS_EMPTY", TRUE);
2401 			gda_set_add_holder (paramlist, param);
2402 			g_object_unref (param);
2403 		}
2404 		if (invalid_as_null) {
2405 			param = gda_holder_new_boolean ("INVALID_AS_NULL", TRUE);
2406 			gda_set_add_holder (paramlist, param);
2407 			g_object_unref (param);
2408 		}
2409 		if (first_row) {
2410 			param = gda_holder_new_boolean ("FIELDS_NAME", TRUE);
2411 			gda_set_add_holder (paramlist, param);
2412 			g_object_unref (param);
2413 		}
2414 
2415 		switch (export_type) {
2416 		case 0:
2417 			param = gda_holder_new_string ("SEPARATOR", "\t");
2418 			gda_set_add_holder (paramlist, param);
2419 			g_object_unref (param);
2420 			body = gda_data_model_export_to_string (GDA_DATA_MODEL (model_to_use),
2421 								GDA_DATA_MODEL_IO_TEXT_SEPARATED,
2422 								cols, nb_cols, rows, nb_rows, paramlist);
2423 			break;
2424 		case 1:
2425 			param = gda_holder_new_string ("SEPARATOR", ",");
2426 			gda_set_add_holder (paramlist, param);
2427 			g_object_unref (param);
2428 			body = gda_data_model_export_to_string (GDA_DATA_MODEL (model_to_use),
2429 								GDA_DATA_MODEL_IO_TEXT_SEPARATED,
2430 								cols, nb_cols, rows, nb_rows, paramlist);
2431 			break;
2432 		case 2:
2433 			param = NULL;
2434 			body = (gchar *) g_object_get_data (G_OBJECT (model_to_use), "name");
2435 			if (body)
2436 				param = gda_holder_new_string ("NAME", body);
2437 			if (param) {
2438 				gda_set_add_holder (paramlist, param);
2439 				g_object_unref (param);
2440 			}
2441 			body = gda_data_model_export_to_string (GDA_DATA_MODEL (model_to_use),
2442 								GDA_DATA_MODEL_IO_DATA_ARRAY_XML,
2443 								cols, nb_cols, rows, nb_rows, paramlist);
2444 			break;
2445 		default:
2446 			g_assert_not_reached ();
2447 			break;
2448 		}
2449 		g_object_unref (paramlist);
2450 		g_free (cols);
2451 		if (rows)
2452 			g_free (rows);
2453 
2454 		/* saving */
2455 		if (body) {
2456 			path = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (filename));
2457 			if (path) {
2458 				if (g_file_test (path, G_FILE_TEST_EXISTS)) {
2459 					if (! confirm_file_overwrite (GTK_WINDOW (dialog), path)) {
2460 						g_free (body);
2461 						g_free (path);
2462 						return;
2463 					}
2464 				}
2465 
2466 				if (! g_file_set_contents (path, body, strlen (body), NULL)) {
2467 					_gdaui_utility_show_error (NULL, _("Could not save file %s"), path);
2468 					g_free (body);
2469 					g_free (path);
2470 					return;
2471 				}
2472 				g_free (path);
2473 			}
2474 			else {
2475 				_gdaui_utility_show_error (NULL, _("You must specify a file name"));
2476 				g_free (body);
2477 				return;
2478 			}
2479 			g_free (body);
2480 		} else
2481 			_gdaui_utility_show_error (NULL,_("Got empty file while converting the data"));
2482 	}
2483 
2484 	gtk_widget_destroy (GTK_WIDGET (dialog));
2485 }
2486 
2487 static gboolean
2488 confirm_file_overwrite (GtkWindow *parent, const gchar *path)
2489 {
2490 	GtkWidget *dialog, *button;
2491 	gboolean yes;
2492 	gchar *msg;
2493 
2494 	msg = g_strdup_printf (_("File '%s' already exists.\n"
2495 				 "Do you want to overwrite it?"), path);
2496 
2497 	/* build the dialog */
2498 	dialog = gtk_message_dialog_new_with_markup (parent,
2499 						     GTK_DIALOG_DESTROY_WITH_PARENT |
2500 						     GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION,
2501 						     GTK_BUTTONS_YES_NO,
2502 						     "<span weight=\"bold\">%s</span>\n%s\n",
2503 						     msg,
2504 						     _("If you choose yes, the contents will be lost."));
2505 	g_free (msg);
2506 
2507 	button = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
2508 	gtk_widget_set_can_default (button, TRUE);
2509 	gtk_dialog_set_default_response (GTK_DIALOG (dialog),
2510 					 GTK_RESPONSE_NO);
2511 
2512 	/* run the dialog */
2513 	gtk_widget_show_all (dialog);
2514 	yes = gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES;
2515 
2516 	/* free memory */
2517 	gtk_widget_destroy (dialog);
2518 
2519 	return yes;
2520 }
2521 
2522 static void
2523 menu_set_filter_cb (G_GNUC_UNUSED GtkWidget *widget, GdauiRawGrid *grid)
2524 {
2525 	action_filter_cb (NULL, grid);
2526 }
2527 
2528 static void
2529 menu_unset_filter_cb (G_GNUC_UNUSED GtkWidget *widget, GdauiRawGrid *grid)
2530 {
2531 	gda_data_proxy_set_filter_expr (grid->priv->proxy, NULL, NULL);
2532 }
2533 
2534 
2535 static void
2536 tree_view_row_activated_cb (G_GNUC_UNUSED GtkTreeView *tree_view, GtkTreePath *path,
2537 			    G_GNUC_UNUSED GtkTreeViewColumn *column, GdauiRawGrid *grid)
2538 {
2539 	gint *indices;
2540 
2541 	indices = gtk_tree_path_get_indices (path);
2542 #ifdef debug_signal
2543 	g_print (">> 'DOUBLE_CLICKED' from %s %p\n", G_OBJECT_TYPE_NAME (grid), grid);
2544 #endif
2545 	g_signal_emit (G_OBJECT (grid), gdaui_raw_grid_signals[DOUBLE_CLICKED], 0, *indices);
2546 #ifdef debug_signal
2547 	g_print ("<< 'DOUBLE_CLICKED' from %s %p\n", G_OBJECT_TYPE_NAME (grid), grid);
2548 #endif
2549 }
2550 
2551 /*
2552  * Synchronize the values of the parameters in priv->iter with the currently selected row
2553  */
2554 static void
2555 tree_view_selection_changed_cb (GtkTreeSelection *selection, GdauiRawGrid *grid)
2556 {
2557 	GtkTreeIter iter;
2558 	GtkTreeModel *model;
2559 	gint has_selection = 0;
2560 
2561 	/* block the GdaDataModelIter' "changed" signal */
2562 	g_signal_handlers_block_by_func (grid->priv->iter,
2563 					 G_CALLBACK (iter_row_changed_cb), grid);
2564 
2565 	if (gtk_tree_selection_get_mode (selection) == GTK_SELECTION_MULTIPLE) {
2566 		has_selection = gtk_tree_selection_count_selected_rows (selection);
2567 		if (has_selection == 1) {
2568 			GList *sel_rows;
2569 
2570 			sel_rows = gtk_tree_selection_get_selected_rows (selection, &model);
2571 			has_selection = gtk_tree_model_get_iter (model, &iter, (GtkTreePath *) (sel_rows->data)) ? 1 : 0;
2572 			g_list_foreach (sel_rows, (GFunc) gtk_tree_path_free, NULL);
2573 			g_list_free (sel_rows);
2574 		}
2575 	}
2576 	else
2577 		has_selection = gtk_tree_selection_get_selected (selection, &model, &iter) ? 1 : 0;
2578 
2579 	if (has_selection == 1) {
2580 		if (!gda_data_model_iter_move_to_row (grid->priv->iter,
2581 						      gdaui_data_store_get_row_from_iter (grid->priv->store,
2582 											  &iter))) {
2583 			/* selection changing is refused, return to the current selected row */
2584 			GtkTreePath *path;
2585 			path = gtk_tree_path_new_from_indices (gda_data_model_iter_get_row (grid->priv->iter), -1);
2586 			g_signal_handlers_block_by_func (G_OBJECT (selection),
2587 							 G_CALLBACK (tree_view_selection_changed_cb), grid);
2588 
2589 			gtk_tree_selection_unselect_all (selection);
2590 			gtk_tree_selection_select_path (selection, path);
2591 			g_signal_handlers_unblock_by_func (G_OBJECT (selection),
2592 							   G_CALLBACK (tree_view_selection_changed_cb), grid);
2593 
2594 			gtk_tree_path_free (path);
2595 		}
2596 	}
2597 	else {
2598 		/* render all the parameters invalid, and make the iter point to row -1 */
2599 		gda_data_model_iter_invalidate_contents (grid->priv->iter);
2600 		gda_data_model_iter_move_to_row (grid->priv->iter, -1);
2601 	}
2602 
2603 	g_signal_emit_by_name (G_OBJECT (grid), "selection-changed");
2604 
2605 	/* unblock the GdaDataModelIter' "changed" signal */
2606 	g_signal_handlers_unblock_by_func (grid->priv->iter,
2607 					   G_CALLBACK (iter_row_changed_cb), grid);
2608 }
2609 
2610 static void
2611 destroy_column_data (GdauiRawGrid *grid)
2612 {
2613 	if (grid->priv->columns_data) {
2614 		GSList *list;
2615 		for (list = grid->priv->columns_data; list; list = list->next) {
2616 			ColumnData *cdata = COLUMN_DATA (list->data);
2617 			g_object_unref (cdata->data_cell);
2618 			g_object_unref (cdata->info_cell);
2619 			g_free (cdata->tooltip_text);
2620 			g_free (cdata->title);
2621 			g_free (cdata);
2622 		}
2623 		g_slist_free (grid->priv->columns_data);
2624 		grid->priv->columns_data = NULL;
2625 		g_hash_table_remove_all (grid->priv->columns_hash);
2626 	}
2627 }
2628 
2629 static ColumnData *
2630 get_column_data_for_group (GdauiRawGrid *grid, GdauiSetGroup *group)
2631 {
2632 	GSList *list;
2633 	for (list = grid->priv->columns_data; list; list = list->next) {
2634 		if (COLUMN_DATA (list->data)->group == group)
2635 			return COLUMN_DATA (list->data);
2636 	}
2637 
2638 	return NULL;
2639 }
2640 
2641 static ColumnData *
2642 get_column_data_for_holder (GdauiRawGrid *grid, GdaHolder *holder)
2643 {
2644 	GSList *list;
2645 	for (list = grid->priv->columns_data; list; list = list->next) {
2646 		if (COLUMN_DATA (list->data)->single_param == holder)
2647 			return COLUMN_DATA (list->data);
2648 	}
2649 
2650 	return NULL;
2651 }
2652 
2653 static ColumnData *
2654 get_column_data_for_id (GdauiRawGrid *grid, const gchar *id)
2655 {
2656 	GSList *list;
2657 	for (list = grid->priv->columns_data; list; list = list->next) {
2658 		ColumnData *cdata = COLUMN_DATA (list->data);
2659 		if (cdata->title && !strcmp (cdata->title, id))
2660 			return cdata;
2661 		if (cdata->single_param) {
2662 			const gchar *hid;
2663 			hid = gda_holder_get_id (cdata->single_param);
2664 			if (hid && !strcmp (hid, id))
2665 				return cdata;
2666 		}
2667 	}
2668 
2669 	return NULL;
2670 }
2671 
2672 /**
2673  * gdaui_raw_grid_set_sample_size:
2674  * @grid: a #GdauiRawGrid
2675  * @sample_size: the size of the sample displayed in @grid
2676  *
2677  * Sets the size of each chunk of data to display: the maximum number of rows which
2678  * can be displayed at a time. See gdaui_grid_set_sample_size() and gda_data_proxy_set_sample_size()
2679  *
2680  * Since: 4.2
2681  */
2682 void
2683 gdaui_raw_grid_set_sample_size (GdauiRawGrid *grid, gint sample_size)
2684 {
2685 	g_return_if_fail (grid && GDAUI_IS_RAW_GRID (grid));
2686 	g_return_if_fail (grid->priv);
2687 
2688 	gda_data_proxy_set_sample_size (grid->priv->proxy, sample_size);
2689 }
2690 
2691 /**
2692  * gdaui_raw_grid_set_sample_start:
2693  * @grid: a #GdauiRawGrid
2694  * @sample_start:
2695  *
2696  *
2697  * Since: 4.2
2698  */
2699 void
2700 gdaui_raw_grid_set_sample_start (GdauiRawGrid *grid, gint sample_start)
2701 {
2702 	g_return_if_fail (grid && GDAUI_IS_RAW_GRID (grid));
2703 	g_return_if_fail (grid->priv);
2704 
2705 	gda_data_proxy_set_sample_start (grid->priv->proxy, sample_start);
2706 }
2707 
2708 /**
2709  * gdaui_raw_grid_set_layout_from_file:
2710  * @grid: a #GdauiRawGrid
2711  * @file_name: XML file name to use
2712  * @grid_name: the name of the grid to use, in @file_name
2713  *
2714  * Sets a grid's columns layout according an XML description contained in @file_name, for the grid identified
2715  * by the @grid_name name (as an XML layout file can contain the descriptions of several forms and grids).
2716  *
2717  * Since: 4.2
2718  */
2719 void
2720 gdaui_raw_grid_set_layout_from_file (GdauiRawGrid *grid, const gchar *file_name, const gchar *grid_name)
2721 {
2722 	g_return_if_fail (GDAUI_IS_RAW_GRID (grid));
2723 	g_return_if_fail (file_name);
2724         g_return_if_fail (grid_name);
2725 
2726 	xmlDocPtr doc;
2727         doc = xmlParseFile (file_name);
2728         if (doc == NULL) {
2729                 g_warning (_("'%s' document not parsed successfully"), file_name);
2730                 return;
2731         }
2732 
2733         xmlDtdPtr dtd = NULL;
2734 
2735 	gchar *file = gda_gbr_get_file_path (GDA_DATA_DIR, LIBGDA_ABI_NAME, "dtd", "gdaui-layout.dtd", NULL);
2736         if (g_file_test (file, G_FILE_TEST_EXISTS))
2737                 dtd = xmlParseDTD (NULL, BAD_CAST file);
2738         if (dtd == NULL) {
2739                 g_warning (_("'%s' DTD not parsed successfully. "
2740                              "XML data layout validation will not be "
2741                              "performed (some errors may occur)"), file);
2742         }
2743         g_free (file);
2744 
2745         /* Get the root element node */
2746         xmlNodePtr root_node = NULL;
2747         root_node = xmlDocGetRootElement (doc);
2748 
2749         /* Must have root element, a name and the name must be "gdaui_layouts" */
2750         if (!root_node || !root_node->name ||
2751             ! xmlStrEqual (root_node->name, BAD_CAST "gdaui_layouts")) {
2752                 xmlFreeDoc (doc);
2753                 return;
2754         }
2755 
2756         xmlNodePtr node;
2757         for (node = root_node->children; node; node = node->next) {
2758                 if ((node->type == XML_ELEMENT_NODE) &&
2759                     xmlStrEqual (node->name, BAD_CAST "gdaui_grid")) {
2760                         xmlChar *str;
2761                         str = xmlGetProp (node, BAD_CAST "name");
2762 			if (str) {
2763 				if (!strcmp ((gchar*) str, grid_name)) {
2764 					g_object_set (G_OBJECT (grid), "xml-layout", node, NULL);
2765 					xmlFree (str);
2766 					break;
2767 				}
2768                                 xmlFree (str);
2769                         }
2770                 }
2771         }
2772 
2773         /* Free the document */
2774         xmlFreeDoc (doc);
2775 }
2776 
2777 /*
2778  * GdauiDataProxy interface
2779  */
2780 
2781 static GdaDataProxy *
2782 gdaui_raw_grid_get_proxy (GdauiDataProxy *iface)
2783 {
2784 	GdauiRawGrid *grid;
2785 	g_return_val_if_fail (GDAUI_IS_RAW_GRID (iface), NULL);
2786 	grid = GDAUI_RAW_GRID (iface);
2787 	g_return_val_if_fail (grid->priv, NULL);
2788 
2789 	return grid->priv->proxy;
2790 }
2791 
2792 static void
2793 gdaui_raw_grid_set_column_editable (GdauiDataProxy *iface, gint column, gboolean editable)
2794 {
2795 	GdauiRawGrid *grid;
2796 	GdaHolder *param;
2797 	ColumnData *cdata;
2798 	GdauiSetGroup *group;
2799 
2800 	g_return_if_fail (GDAUI_IS_RAW_GRID (iface));
2801 	grid = GDAUI_RAW_GRID (iface);
2802 	g_return_if_fail (grid->priv);
2803 
2804 	if (grid->priv->data_model) {
2805 		editable = editable && !gda_data_proxy_is_read_only (grid->priv->proxy);
2806 
2807 		param = gda_data_model_iter_get_holder_for_field (grid->priv->iter, column);
2808 		g_return_if_fail (param);
2809 
2810 		group = _gdaui_set_get_group (grid->priv->iter_info, param);
2811 		g_return_if_fail (group);
2812 
2813 		cdata = get_column_data_for_group (grid, group);
2814 		g_return_if_fail (cdata);
2815 
2816 		if (editable && !gda_data_proxy_is_read_only (grid->priv->proxy))
2817 			cdata->data_locked = FALSE;
2818 		else
2819 			cdata->data_locked = TRUE;
2820 	}
2821 }
2822 
2823 static void
2824 gdaui_raw_grid_show_column_actions (GdauiDataProxy *iface, gint column, gboolean show_actions)
2825 {
2826 	GdauiRawGrid *grid;
2827 
2828 	g_return_if_fail (GDAUI_IS_RAW_GRID (iface));
2829 	grid = GDAUI_RAW_GRID (iface);
2830 	g_return_if_fail (grid->priv);
2831 
2832 	if (column >= 0) {
2833 		GdaHolder *param;
2834 		GdauiSetGroup *group;
2835 		ColumnData *cdata;
2836 
2837 		/* setting applies only to the @column column */
2838 		param = gda_data_model_iter_get_holder_for_field (grid->priv->iter, column);
2839 		g_return_if_fail (param);
2840 
2841 		group = _gdaui_set_get_group (grid->priv->iter_info, param);
2842 		g_return_if_fail (group);
2843 
2844 		cdata = get_column_data_for_group (grid, group);
2845 		g_return_if_fail (cdata);
2846 
2847 		if (show_actions != cdata->info_shown) {
2848 			cdata->info_shown = show_actions;
2849 			g_object_set (G_OBJECT (cdata->info_cell), "visible", cdata->info_shown, NULL);
2850 		}
2851 	}
2852 	else {
2853 		/* setting applies to all columns */
2854 		GSList *list;
2855 		for (list = grid->priv->columns_data; list; list = list->next) {
2856 			ColumnData *cdata = (ColumnData*)(list->data);
2857 			if (show_actions != cdata->info_shown) {
2858 				cdata->info_shown = show_actions;
2859 				g_object_set (G_OBJECT (cdata->info_cell), "visible", cdata->info_shown, NULL);
2860 			}
2861 		}
2862 		grid->priv->default_show_info_cell = show_actions;
2863 	}
2864 }
2865 
2866 static GtkActionGroup *
2867 gdaui_raw_grid_get_actions_group (GdauiDataProxy *iface)
2868 {
2869 	GdauiRawGrid *grid;
2870 
2871 	g_return_val_if_fail (GDAUI_IS_RAW_GRID (iface), NULL);
2872 	grid = GDAUI_RAW_GRID (iface);
2873 	g_return_val_if_fail (grid->priv, NULL);
2874 
2875 	return grid->priv->actions_group;
2876 }
2877 
2878 static void
2879 paramlist_public_data_changed_cb (G_GNUC_UNUSED GdauiSet *info, GdauiRawGrid *grid)
2880 {
2881 	/* info cells */
2882 	destroy_column_data (grid);
2883 	create_columns_data (grid);
2884 	reset_columns_default (grid);
2885 }
2886 
2887 static void
2888 paramlist_param_attr_changed_cb (G_GNUC_UNUSED GdaSet *paramlist, GdaHolder *param,
2889 				 const gchar *att_name, const GValue *att_value, GdauiRawGrid *grid)
2890 {
2891 	if (!strcmp (att_name, GDAUI_ATTRIBUTE_PLUGIN)) {
2892 		ColumnData *cdata;
2893 		const gchar *plugin = NULL;
2894 		cdata = get_column_data_for_holder (grid, param);
2895 		if (!cdata)
2896 			return;
2897 
2898 		if (att_value) {
2899 			if (G_VALUE_TYPE (att_value) == G_TYPE_STRING)
2900 				plugin = g_value_get_string (att_value);
2901 			else {
2902 				g_warning (_("The '%s' attribute should be a G_TYPE_STRING value"),
2903 					   GDAUI_ATTRIBUTE_PLUGIN);
2904 				return;
2905 			}
2906 		}
2907 
2908 		/* get rid of previous renderer */
2909 		g_signal_handlers_disconnect_by_func (G_OBJECT (cdata->data_cell),
2910 						      G_CALLBACK (data_cell_value_changed), grid);
2911 		g_hash_table_remove (grid->priv->columns_hash, cdata->data_cell);
2912 		g_object_unref (cdata->data_cell);
2913 
2914 		/* create new renderer */
2915 		GtkCellRenderer *renderer;
2916 		gint model_col;
2917 		renderer = _gdaui_new_cell_renderer (gda_holder_get_g_type (param), plugin);
2918 		cdata->data_cell = g_object_ref_sink ((GObject*) renderer);
2919 		g_hash_table_insert (grid->priv->columns_hash, renderer, cdata);
2920 
2921 		model_col = g_slist_index (((GdaSet *)grid->priv->iter)->holders, param);
2922 		g_object_set_data (G_OBJECT (renderer), "model_col", GINT_TO_POINTER (model_col));
2923 
2924 		g_object_set (G_OBJECT (renderer), "editable", !cdata->data_locked, NULL);
2925 		if (g_object_class_find_property (G_OBJECT_GET_CLASS (renderer), "set-default-if-invalid"))
2926 			g_object_set (G_OBJECT (renderer), "set-default-if-invalid", TRUE, NULL);
2927 
2928 		g_signal_connect (G_OBJECT (renderer), "changed",
2929 				  G_CALLBACK (data_cell_value_changed), grid);
2930 
2931 		/* replace column */
2932 		GtkTreeViewColumn *column;
2933 		gint pos;
2934 		pos = -1;
2935 		column = cdata->column;
2936 		if (column) {
2937 			GList *cols;
2938 			cols = gtk_tree_view_get_columns (GTK_TREE_VIEW (grid));
2939 			pos = g_list_index (cols, column);
2940 			g_list_free (cols);
2941 			gtk_tree_view_remove_column (GTK_TREE_VIEW (grid), column);
2942 		}
2943 		create_tree_view_column (grid, cdata, pos);
2944 	}
2945 	else if (!strcmp (att_name, GDA_ATTRIBUTE_NAME)) {
2946 		ColumnData *cdata;
2947 		cdata = get_column_data_for_holder (grid, param);
2948 		if (!cdata)
2949 			return;
2950 		if (att_value) {
2951 			if (G_VALUE_TYPE (att_value) == G_TYPE_STRING) {
2952 				g_free (cdata->title);
2953 				cdata->title = g_value_dup_string (att_value);
2954 				gtk_tree_view_column_set_title (cdata->column, cdata->title);
2955 			}
2956 			else
2957 				g_warning (_("The '%s' attribute should be a G_TYPE_STRING value"),
2958 					   GDA_ATTRIBUTE_NAME);
2959 		}
2960 	}
2961 }
2962 
2963 static GError *
2964 iter_validate_set_cb (GdaDataModelIter *iter, GdauiRawGrid *grid)
2965 {
2966 	GError *error = NULL;
2967 	gint row = gda_data_model_iter_get_row (iter);
2968 
2969 	if (row < 0)
2970 		return NULL;
2971 
2972 	if ((grid->priv->write_mode >= GDAUI_DATA_PROXY_WRITE_ON_ROW_CHANGE) &&
2973 	    /* write back the current row */
2974 	    gda_data_proxy_row_has_changed (grid->priv->proxy, row) &&
2975 	    !gda_data_proxy_apply_row_changes (grid->priv->proxy, row, &error)) {
2976 		if (_gdaui_utility_display_error_with_keep_or_discard_choice ((GdauiDataProxy *) grid,
2977 									      error)) {
2978 			gda_data_proxy_cancel_row_changes (grid->priv->proxy, row, -1);
2979 			if (error) {
2980 				g_error_free (error);
2981 				error = NULL;
2982 			}
2983 		}
2984 	}
2985 
2986 	return error;
2987 }
2988 
2989 static void
2990 iter_row_changed_cb (G_GNUC_UNUSED GdaDataModelIter *iter, gint row, GdauiRawGrid *grid)
2991 {
2992 	GtkTreeSelection *selection;
2993 	GtkTreePath *path;
2994 
2995 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (grid));
2996 
2997 	if (row >= 0) {
2998 		GtkSelectionMode mode;
2999 		GtkTreeIter treeiter;
3000 
3001 		mode = gtk_tree_selection_get_mode (selection);
3002 		if (mode != GTK_SELECTION_SINGLE)
3003 			gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
3004 		path = gtk_tree_path_new_from_indices (row, -1);
3005 		if (gtk_tree_model_get_iter (GTK_TREE_MODEL (grid->priv->store), &treeiter, path)) {
3006 			gtk_tree_selection_select_path (selection, path);
3007 			gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (grid), path, NULL,
3008 						      FALSE, 0., 0.);
3009 		}
3010 		gtk_tree_path_free (path);
3011 		if (mode != GTK_SELECTION_SINGLE)
3012 			gtk_tree_selection_set_mode (selection, mode);
3013 	}
3014 	else
3015 		gtk_tree_selection_unselect_all (selection);
3016 }
3017 
3018 static void
3019 proxy_sample_changed_cb (G_GNUC_UNUSED GdaDataProxy *proxy, G_GNUC_UNUSED gint sample_start,
3020 			 G_GNUC_UNUSED gint sample_end, GdauiRawGrid *grid)
3021 {
3022 	/* bring back the vertical scrollbar to the top */
3023 	gtk_adjustment_set_value (gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (grid)), 0.);
3024 }
3025 
3026 static void
3027 proxy_row_updated_cb (GdaDataProxy *proxy, gint proxy_row, GdauiRawGrid *grid)
3028 {
3029 	if (grid->priv->write_mode == GDAUI_DATA_PROXY_WRITE_ON_VALUE_ACTIVATED) {
3030 		gint row;
3031 
3032 		/* REM: this may lead to problems in the proxy because it is not re-entrant, solution: add a very short timeout */
3033 		row = gda_data_model_iter_get_row (grid->priv->iter);
3034 		if ((row >= 0) && (row == proxy_row)) {
3035 			/* write back the current row */
3036 			if (gda_data_proxy_row_has_changed (grid->priv->proxy, row)) {
3037 				GError *error = NULL;
3038 
3039 				/* If the write fails, the proxy sets the fields back to was they were. We do not want
3040 				 * te get notified about this because we would the try again to write to the database,
3041 				 * which would fail again, and so on. */
3042 				g_signal_handlers_block_by_func(G_OBJECT(proxy), G_CALLBACK(proxy_row_updated_cb), grid);
3043 
3044 				if (!gda_data_proxy_apply_row_changes (grid->priv->proxy, row, &error)) {
3045 					gboolean discard;
3046 					discard = _gdaui_utility_display_error_with_keep_or_discard_choice ((GdauiDataProxy *) grid,
3047 													    error);
3048 					if (discard)
3049 						gda_data_proxy_cancel_row_changes (grid->priv->proxy, row, -1);
3050 					g_error_free (error);
3051 				}
3052 
3053 				g_signal_handlers_unblock_by_func(G_OBJECT(proxy), G_CALLBACK(proxy_row_updated_cb), grid);
3054 			}
3055 		}
3056 	}
3057 }
3058 
3059 static gboolean
3060 model_reset_was_soft (GdauiRawGrid *grid, GdaDataModel *new_model)
3061 {
3062 	GdaDataModelIter *iter;
3063 	gboolean retval = FALSE;
3064 
3065 	if (!new_model)
3066 		return FALSE;
3067 	else if (new_model == (GdaDataModel*) grid->priv->proxy)
3068 		return TRUE;
3069 	else if (!grid->priv->iter)
3070 		return FALSE;
3071 
3072 	iter = gda_data_model_create_iter (new_model);
3073 	retval = ! _gdaui_utility_iter_differ (grid->priv->iter, iter);
3074 	g_object_unref (iter);
3075 	return retval;
3076 }
3077 
3078 static void
3079 proxy_reset_pre_cb (GdaDataProxy *proxy, GdauiRawGrid *grid)
3080 {
3081 	grid->priv->iter_row = gda_data_model_iter_get_row (grid->priv->iter);
3082 	grid->priv->reset_soft = model_reset_was_soft (grid, gda_data_proxy_get_proxied_model (grid->priv->proxy));
3083 }
3084 
3085 static void
3086 proxy_reset_cb (GdaDataProxy *proxy, GdauiRawGrid *grid)
3087 {
3088 	GdkRectangle vis = {0, 0, 0, 0};
3089 	if (gtk_widget_get_realized ((GtkWidget*) grid))
3090 				gtk_tree_view_get_visible_rect ((GtkTreeView*) grid, &vis);
3091 
3092 	if (grid->priv->iter_row >= 0)
3093 		gda_data_model_iter_move_to_row (grid->priv->iter, grid->priv->iter_row);
3094 	else
3095 		gda_data_model_iter_invalidate_contents (grid->priv->iter);
3096 	grid->priv->iter_row = -1;
3097 	grid->priv->data_model = gda_data_proxy_get_proxied_model (grid->priv->proxy);
3098 
3099 	if (! grid->priv->reset_soft)
3100 		g_signal_emit_by_name (grid, "proxy-changed", grid->priv->proxy);
3101 
3102 	if (gtk_widget_get_realized ((GtkWidget*) grid))
3103 		gtk_tree_view_scroll_to_point ((GtkTreeView*) grid, vis.x, vis.y);
3104 }
3105 
3106 static void
3107 gdaui_raw_grid_clean (GdauiRawGrid *grid)
3108 {
3109 	/* column data */
3110 	destroy_column_data (grid);
3111 
3112 	/* store */
3113 	if (grid->priv->store) {
3114 		g_object_unref (grid->priv->store);
3115 		grid->priv->store = NULL;
3116 	}
3117 
3118 	/* private data set */
3119 	if (grid->priv->iter) {
3120 		g_signal_handlers_disconnect_by_func (grid->priv->iter_info,
3121 						      G_CALLBACK (paramlist_public_data_changed_cb), grid);
3122 		g_signal_handlers_disconnect_by_func (grid->priv->iter,
3123 						      G_CALLBACK (paramlist_param_attr_changed_cb), grid);
3124 		g_signal_handlers_disconnect_by_func (grid->priv->iter,
3125 						      G_CALLBACK (iter_row_changed_cb), grid);
3126 		g_signal_handlers_disconnect_by_func (grid->priv->iter,
3127 						      G_CALLBACK (iter_validate_set_cb), grid);
3128 		g_object_unref (grid->priv->iter);
3129 		g_object_unref (grid->priv->iter_info);
3130 		grid->priv->iter = NULL;
3131 		grid->priv->iter_info = NULL;
3132 	}
3133 
3134 	/* proxy */
3135 	if (grid->priv->proxy) {
3136 		g_signal_handlers_disconnect_by_func (grid->priv->proxy, G_CALLBACK (proxy_sample_changed_cb), grid);
3137 		g_signal_handlers_disconnect_by_func (grid->priv->proxy, G_CALLBACK (proxy_row_updated_cb), grid);
3138 		g_signal_handlers_disconnect_by_func (grid->priv->proxy, G_CALLBACK (proxy_reset_pre_cb), grid);
3139 		g_signal_handlers_disconnect_by_func (grid->priv->proxy, G_CALLBACK (proxy_reset_cb), grid);
3140 		g_object_unref (grid->priv->proxy);
3141 		grid->priv->proxy = NULL;
3142 	}
3143 }
3144 
3145 static gboolean
3146 gdaui_raw_grid_widget_set_write_mode (GdauiDataProxy *iface, GdauiDataProxyWriteMode mode)
3147 {
3148 	GdauiRawGrid *grid;
3149 
3150 	g_return_val_if_fail (GDAUI_IS_RAW_GRID (iface), FALSE);
3151 	grid = GDAUI_RAW_GRID (iface);
3152 	g_return_val_if_fail (grid->priv, FALSE);
3153 
3154 	grid->priv->write_mode = mode;
3155 	if (mode == GDAUI_DATA_PROXY_WRITE_ON_VALUE_CHANGE) {
3156 		grid->priv->write_mode = GDAUI_DATA_PROXY_WRITE_ON_VALUE_ACTIVATED;
3157 		return FALSE;
3158 	}
3159 
3160 	if (mode == GDAUI_DATA_PROXY_WRITE_ON_ROW_CHANGE) {
3161 		GtkTreeSelection *selection;
3162 		selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (grid));
3163 		gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
3164 	}
3165 	return TRUE;
3166 }
3167 
3168 static GdauiDataProxyWriteMode
3169 gdaui_raw_grid_widget_get_write_mode (GdauiDataProxy *iface)
3170 {
3171 	GdauiRawGrid *grid;
3172 
3173 	g_return_val_if_fail (GDAUI_IS_RAW_GRID (iface), GDAUI_DATA_PROXY_WRITE_ON_DEMAND);
3174 	grid = GDAUI_RAW_GRID (iface);
3175 	g_return_val_if_fail (grid->priv, GDAUI_DATA_PROXY_WRITE_ON_DEMAND);
3176 
3177 	return grid->priv->write_mode;
3178 }
3179 
3180 /* GdauiDataSelector interface */
3181 static GdaDataModel *
3182 gdaui_raw_grid_selector_get_model (GdauiDataSelector *iface)
3183 {
3184 	GdauiRawGrid *grid;
3185 
3186 	g_return_val_if_fail (GDAUI_IS_RAW_GRID (iface), NULL);
3187 	grid = GDAUI_RAW_GRID (iface);
3188 
3189 	return GDA_DATA_MODEL (grid->priv->proxy);
3190 }
3191 
3192 static void
3193 gdaui_raw_grid_selector_set_model (GdauiDataSelector *iface, GdaDataModel *model)
3194 {
3195 	GdauiRawGrid *grid;
3196 
3197 	g_return_if_fail (GDAUI_IS_RAW_GRID (iface));
3198 	grid = GDAUI_RAW_GRID (iface);
3199 
3200 	g_object_set (grid, "model", model, NULL);
3201 }
3202 
3203 static GArray *
3204 gdaui_raw_grid_selector_get_selected_rows (GdauiDataSelector *iface)
3205 {
3206 	GtkTreeSelection *selection;
3207 	GList *selected_rows;
3208 	GdauiRawGrid *grid;
3209 
3210 	g_return_val_if_fail (GDAUI_IS_RAW_GRID (iface), NULL);
3211 	grid = GDAUI_RAW_GRID (iface);
3212 
3213 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (grid));
3214 	selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
3215 	if (selected_rows) {
3216 		GList *list;
3217 		GArray *selarray = NULL;
3218 		GtkTreeIter iter;
3219 		gint row;
3220 
3221 		for (list = selected_rows; list; list = list->next) {
3222 			if (gtk_tree_model_get_iter (GTK_TREE_MODEL (grid->priv->store), &iter,
3223 						     (GtkTreePath *)(list->data))) {
3224 				gint *ind;
3225 				ind = gtk_tree_path_get_indices ((GtkTreePath *)(list->data));
3226 				g_assert (ind);
3227 				row = *ind;
3228 				if (!selarray)
3229 					selarray = g_array_new (FALSE, FALSE, sizeof (gint));
3230 				g_array_append_val (selarray, row);
3231 			}
3232 		}
3233 		g_list_foreach (selected_rows, (GFunc) gtk_tree_path_free, NULL);
3234 		g_list_free (selected_rows);
3235 		return selarray;
3236 	}
3237 	else
3238 		return NULL;
3239 }
3240 
3241 static GdaDataModelIter *
3242 gdaui_raw_grid_selector_get_data_set (GdauiDataSelector *iface)
3243 {
3244 	GdauiRawGrid *grid;
3245 
3246 	g_return_val_if_fail (GDAUI_IS_RAW_GRID (iface), NULL);
3247 	grid = GDAUI_RAW_GRID (iface);
3248 
3249 	return grid->priv->iter;
3250 }
3251 
3252 static gboolean
3253 gdaui_raw_grid_selector_select_row (GdauiDataSelector *iface, gint row)
3254 {
3255 	GdauiRawGrid *grid;
3256 	GtkTreeSelection *selection;
3257 	GtkTreePath *path;
3258 	gboolean retval = TRUE;
3259 	GtkTreeIter iter;
3260 
3261 	g_return_val_if_fail (GDAUI_IS_RAW_GRID (iface), FALSE);
3262 	grid = GDAUI_RAW_GRID (iface);
3263 
3264 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (grid));
3265 	path = gtk_tree_path_new_from_indices (row, -1);
3266 	if (gtk_tree_model_get_iter (GTK_TREE_MODEL (grid->priv->store), &iter, path))
3267 		gtk_tree_selection_select_path (selection, path);
3268 	else
3269 		retval = FALSE;
3270 	gtk_tree_path_free (path);
3271 
3272 	return retval;
3273 }
3274 
3275 static void
3276 gdaui_raw_grid_selector_unselect_row (GdauiDataSelector *iface, gint row)
3277 {
3278 	GdauiRawGrid *grid;
3279 	GtkTreeSelection *selection;
3280 	GtkTreePath *path;
3281 	GtkTreeIter iter;
3282 
3283 	g_return_if_fail (GDAUI_IS_RAW_GRID (iface));
3284 	grid = GDAUI_RAW_GRID (iface);
3285 
3286 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (grid));
3287 	path = gtk_tree_path_new_from_indices (row, -1);
3288 	if (gtk_tree_model_get_iter (GTK_TREE_MODEL (grid->priv->store), &iter, path))
3289 		gtk_tree_selection_unselect_path (selection, path);
3290 	gtk_tree_path_free (path);
3291 }
3292 
3293 static void
3294 gdaui_raw_grid_selector_set_column_visible (GdauiDataSelector *iface, gint column, gboolean visible)
3295 {
3296 	GdauiRawGrid *grid;
3297 	gint pos = -1;
3298 	GtkTreeViewColumn *viewcol;
3299 	GdaSetGroup *group;
3300 	GdaHolder *param;
3301 
3302 	g_return_if_fail (GDAUI_IS_RAW_GRID (iface));
3303 	grid = GDAUI_RAW_GRID (iface);
3304 	g_return_if_fail (grid->priv);
3305 
3306 	param = gda_data_model_iter_get_holder_for_field (grid->priv->iter, column);
3307 	g_return_if_fail (param);
3308 
3309 	group = gda_set_get_group ((GdaSet *)grid->priv->iter, param);
3310 	pos = g_slist_index (((GdaSet *)grid->priv->iter)->groups_list, group);
3311 	g_assert (pos >= 0);
3312 
3313 	viewcol = gtk_tree_view_get_column (GTK_TREE_VIEW (grid), pos);
3314 
3315 	/* Sets the column's visibility */
3316 	gtk_tree_view_column_set_visible (viewcol, visible);
3317 	ColumnData *cdata;
3318 	cdata = g_slist_nth_data (grid->priv->columns_data, column);
3319 	g_assert (cdata);
3320 	cdata->prog_hidden = !visible;
3321 }
3322