1 /*
2  * dialog-stf.c : Controls the widget on the CSV (Comma Separated Value) page.
3  *
4  * Copyright 2001 Almer S. Tigelaar <almer@gnome.org>
5  * Copyright 2003 Morten Welinder <terra@gnome.org>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * 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 <dialogs/dialog-stf.h>
23 #include <gnm-format.h>
24 #include <goffice/goffice.h>
25 #include <string.h>
26 
27 /*************************************************************************************************
28  * SIGNAL HANDLERS
29  *************************************************************************************************/
30 
31 /**
32  * csv_page_global_change
33  * @widget: the widget which emitted the signal
34  * @data: mother struct
35  *
36  * This will update the preview based on the state of
37  * the widgets on the csv page
38  **/
39 static void
csv_page_global_change(G_GNUC_UNUSED GtkWidget * widget,StfDialogData * pagedata)40 csv_page_global_change (G_GNUC_UNUSED GtkWidget *widget,
41 			StfDialogData *pagedata)
42 {
43 	StfParseOptions_t *parseoptions = pagedata->parseoptions;
44 	RenderData_t *renderdata = pagedata->csv.renderdata;
45 	GSList *sepstr;
46 	GString *sepc = g_string_new (NULL);
47 	GStringChunk *lines_chunk;
48 	GPtrArray *lines;
49 	StfTrimType_t trim;
50 
51 	sepstr = NULL;
52 	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (pagedata->csv.csv_custom))) {
53 		char *csvcustomtext = gtk_editable_get_chars (GTK_EDITABLE (pagedata->csv.csv_customseparator), 0, -1);
54 
55 		if (strcmp (csvcustomtext, "") != 0)
56 			sepstr = g_slist_append (sepstr, csvcustomtext);
57 		else
58 			g_free (csvcustomtext);
59 	}
60 
61 	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (pagedata->csv.csv_tab)))
62 		g_string_append_c (sepc, '\t');
63 	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (pagedata->csv.csv_colon)))
64 		g_string_append_c (sepc, ':');
65 	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (pagedata->csv.csv_comma)))
66 		g_string_append_c (sepc, ',');
67 	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (pagedata->csv.csv_space)))
68 		g_string_append_c (sepc, ' ');
69 	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (pagedata->csv.csv_semicolon)))
70 		g_string_append_c (sepc, ';');
71 	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (pagedata->csv.csv_hyphen)))
72 		g_string_append_c (sepc, '-');
73 
74 	stf_parse_options_csv_set_separators (parseoptions,
75 					      strcmp (sepc->str, "") == 0 ? NULL : sepc->str,
76 					      sepstr);
77 	g_string_free (sepc, TRUE);
78 	g_slist_free_full (sepstr, g_free);
79 
80 	stf_parse_options_csv_set_duplicates (parseoptions,
81 					      gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (pagedata->csv.csv_duplicates)));
82 	stf_parse_options_csv_set_trim_seps (parseoptions,
83 					     gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (pagedata->csv.csv_trim_seps)));
84 
85 	lines_chunk = g_string_chunk_new (100 * 1024);
86 
87 	/* Don't trim on this page.  */
88 	trim = parseoptions->trim_spaces;
89 	stf_parse_options_set_trim_spaces (parseoptions, TRIM_TYPE_NEVER);
90 	lines = stf_parse_general (parseoptions, lines_chunk,
91 				   pagedata->cur, pagedata->cur_end);
92 	stf_parse_options_set_trim_spaces (parseoptions, trim);
93 
94 	stf_preview_set_lines (renderdata, lines_chunk, lines);
95 }
96 
97 /**
98  * csv_page_custom_toggled
99  * @button: the Checkbutton that emitted the signal
100  * @data: a mother struct
101  *
102  * This will nicely activate the @data->csv_info->csv_customseparator widget
103  * so the user can enter text into it.
104  * It will also gray out this widget if the @button is not selected.
105  **/
106 static void
csv_page_custom_toggled(GtkCheckButton * button,StfDialogData * data)107 csv_page_custom_toggled (GtkCheckButton *button, StfDialogData *data)
108 {
109 	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) {
110 		gtk_widget_set_sensitive   (GTK_WIDGET (data->csv.csv_customseparator), TRUE);
111 		gtk_widget_grab_focus      (GTK_WIDGET (data->csv.csv_customseparator));
112 		gtk_editable_select_region (GTK_EDITABLE (data->csv.csv_customseparator), 0, -1);
113 
114 	} else {
115 		gtk_widget_set_sensitive (GTK_WIDGET (data->csv.csv_customseparator), FALSE);
116 		gtk_editable_select_region (GTK_EDITABLE (data->csv.csv_customseparator), 0, 0); /* If we don't use this the selection will remain blue */
117 	}
118 
119 	csv_page_global_change (NULL, data);
120 }
121 
122 
123 static void
csv_page_textindicator_change(G_GNUC_UNUSED GtkWidget * widget,StfDialogData * data)124 csv_page_textindicator_change (G_GNUC_UNUSED GtkWidget *widget,
125 			       StfDialogData *data)
126 {
127 	char *textfieldtext = gtk_editable_get_chars (GTK_EDITABLE (data->csv.csv_textfield), 0, -1);
128 	gunichar str_ind = g_utf8_get_char (textfieldtext);
129 
130 	stf_parse_options_csv_set_stringindicator (data->parseoptions, str_ind);
131 	g_free (textfieldtext);
132 
133 	stf_parse_options_csv_set_indicator_2x_is_single  (data->parseoptions,
134 							   gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->csv.csv_2x_indicator)));
135 	csv_page_global_change (NULL, data);
136 }
137 
138 static void
csv_page_parseoptions_to_gui(StfDialogData * pagedata)139 csv_page_parseoptions_to_gui (StfDialogData *pagedata)
140 {
141 	StfParseOptions_t *po = pagedata->parseoptions;
142 
143 	{
144 		const char *sep;
145 		gboolean s_tab = FALSE;
146 		gboolean s_colon = FALSE;
147 		gboolean s_comma = FALSE;
148 		gboolean s_space = FALSE;
149 		gboolean s_semicolon = FALSE;
150 		gboolean s_hyphen = FALSE;
151 
152 		if (po->sep.chr)
153 			for (sep = po->sep.chr; *sep; sep++) {
154 				switch (*sep) {
155 				case '\t': s_tab = TRUE; break;
156 				case ':': s_colon = TRUE; break;
157 				case ',': s_comma = TRUE; break;
158 				case ' ': s_space = TRUE; break;
159 				case ';': s_semicolon = TRUE; break;
160 				case '-': s_hyphen = TRUE; break;
161 				default: break;
162 				}
163 			}
164 		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (pagedata->csv.csv_tab), s_tab);
165 		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (pagedata->csv.csv_colon), s_colon);
166 		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (pagedata->csv.csv_comma), s_comma);
167 		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (pagedata->csv.csv_space), s_space);
168 		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (pagedata->csv.csv_semicolon), s_semicolon);
169 		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (pagedata->csv.csv_hyphen), s_hyphen);
170 	}
171 
172 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (pagedata->csv.csv_duplicates),
173 				      po->sep.duplicates);
174 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (pagedata->csv.csv_trim_seps),
175 				      po->trim_seps);
176 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (pagedata->csv.csv_2x_indicator),
177 				      po->indicator_2x_is_single);
178 
179 	{
180 		char buffer[7];
181 		gint len;
182 		len = g_unichar_to_utf8 (po->stringindicator, buffer);
183 		buffer[len] = 0;
184 		gtk_combo_box_set_active_id
185 			(GTK_COMBO_BOX (pagedata->csv.csv_textindicator),
186 			 buffer);
187 	}
188 }
189 
190 
191 /**
192  * stf_dialog_csv_page_prepare
193  * @pagedata: mother struct
194  *
195  * Will prepare the csv page
196  **/
197 void
stf_dialog_csv_page_prepare(StfDialogData * pagedata)198 stf_dialog_csv_page_prepare (StfDialogData *pagedata)
199 {
200 	csv_page_parseoptions_to_gui (pagedata);
201 
202 	/* Calling this routine will also automatically call global change which updates the preview too */
203 	csv_page_custom_toggled (pagedata->csv.csv_custom, pagedata);
204 }
205 
206 /*************************************************************************************************
207  * CSV EXPORTED FUNCTIONS
208  *************************************************************************************************/
209 
210 /**
211  * stf_dialog_csv_page_cleanup
212  * @pagedata: mother struct
213  *
214  * Will cleanup csv page run-time data
215  **/
216 void
stf_dialog_csv_page_cleanup(StfDialogData * pagedata)217 stf_dialog_csv_page_cleanup (StfDialogData *pagedata)
218 {
219 	stf_preview_free (pagedata->csv.renderdata);
220 	pagedata->csv.renderdata = NULL;
221 }
222 
223 void
stf_dialog_csv_page_init(GtkBuilder * gui,StfDialogData * pagedata)224 stf_dialog_csv_page_init (GtkBuilder *gui, StfDialogData *pagedata)
225 {
226 	g_return_if_fail (gui != NULL);
227 	g_return_if_fail (pagedata != NULL);
228 
229 	/* Create/get object and fill information struct */
230 	pagedata->csv.csv_tab             = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (gui, "csv_tab"));
231 	pagedata->csv.csv_colon           = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (gui, "csv_colon"));
232 	pagedata->csv.csv_comma           = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (gui, "csv_comma"));
233 	pagedata->csv.csv_space           = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (gui, "csv_space"));
234 	pagedata->csv.csv_semicolon       = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (gui, "csv_semicolon"));
235 	pagedata->csv.csv_hyphen          = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (gui, "csv_hyphen"));
236 	pagedata->csv.csv_custom          = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (gui, "csv_custom"));
237 	pagedata->csv.csv_customseparator = GTK_ENTRY        (go_gtk_builder_get_widget (gui, "csv_customseparator"));
238 	pagedata->csv.csv_2x_indicator    = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (gui, "csv_2x_indicator"));
239 
240 	pagedata->csv.csv_textindicator   = go_gtk_builder_get_widget (gui, "csv-textindicator");
241 	pagedata->csv.csv_textfield       = GTK_ENTRY    (gtk_bin_get_child (GTK_BIN (pagedata->csv.csv_textindicator)));
242 
243 	pagedata->csv.csv_duplicates      = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (gui, "csv_duplicates"));
244 	pagedata->csv.csv_trim_seps       = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (gui, "csv_trim_seps"));
245 	pagedata->csv.csv_data_container  =                   go_gtk_builder_get_widget (gui, "csv_data_container");
246 
247 	/* Set properties */
248 	pagedata->csv.renderdata    =
249 		stf_preview_new (pagedata->csv.csv_data_container,
250 				 NULL);
251 	csv_page_parseoptions_to_gui (pagedata);
252 
253 	/* Connect signals */
254 	g_signal_connect (G_OBJECT (pagedata->csv.csv_tab),
255 		"toggled",
256 		G_CALLBACK (csv_page_global_change), pagedata);
257 	g_signal_connect (G_OBJECT (pagedata->csv.csv_colon),
258 		"toggled",
259 		G_CALLBACK (csv_page_global_change), pagedata);
260 	g_signal_connect (G_OBJECT (pagedata->csv.csv_comma),
261 		"toggled",
262 		G_CALLBACK (csv_page_global_change), pagedata);
263 	g_signal_connect (G_OBJECT (pagedata->csv.csv_space),
264 		"toggled",
265 		G_CALLBACK (csv_page_global_change), pagedata);
266 	g_signal_connect (G_OBJECT (pagedata->csv.csv_semicolon),
267 		"toggled",
268 		G_CALLBACK (csv_page_global_change), pagedata);
269 	g_signal_connect (G_OBJECT (pagedata->csv.csv_hyphen),
270 		"toggled",
271 		G_CALLBACK (csv_page_global_change), pagedata);
272 	g_signal_connect (G_OBJECT (pagedata->csv.csv_custom),
273 		"toggled",
274 		G_CALLBACK (csv_page_custom_toggled), pagedata);
275 	g_signal_connect (G_OBJECT (pagedata->csv.csv_customseparator),
276 		"changed",
277 		G_CALLBACK (csv_page_global_change), pagedata);
278 	g_signal_connect (G_OBJECT (pagedata->csv.csv_duplicates),
279 		"toggled",
280 		G_CALLBACK (csv_page_global_change), pagedata);
281 	g_signal_connect (G_OBJECT (pagedata->csv.csv_trim_seps),
282 		"toggled",
283 		G_CALLBACK (csv_page_global_change), pagedata);
284 	g_signal_connect (G_OBJECT (pagedata->csv.csv_2x_indicator),
285 		"toggled",
286 		G_CALLBACK (csv_page_textindicator_change), pagedata);
287 	g_signal_connect (G_OBJECT (pagedata->csv.csv_textfield),
288 		"changed",
289 		G_CALLBACK (csv_page_textindicator_change), pagedata);
290 }
291