Whitespace.
[gnumeric.git] / src / stf-export.c
blob162428c5a1779b3dda47963fc14210a7fbb79a45
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * stf-export.c : Structured Text Format Exporter (STF-E)
4 * Engine to construct CSV files
6 * Copyright (C) Almer. S. Tigelaar.
7 * EMail: almer1@dds.nl or almer-t@bigfoot.com
9 * Based on the csv-io.c plugin by:
10 * Miguel de Icaza <miguel@gnu.org>
11 * Jody Goldberg <jody@gnome.org>
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, see <https://www.gnu.org/licenses/>.
27 #include <gnumeric-config.h>
28 #include "gnumeric.h"
29 #include "gnm-i18n.h"
30 #include "stf-export.h"
32 #include "gnumeric-conf.h"
33 #include "sheet.h"
34 #include "workbook.h"
35 #include "cell.h"
36 #include "value.h"
37 #include "gnm-format.h"
38 #include "gnm-datetime.h"
39 #include <gsf/gsf-output-iconv.h>
40 #include <gsf/gsf-output-memory.h>
41 #include <gsf/gsf-impl-utils.h>
42 #include <goffice/goffice.h>
44 #include <string.h>
45 #include <locale.h>
48 struct _GnmStfExport {
49 GsfOutputCsv csv;
51 GSList *sheet_list;
52 char *charset;
53 char *locale;
54 GnmStfTransliterateMode transliterate_mode;
55 GnmStfFormatMode format;
58 static GObjectClass *parent_class;
60 typedef struct {
61 GsfOutputCsvClass base_class;
62 } GnmStfExportClass;
64 enum {
65 PROP_0,
66 PROP_CHARSET,
67 PROP_LOCALE,
68 PROP_TRANSLITERATE_MODE,
69 PROP_FORMAT
72 /* ------------------------------------------------------------------------- */
74 static void
75 cb_sheet_destroyed (GnmStfExport *stfe, gpointer deadsheet)
77 g_return_if_fail (GNM_IS_STF_EXPORT (stfe));
79 stfe->sheet_list = g_slist_remove (stfe->sheet_list, deadsheet);
82 /**
83 * gnm_stf_export_options_sheet_list_clear:
84 * @stfe: an export options struct
86 * Clears the sheet list.
87 **/
88 void
89 gnm_stf_export_options_sheet_list_clear (GnmStfExport *stfe)
91 GSList *l;
93 g_return_if_fail (GNM_IS_STF_EXPORT (stfe));
96 for (l = stfe->sheet_list; l; l = l->next) {
97 Sheet *sheet = l->data;
99 g_object_weak_unref (G_OBJECT (sheet),
100 (GWeakNotify) cb_sheet_destroyed,
101 stfe);
104 g_slist_free (stfe->sheet_list);
105 stfe->sheet_list = NULL;
110 * gnm_stf_export_options_sheet_list_add:
111 * @stfe: an export options struct
112 * @sheet: a gnumeric sheet
114 * Appends a @sheet to the list of sheets to be exported
116 void
117 gnm_stf_export_options_sheet_list_add (GnmStfExport *stfe, Sheet *sheet)
119 g_return_if_fail (GNM_IS_STF_EXPORT (stfe));
120 g_return_if_fail (IS_SHEET (sheet));
122 g_object_weak_ref (G_OBJECT (sheet),
123 (GWeakNotify) cb_sheet_destroyed,
124 stfe);
125 stfe->sheet_list = g_slist_append (stfe->sheet_list, sheet);
130 * gnm_stf_export_options_sheet_list_get:
131 * @stfe: #GnmStfExport
133 * Returns: (element-type Sheet) (transfer none): the list of #Sheet instances
134 * added to @stfe using gnm_stf_export_options_sheet_list_add().
136 GSList *
137 gnm_stf_export_options_sheet_list_get (const GnmStfExport *stfe)
139 g_return_val_if_fail (GNM_IS_STF_EXPORT (stfe), NULL);
141 return stfe->sheet_list;
144 /* ------------------------------------------------------------------------- */
147 static char *
148 try_auto_float (GnmValue *value, const GOFormat *format,
149 GODateConventions const *date_conv)
151 gboolean is_date;
152 int is_time;
154 if (!VALUE_IS_FLOAT (value))
155 return NULL;
157 format = gnm_format_specialize (format, value);
158 is_date = go_format_is_date (format) > 0;
159 is_time = go_format_is_time (format);
161 if (is_date || is_time > 0)
162 return NULL;
164 return format_value (go_format_general (), value, -1, date_conv);
168 static char *
169 try_auto_date (GnmValue *value, const GOFormat *format,
170 GODateConventions const *date_conv)
172 gnm_float v, vr, vs;
173 GOFormat *actual;
174 char *res;
175 gboolean needs_date, needs_time, needs_frac_sec;
176 gboolean is_date;
177 int is_time;
178 GString *xlfmt;
179 GDate date;
181 format = gnm_format_specialize (format, value);
182 is_date = go_format_is_date (format) > 0;
183 is_time = go_format_is_time (format);
185 if (!is_date && is_time <= 0)
186 return NULL;
188 /* We don't want to coerce strings. */
189 if (!VALUE_IS_FLOAT (value))
190 return NULL;
192 /* Verify that the date is valid. */
193 if (!datetime_value_to_g (&date, value, date_conv))
194 return NULL;
196 v = value_get_as_float (value);
197 vr = gnm_fake_round (v);
198 vs = (24 * 60 * 60) * gnm_abs (v - vr);
200 needs_date = is_time < 2 && (is_date || gnm_abs (v) >= 1);
201 needs_time = is_time > 0 || gnm_abs (v - vr) > 1e-9;
202 needs_frac_sec = needs_time && gnm_abs (vs - gnm_fake_round (vs)) >= 0.5e-3;
204 xlfmt = g_string_new (NULL);
205 if (needs_date) g_string_append (xlfmt, "yyyy/mm/dd");
206 if (needs_time) {
207 if (needs_date)
208 g_string_append_c (xlfmt, ' ');
209 if (is_time == 2)
210 g_string_append (xlfmt, "[h]:mm:ss");
211 else
212 g_string_append (xlfmt, "hh:mm:ss");
213 if (needs_frac_sec)
214 g_string_append (xlfmt, ".000");
216 actual = go_format_new_from_XL (xlfmt->str);
217 g_string_free (xlfmt, TRUE);
218 res = format_value (actual, value, -1, date_conv);
219 go_format_unref (actual);
221 return res;
225 * stf_export_cell:
226 * @stfe: an export options struct
227 * @cell: the cell to write to the file
229 * Return value: return TRUE on success, FALSE otherwise.
231 static gboolean
232 stf_export_cell (GnmStfExport *stfe, GnmCell *cell)
234 char const *text = NULL;
235 char *tmp = NULL;
236 gboolean ok;
237 g_return_val_if_fail (stfe != NULL, FALSE);
239 if (cell) {
240 switch (stfe->format) {
241 case GNM_STF_FORMAT_PRESERVE:
242 text = tmp = gnm_cell_get_rendered_text (cell);
243 break;
244 default:
245 case GNM_STF_FORMAT_AUTO:
246 if (cell->value) {
247 GODateConventions const *date_conv =
248 workbook_date_conv (cell->base.sheet->workbook);
249 GOFormat const *format = gnm_cell_get_format (cell);
250 text = tmp = try_auto_date (cell->value, format, date_conv);
251 if (!text)
252 text = tmp = try_auto_float (cell->value, format, date_conv);
253 if (!text)
254 text = value_peek_string (cell->value);
256 break;
257 case GNM_STF_FORMAT_RAW:
258 if (cell->value)
259 text = value_peek_string (cell->value);
260 break;
264 ok = gsf_output_csv_write_field (GSF_OUTPUT_CSV (stfe),
265 text ? text : "",
266 -1);
267 g_free (tmp);
269 return ok;
273 * stf_export_sheet:
274 * @stfe: an export options struct
275 * @sheet: the sheet to export
277 * Writes the @sheet to the callback function
279 * Return value: returns TRUE on success, FALSE otherwise
281 static gboolean
282 stf_export_sheet (GnmStfExport *stfe, Sheet *sheet)
284 int col, row;
285 GnmRange r;
286 GnmRangeRef *range;
288 g_return_val_if_fail (stfe != NULL, FALSE);
289 g_return_val_if_fail (IS_SHEET (sheet), FALSE);
291 range = g_object_get_data (G_OBJECT (sheet->workbook), "ssconvert-range");
292 if (range) {
293 Sheet *start_sheet, *end_sheet;
294 GnmEvalPos ep;
296 gnm_rangeref_normalize (range,
297 eval_pos_init_sheet (&ep, sheet),
298 &start_sheet, &end_sheet,
299 &r);
301 if (start_sheet != sheet)
302 return TRUE;
303 } else
304 r = sheet_get_extent (sheet, FALSE, TRUE);
306 for (row = r.start.row; row <= r.end.row; row++) {
307 for (col = r.start.col; col <= r.end.col; col++) {
308 GnmCell *cell = sheet_cell_get (sheet, col, row);
309 if (!stf_export_cell (stfe, cell))
310 return FALSE;
312 if (!gsf_output_csv_write_eol (GSF_OUTPUT_CSV (stfe)))
313 return FALSE;
316 return TRUE;
320 * gnm_stf_export:
321 * @export_options: an export options struct
323 * Exports the sheets given in @stfe
325 * Return value: TRUE on success, FALSE otherwise
327 gboolean
328 gnm_stf_export (GnmStfExport *stfe)
330 GSList *ptr;
331 GsfOutput *sink;
332 gboolean result = TRUE;
333 char *old_locale = NULL;
335 g_return_val_if_fail (GNM_IS_STF_EXPORT (stfe), FALSE);
336 g_return_val_if_fail (stfe->sheet_list != NULL, FALSE);
337 g_object_get (G_OBJECT (stfe), "sink", &sink, NULL);
338 g_return_val_if_fail (sink != NULL, FALSE);
340 if (stfe->charset &&
341 strcmp (stfe->charset, "UTF-8") != 0) {
342 char *charset;
343 GsfOutput *converter;
345 switch (stfe->transliterate_mode) {
346 default:
347 case GNM_STF_TRANSLITERATE_MODE_ESCAPE:
348 charset = g_strdup (stfe->charset);
349 break;
350 case GNM_STF_TRANSLITERATE_MODE_TRANS:
351 charset = g_strconcat (stfe->charset,
352 "//TRANSLIT",
353 NULL);
354 break;
356 converter = gsf_output_iconv_new (sink, charset, "UTF-8");
357 g_free (charset);
359 if (converter) {
360 g_object_set (G_OBJECT (stfe), "sink", converter, NULL);
361 g_object_unref (converter);
362 } else {
363 g_warning ("Failed to create converter.");
364 result = FALSE;
368 if (stfe->locale) {
369 old_locale = g_strdup (go_setlocale (LC_ALL, NULL));
370 go_setlocale (LC_ALL, stfe->locale);
373 for (ptr = stfe->sheet_list; ptr != NULL ; ptr = ptr->next) {
374 Sheet *sheet = ptr->data;
375 if (!stf_export_sheet (stfe, sheet)) {
376 result = FALSE;
377 break;
381 if (old_locale) /* go_setlocale clears the cache, */
382 /*so we don't want to call it with NULL*/
383 go_setlocale (LC_ALL, old_locale);
384 g_free (old_locale);
387 g_object_set (G_OBJECT (stfe), "sink", sink, NULL);
388 g_object_unref (sink);
390 return result;
393 /* ------------------------------------------------------------------------- */
396 * gnm_stf_export_can_transliterate:
398 * Return value: TRUE iff //TRANSLIT is supported
401 gboolean
402 gnm_stf_export_can_transliterate (void)
404 char const *text = "G\xc3\xbclzow";
405 char *encoded_text;
406 GError *error = NULL;
408 encoded_text = g_convert (text, -1,
409 "ASCII//TRANSLIT", "UTF-8",
410 NULL, NULL, &error);
411 g_free (encoded_text);
413 if (error == NULL)
414 return TRUE;
416 g_error_free (error);
417 return FALSE;
420 /* ------------------------------------------------------------------------- */
422 GType
423 gnm_stf_transliterate_mode_get_type (void)
425 static GType etype = 0;
426 if (etype == 0) {
427 static GEnumValue const values[] = {
428 { GNM_STF_TRANSLITERATE_MODE_TRANS, "GNM_STF_TRANSLITERATE_MODE_TRANS", "transliterate" },
429 { GNM_STF_TRANSLITERATE_MODE_ESCAPE, "GNM_STF_TRANSLITERATE_MODE_ESCAPE", "escape" },
430 { 0, NULL, NULL }
432 etype = g_enum_register_static ("GnmStfTransliterateMode", values);
434 return etype;
437 /* ------------------------------------------------------------------------- */
439 GType
440 gnm_stf_format_mode_get_type (void)
442 static GType etype = 0;
443 if (etype == 0) {
444 static GEnumValue const values[] = {
445 { GNM_STF_FORMAT_AUTO, "GNM_STF_FORMAT_AUTO", "automatic" },
446 { GNM_STF_FORMAT_RAW, "GNM_STF_FORMAT_RAW", "raw" },
447 { GNM_STF_FORMAT_PRESERVE, "GNM_STF_FORMAT_PRESERVE", "preserve" },
448 { 0, NULL, NULL }
450 etype = g_enum_register_static ("GnmStfFormatMode", values);
452 return etype;
455 /* ------------------------------------------------------------------------- */
457 static void
458 gnm_stf_export_init (G_GNUC_UNUSED GObject *obj)
462 /* ------------------------------------------------------------------------- */
464 static void
465 gnm_stf_export_finalize (GObject *obj)
467 GnmStfExport *stfe = (GnmStfExport *)obj;
469 gnm_stf_export_options_sheet_list_clear (stfe);
470 g_free (stfe->charset);
471 g_free (stfe->locale);
473 G_OBJECT_CLASS (parent_class)->finalize (obj);
476 /* ------------------------------------------------------------------------- */
478 static void
479 gnm_stf_export_get_property (GObject *object,
480 guint property_id,
481 GValue *value,
482 GParamSpec *pspec)
484 GnmStfExport *stfe = (GnmStfExport *)object;
486 switch (property_id) {
487 case PROP_CHARSET:
488 g_value_set_string (value, stfe->charset);
489 break;
490 case PROP_LOCALE:
491 g_value_set_string (value, stfe->locale);
492 break;
493 case PROP_TRANSLITERATE_MODE:
494 g_value_set_enum (value, stfe->transliterate_mode);
495 break;
496 case PROP_FORMAT:
497 g_value_set_enum (value, stfe->format);
498 break;
499 default:
500 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
501 break;
505 /* ------------------------------------------------------------------------- */
507 static void
508 gnm_stf_export_set_property (GObject *object,
509 guint property_id,
510 GValue const *value,
511 GParamSpec *pspec)
513 GnmStfExport *stfe = (GnmStfExport *)object;
514 char *scopy;
516 switch (property_id) {
517 case PROP_CHARSET:
518 scopy = g_value_dup_string (value);
519 g_free (stfe->charset);
520 stfe->charset = scopy;
521 break;
522 case PROP_LOCALE:
523 scopy = g_value_dup_string (value);
524 g_free (stfe->locale);
525 stfe->locale = scopy;
526 break;
527 case PROP_TRANSLITERATE_MODE:
528 stfe->transliterate_mode = g_value_get_enum (value);
529 break;
530 case PROP_FORMAT:
531 stfe->format = g_value_get_enum (value);
532 break;
533 default:
534 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
535 break;
539 /* ------------------------------------------------------------------------- */
541 static void
542 gnm_stf_export_class_init (GObjectClass *gobject_class)
544 parent_class = g_type_class_peek_parent (gobject_class);
546 gobject_class->finalize = gnm_stf_export_finalize;
547 gobject_class->get_property = gnm_stf_export_get_property;
548 gobject_class->set_property = gnm_stf_export_set_property;
550 g_object_class_install_property
551 (gobject_class,
552 PROP_CHARSET,
553 g_param_spec_string ("charset",
554 P_("Character set"),
555 P_("The character encoding of the output."),
556 NULL,
557 GSF_PARAM_STATIC |
558 G_PARAM_READWRITE));
559 g_object_class_install_property
560 (gobject_class,
561 PROP_LOCALE,
562 g_param_spec_string ("locale",
563 P_("Locale"),
564 P_("The locale to use for number and date formatting."),
565 NULL,
566 GSF_PARAM_STATIC |
567 G_PARAM_READWRITE));
568 g_object_class_install_property
569 (gobject_class,
570 PROP_TRANSLITERATE_MODE,
571 g_param_spec_enum ("transliterate-mode",
572 P_("Transliterate mode"),
573 P_("What to do with unrepresentable characters."),
574 GNM_STF_TRANSLITERATE_MODE_TYPE,
575 GNM_STF_TRANSLITERATE_MODE_ESCAPE,
576 GSF_PARAM_STATIC |
577 G_PARAM_READWRITE));
578 g_object_class_install_property
579 (gobject_class,
580 PROP_FORMAT,
581 g_param_spec_enum ("format",
582 P_("Format"),
583 P_("How should cells be formatted?"),
584 GNM_STF_FORMAT_MODE_TYPE,
585 GNM_STF_FORMAT_AUTO,
586 GSF_PARAM_STATIC |
587 G_PARAM_READWRITE));
590 /* ------------------------------------------------------------------------- */
592 GSF_CLASS (GnmStfExport, gnm_stf_export,
593 gnm_stf_export_class_init, gnm_stf_export_init, GSF_OUTPUT_CSV_TYPE)
595 /* ------------------------------------------------------------------------- */
596 /* ------------------------------------------------------------------------- */
599 #include "wbc-gtk.h"
600 #include "dialog-stf-export.h"
601 #include "workbook-view.h"
604 * gnm_stf_get_stfe:
605 * @obj: #GObject with a #GnmStfExport attached as data.
607 * If none is found, a new one is created and attached to @obj.
608 * Returns: (transfer none): the #GnmStfExport.
610 GnmStfExport *
611 gnm_stf_get_stfe (GObject *obj)
613 GnmStfExport *stfe = g_object_get_data (obj, "stfe");
614 if (!stfe) {
615 const char * sep = gnm_conf_get_stf_export_separator ();
616 const char * string_indicator = gnm_conf_get_stf_export_stringindicator ();
617 const char * terminator = gnm_conf_get_stf_export_terminator ();
618 const char * locale = gnm_conf_get_stf_export_locale ();
619 const char * encoding = gnm_conf_get_stf_export_encoding ();
620 int quotingmode = gnm_conf_get_stf_export_quoting ();
621 int format = gnm_conf_get_stf_export_format ();
622 int transliteratemode = gnm_conf_get_stf_export_transliteration () ?
623 GNM_STF_TRANSLITERATE_MODE_TRANS : GNM_STF_TRANSLITERATE_MODE_ESCAPE;
624 GString *triggers = g_string_new (NULL);
626 if (strlen (locale) == 0)
627 locale = NULL;
628 if (strlen (encoding) == 0)
629 encoding = NULL;
631 /* Workaround GConf bug #641807. */
632 if (terminator == NULL || strlen (terminator) == 0)
633 terminator = "\n";
635 if (quotingmode == GSF_OUTPUT_CSV_QUOTING_MODE_AUTO) {
636 g_string_append (triggers, " \t");
637 g_string_append (triggers, terminator);
638 g_string_append (triggers, string_indicator);
639 g_string_append (triggers, sep);
642 stfe = g_object_new (GNM_STF_EXPORT_TYPE,
643 "quoting-triggers", triggers->str,
644 "separator", sep,
645 "quote", string_indicator,
646 "eol", terminator,
647 "charset", encoding,
648 "locale", locale,
649 "quoting-mode", quotingmode,
650 "transliterate-mode", transliteratemode,
651 "format", format,
652 NULL);
653 g_object_set_data_full (obj, "stfe", stfe, g_object_unref);
654 g_string_free (triggers, TRUE);
656 return stfe;
659 static void
660 gnm_stf_file_saver_save (G_GNUC_UNUSED GOFileSaver const *fs, GOIOContext *context,
661 GoView const *view, GsfOutput *output)
663 WorkbookView *wbv = GNM_WORKBOOK_VIEW (view);
664 Workbook *wb = wb_view_get_workbook (wbv);
665 GnmStfExport *stfe = gnm_stf_get_stfe (G_OBJECT (wb));
666 GsfOutput *dummy_sink;
667 gboolean nosheets;
669 /* TODO: move this GUI dependent code out of this
670 * filesaver into gui-file.c. After this, remove includes (see above). */
671 if (GNM_IS_WBC_GTK (context->impl)) {
672 gboolean cancelled =
673 stf_export_dialog (WBC_GTK (context->impl), stfe, wb);
674 if (cancelled) {
675 go_io_error_unknown (context);
676 return;
680 nosheets = (stfe->sheet_list == NULL);
681 if (nosheets)
682 gnm_stf_export_options_sheet_list_add
683 (stfe, wb_view_cur_sheet (wbv));
685 g_object_set (G_OBJECT (stfe), "sink", output, NULL);
686 if (gnm_stf_export (stfe) == FALSE)
687 go_cmd_context_error_import (GO_CMD_CONTEXT (context),
688 _("Error while trying to export file as text"));
690 /* We're not allowed to set a NULL sink, so use a dummy. */
691 dummy_sink = gsf_output_memory_new ();
692 g_object_set (G_OBJECT (stfe), "sink", dummy_sink, NULL);
693 g_object_unref (dummy_sink);
695 if (nosheets)
696 gnm_stf_export_options_sheet_list_clear (stfe);
699 static gboolean
700 cb_set_export_option (const char *key, const char *value,
701 GError **err, gpointer user)
703 Workbook *wb = user;
704 GnmStfExport *stfe = gnm_stf_get_stfe (G_OBJECT (wb));
705 const char *errtxt;
707 if (strcmp (key, "sheet") == 0) {
708 Sheet *sheet = workbook_sheet_by_name (wb, value);
709 if (!sheet) {
710 errtxt = _("There is no such sheet");
711 goto error;
714 gnm_stf_export_options_sheet_list_add (stfe, sheet);
716 return FALSE;
719 if (strcmp (key, "eol") == 0) {
720 const char *eol;
721 if (g_ascii_strcasecmp ("unix", value) == 0)
722 eol = "\n";
723 else if (g_ascii_strcasecmp ("mac", value) == 0)
724 eol = "\r";
725 else if (g_ascii_strcasecmp ("windows", value) == 0)
726 eol = "\r\n";
727 else {
728 errtxt = _("eol must be one of unix, mac, and windows");
729 goto error;
732 g_object_set (G_OBJECT (stfe), "eol", eol, NULL);
733 return FALSE;
736 if (strcmp (key, "charset") == 0 ||
737 strcmp (key, "locale") == 0 ||
738 strcmp (key, "quote") == 0 ||
739 strcmp (key, "separator") == 0 ||
740 strcmp (key, "format") == 0 ||
741 strcmp (key, "transliterate-mode") == 0 ||
742 strcmp (key, "quoting-mode") == 0 ||
743 strcmp (key, "quoting-on-whitespace") == 0)
744 return go_object_set_property
745 (G_OBJECT (stfe),
746 key, key, value,
747 err,
748 (_("Invalid value for option %s: \"%s\"")));
750 errtxt = _("Invalid option for stf exporter");
751 error:
752 if (err)
753 *err = g_error_new (go_error_invalid (), 0, "%s", errtxt);
755 return TRUE;
758 static gboolean
759 gnm_stf_fs_set_export_options (G_GNUC_UNUSED GOFileSaver *fs,
760 GODoc *doc,
761 const char *options,
762 GError **err,
763 G_GNUC_UNUSED gpointer user)
765 GnmStfExport *stfe = gnm_stf_get_stfe (G_OBJECT (doc));
766 gnm_stf_export_options_sheet_list_clear (stfe);
767 return go_parse_key_value (options, err, cb_set_export_option, doc);
771 * gnm_stf_file_saver_create:
772 * @id:
774 * Returns: (transfer full): the newly allocated #GOFileSaver.
776 GOFileSaver *
777 gnm_stf_file_saver_create (gchar const *id)
779 GOFileSaver *fs = go_file_saver_new (id,
780 "txt",
781 _("Text (configurable)"),
782 GO_FILE_FL_WRITE_ONLY,
783 gnm_stf_file_saver_save);
784 go_file_saver_set_save_scope (fs, GO_FILE_SAVE_WORKBOOK);
785 g_signal_connect (G_OBJECT (fs), "set-export-options",
786 G_CALLBACK (gnm_stf_fs_set_export_options),
787 NULL);
788 return GO_FILE_SAVER (fs);