1 /*
2 * Copyright (C) 2010 David King <davidk@openismus.com>
3 * Copyright (C) 2010 - 2011 Vivien Malerba <malerba@gnome-db.org>
4 * Copyright (C) 2011 Murray Cumming <murrayc@murrayc.com>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program 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
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 */
20
21 #include <glib/gi18n-lib.h>
22 #include <string.h>
23 #include "data-widget.h"
24 #include "../browser-connection.h"
25 #include "../browser-spinner.h"
26 #include "../common/ui-formgrid.h"
27 #include "../browser-window.h"
28 #include "../support.h"
29 #include "data-source-editor.h"
30 #include "analyser.h"
31
32 /*
33 * The DataPart structure represents the execution of a single DataSource
34 */
35 typedef struct {
36 DataWidget *dwid;
37 DataSource *source;
38
39 GtkWidget *top;
40 GtkNotebook *nb; /* page 0: spinner
41 page 1 or 2, depends on @data_widget_page, @error_widget_page and @edit_widget_page*/
42 gint data_widget_page;
43 gint error_widget_page;
44 gint edit_widget_page;
45 gint edit_widget_previous_page;
46
47 BrowserSpinner *spinner;
48 guint spinner_show_timer_id;
49 GtkWidget *data_widget;
50 GtkWidget *error_widget;
51 GtkWidget *edit_widget;
52 GdaSet *export_data;
53
54 GSList *dep_parts; /* list of DataPart which need to be re-run when anything in @export_data
55 * changes */
56 GtkWidget *menu;
57 } DataPart;
58 #define DATA_PART(x) ((DataPart*)(x))
59
60 static DataPart *data_part_find (DataWidget *dwid, DataSource *source);
61 static void data_part_free (DataPart *part, GSList *all_parts);
62 static void data_part_show_error (DataPart *part, GError *error);
63
64 struct _DataWidgetPrivate {
65 DataSourceManager *mgr;
66 GtkWidget *top_nb; /* page 0 => error, page 1 => contents */
67 GtkWidget *info_label;
68 GtkWidget *contents_page_vbox;
69 GtkWidget *contents_page;
70 GSList *parts;
71 };
72
73 static void data_widget_class_init (DataWidgetClass *klass);
74 static void data_widget_init (DataWidget *dwid, DataWidgetClass *klass);
75 static void data_widget_dispose (GObject *object);
76 static gboolean compute_sources_dependencies (DataPart *part, GError **error);
77 static void mgr_list_changed_cb (DataSourceManager *mgr, DataWidget *dwid);
78
79 static GObjectClass *parent_class = NULL;
80
81 /*
82 * DataWidget class implementation
83 */
84 static void
data_widget_class_init(DataWidgetClass * klass)85 data_widget_class_init (DataWidgetClass *klass)
86 {
87 GObjectClass *object_class = G_OBJECT_CLASS (klass);
88
89 parent_class = g_type_class_peek_parent (klass);
90
91 object_class->dispose = data_widget_dispose;
92 }
93
94
95 static void
data_widget_init(DataWidget * dwid,G_GNUC_UNUSED DataWidgetClass * klass)96 data_widget_init (DataWidget *dwid, G_GNUC_UNUSED DataWidgetClass *klass)
97 {
98 g_return_if_fail (IS_DATA_WIDGET (dwid));
99
100 /* allocate private structure */
101 dwid->priv = g_new0 (DataWidgetPrivate, 1);
102
103 gtk_orientable_set_orientation (GTK_ORIENTABLE (dwid), GTK_ORIENTATION_VERTICAL);
104
105 /* init Widgets's structure */
106 dwid->priv->top_nb = gtk_notebook_new ();
107 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (dwid->priv->top_nb), FALSE);
108 gtk_notebook_set_show_border (GTK_NOTEBOOK (dwid->priv->top_nb), FALSE);
109 gtk_box_pack_start (GTK_BOX (dwid), dwid->priv->top_nb, TRUE, TRUE, 0);
110
111 /* error page */
112 GtkWidget *info;
113 info = gtk_info_bar_new ();
114 gtk_notebook_append_page (GTK_NOTEBOOK (dwid->priv->top_nb), info, NULL);
115 dwid->priv->info_label = gtk_label_new ("");
116 gtk_misc_set_alignment (GTK_MISC (dwid->priv->info_label), 0., -1);
117 gtk_label_set_ellipsize (GTK_LABEL (dwid->priv->info_label), PANGO_ELLIPSIZE_END);
118 gtk_container_add (GTK_CONTAINER (gtk_info_bar_get_content_area (GTK_INFO_BAR (info))),
119 dwid->priv->info_label);
120
121 /* contents page */
122 GtkWidget *vbox;
123 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
124 gtk_notebook_append_page (GTK_NOTEBOOK (dwid->priv->top_nb), vbox, NULL);
125 dwid->priv->contents_page_vbox = vbox;
126
127 gtk_widget_show_all (dwid->priv->top_nb);
128 }
129
130 GType
data_widget_get_type(void)131 data_widget_get_type (void)
132 {
133 static GType type = 0;
134
135 if (G_UNLIKELY (type == 0)) {
136 static const GTypeInfo info = {
137 sizeof (DataWidgetClass),
138 (GBaseInitFunc) NULL,
139 (GBaseFinalizeFunc) NULL,
140 (GClassInitFunc) data_widget_class_init,
141 NULL,
142 NULL,
143 sizeof (DataWidget),
144 0,
145 (GInstanceInitFunc) data_widget_init,
146 0
147 };
148 type = g_type_register_static (GTK_TYPE_BOX, "DataWidget", &info, 0);
149 }
150 return type;
151 }
152
153 static void
data_part_free_func(DataPart * part)154 data_part_free_func (DataPart *part)
155 {
156 data_part_free (part, NULL);
157 }
158
159 static void
data_widget_dispose(GObject * object)160 data_widget_dispose (GObject *object)
161 {
162 DataWidget *dwid = (DataWidget*) object;
163 if (dwid->priv) {
164 if (dwid->priv->mgr) {
165 g_signal_handlers_disconnect_by_func (dwid->priv->mgr,
166 G_CALLBACK (mgr_list_changed_cb), dwid);
167 g_object_unref (dwid->priv->mgr);
168 }
169 if (dwid->priv->parts) {
170 g_slist_foreach (dwid->priv->parts, (GFunc) data_part_free_func, NULL);
171 g_slist_free (dwid->priv->parts);
172 }
173 g_free (dwid->priv);
174 dwid->priv = NULL;
175 }
176 parent_class->dispose (object);
177 }
178
179 static void source_exec_started_cb (DataSource *source, DataPart *part);
180 static void source_exec_finished_cb (DataSource *source, GError *error, DataPart *part);
181 static void data_source_menu_clicked_cb (GtkButton *button, DataPart *part);
182
183 static DataPart *
create_or_reuse_part(DataWidget * dwid,DataSource * source,gboolean * out_reused)184 create_or_reuse_part (DataWidget *dwid, DataSource *source, gboolean *out_reused)
185 {
186
187 DataPart *part;
188 *out_reused = FALSE;
189
190 part = data_part_find (dwid, source);
191 if (part) {
192 GtkWidget *parent;
193 parent = gtk_widget_get_parent (part->top);
194 if (parent) {
195 g_object_ref ((GObject*) part->top);
196 gtk_container_remove (GTK_CONTAINER (parent), part->top);
197 }
198 *out_reused = TRUE;
199 return part;
200 }
201
202 part = g_new0 (DataPart, 1);
203 part->dwid = dwid;
204 part->source = g_object_ref (source);
205 part->edit_widget_previous_page = -1;
206 g_signal_connect (source, "execution-started",
207 G_CALLBACK (source_exec_started_cb), part);
208 g_signal_connect (source, "execution-finished",
209 G_CALLBACK (source_exec_finished_cb), part);
210
211 dwid->priv->parts = g_slist_append (dwid->priv->parts, part);
212
213 GtkWidget *vbox;
214 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
215 part->top = vbox;
216 g_object_ref_sink ((GObject*) part->top);
217
218 GtkWidget *header, *label, *button, *image;
219 const gchar *cstr;
220
221 header = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
222 gtk_box_pack_start (GTK_BOX (vbox), header, FALSE, FALSE, 0);
223
224 label = gtk_label_new ("");
225 cstr = data_source_get_title (source);
226 if (cstr) {
227 gchar *tmp;
228 tmp = g_markup_printf_escaped ("<b><small>%s</small></b>", cstr);
229 gtk_label_set_markup (GTK_LABEL (label), tmp);
230 g_free (tmp);
231 }
232 else
233 gtk_label_set_markup (GTK_LABEL (label), "<b><small> </small></b>");
234 gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
235 gtk_widget_set_size_request (label, 150, -1);
236 gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_END);
237 gtk_box_pack_start (GTK_BOX (header), label, TRUE, TRUE, 0);
238
239 image = gtk_image_new_from_pixbuf (browser_get_pixbuf_icon (BROWSER_ICON_MENU_INDICATOR));
240 button = gtk_button_new ();
241 gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE);
242 gtk_button_set_focus_on_click (GTK_BUTTON (button), FALSE);
243 gtk_widget_set_name (button, "browser-tab-close-button");
244 gtk_widget_set_tooltip_text (button, _("Link to other data"));
245 g_signal_connect (button, "clicked",
246 G_CALLBACK (data_source_menu_clicked_cb), part);
247
248 gtk_container_add (GTK_CONTAINER (button), image);
249 gtk_container_set_border_width (GTK_CONTAINER (button), 0);
250 gtk_box_pack_start (GTK_BOX (header), button, FALSE, FALSE, 0);
251
252 GtkWidget *nb, *page;
253 nb = gtk_notebook_new ();
254 gtk_notebook_set_show_border (GTK_NOTEBOOK (nb), FALSE);
255 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (nb), FALSE);
256 part->nb = GTK_NOTEBOOK (nb);
257
258 part->spinner = BROWSER_SPINNER (browser_spinner_new ());
259 browser_spinner_set_size ((BrowserSpinner*) part->spinner, GTK_ICON_SIZE_LARGE_TOOLBAR);
260 page = gtk_alignment_new (0.5, 0.5, 0., 0.);
261 gtk_container_add (GTK_CONTAINER (page), (GtkWidget*) part->spinner);
262 gtk_notebook_append_page (GTK_NOTEBOOK (nb), page, NULL);
263 part->data_widget = NULL;
264
265 gtk_box_pack_start (GTK_BOX (vbox), nb, TRUE, TRUE, 0);
266
267 gtk_widget_show_all (vbox);
268 if (data_source_execution_going_on (source))
269 source_exec_started_cb (source, part);
270
271 return part;
272 }
273
274 /* make a super-container to contain @size items: the list
275 * will have @size-2 paned widgets */
276 GSList *
make_paned_list(gint size,gboolean horiz)277 make_paned_list (gint size, gboolean horiz)
278 {
279 GSList *list = NULL;
280 gint i;
281 GtkWidget *paned;
282
283 g_assert (size >= 2);
284 paned = gtk_paned_new (horiz ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL);
285 list = g_slist_prepend (list, paned);
286
287 for (i = 2; i < size; i++) {
288 GtkWidget *paned2;
289 paned2 = gtk_paned_new (horiz ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL);
290 gtk_paned_add2 (GTK_PANED (paned), paned2);
291 list = g_slist_prepend (list, paned2);
292 paned = paned2;
293 }
294 return g_slist_reverse (list);
295 }
296
297 static void
pack_in_paned_list(GSList * paned_list,gint length,gint pos,GtkWidget * wid)298 pack_in_paned_list (GSList *paned_list, gint length, gint pos, GtkWidget *wid)
299 {
300 GtkPaned *paned;
301 if (pos < length - 1) {
302 paned = g_slist_nth_data (paned_list, pos);
303 gtk_paned_add1 (paned, wid);
304 }
305 else {
306 paned = g_slist_nth_data (paned_list, pos - 1);
307 gtk_paned_add2 (paned, wid);
308 }
309 }
310
311 static void
remove_data_source_mitem_activated_cb(G_GNUC_UNUSED GtkMenuItem * mitem,DataPart * part)312 remove_data_source_mitem_activated_cb (G_GNUC_UNUSED GtkMenuItem *mitem, DataPart *part)
313 {
314 data_source_manager_remove_source (part->dwid->priv->mgr, part->source);
315 }
316
317 static void
data_source_props_activated_cb(GtkCheckMenuItem * mitem,DataPart * part)318 data_source_props_activated_cb (GtkCheckMenuItem *mitem, DataPart *part)
319 {
320 if (gtk_check_menu_item_get_active (mitem)) {
321 part->edit_widget_previous_page = gtk_notebook_get_current_page (part->nb);
322 if (! part->edit_widget) {
323 part->edit_widget = data_source_editor_new ();
324 data_source_editor_set_readonly (DATA_SOURCE_EDITOR (part->edit_widget));
325 part->edit_widget_page = gtk_notebook_append_page (part->nb, part->edit_widget,
326 NULL);
327 gtk_widget_show (part->edit_widget);
328 }
329 data_source_editor_display_source (DATA_SOURCE_EDITOR (part->edit_widget),
330 part->source);
331 gtk_notebook_set_current_page (part->nb, part->edit_widget_page);
332 }
333 else
334 gtk_notebook_set_current_page (part->nb, part->edit_widget_previous_page);
335 }
336
337 static void
data_source_menu_clicked_cb(G_GNUC_UNUSED GtkButton * button,DataPart * part)338 data_source_menu_clicked_cb (G_GNUC_UNUSED GtkButton *button, DataPart *part)
339 {
340 if (! part->menu) {
341 GtkWidget *menu, *mitem;
342 GSList *added_list;
343 menu = gtk_menu_new ();
344 part->menu = menu;
345
346 mitem = gtk_menu_item_new_with_label (_("Remove data source"));
347 g_signal_connect (mitem, "activate",
348 G_CALLBACK (remove_data_source_mitem_activated_cb), part);
349 gtk_widget_show (mitem);
350 gtk_menu_shell_append (GTK_MENU_SHELL (menu), mitem);
351
352 mitem = gtk_check_menu_item_new_with_label (_("Show data source's properties"));
353 g_signal_connect (mitem, "activate",
354 G_CALLBACK (data_source_props_activated_cb), part);
355 gtk_widget_show (mitem);
356 gtk_menu_shell_append (GTK_MENU_SHELL (menu), mitem);
357
358 added_list = data_manager_add_proposals_to_menu (menu, part->dwid->priv->mgr,
359 part->source, GTK_WIDGET (part->dwid));
360 if (added_list)
361 g_slist_free (added_list);
362 }
363
364 gtk_menu_popup (GTK_MENU (part->menu), NULL, NULL,
365 NULL, NULL, 0,
366 gtk_get_current_event_time ());
367 }
368
369 static void
update_layout(DataWidget * dwid)370 update_layout (DataWidget *dwid)
371 {
372 GSList *newparts_list = NULL;
373 GArray *sources_array;
374 GError *lerror = NULL;
375 GtkWidget *new_contents;
376
377 new_contents = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
378
379 sources_array = data_source_manager_get_sources_array (dwid->priv->mgr, &lerror);
380 if (!sources_array) {
381 gchar *str;
382 if (lerror && lerror->message)
383 str = g_strdup_printf (_("Error: %s"), lerror->message);
384 else {
385 const GSList *list;
386 list = data_source_manager_get_sources (dwid->priv->mgr);
387 if (list)
388 str = g_strdup_printf (_("Error: %s"), _("No detail"));
389 else
390 str = g_strdup_printf (_("Error: %s"), _("No data source defined"));
391 }
392 g_clear_error (&lerror);
393 gtk_label_set_text (GTK_LABEL (dwid->priv->info_label), str);
394 g_free (str);
395 gtk_notebook_set_current_page (GTK_NOTEBOOK (dwid->priv->top_nb), 0);
396 goto cleanups;
397 }
398
399 if (sources_array->len == 1) {
400 GArray *subarray = g_array_index (sources_array, GArray*, 0);
401 if (subarray->len == 1) {
402 DataPart *part;
403 gboolean reused;
404 DataSource *source;
405 source = g_array_index (subarray, DataSource*, 0);
406 part = create_or_reuse_part (dwid, source, &reused);
407 gtk_box_pack_start (GTK_BOX (new_contents), part->top, TRUE, TRUE, 0);
408 g_object_unref ((GObject*) part->top);
409 newparts_list = g_slist_prepend (newparts_list, part);
410 if (!reused) {
411 if (compute_sources_dependencies (part, &lerror))
412 data_source_execute (source, NULL);
413 else {
414 data_part_show_error (part, lerror);
415 g_clear_error (&lerror);
416 }
417 }
418 }
419 else {
420 GSList *paned_list;
421 gsize i;
422 paned_list = make_paned_list (subarray->len, FALSE);
423 gtk_box_pack_start (GTK_BOX (new_contents),
424 GTK_WIDGET (paned_list->data), TRUE, TRUE, 0);
425 for (i = 0; i < subarray->len; i++) {
426 DataPart *part;
427 gboolean reused;
428 DataSource *source;
429 source = g_array_index (subarray, DataSource*, i);
430 part = create_or_reuse_part (dwid, source, &reused);
431 pack_in_paned_list (paned_list, subarray->len, i, part->top);
432 g_object_unref ((GObject*) part->top);
433 newparts_list = g_slist_prepend (newparts_list, part);
434 if (!reused) {
435 if (compute_sources_dependencies (part, &lerror))
436 data_source_execute (source, NULL);
437 else {
438 data_part_show_error (part, lerror);
439 g_clear_error (&lerror);
440 }
441 }
442 }
443 g_slist_free (paned_list);
444 }
445 }
446 else {
447 GSList *top_paned_list;
448 gsize j;
449
450 top_paned_list = make_paned_list (sources_array->len, TRUE);
451 gtk_box_pack_start (GTK_BOX (new_contents),
452 GTK_WIDGET (top_paned_list->data), TRUE, TRUE, 0);
453 for (j = 0; j < sources_array->len; j++) {
454 GArray *subarray = g_array_index (sources_array, GArray*, j);
455 DataSource *source;
456
457 if (subarray->len == 1) {
458 DataPart *part;
459 gboolean reused;
460 source = g_array_index (subarray, DataSource*, 0);
461 part = create_or_reuse_part (dwid, source, &reused);
462 pack_in_paned_list (top_paned_list, sources_array->len, j, part->top);
463 g_object_unref ((GObject*) part->top);
464 newparts_list = g_slist_prepend (newparts_list, part);
465 if (!reused) {
466 if (compute_sources_dependencies (part, &lerror))
467 data_source_execute (source, NULL);
468 else {
469 data_part_show_error (part, lerror);
470 g_clear_error (&lerror);
471 }
472 }
473 }
474 else {
475 GSList *paned_list;
476 gsize i;
477 paned_list = make_paned_list (subarray->len, FALSE);
478 pack_in_paned_list (top_paned_list, sources_array->len, j,
479 GTK_WIDGET (paned_list->data));
480 for (i = 0; i < subarray->len; i++) {
481 DataPart *part;
482 gboolean reused;
483 source = g_array_index (subarray, DataSource*, i);
484 part = create_or_reuse_part (dwid, source, &reused);
485 pack_in_paned_list (paned_list, subarray->len, i, part->top);
486 g_object_unref ((GObject*) part->top);
487 newparts_list = g_slist_prepend (newparts_list, part);
488 if (!reused) {
489 if (compute_sources_dependencies (part, &lerror))
490 data_source_execute (source, NULL);
491 else {
492 data_part_show_error (part, lerror);
493 g_clear_error (&lerror);
494 }
495 }
496 }
497 g_slist_free (paned_list);
498 }
499 }
500 g_slist_free (top_paned_list);
501 }
502 data_source_manager_destroy_sources_array (sources_array);
503 gtk_notebook_set_current_page (GTK_NOTEBOOK (dwid->priv->top_nb), 1);
504
505 cleanups:
506 {
507 GSList *list, *nlist = NULL;
508 for (list = dwid->priv->parts; list; list = list->next) {
509 if (!g_slist_find (newparts_list, list->data)) {
510 /* useless part */
511 data_part_free ((DataPart *) list->data, dwid->priv->parts);
512 }
513 else
514 nlist = g_slist_prepend (nlist, list->data);
515 }
516 g_slist_free (newparts_list);
517 g_slist_free (dwid->priv->parts);
518 dwid->priv->parts = g_slist_reverse (nlist);
519 }
520
521 gtk_box_pack_start (GTK_BOX (dwid->priv->contents_page_vbox), new_contents, TRUE, TRUE, 0);
522 gtk_widget_show_all (new_contents);
523 if (dwid->priv->contents_page)
524 gtk_widget_destroy (dwid->priv->contents_page);
525 dwid->priv->contents_page = new_contents;
526 }
527
528 static void
mgr_list_changed_cb(G_GNUC_UNUSED DataSourceManager * mgr,DataWidget * dwid)529 mgr_list_changed_cb (G_GNUC_UNUSED DataSourceManager *mgr, DataWidget *dwid)
530 {
531 update_layout (dwid);
532 }
533
534 /**
535 * data_widget_new
536 *
537 * Returns: the newly created widget.
538 */
539 GtkWidget *
data_widget_new(DataSourceManager * mgr)540 data_widget_new (DataSourceManager *mgr)
541 {
542 DataWidget *dwid;
543
544 g_return_val_if_fail (IS_DATA_SOURCE_MANAGER (mgr), NULL);
545
546 dwid = g_object_new (DATA_WIDGET_TYPE, NULL);
547 dwid->priv->mgr = DATA_SOURCE_MANAGER (mgr);
548 g_object_ref ((GObject*) dwid->priv->mgr);
549 g_signal_connect (mgr, "list_changed",
550 G_CALLBACK (mgr_list_changed_cb), dwid);
551
552 update_layout (dwid);
553 return GTK_WIDGET (dwid);
554 }
555
556 static void
data_part_free(DataPart * part,GSList * all_parts)557 data_part_free (DataPart *part, GSList *all_parts)
558 {
559 if (part->spinner_show_timer_id) {
560 g_source_remove (part->spinner_show_timer_id);
561 part->spinner_show_timer_id = 0;
562 }
563
564 if (all_parts) {
565 GSList *list;
566 for (list = all_parts; list; list = list->next) {
567 DataPart *apart = (DataPart *) list->data;
568 if (apart == part)
569 continue;
570 apart->dep_parts = g_slist_remove_all (apart->dep_parts, part);
571 }
572 }
573
574 if (part->top)
575 gtk_widget_destroy (part->top);
576
577 if (part->source) {
578 g_signal_handlers_disconnect_by_func (part->source,
579 G_CALLBACK (source_exec_started_cb), part);
580 g_signal_handlers_disconnect_by_func (part->source,
581 G_CALLBACK (source_exec_finished_cb), part);
582 g_object_unref (part->source);
583 }
584
585 if (part->export_data)
586 g_object_unref (part->export_data);
587 if (part->dep_parts)
588 g_slist_free (part->dep_parts);
589 if (part->menu)
590 gtk_widget_destroy (part->menu);
591 g_free (part);
592 }
593
594 static DataPart *
data_part_find(DataWidget * dwid,DataSource * source)595 data_part_find (DataWidget *dwid, DataSource *source)
596 {
597 GSList *list;
598 for (list = dwid->priv->parts; list; list = list->next) {
599 if (DATA_PART (list->data)->source == source)
600 return DATA_PART (list->data);
601 }
602 return NULL;
603 }
604
605 static void
data_part_show_error(DataPart * part,GError * error)606 data_part_show_error (DataPart *part, GError *error)
607 {
608 gchar *tmp;
609 g_assert (part);
610 tmp = g_markup_printf_escaped ("\n<b>Error:\n</b>%s",
611 error && error->message ? error->message : _("no detail"));
612 if (! part->error_widget) {
613 part->error_widget = gtk_label_new ("");
614 gtk_misc_set_alignment (GTK_MISC (part->error_widget), 0., 0.);
615 part->error_widget_page = gtk_notebook_append_page (part->nb, part->error_widget,
616 NULL);
617 gtk_widget_show (part->error_widget);
618 }
619 gtk_label_set_markup (GTK_LABEL (part->error_widget), tmp);
620 g_free (tmp);
621 gtk_notebook_set_current_page (part->nb, part->error_widget_page);
622 }
623
624 static gboolean
source_exec_started_cb_timeout(DataPart * part)625 source_exec_started_cb_timeout (DataPart *part)
626 {
627 gtk_notebook_set_current_page (part->nb, 0);
628 browser_spinner_start (part->spinner);
629 part->spinner_show_timer_id = 0;
630 return FALSE; /* remove timer */
631 }
632
633 static void
source_exec_started_cb(G_GNUC_UNUSED DataSource * source,DataPart * part)634 source_exec_started_cb (G_GNUC_UNUSED DataSource *source, DataPart *part)
635 {
636 if (! part->spinner_show_timer_id)
637 part->spinner_show_timer_id = g_timeout_add (300,
638 (GSourceFunc) source_exec_started_cb_timeout,
639 part);
640 }
641
642 /*
643 * creates a new string where double underscores '__' are replaced by a single underscore '_'
644 */
645 static gchar *
replace_double_underscores(const gchar * str)646 replace_double_underscores (const gchar *str)
647 {
648 gchar **arr;
649 gchar *ret;
650
651 arr = g_strsplit (str, "__", 0);
652 ret = g_strjoinv ("_", arr);
653 g_strfreev (arr);
654
655 return ret;
656 }
657
658 static void
customize_form_grid(UiFormGrid * cwid)659 customize_form_grid (UiFormGrid *cwid)
660 {
661 GdauiRawGrid *grid;
662 grid = ui_formgrid_get_grid_widget (UI_FORMGRID (cwid));
663
664 GList *columns, *list;
665 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (grid));
666 for (list = columns; list; list = list->next) {
667 /* reduce column's title */
668 const gchar *title;
669 GtkWidget *header;
670 title = gtk_tree_view_column_get_title (GTK_TREE_VIEW_COLUMN (list->data));
671 header = gtk_label_new ("");
672 if (title) {
673 gchar *tmp, *str;
674 str = replace_double_underscores (title);
675 tmp = g_markup_printf_escaped ("<small>%s</small>", str);
676 g_free (str);
677 gtk_label_set_markup (GTK_LABEL (header), tmp);
678 g_free (tmp);
679 }
680 else
681 gtk_label_set_markup (GTK_LABEL (header), "<small></small>");
682 gtk_widget_show (header);
683 gtk_tree_view_column_set_widget (GTK_TREE_VIEW_COLUMN (list->data),
684 header);
685 }
686
687 /*if (!columns || !columns->next)*/
688 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (grid), FALSE);
689 g_list_free (columns);
690 }
691
692
693 static void formgrid_data_set_changed_cb (UiFormGrid *cwid, DataPart *part);
694 static void data_part_selection_changed_cb (GdauiDataSelector *gdauidataselector, DataPart *part);
695 static void
source_exec_finished_cb(G_GNUC_UNUSED DataSource * source,GError * error,DataPart * part)696 source_exec_finished_cb (G_GNUC_UNUSED DataSource *source, GError *error, DataPart *part)
697 {
698 if (part->spinner_show_timer_id) {
699 g_source_remove (part->spinner_show_timer_id);
700 part->spinner_show_timer_id = 0;
701 }
702 else
703 browser_spinner_stop (part->spinner);
704
705 #ifdef GDA_DEBUG_NO
706 g_print ("==== Execution of source [%s] finished\n", data_source_get_title (part->source));
707 #endif
708 if (error) {
709 data_part_show_error (part, error);
710 return;
711 }
712
713 if (! part->data_widget) {
714 GtkWidget *cwid, *wid;
715 BrowserConnection *bcnc;
716 bcnc = browser_window_get_connection ((BrowserWindow*) gtk_widget_get_toplevel ((GtkWidget*) part->dwid));
717 cwid = (GtkWidget*) data_source_create_grid (part->source);
718 ui_formgrid_handle_user_prefs (UI_FORMGRID (cwid), bcnc,
719 data_source_get_statement (part->source));
720 g_signal_connect (cwid, "data-set-changed",
721 G_CALLBACK (formgrid_data_set_changed_cb), part);
722
723 wid = (GtkWidget*) ui_formgrid_get_grid_widget (UI_FORMGRID (cwid));
724 part->data_widget = wid;
725 part->data_widget_page = gtk_notebook_append_page (part->nb, cwid, NULL);
726 g_signal_connect (part->data_widget, "selection-changed",
727 G_CALLBACK (data_part_selection_changed_cb), part);
728 gtk_widget_show (cwid);
729
730 #ifdef GDA_DEBUG_NO
731 g_print ("Creating data widget for source [%s]\n",
732 data_source_get_title (part->source));
733 #endif
734
735 /* compute part->export_data */
736 formgrid_data_set_changed_cb (UI_FORMGRID (cwid), part);
737 }
738 else {
739 GError *lerror = NULL;
740 if (! compute_sources_dependencies (part, &lerror)) {
741 data_part_show_error (part, lerror);
742 g_clear_error (&lerror);
743 }
744 }
745 gtk_notebook_set_current_page (part->nb, part->data_widget_page);
746 }
747
748 static void
formgrid_data_set_changed_cb(UiFormGrid * cwid,DataPart * part)749 formgrid_data_set_changed_cb (UiFormGrid *cwid, DataPart *part)
750 {
751 GtkWidget *wid;
752 GArray *export_names;
753
754 customize_form_grid (cwid);
755 wid = (GtkWidget*) ui_formgrid_get_grid_widget (UI_FORMGRID (cwid));
756
757 if (part->export_data) {
758 g_object_unref (part->export_data);
759 part->export_data = NULL;
760 }
761
762 #ifdef GDA_DEBUG_NO
763 g_print ("+++++ Reset export_data for data source [%s]\n", data_source_get_id (part->source));
764 #endif
765
766 export_names = data_source_get_export_names (part->source);
767 if (export_names && (export_names->len > 0)) {
768 GSList *holders = NULL;
769 GdaDataModel *model;
770 GHashTable *export_columns;
771 gsize i;
772 GdaDataModelIter *iter;
773
774 iter = gdaui_data_selector_get_data_set (GDAUI_DATA_SELECTOR (wid));
775 g_object_get (wid, "model", &model, NULL);
776
777 export_columns = data_source_get_export_columns (part->source);
778 for (i = 0; i < export_names->len; i++) {
779 gint col;
780 GdaHolder *bindto;
781 col = GPOINTER_TO_INT (g_hash_table_lookup (export_columns,
782 g_array_index (export_names,
783 gchar*, i))) - 1;
784 bindto = gda_data_model_iter_get_holder_for_field (iter, col);
785 if (bindto) {
786 GdaHolder *holder;
787 holder = gda_holder_copy (bindto);
788 g_object_set ((GObject*) holder, "id",
789 g_array_index (export_names, gchar*, i), NULL);
790 holders = g_slist_prepend (holders, holder);
791 #ifdef GDA_DEBUG_NO
792 g_print ("HOLDER [%s::%s]\n",
793 gda_holder_get_id (holder),
794 g_type_name (gda_holder_get_g_type (holder)));
795 #endif
796 g_assert (gda_holder_set_bind (holder, bindto, NULL));
797 }
798 }
799
800 g_object_unref (model);
801 if (holders) {
802 part->export_data = gda_set_new (holders);
803 g_slist_foreach (holders, (GFunc) g_object_unref, NULL);
804 g_slist_free (holders);
805 }
806 }
807
808 GError *lerror = NULL;
809 if (! compute_sources_dependencies (part, &lerror)) {
810 data_part_show_error (part, lerror);
811 g_clear_error (&lerror);
812 lerror = NULL;
813 }
814
815 if (part->dep_parts) {
816 GSList *list;
817 for (list = part->dep_parts; list; list = list->next) {
818 if (! compute_sources_dependencies ((DataPart*) list->data, &lerror)) {
819 data_part_show_error (part, lerror);
820 g_clear_error (&lerror);
821 lerror = NULL;
822 }
823 }
824 }
825 }
826
827 static void
data_part_selection_changed_cb(G_GNUC_UNUSED GdauiDataSelector * gdauidataselector,DataPart * part)828 data_part_selection_changed_cb (G_GNUC_UNUSED GdauiDataSelector *gdauidataselector, DataPart *part)
829 {
830 if (part->export_data) {
831 GSList *list;
832 #ifdef GDA_DEBUG_NO
833 for (list = part->export_data->holders; list; list = list->next) {
834 GdaHolder *holder = GDA_HOLDER (list->data);
835 gchar *tmp;
836 tmp = gda_value_stringify (gda_holder_get_value (holder));
837 if (strlen (tmp) > 10)
838 tmp [15] = 0;
839 g_print ("%s=>[%s]\n", gda_holder_get_id (holder), tmp);
840 g_free (tmp);
841 }
842 #endif
843
844 for (list = part->dep_parts; list; list = list->next) {
845 DataPart *spart = DATA_PART (list->data);
846 data_source_execute (spart->source, NULL);
847 }
848 }
849 }
850
851 static gboolean
compute_sources_dependencies(DataPart * part,GError ** error)852 compute_sources_dependencies (DataPart *part, GError **error)
853 {
854 GdaSet *import;
855 gboolean retval = TRUE;
856
857 import = data_source_get_import (part->source);
858 if (!import)
859 return TRUE;
860
861 GSList *holders;
862 for (holders = import->holders; holders; holders = holders->next) {
863 GdaHolder *holder = (GdaHolder*) holders->data;
864 const gchar *hid = gda_holder_get_id (holder);
865 GSList *list;
866
867 for (list = part->dwid->priv->parts; list; list = list->next) {
868 DataPart *opart = DATA_PART (list->data);
869 if (opart == part)
870 continue;
871
872 GdaSet *export;
873 GdaHolder *holder2;
874 opart->dep_parts = g_slist_remove (opart->dep_parts, part);
875 export = data_widget_get_export (part->dwid, opart->source);
876 if (!export)
877 continue;
878
879 holder2 = gda_set_get_holder (export, hid);
880 if (holder2) {
881 GError *lerror = NULL;
882 if (! gda_holder_set_bind (holder, holder2, &lerror)) {
883 if (retval) {
884 if (lerror &&
885 (lerror->domain == GDA_HOLDER_ERROR) &&
886 (lerror->code == GDA_HOLDER_VALUE_TYPE_ERROR)) {
887 g_set_error (error, GDA_HOLDER_ERROR,
888 GDA_HOLDER_VALUE_TYPE_ERROR,
889 _("Can't bind parameter '%s' of type '%s' "
890 "to a parameter of type '%s'"),
891 gda_holder_get_id (holder),
892 gda_g_type_to_string (gda_holder_get_g_type (holder)),
893 gda_g_type_to_string (gda_holder_get_g_type (holder2)));
894 g_clear_error (&lerror);
895 }
896 else
897 g_propagate_error (error, lerror);
898 retval = FALSE;
899 }
900 else
901 g_clear_error (&lerror);
902 }
903 #ifdef GDA_DEBUG_NO
904 else {
905 g_print ("[%s].[%s] bound to [%s].[%s]\n",
906 data_source_get_id (part->source),
907 hid,
908 data_source_get_id (opart->source),
909 gda_holder_get_id (holder2));
910 }
911 #endif
912 opart->dep_parts = g_slist_append (opart->dep_parts, part);
913 continue;
914 }
915 }
916 }
917
918 return retval;
919 }
920
921 /**
922 * data_widget_get_export
923 *
924 * Returns: the #GdaSet or %NULL, no ref held to it by the caller
925 */
926 GdaSet *
data_widget_get_export(DataWidget * dwid,DataSource * source)927 data_widget_get_export (DataWidget *dwid, DataSource *source)
928 {
929 DataPart *part;
930 g_return_val_if_fail (IS_DATA_WIDGET (dwid), NULL);
931 g_return_val_if_fail (IS_DATA_SOURCE (source), NULL);
932 part = data_part_find (dwid, source);
933 if (!part) {
934 g_warning ("Can't find DataPart for DataSource");
935 return NULL;
936 }
937 return part->export_data;
938 }
939
940 /**
941 * data_widget_rerun
942 */
943 void
data_widget_rerun(DataWidget * dwid)944 data_widget_rerun (DataWidget *dwid)
945 {
946 GSList *parts;
947 g_return_if_fail (IS_DATA_WIDGET (dwid));
948
949 for (parts = dwid->priv->parts; parts; parts = parts->next) {
950 DataPart *part;
951 part = (DataPart*) parts->data;
952 data_source_execute (part->source, NULL);
953 }
954 }
955