1 /*
2  * Copyright (C) 2009 - 2011 Vivien Malerba <malerba@gnome-db.org>
3  * Copyright (C) 2010 David King <davidk@openismus.com>
4  * Copyright (C) 2011 Murray Cumming <murrayc@murrayc.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA  02110-1301, USA.
20  */
21 
22 #include <string.h>
23 #include <glib/gi18n-lib.h>
24 #include <libgda/libgda.h>
25 #include "gdaui-data-proxy.h"
26 #include "gdaui-data-selector.h"
27 #include "gdaui-raw-grid.h"
28 #include "gdaui-data-proxy-info.h"
29 #include "gdaui-enum-types.h"
30 
31 static void gdaui_data_proxy_info_class_init (GdauiDataProxyInfoClass * class);
32 static void gdaui_data_proxy_info_init (GdauiDataProxyInfo *wid);
33 static void gdaui_data_proxy_info_dispose (GObject *object);
34 
35 static void gdaui_data_proxy_info_set_property (GObject *object,
36 						guint param_id,
37 						const GValue *value,
38 						GParamSpec *pspec);
39 static void gdaui_data_proxy_info_get_property (GObject *object,
40 						guint param_id,
41 						GValue *value,
42 						GParamSpec *pspec);
43 
44 static void modif_buttons_make (GdauiDataProxyInfo *info);
45 static void modif_buttons_update (GdauiDataProxyInfo *info);
46 
47 static void data_proxy_proxy_changed_cb (GdauiDataProxy *data_proxy, GdaDataProxy *proxy, GdauiDataProxyInfo *info);
48 static void proxy_changed_cb (GdaDataProxy *proxy, GdauiDataProxyInfo *info);
49 static void proxy_sample_changed_cb (GdaDataProxy *proxy, gint sample_start, gint sample_end, GdauiDataProxyInfo *info);
50 static void proxy_row_changed_cb (GdaDataProxy *proxy, gint row, GdauiDataProxyInfo *info);
51 static void proxy_reset_cb (GdaDataProxy *wid, GdauiDataProxyInfo *info);
52 
53 static void raw_grid_selection_changed_cb (GdauiRawGrid *grid, GdauiDataProxyInfo *info);
54 
55 
56 struct _GdauiDataProxyInfoPriv
57 {
58 	GdauiDataProxy *data_proxy;
59 	GdaDataProxy      *proxy;
60 	GdaDataModelIter  *iter;
61 	GdauiDataProxyInfoFlag flags; /* ORed values. */
62 
63 	GtkUIManager      *uimanager;
64 	GtkActionGroup    *agroup; /* no ref held! */
65 	guint              merge_id_row_modif;
66 	guint              merge_id_row_move;
67 	guint              merge_id_chunck_change;
68 
69 	GtkWidget         *buttons_bar;
70 	gboolean           buttons_bar_from_ui;
71 	GtkWidget         *tool_item;
72 	GtkWidget         *current_sample;
73 	GtkWidget         *row_spin;
74 
75 	guint              idle_id;
76 };
77 
78 /* get a pointer to the parents to be able to call their destructor */
79 static GObjectClass *parent_class = NULL;
80 
81 /* properties */
82 enum {
83 	PROP_0,
84 	PROP_DATA_PROXY,
85 	PROP_FLAGS,
86 	PROP_UI_MANAGER
87 };
88 
89 GType
gdaui_data_proxy_info_get_type(void)90 gdaui_data_proxy_info_get_type (void)
91 {
92 	static GType type = 0;
93 
94 	if (G_UNLIKELY (type == 0)) {
95 		static const GTypeInfo info = {
96 			sizeof (GdauiDataProxyInfoClass),
97 			(GBaseInitFunc) NULL,
98 			(GBaseFinalizeFunc) NULL,
99 			(GClassInitFunc) gdaui_data_proxy_info_class_init,
100 			NULL,
101 			NULL,
102 			sizeof (GdauiDataProxyInfo),
103 			0,
104 			(GInstanceInitFunc) gdaui_data_proxy_info_init,
105 			0
106 		};
107 
108 		type = g_type_register_static (GTK_TYPE_BOX, "GdauiDataProxyInfo", &info, 0);
109 	}
110 
111 	return type;
112 }
113 
114 static void
gdaui_data_proxy_info_class_init(GdauiDataProxyInfoClass * klass)115 gdaui_data_proxy_info_class_init (GdauiDataProxyInfoClass *klass)
116 {
117 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
118 
119 	parent_class = g_type_class_peek_parent (klass);
120 
121 
122 	object_class->dispose = gdaui_data_proxy_info_dispose;
123 
124 	/* Properties */
125         object_class->set_property = gdaui_data_proxy_info_set_property;
126         object_class->get_property = gdaui_data_proxy_info_get_property;
127 	g_object_class_install_property (object_class, PROP_DATA_PROXY,
128                                          g_param_spec_object ("data-proxy", NULL, NULL, GDAUI_TYPE_DATA_PROXY,
129 							      G_PARAM_READABLE | G_PARAM_WRITABLE));
130 	g_object_class_install_property (object_class, PROP_FLAGS,
131                                          g_param_spec_flags ("flags", NULL, NULL, GDAUI_TYPE_DATA_PROXY_INFO_FLAG,
132 							     GDAUI_DATA_PROXY_INFO_CURRENT_ROW,
133 							     G_PARAM_READABLE | G_PARAM_WRITABLE));
134 	/**
135 	 * GdauiDataProxyInfo:ui-manager:
136 	 *
137 	 * Use this property to obtain the #GtkUIManager object internally used (to add new actions
138 	 * for example).
139 	 *
140 	 * Since: 4.2.9
141 	 */
142 	g_object_class_install_property (object_class, PROP_UI_MANAGER,
143                                          g_param_spec_object ("ui-manager", NULL, NULL, GTK_TYPE_UI_MANAGER,
144 							      G_PARAM_READABLE));
145 }
146 
147 static void
gdaui_data_proxy_info_init(GdauiDataProxyInfo * wid)148 gdaui_data_proxy_info_init (GdauiDataProxyInfo *wid)
149 {
150 	wid->priv = g_new0 (GdauiDataProxyInfoPriv, 1);
151 	wid->priv->data_proxy = NULL;
152 	wid->priv->proxy = NULL;
153 	wid->priv->row_spin = NULL;
154 	wid->priv->buttons_bar = NULL;
155 	wid->priv->tool_item = NULL;
156 }
157 
158 /**
159  * gdaui_data_proxy_info_new:
160  * @data_proxy: a widget implementing the #GdauiDataProxy interface
161  * @flags: OR'ed values, specifying what to display in the new widget
162  *
163  * Creates a new #GdauiDataProxyInfo widget suitable to display information about @data_proxy
164  *
165  * Returns: (transfer full): the new widget
166  *
167  * Since: 4.2
168  */
169 GtkWidget *
gdaui_data_proxy_info_new(GdauiDataProxy * data_proxy,GdauiDataProxyInfoFlag flags)170 gdaui_data_proxy_info_new (GdauiDataProxy *data_proxy, GdauiDataProxyInfoFlag flags)
171 {
172 	GtkWidget *info;
173 
174 	g_return_val_if_fail (!data_proxy || GDAUI_IS_DATA_PROXY (data_proxy), NULL);
175 
176 	info = (GtkWidget *) g_object_new (GDAUI_TYPE_DATA_PROXY_INFO,
177 					   "data-proxy", data_proxy,
178 					   "flags", flags, NULL);
179 
180 	return info;
181 }
182 
183 static void
data_proxy_destroyed_cb(GdauiDataProxy * wid,GdauiDataProxyInfo * info)184 data_proxy_destroyed_cb (GdauiDataProxy *wid, GdauiDataProxyInfo *info)
185 {
186 	g_assert (wid == info->priv->data_proxy);
187 	g_signal_handlers_disconnect_by_func (G_OBJECT (wid),
188 					      G_CALLBACK (data_proxy_destroyed_cb), info);
189 	g_signal_handlers_disconnect_by_func (G_OBJECT (wid),
190 					      G_CALLBACK (data_proxy_proxy_changed_cb), info);
191 	if (GDAUI_IS_RAW_GRID (info->priv->data_proxy))
192 		g_signal_handlers_disconnect_by_func (info->priv->data_proxy,
193 						      G_CALLBACK (raw_grid_selection_changed_cb), info);
194 
195 	info->priv->data_proxy = NULL;
196 }
197 
198 static void
release_proxy(GdauiDataProxyInfo * info)199 release_proxy (GdauiDataProxyInfo *info)
200 {
201 	g_signal_handlers_disconnect_by_func (G_OBJECT (info->priv->proxy),
202 					      G_CALLBACK (proxy_changed_cb), info);
203 	g_signal_handlers_disconnect_by_func (G_OBJECT (info->priv->proxy),
204 					      G_CALLBACK (proxy_sample_changed_cb), info);
205 	g_signal_handlers_disconnect_by_func (G_OBJECT (info->priv->proxy),
206 					      G_CALLBACK (proxy_row_changed_cb), info);
207 	g_signal_handlers_disconnect_by_func (G_OBJECT (info->priv->proxy),
208 					      G_CALLBACK (proxy_reset_cb), info);
209 	g_object_unref (info->priv->proxy);
210 	info->priv->proxy = NULL;
211 }
212 
213 static void iter_row_changed_cb (GdaDataModelIter *iter, gint row, GdauiDataProxyInfo *info);
214 static void
release_iter(GdauiDataProxyInfo * info)215 release_iter (GdauiDataProxyInfo *info)
216 {
217 	g_signal_handlers_disconnect_by_func (info->priv->iter,
218 					      G_CALLBACK (iter_row_changed_cb), info);
219 	g_object_unref (info->priv->iter);
220 	info->priv->iter = NULL;
221 }
222 
223 static void
data_proxy_proxy_changed_cb(GdauiDataProxy * data_proxy,G_GNUC_UNUSED GdaDataProxy * proxy,GdauiDataProxyInfo * info)224 data_proxy_proxy_changed_cb (GdauiDataProxy *data_proxy, G_GNUC_UNUSED GdaDataProxy *proxy, GdauiDataProxyInfo *info)
225 {
226 	g_object_set (G_OBJECT (info), "data-proxy", data_proxy, NULL);
227 }
228 
229 static void
gdaui_data_proxy_info_dispose(GObject * object)230 gdaui_data_proxy_info_dispose (GObject *object)
231 {
232 	GdauiDataProxyInfo *info;
233 
234 	g_return_if_fail (object != NULL);
235 	g_return_if_fail (GDAUI_IS_DATA_PROXY_INFO (object));
236 	info = GDAUI_DATA_PROXY_INFO (object);
237 
238 	if (info->priv) {
239 		if (info->priv->proxy)
240 			release_proxy (info);
241 		if (info->priv->iter)
242 			release_iter (info);
243 		if (info->priv->data_proxy)
244 			data_proxy_destroyed_cb (info->priv->data_proxy, info);
245 		if (info->priv->idle_id)
246 			g_source_remove (info->priv->idle_id);
247 
248 		if (info->priv->uimanager) {
249 			if (info->priv->merge_id_row_modif)
250 				gtk_ui_manager_remove_ui (info->priv->uimanager,
251 							  info->priv->merge_id_row_modif);
252 			if (info->priv->merge_id_row_move)
253 				gtk_ui_manager_remove_ui (info->priv->uimanager,
254 							  info->priv->merge_id_row_move);
255 			if (info->priv->merge_id_chunck_change)
256 				gtk_ui_manager_remove_ui (info->priv->uimanager,
257 							  info->priv->merge_id_chunck_change);
258 			g_object_unref (info->priv->uimanager);
259 		}
260 
261 		/* the private area itself */
262 		g_free (info->priv);
263 		info->priv = NULL;
264 	}
265 
266 	/* for the parent class */
267 	parent_class->dispose (object);
268 }
269 
270 static void
gdaui_data_proxy_info_set_property(GObject * object,guint param_id,const GValue * value,GParamSpec * pspec)271 gdaui_data_proxy_info_set_property (GObject *object,
272 				    guint param_id,
273 				    const GValue *value,
274 				    GParamSpec *pspec)
275 {
276 	GdauiDataProxyInfo *info;
277 
278         info = GDAUI_DATA_PROXY_INFO (object);
279         if (info->priv) {
280                 switch (param_id) {
281                 case PROP_DATA_PROXY:
282 			if (info->priv->data_proxy)
283 				data_proxy_destroyed_cb (info->priv->data_proxy, info);
284 			if (info->priv->iter)
285 				release_iter (info);
286 			if (info->priv->proxy)
287 				release_proxy (info);
288 
289 			info->priv->data_proxy = GDAUI_DATA_PROXY (g_value_get_object (value));
290 			if (info->priv->data_proxy) {
291 				GdaDataProxy *proxy;
292 				GdaDataModelIter *iter;
293 
294 				/* data widget */
295 				g_signal_connect (info->priv->data_proxy, "destroy",
296 						  G_CALLBACK (data_proxy_destroyed_cb), info);
297 				g_signal_connect (info->priv->data_proxy, "proxy-changed",
298 						  G_CALLBACK (data_proxy_proxy_changed_cb), info);
299 				if (GDAUI_IS_RAW_GRID (info->priv->data_proxy))
300 					g_signal_connect (info->priv->data_proxy, "selection-changed",
301 							  G_CALLBACK (raw_grid_selection_changed_cb), info);
302 
303 				/* proxy */
304 				proxy = gdaui_data_proxy_get_proxy (info->priv->data_proxy);
305 				if (proxy) {
306 					info->priv->proxy = proxy;
307 					g_object_ref (info->priv->proxy);
308 					g_signal_connect (G_OBJECT (proxy), "changed",
309 							  G_CALLBACK (proxy_changed_cb), info);
310 					g_signal_connect (G_OBJECT (proxy), "sample-changed",
311 							  G_CALLBACK (proxy_sample_changed_cb), info);
312 					g_signal_connect (G_OBJECT (proxy), "row-inserted",
313 							  G_CALLBACK (proxy_row_changed_cb), info);
314 					g_signal_connect (G_OBJECT (proxy), "row-removed",
315 							  G_CALLBACK (proxy_row_changed_cb), info);
316 					g_signal_connect (G_OBJECT (proxy), "reset",
317 							  G_CALLBACK (proxy_reset_cb), info);
318 
319 
320 					/* iter */
321 					iter = gdaui_data_selector_get_data_set (GDAUI_DATA_SELECTOR
322 											  (info->priv->data_proxy));
323 					info->priv->iter = iter;
324 					if (iter) {
325 						g_object_ref (G_OBJECT (iter));
326 						g_signal_connect (iter, "row-changed",
327 								  G_CALLBACK (iter_row_changed_cb), info);
328 					}
329 				}
330 				modif_buttons_update (info);
331 			}
332                         break;
333                 case PROP_FLAGS:
334 			info->priv->flags = g_value_get_flags (value);
335 			modif_buttons_make (info);
336 			modif_buttons_update (info);
337                         break;
338 		default:
339 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
340 			break;
341                 }
342         }
343 }
344 
345 static void
gdaui_data_proxy_info_get_property(GObject * object,guint param_id,GValue * value,GParamSpec * pspec)346 gdaui_data_proxy_info_get_property (GObject *object,
347 				    guint param_id,
348 				    GValue *value,
349 				    GParamSpec *pspec)
350 {
351 	GdauiDataProxyInfo *info;
352 
353         info = GDAUI_DATA_PROXY_INFO (object);
354         if (info->priv) {
355                 switch (param_id) {
356 		case PROP_DATA_PROXY:
357 			g_value_set_object (value, info->priv->data_proxy);
358 			break;
359 		case PROP_FLAGS:
360 			g_value_set_flags (value, info->priv->flags);
361 			break;
362 		case PROP_UI_MANAGER:
363 			g_value_set_object (value, info->priv->uimanager);
364 			break;
365 		default:
366 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
367 			break;
368                 }
369         }
370 }
371 
372 
373 static void
proxy_changed_cb(G_GNUC_UNUSED GdaDataProxy * proxy,G_GNUC_UNUSED GdauiDataProxyInfo * info)374 proxy_changed_cb (G_GNUC_UNUSED GdaDataProxy *proxy, G_GNUC_UNUSED GdauiDataProxyInfo *info)
375 {
376 	modif_buttons_update (info);
377 }
378 
379 static void
proxy_sample_changed_cb(G_GNUC_UNUSED GdaDataProxy * proxy,G_GNUC_UNUSED gint sample_start,G_GNUC_UNUSED gint sample_end,G_GNUC_UNUSED GdauiDataProxyInfo * info)380 proxy_sample_changed_cb (G_GNUC_UNUSED GdaDataProxy *proxy, G_GNUC_UNUSED gint sample_start,
381 			 G_GNUC_UNUSED gint sample_end, G_GNUC_UNUSED GdauiDataProxyInfo *info)
382 {
383 	modif_buttons_update (info);
384 }
385 
386 static void
proxy_row_changed_cb(G_GNUC_UNUSED GdaDataProxy * proxy,G_GNUC_UNUSED gint row,GdauiDataProxyInfo * info)387 proxy_row_changed_cb (G_GNUC_UNUSED GdaDataProxy *proxy, G_GNUC_UNUSED gint row, GdauiDataProxyInfo *info)
388 {
389 	modif_buttons_update (info);
390 }
391 
392 static void
proxy_reset_cb(G_GNUC_UNUSED GdaDataProxy * wid,GdauiDataProxyInfo * info)393 proxy_reset_cb (G_GNUC_UNUSED GdaDataProxy *wid, GdauiDataProxyInfo *info)
394 {
395 	modif_buttons_make (info);
396 	modif_buttons_update (info);
397 }
398 
399 static void
iter_row_changed_cb(G_GNUC_UNUSED GdaDataModelIter * iter,G_GNUC_UNUSED gint row,GdauiDataProxyInfo * info)400 iter_row_changed_cb (G_GNUC_UNUSED GdaDataModelIter *iter, G_GNUC_UNUSED gint row, GdauiDataProxyInfo *info)
401 {
402 	modif_buttons_update (info);
403 }
404 
405 static void
raw_grid_selection_changed_cb(G_GNUC_UNUSED GdauiRawGrid * grid,GdauiDataProxyInfo * info)406 raw_grid_selection_changed_cb (G_GNUC_UNUSED GdauiRawGrid *grid, GdauiDataProxyInfo *info)
407 {
408 	modif_buttons_update (info);
409 }
410 
411 /*
412  *
413  * Modification buttons (Commit changes, Reset info, New entry, Delete)
414  *
415  */
416 static const gchar *ui_base =
417 	"<ui>"
418 	"  <toolbar name='ToolBar'>"
419 	"    <placeholder name='RowModif'/>"
420 	"    <placeholder name='RowModifExtension'/>"
421 	"    <placeholder name='RowMove'/>"
422 	"    <placeholder name='ChunkChange'/>"
423 	"    <toolitem action='ActionFilter'/>"
424 	"  </toolbar>"
425 	"</ui>";
426 
427 static const gchar *ui_row_modif =
428 	"<ui>"
429 	"  <toolbar name='ToolBar'>"
430 	"    <placeholder name='RowModif'>"
431 	"      <toolitem action='ActionNew'/>"
432 	"      <toolitem action='ActionDelete'/>"
433 	"      <toolitem action='ActionCommit'/>"
434 	"      <toolitem action='ActionReset'/>"
435 	"    </placeholder>"
436 	"  </toolbar>"
437 	"</ui>";
438 static const gchar *ui_row_move =
439 	"<ui>"
440 	"  <toolbar name='ToolBar'>"
441 	"    <placeholder name='RowMove'>"
442 	"      <toolitem action='ActionFirstRecord'/>"
443 	"      <toolitem action='ActionPrevRecord'/>"
444 	"      <toolitem action='ActionNextRecord'/>"
445 	"      <toolitem action='ActionLastRecord'/>"
446 	"    </placeholder>"
447 	"  </toolbar>"
448 	"</ui>";
449 static const gchar *ui_chunck_change =
450 	"<ui>"
451 	"  <toolbar name='ToolBar'>"
452 	"    <placeholder name='ChunkChange'>"
453 	"      <toolitem action='ActionFirstChunck'/>"
454 	"      <toolitem action='ActionPrevChunck'/>"
455 	"      <toolitem action='ActionNextChunck'/>"
456 	"      <toolitem action='ActionLastChunck'/>"
457 	"    </placeholder>"
458 	"  </toolbar>"
459 	"</ui>";
460 
461 
462 static void row_spin_changed_cb (GtkSpinButton *spin, GdauiDataProxyInfo *info);
463 static void
modif_buttons_make(GdauiDataProxyInfo * info)464 modif_buttons_make (GdauiDataProxyInfo *info)
465 {
466 	GtkWidget *wid;
467 	GdauiDataProxyInfoFlag flags = info->priv->flags;
468 
469 	if (! info->priv->data_proxy)
470 		return;
471 
472 	if (info->priv->tool_item) {
473 		/* remove tool_item from toolbar */
474 		gtk_container_remove (GTK_CONTAINER (info->priv->buttons_bar),
475 				      info->priv->tool_item);
476 		gtk_widget_unparent (info->priv->tool_item);
477 	}
478 
479 	if (info->priv->uimanager) {
480 		if (info->priv->merge_id_row_modif) {
481 			gtk_ui_manager_remove_ui (info->priv->uimanager, info->priv->merge_id_row_modif);
482 			info->priv->merge_id_row_modif = 0;
483 		}
484 		if (info->priv->merge_id_row_move) {
485 			gtk_ui_manager_remove_ui (info->priv->uimanager, info->priv->merge_id_row_move);
486 			info->priv->merge_id_row_move = 0;
487 		}
488 		if (info->priv->merge_id_chunck_change) {
489 			gtk_ui_manager_remove_ui (info->priv->uimanager, info->priv->merge_id_chunck_change);
490 			info->priv->merge_id_chunck_change = 0;
491 		}
492 		gtk_ui_manager_remove_action_group (info->priv->uimanager, info->priv->agroup);
493 		info->priv->agroup = NULL;
494 	}
495 	else {
496 		info->priv->uimanager = gtk_ui_manager_new ();
497 		gtk_ui_manager_add_ui_from_string (info->priv->uimanager, ui_base, -1, NULL);
498 	}
499 
500 	info->priv->agroup = gdaui_data_proxy_get_actions_group (info->priv->data_proxy);
501 	gtk_ui_manager_insert_action_group (info->priv->uimanager, info->priv->agroup, 0);
502 
503 	if (flags & (GDAUI_DATA_PROXY_INFO_ROW_MODIFY_BUTTONS |
504 		     GDAUI_DATA_PROXY_INFO_ROW_MOVE_BUTTONS |
505 		     GDAUI_DATA_PROXY_INFO_CHUNCK_CHANGE_BUTTONS)) {
506 		GtkUIManager *ui;
507 		ui = info->priv->uimanager;
508 		if (flags & GDAUI_DATA_PROXY_INFO_ROW_MODIFY_BUTTONS)
509 			info->priv->merge_id_row_modif = gtk_ui_manager_add_ui_from_string (ui, ui_row_modif,
510 											    -1, NULL);
511 		if (flags & GDAUI_DATA_PROXY_INFO_ROW_MOVE_BUTTONS)
512 			info->priv->merge_id_row_move = gtk_ui_manager_add_ui_from_string (ui, ui_row_move,
513 											   -1, NULL);
514 		if (flags & GDAUI_DATA_PROXY_INFO_CHUNCK_CHANGE_BUTTONS)
515 			info->priv->merge_id_chunck_change = gtk_ui_manager_add_ui_from_string (ui, ui_chunck_change,
516 												-1, NULL);
517 	}
518 
519 	/* get rid of previous toolbar if any */
520 	if (info->priv->buttons_bar) {
521 		if (gtk_widget_get_parent (info->priv->buttons_bar)) {
522 			gtk_container_remove (GTK_CONTAINER (info), info->priv->buttons_bar);
523 			gtk_widget_unparent (info->priv->buttons_bar);
524 		}
525 		if (info->priv->buttons_bar_from_ui)
526 			g_object_unref (info->priv->buttons_bar);
527 		else
528 			gtk_widget_destroy (info->priv->buttons_bar);
529 		info->priv->buttons_bar = NULL;
530 	}
531 
532 	/* create new toolbar */
533 	GtkUIManager *ui;
534 	ui = info->priv->uimanager;
535 	info->priv->buttons_bar = gtk_ui_manager_get_widget (ui, "/ToolBar");
536 	if (info->priv->buttons_bar)
537 		info->priv->buttons_bar_from_ui = TRUE;
538 	else {
539 		info->priv->buttons_bar = gtk_toolbar_new ();
540 		info->priv->buttons_bar_from_ui = FALSE;
541 	}
542 	g_object_ref_sink (info->priv->buttons_bar);
543 	gtk_toolbar_set_icon_size (GTK_TOOLBAR (info->priv->buttons_bar), GTK_ICON_SIZE_MENU);
544 	g_object_set (G_OBJECT (info->priv->buttons_bar), "toolbar-style", GTK_TOOLBAR_ICONS, NULL);
545 	gtk_box_pack_start (GTK_BOX (info), info->priv->buttons_bar, TRUE, TRUE, 0);
546 	gtk_widget_show (info->priv->buttons_bar);
547 
548 	if (flags & GDAUI_DATA_PROXY_INFO_CURRENT_ROW) {
549 		if (info->priv->tool_item) {
550 			/* remove the current contents */
551 			gtk_widget_destroy (gtk_bin_get_child (GTK_BIN (info->priv->tool_item)));
552 			info->priv->row_spin = NULL;
553 			info->priv->current_sample = NULL;
554 			gtk_toolbar_insert (GTK_TOOLBAR (info->priv->buttons_bar),
555 					    GTK_TOOL_ITEM (info->priv->tool_item), -1);
556 		}
557 		else {
558 			GtkToolItem *ti;
559 			ti = gtk_tool_item_new  ();
560 			gtk_toolbar_insert (GTK_TOOLBAR (info->priv->buttons_bar), ti, -1);
561 			info->priv->tool_item = GTK_WIDGET (g_object_ref (G_OBJECT (ti)));
562 		}
563 
564 		GtkWidget *toolwid;
565 		PangoContext *pc;
566 		PangoFontDescription *fd, *fdc;
567 		pc = gtk_widget_get_pango_context (GTK_WIDGET (info));
568 		fd = pango_context_get_font_description (pc);
569 		fdc = pango_font_description_copy (fd);
570 		pango_font_description_set_size (fdc,
571 						 pango_font_description_get_size (fd) * .8);
572 
573 		if (flags & GDAUI_DATA_PROXY_INFO_ROW_MOVE_BUTTONS) {
574 			toolwid = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
575 
576 			/* read-write spin counter (mainly for forms) */
577 			wid = gtk_spin_button_new_with_range (0, 1, 1);
578 
579 			gtk_widget_override_font (wid, fdc);
580 			gtk_spin_button_set_digits (GTK_SPIN_BUTTON (wid), 0);
581 			gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (wid), TRUE);
582 			gtk_box_pack_start (GTK_BOX (toolwid), wid, FALSE, TRUE, 2);
583 			gtk_widget_set_sensitive (wid, FALSE);
584 			info->priv->row_spin = wid;
585 			g_signal_connect (G_OBJECT (wid), "value-changed",
586 					  G_CALLBACK (row_spin_changed_cb), info);
587 
588 			/* rows counter */
589 			wid = gtk_label_new (" /?");
590 			gtk_widget_override_font (wid, fdc);
591 			info->priv->current_sample = wid;
592 			gtk_box_pack_start (GTK_BOX (toolwid), wid, FALSE, FALSE, 2);
593 		}
594 		else {
595 			/* read-only counter (mainly for grids) */
596 			wid = gtk_label_new ("? - ? /?");
597 			gtk_widget_override_font (wid, fdc);
598 			info->priv->current_sample = wid;
599 			toolwid = wid;
600 		}
601 
602 		pango_font_description_free (fdc);
603 
604 		gtk_container_add (GTK_CONTAINER (info->priv->tool_item), toolwid);
605 		gtk_widget_show_all (info->priv->tool_item);
606 	}
607 	else if (info->priv->tool_item)
608 		gtk_widget_hide (info->priv->tool_item);
609 }
610 
611 static void
row_spin_changed_cb(GtkSpinButton * spin,GdauiDataProxyInfo * info)612 row_spin_changed_cb (GtkSpinButton *spin, GdauiDataProxyInfo *info)
613 {
614 	gint row, nrows;
615 	gint value = gtk_spin_button_get_value (spin);
616 	nrows = gda_data_model_get_n_rows (GDA_DATA_MODEL (info->priv->proxy));
617 
618 	if ((value >= 1) && (value <= nrows))
619 		row = value - 1;
620 	else if (value > nrows)
621 		row = nrows - 1;
622 	else
623 		row = 0;
624 	gda_data_model_iter_move_to_row (gdaui_data_selector_get_data_set (GDAUI_DATA_SELECTOR (info->priv->data_proxy)),
625 					 row);
626 }
627 
628 static gboolean idle_modif_buttons_update (GdauiDataProxyInfo *info);
629 static void
modif_buttons_update(GdauiDataProxyInfo * info)630 modif_buttons_update (GdauiDataProxyInfo *info)
631 {
632 	if (info->priv->idle_id == 0)
633 		info->priv->idle_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE,
634 						       (GSourceFunc) idle_modif_buttons_update,
635 						       g_object_ref (info), g_object_unref);
636 }
637 
638 
639 #define BLOCK_SPIN (g_signal_handlers_block_by_func (G_OBJECT (info->priv->row_spin), \
640 						     G_CALLBACK (row_spin_changed_cb), info))
641 #define UNBLOCK_SPIN (g_signal_handlers_unblock_by_func (G_OBJECT (info->priv->row_spin), \
642 							 G_CALLBACK (row_spin_changed_cb), info))
643 static gboolean
idle_modif_buttons_update(GdauiDataProxyInfo * info)644 idle_modif_buttons_update (GdauiDataProxyInfo *info)
645 {
646 	GdaDataModelIter *model_iter;
647 	gboolean wrows, filtered_proxy = FALSE;
648 	gint row;
649 
650 	gint proxy_rows = 0;
651 	gint proxied_rows = 0;
652 	gint all_rows = 0;
653 
654 	GtkAction *action;
655 	gint sample_first_row = 0, sample_last_row = 0, sample_size = 0;
656 	GdauiDataProxyInfoFlag flags = 0;
657 
658 	model_iter = gdaui_data_selector_get_data_set (GDAUI_DATA_SELECTOR (info->priv->data_proxy));
659 	if (info->priv->proxy) {
660 		filtered_proxy = gda_data_proxy_get_filter_expr (info->priv->proxy) ? TRUE : FALSE;
661 		proxy_rows = gda_data_model_get_n_rows (GDA_DATA_MODEL (info->priv->proxy));
662 		if (filtered_proxy) {
663 			proxied_rows = gda_data_proxy_get_filtered_n_rows (info->priv->proxy);
664 			all_rows = proxied_rows;
665 		}
666 		else {
667 			proxied_rows = gda_data_proxy_get_proxied_model_n_rows (info->priv->proxy);
668 			all_rows = proxied_rows + gda_data_proxy_get_n_new_rows (info->priv->proxy);
669 		}
670 
671 		/* samples don't take into account the proxy's inserted rows */
672 		sample_first_row = gda_data_proxy_get_sample_start (info->priv->proxy);
673 		sample_last_row = gda_data_proxy_get_sample_end (info->priv->proxy);
674 		sample_size = gda_data_proxy_get_sample_size (info->priv->proxy);
675 
676 		flags = gda_data_model_get_access_flags (GDA_DATA_MODEL (info->priv->proxy));
677 	}
678 
679 	/* sensitiveness of the text indications and of the spin button */
680 	wrows = (proxy_rows <= 0) ? FALSE : TRUE;
681 	row = model_iter ? gda_data_model_iter_get_row (model_iter) : 0;
682 	if (info->priv->flags & GDAUI_DATA_PROXY_INFO_CURRENT_ROW) {
683 		if (proxy_rows < 0) {
684 			if (info->priv->flags & GDAUI_DATA_PROXY_INFO_ROW_MOVE_BUTTONS) {
685 				BLOCK_SPIN;
686 				gtk_spin_button_set_range (GTK_SPIN_BUTTON (info->priv->row_spin), 0, 1);
687 				gtk_spin_button_set_value (GTK_SPIN_BUTTON (info->priv->row_spin), 0);
688 				UNBLOCK_SPIN;
689 				gtk_label_set_text (GTK_LABEL (info->priv->current_sample), " /?");
690 			}
691 			else
692 				gtk_label_set_text (GTK_LABEL (info->priv->current_sample), "? - ? /?");
693 		}
694 		else {
695 			gchar *str;
696 			gint total;
697 
698 			total = sample_first_row + proxy_rows;
699 			if (info->priv->flags & GDAUI_DATA_PROXY_INFO_ROW_MOVE_BUTTONS) {
700 				if (total <= 0)
701 					str = g_strdup (" / 0");
702 				else {
703 					if (filtered_proxy)
704 						str = g_strdup_printf (" / (%d)", proxy_rows);
705 					else
706 						str = g_strdup_printf (" / %d", proxy_rows);
707 				}
708 				BLOCK_SPIN;
709 				gtk_spin_button_set_range (GTK_SPIN_BUTTON (info->priv->row_spin),
710 							   proxy_rows > 0 ? 1 : 0,
711 							   proxy_rows);
712 				if (row >= 0)
713 					if (gtk_spin_button_get_value (GTK_SPIN_BUTTON (info->priv->row_spin)) != row+1)
714 						gtk_spin_button_set_value (GTK_SPIN_BUTTON (info->priv->row_spin), row+1);
715 				UNBLOCK_SPIN;
716 			}
717 			else {
718 				if (total <= 0)
719 					str = g_strdup_printf ("0 - 0 / 0");
720 				else {
721 					if (all_rows < 0)
722 						str = g_strdup_printf ("%d - %d /?", sample_first_row + 1, total);
723 					else {
724 						if (filtered_proxy)
725 							str = g_strdup_printf ("%d - %d / (%d)",
726 									       sample_first_row + 1, total, all_rows);
727 						else
728 							str = g_strdup_printf ("%d - %d / %d",
729 									       sample_first_row + 1, total, all_rows);
730 					}
731 				}
732 			}
733 
734 			gtk_label_set_text (GTK_LABEL (info->priv->current_sample), str);
735 			g_free (str);
736 		}
737 
738 		gtk_widget_set_sensitive (info->priv->current_sample, wrows);
739 		if (info->priv->row_spin)
740 			gtk_widget_set_sensitive (info->priv->row_spin, wrows && (row >= 0));
741 	}
742 
743 	/* current row modifications */
744 	if (info->priv->buttons_bar) {
745 		gboolean changed = FALSE;
746 		gboolean to_be_deleted = FALSE;
747 		gboolean is_inserted = FALSE;
748 		gboolean force_del_btn = FALSE;
749 		gboolean force_undel_btn = FALSE;
750 		gboolean has_selection;
751 
752 		has_selection = (row >= 0) ? TRUE : FALSE;
753 		if (info->priv->proxy) {
754 			changed = gda_data_proxy_has_changed (info->priv->proxy);
755 
756 			if (has_selection) {
757 				to_be_deleted = gda_data_proxy_row_is_deleted (info->priv->proxy, row);
758 				is_inserted = gda_data_proxy_row_is_inserted (info->priv->proxy, row);
759 			}
760 			else
761 				if (GDAUI_IS_RAW_GRID (info->priv->data_proxy)) {
762 					/* bad for encapsulation, but very useful... */
763 					GList *sel, *list;
764 
765 					sel = _gdaui_raw_grid_get_selection ((GdauiRawGrid*) info->priv->data_proxy);
766 					if (sel) {
767 						list = sel;
768 						while (list && (!force_del_btn || !force_undel_btn)) {
769 							if ((GPOINTER_TO_INT (list->data) != -1) &&
770 							    gda_data_proxy_row_is_deleted (info->priv->proxy,
771 											   GPOINTER_TO_INT (list->data)))
772 								force_undel_btn = TRUE;
773 							else
774 								force_del_btn = TRUE;
775 							list = g_list_next (list);
776 						}
777 						list = sel;
778 
779 						is_inserted = TRUE;
780 						while (list && is_inserted) {
781 							if (GPOINTER_TO_INT (list->data) != -1)
782 								is_inserted = FALSE;
783 							list = g_list_next (list);
784 						}
785 						g_list_free (sel);
786 					}
787 				}
788 		}
789 
790 		if (info->priv->flags & GDAUI_DATA_PROXY_INFO_ROW_MODIFY_BUTTONS) {
791 			GdauiDataProxyWriteMode mode;
792 			mode = gdaui_data_proxy_get_write_mode (info->priv->data_proxy);
793 
794 			action = gtk_ui_manager_get_action (info->priv->uimanager, "/ToolBar/RowModif/ActionCommit");
795 			g_object_set (G_OBJECT (action), "sensitive", changed ? TRUE : FALSE, NULL);
796 			if (mode == GDAUI_DATA_PROXY_WRITE_ON_VALUE_CHANGE)
797 				gtk_action_set_visible (action, FALSE);
798 			else
799 				gtk_action_set_visible (action, TRUE);
800 
801 			action = gtk_ui_manager_get_action (info->priv->uimanager, "/ToolBar/RowModif/ActionReset");
802 			g_object_set (G_OBJECT (action), "sensitive", changed ? TRUE : FALSE, NULL);
803 			if (mode == GDAUI_DATA_PROXY_WRITE_ON_VALUE_CHANGE)
804 				gtk_action_set_visible (action, FALSE);
805 			else
806 				gtk_action_set_visible (action, TRUE);
807 
808 			action = gtk_ui_manager_get_action (info->priv->uimanager, "/ToolBar/RowModif/ActionNew");
809 			g_object_set (G_OBJECT (action), "sensitive",
810 				      flags & GDA_DATA_MODEL_ACCESS_INSERT ? TRUE : FALSE, NULL);
811 
812 			action = gtk_ui_manager_get_action (info->priv->uimanager, "/ToolBar/RowModif/ActionDelete");
813 			gtk_action_block_activate (action);
814 			gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), to_be_deleted);
815 			gtk_action_unblock_activate (action);
816 
817 			if (to_be_deleted) {
818 				wrows = (flags & GDA_DATA_MODEL_ACCESS_DELETE) &&
819 					(force_undel_btn || has_selection);
820 				g_object_set (G_OBJECT (action), "sensitive", wrows, NULL);
821 				gtk_action_set_tooltip (action, _("Undelete the selected entry"));
822 			}
823 			else {
824 				wrows = is_inserted ||
825 					((flags & GDA_DATA_MODEL_ACCESS_DELETE) &&
826 					 (force_del_btn || has_selection));
827 				g_object_set (G_OBJECT (action), "sensitive", wrows, NULL);
828 				gtk_action_set_tooltip (action, _("Delete the selected entry"));
829 			}
830 
831 			if ((mode == GDAUI_DATA_PROXY_WRITE_ON_ROW_CHANGE) ||
832 			    (mode == GDAUI_DATA_PROXY_WRITE_ON_VALUE_CHANGE) ||
833 			    (mode == GDAUI_DATA_PROXY_WRITE_ON_VALUE_ACTIVATED))
834 				gtk_action_set_visible (action, FALSE);
835 			else
836 				gtk_action_set_visible (action, TRUE);
837 		}
838 	}
839 
840 	/* current row moving */
841 	if (info->priv->flags & GDAUI_DATA_PROXY_INFO_ROW_MOVE_BUTTONS) {
842 		action = gtk_ui_manager_get_action (info->priv->uimanager, "/ToolBar/RowMove/ActionFirstRecord");
843 		g_object_set (G_OBJECT (action), "sensitive", (row <= 0) ? FALSE : TRUE, NULL);
844 		action = gtk_ui_manager_get_action (info->priv->uimanager, "/ToolBar/RowMove/ActionPrevRecord");
845 		g_object_set (G_OBJECT (action), "sensitive", (row <= 0) ? FALSE : TRUE, NULL);
846 		action = gtk_ui_manager_get_action (info->priv->uimanager, "/ToolBar/RowMove/ActionNextRecord");
847 		g_object_set (G_OBJECT (action), "sensitive", (row == proxy_rows -1) || (row < 0) ? FALSE : TRUE,
848 			      NULL);
849 		action = gtk_ui_manager_get_action (info->priv->uimanager, "/ToolBar/RowMove/ActionLastRecord");
850 		g_object_set (G_OBJECT (action), "sensitive", (row == proxy_rows -1) || (row < 0) ? FALSE : TRUE,
851 			      NULL);
852 	}
853 
854 	/* chunck indications */
855 	if (info->priv->flags & GDAUI_DATA_PROXY_INFO_CHUNCK_CHANGE_BUTTONS) {
856 		gboolean abool;
857 		wrows = (sample_size > 0) ? TRUE : FALSE;
858 		action = gtk_ui_manager_get_action (info->priv->uimanager, "/ToolBar/ChunkChange/ActionFirstChunck");
859 		g_object_set (G_OBJECT (action), "sensitive", wrows && sample_first_row > 0 ? TRUE : FALSE, NULL);
860 		action = gtk_ui_manager_get_action (info->priv->uimanager, "/ToolBar/ChunkChange/ActionPrevChunck");
861 		g_object_set (G_OBJECT (action), "sensitive", wrows && sample_first_row > 0 ? TRUE : FALSE, NULL);
862 		action = gtk_ui_manager_get_action (info->priv->uimanager, "/ToolBar/ChunkChange/ActionNextChunck");
863 		abool = (proxied_rows != -1) ? wrows && (sample_last_row < proxied_rows - 1) : TRUE;
864 		g_object_set (G_OBJECT (action), "sensitive", abool, NULL);
865 		action = gtk_ui_manager_get_action (info->priv->uimanager, "/ToolBar/ChunkChange/ActionLastChunck");
866 		g_object_set (G_OBJECT (action), "sensitive", wrows && (sample_last_row < proxied_rows - 1), NULL);
867 	}
868 
869 	/* filter */
870 	action = gtk_ui_manager_get_action (info->priv->uimanager, "/ToolBar/ActionFilter");
871 	if (info->priv->flags & GDAUI_DATA_PROXY_INFO_NO_FILTER)
872 		g_object_set (G_OBJECT (action), "visible", FALSE, NULL);
873 	else
874 		g_object_set (G_OBJECT (action), "visible", TRUE, NULL);
875 
876 	if (info->priv->uimanager)
877 		gtk_ui_manager_ensure_update (info->priv->uimanager);
878 
879 	/* remove IDLE */
880 	info->priv->idle_id = 0;
881 	return FALSE;
882 }
883