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 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 <string.h>
22 #include "gdaui-data-import.h"
23 #include <libgda/libgda.h>
24 #include <glib/gi18n-lib.h>
25 #include <libgda-ui/libgda-ui.h>
26 #include <libgda/binreloc/gda-binreloc.h>
27
28 static void gdaui_data_import_class_init (GdauiDataImportClass *class);
29 static void gdaui_data_import_init (GdauiDataImport *wid);
30 static void gdaui_data_import_dispose (GObject *object);
31
32
33 enum {
34 SEP_COMMA,
35 SEP_SEMICOL,
36 SEP_TAB,
37 SEP_SPACE,
38 SEP_PIPE,
39 SEP_OTHER,
40 SEP_LAST
41 };
42
43 struct _GdauiDataImportPriv
44 {
45 GdaDataModel *model;
46
47 /* spec widgets */
48 GtkWidget *file_chooser;
49 GtkWidget *encoding_combo;
50 GtkWidget *first_line_check;
51 GtkWidget *sep_array [SEP_LAST];
52 GtkWidget *sep_other_entry;
53
54 /* preview widgets */
55 GtkWidget *preview_box;
56 GtkWidget *no_data_label;
57 GtkWidget *preview_grid;
58 };
59
60 static void spec_changed_cb (GtkWidget *wid, GdauiDataImport *import);
61
62 /* get a pointer to the parents to be able to call their destructor */
63 static GObjectClass *parent_class = NULL;
64
65 GType
gdaui_data_import_get_type(void)66 gdaui_data_import_get_type (void)
67 {
68 static GType type = 0;
69
70 if (G_UNLIKELY (type == 0)) {
71 static const GTypeInfo info = {
72 sizeof (GdauiDataImportClass),
73 (GBaseInitFunc) NULL,
74 (GBaseFinalizeFunc) NULL,
75 (GClassInitFunc) gdaui_data_import_class_init,
76 NULL,
77 NULL,
78 sizeof (GdauiDataImport),
79 0,
80 (GInstanceInitFunc) gdaui_data_import_init,
81 0
82 };
83
84 type = g_type_register_static (GTK_TYPE_PANED, "GdauiDataImport", &info, 0);
85 }
86
87 return type;
88 }
89
90 static void
gdaui_data_import_class_init(GdauiDataImportClass * class)91 gdaui_data_import_class_init (GdauiDataImportClass * class)
92 {
93 GObjectClass *object_class = G_OBJECT_CLASS (class);
94
95 parent_class = g_type_class_peek_parent (class);
96
97 object_class->dispose = gdaui_data_import_dispose;
98 }
99
100 static void
gdaui_data_import_init(GdauiDataImport * import)101 gdaui_data_import_init (GdauiDataImport * import)
102 {
103 GtkWidget *label, *vbox, *hbox;
104 gchar *str;
105 GtkWidget *grid, *entry;
106 GtkFileFilter *filter;
107 GdaDataModel *encs;
108 GSList *encs_errors;
109
110 import->priv = g_new0 (GdauiDataImportPriv, 1);
111 import->priv->model = NULL;
112
113 gtk_orientable_set_orientation (GTK_ORIENTABLE (import), GTK_ORIENTATION_VERTICAL);
114
115 /*
116 * top part: import specs.
117 */
118 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
119 gtk_paned_pack1 (GTK_PANED (import), vbox, FALSE, FALSE);
120
121 str = g_strdup_printf ("<b>%s:</b>", _("Import specifications"));
122 label = gtk_label_new ("");
123 gtk_label_set_markup (GTK_LABEL (label), str);
124 gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
125 g_free (str);
126 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
127
128 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); /* HIG */
129 gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
130 gtk_widget_show (hbox);
131 label = gtk_label_new (" ");
132 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
133
134 grid = gtk_grid_new ();
135 gtk_box_pack_start (GTK_BOX (hbox), grid, TRUE, TRUE, 0);
136 gtk_grid_set_column_spacing (GTK_GRID (grid), 5);
137 gtk_grid_set_row_spacing (GTK_GRID (grid), 5);
138
139 /* file to import from */
140 label = gtk_label_new (_("File to import from:"));
141 gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
142 gtk_grid_attach (GTK_GRID (grid), label, 0, 0, 1, 1);
143
144 entry = gtk_file_chooser_button_new (_("File to import data from"), GTK_FILE_CHOOSER_ACTION_OPEN);
145 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (entry), gdaui_get_default_path ());
146 import->priv->file_chooser = entry;
147 filter = gtk_file_filter_new ();
148 gtk_file_filter_set_name (filter, _("Comma separated values"));
149 gtk_file_filter_add_pattern (filter, "*.csv");
150 gtk_file_filter_add_pattern (filter, "*.txt");
151 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (entry), filter);
152 filter = gtk_file_filter_new ();
153 gtk_file_filter_set_name (filter, _("XML exported"));
154 gtk_file_filter_add_pattern (filter, "*.xml");
155 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (entry), filter);
156 filter = gtk_file_filter_new ();
157 gtk_file_filter_set_name (filter, _("All files"));
158 gtk_file_filter_add_pattern (filter, "*");
159 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (entry), filter);
160 gtk_grid_attach (GTK_GRID (grid), entry, 1, 0, 3, 1);
161 g_signal_connect (G_OBJECT (entry), "selection-changed",
162 G_CALLBACK (spec_changed_cb), import);
163
164 /* Encoding */
165 label = gtk_label_new (_("Encoding:"));
166 gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
167 gtk_grid_attach (GTK_GRID (grid), label, 0, 1, 1, 1);
168
169 gchar *fname = gda_gbr_get_file_path (GDA_DATA_DIR, LIBGDA_ABI_NAME, "import_encodings.xml", NULL);
170 encs = gda_data_model_import_new_file (fname, TRUE, NULL);
171 g_free (fname);
172 encs_errors = gda_data_model_import_get_errors (GDA_DATA_MODEL_IMPORT (encs));
173 if (encs_errors) {
174 for (; encs_errors; encs_errors = g_slist_next (encs_errors)) {
175 gda_log_message ("Error importing import_encodings.xml: %s\n",
176 encs_errors->data && ((GError *) encs_errors->data)->message ?
177 ((GError *) encs_errors->data)->message : _("no detail"));
178 }
179 import->priv->encoding_combo = NULL;
180 }
181 else {
182 gint cols[] = {0};
183 entry = gdaui_combo_new_with_model (encs, 1, cols);
184 import->priv->encoding_combo = entry;
185 }
186 g_object_unref (encs);
187 gtk_grid_attach (GTK_GRID (grid), entry, 1, 1, 3, 1);
188 g_signal_connect (G_OBJECT (entry), "changed",
189 G_CALLBACK (spec_changed_cb), import);
190
191 /* first line as title */
192 label = gtk_label_new (_("First line as title:"));
193 gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
194 gtk_grid_attach (GTK_GRID (grid), label, 0, 2, 1, 1);
195
196 entry = gtk_check_button_new ();
197 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (entry), TRUE);
198 import->priv->first_line_check = entry;
199 gtk_grid_attach (GTK_GRID (grid), entry, 1, 2, 2, 1);
200 g_signal_connect (G_OBJECT (entry), "toggled",
201 G_CALLBACK (spec_changed_cb), import);
202
203 /* separator */
204 label = gtk_label_new (_("Separator:"));
205 gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
206 gtk_grid_attach (GTK_GRID (grid), label, 0, 3, 1, 1);
207
208 entry = gtk_radio_button_new_with_label (NULL, _("Comma"));
209 import->priv->sep_array [SEP_COMMA] = entry;
210 gtk_grid_attach (GTK_GRID (grid), entry, 1, 3, 1, 1);
211 g_object_set_data (G_OBJECT (entry), "_sep", ",");
212 g_signal_connect (G_OBJECT (entry), "toggled",
213 G_CALLBACK (spec_changed_cb), import);
214
215 entry = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (entry), _("Semi colon"));
216 import->priv->sep_array [SEP_SEMICOL] = entry;
217 gtk_grid_attach (GTK_GRID (grid), entry, 2, 3, 1, 1);
218 g_object_set_data (G_OBJECT (entry), "_sep", ";");
219 g_signal_connect (G_OBJECT (entry), "toggled",
220 G_CALLBACK (spec_changed_cb), import);
221
222 entry = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (entry), _("Tab"));
223 import->priv->sep_array [SEP_TAB] = entry;
224 gtk_grid_attach (GTK_GRID (grid), entry, 1, 4, 1, 1);
225 g_object_set_data (G_OBJECT (entry), "_sep", "\t");
226 g_signal_connect (G_OBJECT (entry), "toggled",
227 G_CALLBACK (spec_changed_cb), import);
228
229 entry = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (entry), _("Space"));
230 import->priv->sep_array [SEP_SPACE] = entry;
231 gtk_grid_attach (GTK_GRID (grid), entry, 2, 4, 1, 1);
232 g_object_set_data (G_OBJECT (entry), "_sep", " ");
233 g_signal_connect (G_OBJECT (entry), "toggled",
234 G_CALLBACK (spec_changed_cb), import);
235
236 entry = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (entry), _("Pipe"));
237 import->priv->sep_array [SEP_PIPE] = entry;
238 gtk_grid_attach (GTK_GRID (grid), entry, 1, 5, 1, 1);
239 g_object_set_data (G_OBJECT (entry), "_sep", "|");
240 g_signal_connect (G_OBJECT (entry), "toggled",
241 G_CALLBACK (spec_changed_cb), import);
242
243 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
244 gtk_grid_attach (GTK_GRID (grid), hbox, 2, 5, 1, 1);
245 entry = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON (entry), _("Other:"));
246 import->priv->sep_array [SEP_OTHER] = entry;
247 gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, FALSE, 0);
248 g_object_set_data (G_OBJECT (entry), "_sep", "");
249 g_signal_connect (G_OBJECT (entry), "toggled",
250 G_CALLBACK (spec_changed_cb), import);
251
252 entry = gtk_entry_new ();
253 import->priv->sep_other_entry = entry;
254 gtk_entry_set_max_length (GTK_ENTRY (entry), 1);
255 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
256 gtk_widget_set_sensitive (entry, FALSE);
257 g_signal_connect (G_OBJECT (entry), "changed",
258 G_CALLBACK (spec_changed_cb), import);
259
260 gtk_widget_show_all (vbox);
261
262
263 /*
264 * bottom part: import preview
265 */
266 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
267 gtk_paned_pack2 (GTK_PANED (import), vbox, TRUE, FALSE);
268
269 str = g_strdup_printf ("<b>%s:</b>", _("Import preview"));
270 label = gtk_label_new ("");
271 gtk_label_set_markup (GTK_LABEL (label), str);
272 gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
273 g_free (str);
274 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
275
276 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); /* HIG */
277 import->priv->preview_box = hbox;
278
279 gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
280 gtk_widget_show (hbox);
281 label = gtk_label_new (" ");
282 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
283
284 label = gtk_label_new ("");
285 gtk_label_set_markup (GTK_LABEL (label), _("No data."));
286 gtk_misc_set_alignment (GTK_MISC (label), 0., 0.);
287 gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
288 import->priv->no_data_label = label;
289
290 gtk_widget_show_all (vbox);
291
292 gtk_paned_set_position (GTK_PANED (import), 1);
293 }
294
295 /**
296 * gdaui_data_import_new
297 *
298 * Creates a new #GdauiDataImport widget. After import, a #GdaDataModel will be created.
299 *
300 * Returns: the new widget
301 */
302 GtkWidget *
gdaui_data_import_new(void)303 gdaui_data_import_new (void)
304 {
305 GdauiDataImport *import;
306
307 import = GDAUI_DATA_IMPORT (g_object_new (GDAUI_TYPE_DATA_IMPORT, NULL));
308
309 return GTK_WIDGET (import);
310 }
311
312
313 static void
gdaui_data_import_dispose(GObject * object)314 gdaui_data_import_dispose (GObject *object)
315 {
316 GdauiDataImport *import;
317
318 g_return_if_fail (object != NULL);
319 g_return_if_fail (GDAUI_IS_DATA_IMPORT (object));
320 import = GDAUI_DATA_IMPORT (object);
321
322 if (import->priv) {
323 if (import->priv->model) {
324 g_object_unref (import->priv->model);
325 import->priv->model = NULL;
326 }
327
328 /* the private area itself */
329 g_free (import->priv);
330 import->priv = NULL;
331 }
332
333 /* for the parent class */
334 parent_class->dispose (object);
335 }
336
337 static void
spec_changed_cb(GtkWidget * wid,GdauiDataImport * import)338 spec_changed_cb (GtkWidget *wid, GdauiDataImport *import)
339 {
340 gchar *file;
341 GdaSet *options;
342 gchar *sep;
343 GdaHolder *psep = NULL;
344 gint sepno;
345
346 if (import->priv->preview_grid) {
347 gtk_widget_destroy (import->priv->preview_grid);
348 import->priv->preview_grid = NULL;
349 }
350 if (import->priv->model) {
351 g_object_unref (import->priv->model);
352 import->priv->model = NULL;
353 }
354
355 sep = g_object_get_data (G_OBJECT (wid), "_sep");
356 if (sep) {
357 if (*sep == 0)
358 gtk_widget_set_sensitive (import->priv->sep_other_entry,
359 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (wid)));
360
361 if (! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (wid)))
362 return;
363 }
364
365 for (sepno = SEP_COMMA; sepno < SEP_LAST; sepno++) {
366 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (import->priv->sep_array [sepno]))) {
367 sep = g_object_get_data (G_OBJECT (import->priv->sep_array [sepno]), "_sep");
368 psep = gda_holder_new (G_TYPE_STRING);
369 g_object_set (G_OBJECT (psep), "id", "SEPARATOR", NULL);
370 if (sepno != SEP_OTHER)
371 gda_holder_set_value_str (psep, NULL, sep, NULL);
372 else
373 gda_holder_set_value_str (psep, NULL,
374 gtk_entry_get_text (GTK_ENTRY (import->priv->sep_other_entry)),
375 NULL);
376 break;
377 }
378 }
379
380 options = gda_set_new (NULL);
381 if (psep) {
382 gda_set_add_holder (options, psep);
383 g_object_unref (psep);
384 }
385
386 if (import->priv->encoding_combo) {
387 GdaDataModelIter *iter;
388
389 iter = gdaui_data_selector_get_data_set (GDAUI_DATA_SELECTOR (import->priv->encoding_combo));;
390 if (iter) {
391 GdaHolder *h;
392 h = g_object_new (GDA_TYPE_HOLDER, "id", "ENCODING", "g-type", G_TYPE_STRING, NULL);
393
394 gda_holder_set_value (h, (GValue *) gda_data_model_iter_get_value_at (iter, 0), NULL);
395 gda_set_add_holder (options, h);
396 g_object_unref (h);
397 }
398 }
399
400 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (import->priv->first_line_check))) {
401 GdaHolder *h;
402 h = gda_holder_new_inline (G_TYPE_BOOLEAN, "TITLE_AS_FIRST_LINE", TRUE);
403 gda_set_add_holder (options, h);
404 g_object_unref (h);
405 }
406
407 file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (import->priv->file_chooser));
408 if (file) {
409 import->priv->model = gda_data_model_import_new_file (file, TRUE, options);
410 g_free (file);
411 }
412
413 if (options)
414 g_object_unref (options);
415
416 if (import->priv->model) {
417 gtk_widget_hide (import->priv->no_data_label);
418 import->priv->preview_grid = gdaui_grid_new (import->priv->model);
419
420 gdaui_data_proxy_column_show_actions (GDAUI_DATA_PROXY (import->priv->preview_grid), -1, FALSE);
421 gdaui_grid_set_sample_size (GDAUI_GRID (import->priv->preview_grid), 50);
422 g_object_set (G_OBJECT (import->priv->preview_grid), "info-flags",
423 GDAUI_DATA_PROXY_INFO_CHUNCK_CHANGE_BUTTONS |
424 GDAUI_DATA_PROXY_INFO_CURRENT_ROW, NULL);
425 gtk_box_pack_start (GTK_BOX (import->priv->preview_box), import->priv->preview_grid, TRUE, TRUE, 0);
426 gtk_widget_show (import->priv->preview_grid);
427 gdaui_set_default_path (gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (import->priv->file_chooser)));
428 }
429 else
430 gtk_widget_show (import->priv->no_data_label);
431 }
432
433 /**
434 * gdaui_data_import_get_model
435 * @import: a #GdauiDataImport widget
436 *
437 * Get the current imported data model. The caller has to reference it if needed.
438 *
439 * Returns: the #GdaDataModel, or %NULL
440 */
441 GdaDataModel *
gdaui_data_import_get_model(GdauiDataImport * import)442 gdaui_data_import_get_model (GdauiDataImport *import)
443 {
444 g_return_val_if_fail (GDAUI_IS_DATA_IMPORT (import), NULL);
445 return import->priv->model;
446 }
447