dialog-stf-main-page.c 13.9 KB
Newer Older
1
/*
2
 * dialog-stf-main-page.c : controls the widget on the main page
3
 *
4 5
 * Copyright 2001 Almer S. Tigelaar <almer@gnome.org>
 * Copyright 2003 Morten Welinder <terra@gnome.org>
6
 *
jpekka's avatar
jpekka committed
7 8 9 10 11 12 13 14 15 16 17
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
18
 * along with this program; if not, see <https://www.gnu.org/licenses/>.
19
 */
20

21
#include <gnumeric-config.h>
22
#include <glib/gi18n-lib.h>
23
#include <gnumeric.h>
24
#include <libgnumeric.h>
25
#include <dialogs/dialog-stf.h>
26
#include <gui-util.h>
27
#include <sheet.h>
28
#include <workbook.h>
29
#include <goffice/goffice.h>
Morten Welinder's avatar
Morten Welinder committed
30
#include <string.h>
31 32 33 34 35

/*************************************************************************************************
 * MISC UTILITY FUNCTIONS
 *************************************************************************************************/

36
static gboolean
37
main_page_set_encoding (StfDialogData *pagedata, const char *enc)
38 39 40 41 42 43 44 45
{
	char *utf8_data;
	gsize bytes_read = -1;
	gsize bytes_written = -1;
	GError *error = NULL;

	if (!enc) return FALSE;

Morten Welinder's avatar
Morten Welinder committed
46
	utf8_data = g_convert (pagedata->raw_data, pagedata->raw_data_len,
47 48
			       "UTF-8", enc,
			       &bytes_read, &bytes_written, &error);
49 50 51 52 53 54

	/*
	 * It _is_ possible to get NULL error, but not have valid UTF-8.
	 * Specifically observed with UTF-16BE.
	 */
	if (error || !g_utf8_validate (utf8_data, -1, NULL)) {
55
		g_free (utf8_data);
56 57 58 59
		if (error) {
			/* FIXME: What to do with error?  */
			g_error_free (error);
		}
60 61 62
		return FALSE;
	}

63

64
	if (!go_charmap_sel_set_encoding (pagedata->main.charmap_selector, enc)) {
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
		g_free (utf8_data);
		return FALSE;
	}

	g_free (pagedata->utf8_data);
	pagedata->utf8_data = utf8_data;

	if (enc != pagedata->encoding) {
		g_free (pagedata->encoding);
		pagedata->encoding = g_strdup (enc);
	}

	return TRUE;
}


81
static void
82
main_page_update_preview (StfDialogData *pagedata)
83
{
Morten Welinder's avatar
Morten Welinder committed
84
	RenderData_t *renderdata = pagedata->main.renderdata;
Morten Welinder's avatar
Morten Welinder committed
85
	GStringChunk *lines_chunk = g_string_chunk_new (100 * 1024);
86
	GPtrArray *lines = stf_parse_lines (pagedata->parseoptions,
87
					    lines_chunk,
88
					    pagedata->utf8_data,
89
					    INT_MAX,
90
					    TRUE);
91 92
        unsigned int ui;

93
	pagedata->rowcount = lines->len;
94 95 96
	pagedata->longest_line = 0;
	for (ui = 0; ui < lines->len; ui++) {
		GPtrArray *line = g_ptr_array_index (lines, ui);
97
		int thislen = g_utf8_strlen (g_ptr_array_index (line, 1), -1);
98 99
		pagedata->longest_line = MAX (pagedata->longest_line, thislen);
	}
100

101
	stf_preview_set_lines (renderdata, lines_chunk, lines);
102 103 104
}


105 106
/**
 * main_page_set_spin_button_adjustment
107 108 109
 * @spinbutton: the spinbutton to adjust
 * @min: the minimum number the user may enter into the spinbutton
 * @max: the maximum number the user may enter into the spinbutton
110 111 112 113 114 115 116 117 118
 *
 * returns : nothing
 **/
static void
main_page_set_spin_button_adjustment (GtkSpinButton* spinbutton, int min, int max)
{
	GtkAdjustment *spinadjust;

	spinadjust = gtk_spin_button_get_adjustment (spinbutton);
119 120
	gtk_adjustment_set_lower (spinadjust, min);
	gtk_adjustment_set_upper (spinadjust, max);
121 122 123 124
}

/**
 * main_page_import_range_changed
125
 * @data: mother struct
126
 *
127
 * Updates the "number of lines to import" label on the main page
128 129 130 131
 *
 * returns : nothing
 **/
static void
132
main_page_import_range_changed (StfDialogData *data)
133
{
Morten Welinder's avatar
Morten Welinder committed
134
	RenderData_t *renderdata = data->main.renderdata;
135
	int startrow, stoprow, stoplimit;
136 137
	char *linescaption;

138 139
	g_return_if_fail (renderdata->lines != NULL);

140 141
	startrow = gtk_spin_button_get_value_as_int (data->main.main_startrow);
	stoprow  = gtk_spin_button_get_value_as_int (data->main.main_stoprow);
142

143 144
	stoprow = MAX (1, stoprow);
	startrow = MIN (stoprow, MAX (1, startrow));
145

146
	stoplimit = MIN ((int)renderdata->lines->len,
147
			 startrow + (GNM_MAX_ROWS - 1));
148
	stoprow = MIN (stoprow, stoplimit);
149

150
	gtk_spin_button_set_value (data->main.main_startrow, startrow);
151
	main_page_set_spin_button_adjustment (data->main.main_startrow, 1, stoprow);
152 153

	gtk_spin_button_set_value (data->main.main_stoprow, stoprow);
154
	main_page_set_spin_button_adjustment (data->main.main_stoprow, startrow, stoplimit);
155

156 157
	data->cur = stf_parse_find_line (data->parseoptions, data->utf8_data, startrow - 1);
	data->cur_end = stf_parse_find_line (data->parseoptions, data->utf8_data, stoprow);
158

159 160 161
	linescaption = g_strdup_printf (ngettext("%d of %d line to import",
						 "%d of %d lines to import",
						 renderdata->lines->len),
162
					(stoprow - startrow) + 1,
Morten Welinder's avatar
Morten Welinder committed
163
					renderdata->lines->len);
164
	gtk_label_set_text (data->main.main_lines, linescaption);
165
	g_free (linescaption);
166 167 168 169 170 171
}

/*************************************************************************************************
 * SIGNAL HANDLERS
 *************************************************************************************************/

172
static void
173
encodings_changed_cb (GOCharmapSel *cs, char const *new_charmap,
174
		      StfDialogData *pagedata)
175 176 177
{
	if (main_page_set_encoding (pagedata, new_charmap)) {
		main_page_update_preview (pagedata);
178
		main_page_import_range_changed (pagedata);
179
	} else {
180
		const char *name = go_charmap_sel_get_encoding_name (cs, new_charmap);
181 182 183
		char *msg = g_strdup_printf
			(_("The data is not valid in encoding %s; "
			   "please select another encoding."),
184
			 name ? name : new_charmap);
Jody Goldberg's avatar
Jody Goldberg committed
185
		go_gtk_notice_dialog (GTK_WINDOW (pagedata->dialog),
186 187
				      GTK_MESSAGE_ERROR,
				      "%s", msg);
188 189
		g_free (msg);

190
		go_charmap_sel_set_encoding (pagedata->main.charmap_selector,
191
					       pagedata->encoding);
192 193 194
	}
}

195 196
/**
 * main_page_startrow_changed
197 198
 * @button: the spinbutton the event handler is attached to
 * @data: mother struct
199
 *
200
 * This function will adjust the amount of displayed text to
201 202 203 204
 * reflect the number of lines the user wants to import
 *
 * returns : nothing
 **/
205
static void
206
main_page_startrow_changed (GtkSpinButton* button, StfDialogData *data)
207
{
208
	main_page_import_range_changed (data);
209 210 211
}

/**
212
 * main_page_stoprow_changed
213 214
 * @button: the spinbutton the event handler is attached to
 * @data: mother struct
215 216 217
 *
 * returns : nothing
 **/
218
static void
219
main_page_stoprow_changed (GtkSpinButton* button,
220
			   StfDialogData *data)
221
{
222
	main_page_import_range_changed (data);
223 224
}

225 226
static void
main_page_source_format_toggled (G_GNUC_UNUSED GtkWidget *widget,
227
				 StfDialogData *data)
228
{
229
	gboolean separated = gtk_toggle_button_get_active
230 231
		(GTK_TOGGLE_BUTTON (data->main.main_separated));

232
	stf_parse_options_set_type (data->parseoptions,
233
				    separated ? PARSE_TYPE_CSV : PARSE_TYPE_FIXED);
234 235
}

236 237
static void
cb_line_breaks (G_GNUC_UNUSED GtkWidget *widget,
238
		StfDialogData *data)
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
{
	gboolean to_end = (gtk_spin_button_get_value_as_int (data->main.main_stoprow) ==
			   (int)data->main.renderdata->lines->len);

	stf_parse_options_clear_line_terminator (data->parseoptions);
	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->main.line_break_unix)))
		stf_parse_options_add_line_terminator (data->parseoptions, "\n");
	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->main.line_break_windows)))
		stf_parse_options_add_line_terminator (data->parseoptions, "\r\n");
	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->main.line_break_mac)))
		stf_parse_options_add_line_terminator (data->parseoptions, "\r");

	main_page_update_preview (data);
	main_page_import_range_changed (data);

	/* If the selected area went all the way to the end, follow it there.  */
	if (to_end) {
		gtk_spin_button_set_value (data->main.main_stoprow,
					   data->main.renderdata->lines->len);
		main_page_import_range_changed (data);
	}
}

262
/**
263
 * stf_dialog_main_page_prepare
264
 * @pagedata: mother struct
265 266 267 268 269 270
 *
 * This will prepare the widgets on the format page before
 * the page gets displayed
 *
 * returns : nothing
 **/
271 272
void
stf_dialog_main_page_prepare (StfDialogData *pagedata)
273
{
274
	main_page_source_format_toggled (NULL, pagedata);
275 276
	main_page_update_preview (pagedata);
}
277

278
static void
279
main_page_parseoptions_to_gui (StfDialogData *pagedata)
280 281 282 283
{
	StfParseOptions_t *po = pagedata->parseoptions;

#if 0
284
	go_charmap_sel_set_encoding (pagedata->main.charmap_selector, pagedata->encoding);
285 286 287 288 289 290 291 292 293 294 295 296 297
#endif

	switch (po->parsetype) {
	case PARSE_TYPE_CSV:
		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (pagedata->main.main_separated),
					      TRUE);
		break;
	case PARSE_TYPE_FIXED:
		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (pagedata->main.main_fixed),
					      TRUE);
		break;
	default:
		break;
298
	}
299 300 301 302

	{
		gboolean lb_unix = FALSE, lb_windows = FALSE, lb_mac = FALSE;
		GSList *l;
Morten Welinder's avatar
Morten Welinder committed
303

304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
		for (l = po->terminator; l; l = l->next) {
			const char *term = l->data;
			if (strcmp (term, "\n") == 0)
				lb_unix = TRUE;
			else if (strcmp (term, "\r\n") == 0)
				lb_windows = TRUE;
			else if (strcmp (term, "\r") == 0)
				lb_mac = TRUE;
		}

		gtk_toggle_button_set_active
			(GTK_TOGGLE_BUTTON (pagedata->main.line_break_unix),
			 lb_unix);
		gtk_toggle_button_set_active
			(GTK_TOGGLE_BUTTON (pagedata->main.line_break_windows),
			 lb_windows);
		gtk_toggle_button_set_active
			(GTK_TOGGLE_BUTTON (pagedata->main.line_break_mac),
			 lb_mac);
	}
}
325

326 327 328 329
/*************************************************************************************************
 * MAIN EXPORTED FUNCTIONS
 *************************************************************************************************/

330
void
331
stf_dialog_main_page_cleanup (StfDialogData *pagedata)
332
{
Morten Welinder's avatar
Morten Welinder committed
333
	stf_preview_free (pagedata->main.renderdata);
334 335
}

336
void
337
stf_dialog_main_page_init (GtkBuilder *gui, StfDialogData *pagedata)
338
{
339 340
	RenderData_t *renderdata;
	GtkTreeViewColumn *column;
341
	const char *encoding_guess;
342

343 344
	encoding_guess = go_guess_encoding (pagedata->raw_data, pagedata->raw_data_len,
					    "ASCII",
345
					    NULL, NULL);
346

Morten Welinder's avatar
Morten Welinder committed
347 348 349 350 351 352 353 354 355
	pagedata->main.main_separated = GTK_RADIO_BUTTON (go_gtk_builder_get_widget (gui, "main_separated"));
	pagedata->main.main_fixed     = GTK_RADIO_BUTTON (go_gtk_builder_get_widget (gui, "main_fixed"));
	pagedata->main.main_startrow  = GTK_SPIN_BUTTON  (go_gtk_builder_get_widget (gui, "main_startrow"));
	pagedata->main.main_stoprow   = GTK_SPIN_BUTTON  (go_gtk_builder_get_widget (gui, "main_stoprow"));
	pagedata->main.main_lines     = GTK_LABEL        (go_gtk_builder_get_widget (gui, "main_lines"));
	pagedata->main.main_data_container =              go_gtk_builder_get_widget (gui, "main_data_container");
	pagedata->main.line_break_unix    = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (gui, "line_break_unix"));
	pagedata->main.line_break_windows = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (gui, "line_break_windows"));
	pagedata->main.line_break_mac     = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (gui, "line_break_mac"));
356

357
	pagedata->main.charmap_selector = GO_CHARMAP_SEL (go_charmap_sel_new (GO_CHARMAP_SEL_TO_UTF8));
358
	if (!main_page_set_encoding (pagedata, pagedata->encoding) &&
359
	    !main_page_set_encoding (pagedata, encoding_guess)) {
360
		g_warning ("This is not good -- failed to find a valid encoding of data!");
361 362 363
		pagedata->raw_data_len = 0;
		main_page_set_encoding (pagedata, "ASCII");
	}
364 365
	gtk_grid_attach (GTK_GRID (go_gtk_builder_get_widget (gui, "format-grid")),
			   GTK_WIDGET (pagedata->main.charmap_selector), 1, 0, 1, 1);
366
	gtk_widget_show_all (GTK_WIDGET (pagedata->main.charmap_selector));
367 368
	gtk_widget_set_sensitive (GTK_WIDGET (pagedata->main.charmap_selector),
				  !pagedata->fixed_encoding);
369

370 371 372
	pagedata->parseoptions = stf_parse_options_guess (pagedata->utf8_data);
	main_page_parseoptions_to_gui (pagedata);

Morten Welinder's avatar
Morten Welinder committed
373
	renderdata = pagedata->main.renderdata = stf_preview_new
374 375 376
		(pagedata->main.main_data_container,
		 NULL);
	renderdata->ignore_formats = TRUE;
377

378
	main_page_update_preview (pagedata);
379 380

	column = stf_preview_get_column (renderdata, 0);
381 382 383 384 385 386 387 388 389 390
	if (column) {
		/* This probably cannot happen.  */
		GtkCellRenderer *cell = stf_preview_get_cell_renderer (renderdata, 0);
		gtk_tree_view_column_set_title (column, _("Line"));
		g_object_set (G_OBJECT (cell),
			      "xalign", 1.0,
			      "style", PANGO_STYLE_ITALIC,
			      "background", "lightgrey",
			      NULL);
	}
391 392

	column = stf_preview_get_column (renderdata, 1);
393 394 395 396 397 398 399 400
	if (column) {
		/* In case of an empty file, there will be no column.  */
		GtkCellRenderer *cell = stf_preview_get_cell_renderer (renderdata, 1);
		gtk_tree_view_column_set_title (column, _("Text"));
		g_object_set (G_OBJECT (cell),
			      "family", "monospace",
			      NULL);
	}
401 402

	/* Set properties */
Morten Welinder's avatar
Morten Welinder committed
403 404 405
	main_page_set_spin_button_adjustment (pagedata->main.main_startrow, 1, renderdata->lines->len);
	main_page_set_spin_button_adjustment (pagedata->main.main_stoprow, 1, renderdata->lines->len);
	gtk_spin_button_set_value (pagedata->main.main_stoprow, renderdata->lines->len);
406 407

	{
408
		GtkLabel *data_label = GTK_LABEL (gtk_builder_get_object (gui, "data-lbl"));
409
		char *label = g_strdup_printf (_("Data (from %s)"), pagedata->source);
410
		gtk_label_set_label (data_label, label);
411 412
		g_free (label);
	}
413 414

	/* Connect signals */
415
	g_signal_connect (G_OBJECT (pagedata->main.main_startrow),
Jody Goldberg's avatar
Jody Goldberg committed
416
		"value-changed",
417
		G_CALLBACK (main_page_startrow_changed), pagedata);
418
	g_signal_connect (G_OBJECT (pagedata->main.main_stoprow),
Jody Goldberg's avatar
Jody Goldberg committed
419
		"value-changed",
420
		G_CALLBACK (main_page_stoprow_changed), pagedata);
421
	g_signal_connect (G_OBJECT (pagedata->main.main_separated),
422 423 424
		"toggled",
		G_CALLBACK (main_page_source_format_toggled), pagedata);

425 426 427 428 429 430 431 432 433 434
	g_signal_connect (G_OBJECT (pagedata->main.line_break_unix),
			  "toggled",
			  G_CALLBACK (cb_line_breaks), pagedata);
	g_signal_connect (G_OBJECT (pagedata->main.line_break_windows),
			  "toggled",
			  G_CALLBACK (cb_line_breaks), pagedata);
	g_signal_connect (G_OBJECT (pagedata->main.line_break_mac),
			  "toggled",
			  G_CALLBACK (cb_line_breaks), pagedata);

435 436 437
	g_signal_connect (G_OBJECT (pagedata->main.charmap_selector),
			  "charmap_changed",
			  G_CALLBACK (encodings_changed_cb), pagedata);
438

439
	main_page_source_format_toggled (NULL, pagedata);
440
	main_page_import_range_changed (pagedata);
441
}