1 /*
2 * file.c: File loading and saving routines
3 *
4 * Authors:
5 * Miguel de Icaza (miguel@kernel.org)
6 * Zbigniew Chyla (cyba@gnome.pl)
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) version 3.
12 *
13 * This library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
21 * USA.
22 */
23 #include <goffice/goffice-config.h>
24 #include <goffice/utils/go-file.h>
25 #include <goffice/utils/go-glib-extras.h>
26 #include <goffice/utils/go-marshalers.h>
27 #include <goffice/app/file.h>
28 #include <goffice/app/go-doc.h>
29 #include <goffice/app/file-priv.h>
30 #include <goffice/app/error-info.h>
31 #include <goffice/app/io-context.h>
32 #include <goffice/app/go-cmd-context.h>
33
34 #include <gsf/gsf-input.h>
35 #include <gsf/gsf-output.h>
36 #include <gsf/gsf-output-stdio.h>
37 #include <gsf/gsf-impl-utils.h>
38 #include <gsf/gsf-output-stdio.h>
39 #include <gsf/gsf-utils.h>
40 #include <string.h>
41 #include <glib/gi18n-lib.h>
42
43 enum {
44 FO_PROP_0,
45 FO_PROP_ID,
46 FO_PROP_DESCRIPTION,
47 FO_PROP_INTERACTIVE_ONLY
48 };
49
50 static void
go_file_opener_init(GOFileOpener * fo)51 go_file_opener_init (GOFileOpener *fo)
52 {
53 fo->id = NULL;
54 fo->description = NULL;
55 fo->probe_func = NULL;
56 fo->open_func = NULL;
57 fo->interactive_only = FALSE;
58 }
59
60 static void
go_file_opener_finalize(GObject * obj)61 go_file_opener_finalize (GObject *obj)
62 {
63 GOFileOpener *fo;
64
65 g_return_if_fail (GO_IS_FILE_OPENER (obj));
66
67 fo = GO_FILE_OPENER (obj);
68 g_free (fo->id);
69 g_free (fo->description);
70 g_slist_foreach (fo->suffixes, (GFunc)g_free, NULL);
71 g_slist_free (fo->suffixes);
72 g_slist_foreach (fo->mimes, (GFunc)g_free, NULL);
73 g_slist_free (fo->mimes);
74
75 G_OBJECT_CLASS (g_type_class_peek (G_TYPE_OBJECT))->finalize (obj);
76 }
77
78 static void
go_file_opener_set_property(GObject * object,guint property_id,GValue const * value,GParamSpec * pspec)79 go_file_opener_set_property (GObject *object, guint property_id,
80 GValue const *value, GParamSpec *pspec)
81 {
82 GOFileOpener *fo = (GOFileOpener *)object;
83
84 switch (property_id) {
85 case FO_PROP_ID: {
86 char *s = g_value_dup_string (value);
87 g_free (fo->id);
88 fo->id = s;
89 break;
90 }
91 case FO_PROP_DESCRIPTION: {
92 char *s = g_value_dup_string (value);
93 g_free (fo->description);
94 fo->description = s;
95 break;
96 }
97 case FO_PROP_INTERACTIVE_ONLY:
98 fo->interactive_only = g_value_get_boolean (value);
99 break;
100 default:
101 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
102 break;
103 }
104 }
105
106 static void
go_file_opener_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)107 go_file_opener_get_property (GObject *object,
108 guint property_id,
109 GValue *value,
110 GParamSpec *pspec)
111 {
112 GOFileOpener *fo = GO_FILE_OPENER (object);
113
114 switch (property_id) {
115 case FO_PROP_ID:
116 g_value_set_string (value, fo->id);
117 break;
118 case FO_PROP_DESCRIPTION:
119 g_value_set_string (value, fo->description);
120 break;
121 case FO_PROP_INTERACTIVE_ONLY:
122 g_value_set_boolean (value, fo->interactive_only);
123 break;
124 default:
125 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
126 break;
127 }
128 }
129
130 static gboolean
go_file_opener_can_probe_real(GOFileOpener const * fo,GOFileProbeLevel pl)131 go_file_opener_can_probe_real (GOFileOpener const *fo, GOFileProbeLevel pl)
132 {
133 return fo->probe_func != NULL;
134 }
135
136 static gboolean
go_file_opener_probe_real(GOFileOpener const * fo,GsfInput * input,GOFileProbeLevel pl)137 go_file_opener_probe_real (GOFileOpener const *fo, GsfInput *input,
138 GOFileProbeLevel pl)
139 {
140 gboolean ret = FALSE;
141
142 if (fo->probe_func != NULL) {
143 ret = fo->probe_func (fo, input, pl);
144 gsf_input_seek (input, 0, G_SEEK_SET);
145 }
146 return ret;
147 }
148
149 static void
go_file_opener_open_real(GOFileOpener const * fo,gchar const * opt_enc,GOIOContext * io_context,GoView * view,GsfInput * input)150 go_file_opener_open_real (GOFileOpener const *fo, gchar const *opt_enc,
151 GOIOContext *io_context, GoView *view,
152 GsfInput *input)
153 {
154 if (fo->open_func != NULL) {
155 if (fo->encoding_dependent)
156 ((GOFileOpenerOpenFuncWithEnc)fo->open_func)
157 (fo, opt_enc, io_context, view, input);
158 else
159 fo->open_func (fo, io_context, view, input);
160 } else
161 go_io_error_unknown (io_context);
162 }
163
164 static void
go_file_opener_class_init(GOFileOpenerClass * klass)165 go_file_opener_class_init (GOFileOpenerClass *klass)
166 {
167 GObjectClass *goc = G_OBJECT_CLASS (klass);
168
169 goc->finalize = go_file_opener_finalize;
170 goc->set_property = go_file_opener_set_property;
171 goc->get_property = go_file_opener_get_property;
172
173 g_object_class_install_property
174 (goc,
175 FO_PROP_ID,
176 g_param_spec_string ("id",
177 _("ID"),
178 _("The identifier of the opener"),
179 NULL,
180 GSF_PARAM_STATIC |
181 G_PARAM_READABLE));
182
183 g_object_class_install_property
184 (goc,
185 FO_PROP_DESCRIPTION,
186 g_param_spec_string ("description",
187 _("Description"),
188 _("The description of the opener"),
189 NULL,
190 GSF_PARAM_STATIC |
191 G_PARAM_READWRITE));
192
193 g_object_class_install_property
194 (goc,
195 FO_PROP_INTERACTIVE_ONLY,
196 g_param_spec_boolean ("interactive-only",
197 _("Interactive only"),
198 _("TRUE if this opener requires interaction"),
199 FALSE,
200 GSF_PARAM_STATIC |
201 G_PARAM_READWRITE));
202
203 klass->can_probe = go_file_opener_can_probe_real;
204 klass->probe = go_file_opener_probe_real;
205 klass->open = go_file_opener_open_real;
206 }
207
GSF_CLASS(GOFileOpener,go_file_opener,go_file_opener_class_init,go_file_opener_init,G_TYPE_OBJECT)208 GSF_CLASS (GOFileOpener, go_file_opener,
209 go_file_opener_class_init, go_file_opener_init,
210 G_TYPE_OBJECT)
211
212 /**
213 * go_file_opener_setup:
214 * @fo: Newly created GOFileOpener object
215 * @id: (allow-none): ID of the opener
216 * @description: Description of supported file format
217 * @suffixes: (element-type utf8) (transfer full): List of suffixes to
218 * associate with the opener
219 * @mimes: (element-type utf8) (transfer full): List of mime types to
220 * associate with the opener
221 * @encoding_dependent: whether the opener depends on an encoding sel.
222 * @probe_func: (scope async) (nullable): "probe" function
223 * @open_func: (scope async): "open" function
224 *
225 * Sets up GOFileOpener object, newly created with g_object_new function.
226 * This is intended to be used only by GOFileOpener derivates.
227 * Use go_file_opener_new, if you want to create GOFileOpener object.
228 **/
229 void
230 go_file_opener_setup (GOFileOpener *fo, gchar const *id,
231 gchar const *description,
232 GSList *suffixes,
233 GSList *mimes,
234 gboolean encoding_dependent,
235 GOFileOpenerProbeFunc probe_func,
236 GOFileOpenerOpenFunc open_func)
237 {
238 g_return_if_fail (GO_IS_FILE_OPENER (fo));
239 g_return_if_fail (description != NULL);
240
241 fo->id = g_strdup (id);
242 fo->description = g_strdup (description);
243 fo->suffixes = suffixes;
244 fo->mimes = mimes;
245
246 fo->encoding_dependent = encoding_dependent;
247 fo->probe_func = probe_func;
248 fo->open_func = open_func;
249 }
250
251 /**
252 * go_file_opener_new:
253 * @id: (nullable): Optional ID of the opener
254 * @description: Description of supported file format
255 * @suffixes: (element-type utf8) (transfer full): List of suffixes to
256 * associate with the opener
257 * @mimes: (element-type utf8) (transfer full): List of mime types to
258 * associate with the opener
259 * @probe_func: (scope async) (nullable): "probe" function
260 * @open_func: (scope async): "open" function
261 *
262 * Creates new GOFileOpener object. Optional @id will be used
263 * after registering it with go_file_opener_register function.
264 *
265 * Returns: (transfer full): newly created GOFileOpener object.
266 **/
267 GOFileOpener *
go_file_opener_new(gchar const * id,gchar const * description,GSList * suffixes,GSList * mimes,GOFileOpenerProbeFunc probe_func,GOFileOpenerOpenFunc open_func)268 go_file_opener_new (gchar const *id,
269 gchar const *description,
270 GSList *suffixes,
271 GSList *mimes,
272 GOFileOpenerProbeFunc probe_func,
273 GOFileOpenerOpenFunc open_func)
274 {
275 GOFileOpener *fo;
276
277 fo = GO_FILE_OPENER (g_object_new (GO_TYPE_FILE_OPENER, NULL));
278 go_file_opener_setup (fo, id, description, suffixes, mimes, FALSE,
279 probe_func, open_func);
280
281 return fo;
282 }
283
284 /**
285 * go_file_opener_new_with_enc:
286 * @id: (nullable): Optional ID of the opener
287 * @description: Description of supported file format
288 * @suffixes: (element-type utf8) (transfer full): List of suffixes to
289 * associate with the opener
290 * @mimes: (element-type utf8) (transfer full): List of mime types to
291 * associate with the opener
292 * @probe_func: (scope async) (nullable): "probe" function
293 * @open_func: (scope async): "open" function
294 *
295 * Creates new #GOFileOpener object. Optional @id will be used
296 * after registering it with go_file_opener_register function.
297 *
298 * Returns: (transfer full): newly created #GOFileOpener object.
299 **/
300 GOFileOpener *
go_file_opener_new_with_enc(gchar const * id,gchar const * description,GSList * suffixes,GSList * mimes,GOFileOpenerProbeFunc probe_func,GOFileOpenerOpenFuncWithEnc open_func)301 go_file_opener_new_with_enc (gchar const *id,
302 gchar const *description,
303 GSList *suffixes,
304 GSList *mimes,
305 GOFileOpenerProbeFunc probe_func,
306 GOFileOpenerOpenFuncWithEnc open_func)
307 {
308 GOFileOpener *fo;
309
310 fo = GO_FILE_OPENER (g_object_new (GO_TYPE_FILE_OPENER, NULL));
311 go_file_opener_setup (fo, id, description, suffixes, mimes, TRUE,
312 probe_func, (GOFileOpenerOpenFunc)open_func);
313 return fo;
314 }
315
316
317
318 /**
319 * go_file_opener_get_id:
320 * @fo: #GOFileOpener to query
321 *
322 * Returns: (transfer none) (nullable): the id of @fo
323 */
324 gchar const *
go_file_opener_get_id(GOFileOpener const * fo)325 go_file_opener_get_id (GOFileOpener const *fo)
326 {
327 g_return_val_if_fail (GO_IS_FILE_OPENER (fo), NULL);
328
329 return fo->id;
330 }
331
332 /**
333 * go_file_opener_get_description:
334 * @fo: #GOFileOpener to query
335 *
336 * Returns: (transfer none): the description of @fo
337 */
338 gchar const *
go_file_opener_get_description(GOFileOpener const * fo)339 go_file_opener_get_description (GOFileOpener const *fo)
340 {
341 g_return_val_if_fail (GO_IS_FILE_OPENER (fo), NULL);
342
343 return fo->description;
344 }
345
346 /**
347 * go_file_opener_is_encoding_dependent:
348 * @fo: #GOFileOpener to query
349 *
350 * Returns: %TRUE if @fo is encoding dependent
351 */
352 gboolean
go_file_opener_is_encoding_dependent(GOFileOpener const * fo)353 go_file_opener_is_encoding_dependent (GOFileOpener const *fo)
354 {
355 g_return_val_if_fail (GO_IS_FILE_OPENER (fo), FALSE);
356
357 return fo->encoding_dependent;
358 }
359
360 /**
361 * go_file_opener_can_probe:
362 * @fo: #GOFileOpener to query
363 * @pl: probe level
364 *
365 * Returns: %TRUE if @fo has a probe function
366 */
367 gboolean
go_file_opener_can_probe(GOFileOpener const * fo,GOFileProbeLevel pl)368 go_file_opener_can_probe (GOFileOpener const *fo, GOFileProbeLevel pl)
369 {
370 g_return_val_if_fail (GO_IS_FILE_OPENER (fo), FALSE);
371
372 return GO_FILE_OPENER_METHOD (fo, can_probe) (fo, pl);
373 }
374
375 /**
376 * go_file_opener_get_suffixes:
377 * @fo: #GOFileOpener
378 *
379 * Returns: (element-type utf8) (transfer none): the suffixes for the
380 * supported file types.
381 **/
382 GSList const *
go_file_opener_get_suffixes(GOFileOpener const * fo)383 go_file_opener_get_suffixes (GOFileOpener const *fo)
384 {
385 g_return_val_if_fail (GO_IS_FILE_OPENER (fo), NULL);
386 return fo->suffixes;
387 }
388
389 /**
390 * go_file_opener_get_mimes:
391 * @fo: #GOFileOpener to query
392 *
393 * Returns: (element-type utf8) (transfer none): the supported mime types.
394 **/
395 GSList const *
go_file_opener_get_mimes(GOFileOpener const * fo)396 go_file_opener_get_mimes (GOFileOpener const *fo)
397 {
398 g_return_val_if_fail (GO_IS_FILE_OPENER (fo), NULL);
399 return fo->mimes;
400 }
401
402
403 /**
404 * go_file_opener_probe:
405 * @fo: #GOFileOpener
406 * @input: #GsfInput
407 * @pl: #GOFileProbeLevel
408 *
409 * Checks if a given file is supported by the opener.
410 *
411 * Returns: %TRUE, if the opener can read given file and %FALSE otherwise.
412 **/
413 gboolean
go_file_opener_probe(GOFileOpener const * fo,GsfInput * input,GOFileProbeLevel pl)414 go_file_opener_probe (GOFileOpener const *fo, GsfInput *input, GOFileProbeLevel pl)
415 {
416 g_return_val_if_fail (GO_IS_FILE_OPENER (fo), FALSE);
417 g_return_val_if_fail (GSF_IS_INPUT (input), FALSE);
418
419 #if 0
420 g_print ("Trying format %s at level %d...\n",
421 go_file_opener_get_id (fo),
422 (int)pl);
423 #endif
424 return GO_FILE_OPENER_METHOD (fo, probe) (fo, input, pl);
425 }
426
427 /**
428 * go_file_opener_open:
429 * @fo: GOFileOpener object
430 * @opt_enc: (nullable): Optional encoding
431 * @io_context: Context for i/o operation
432 * @view: #GoView
433 * @input: Gsf input stream
434 *
435 * Reads content of @file_name file into workbook @wbv is attached to.
436 * Results are reported using @io_context object, use
437 * go_io_error_occurred to find out if operation was successful.
438 * The state of @wbv and its workbook is undefined if operation fails, you
439 * should destroy them in that case.
440 */
441 void
go_file_opener_open(GOFileOpener const * fo,gchar const * opt_enc,GOIOContext * io_context,GoView * view,GsfInput * input)442 go_file_opener_open (GOFileOpener const *fo, gchar const *opt_enc,
443 GOIOContext *io_context,
444 GoView *view, GsfInput *input)
445 {
446 g_return_if_fail (GO_IS_FILE_OPENER (fo));
447 g_return_if_fail (GSF_IS_INPUT (input));
448
449 GO_FILE_OPENER_METHOD (fo, open) (fo, opt_enc, io_context, view, input);
450 }
451
452 /*
453 * GOFileSaver
454 */
455
456 /**
457 * GOFileSaverClass:
458 * @parent_class: parent class.
459 * @save: saves the file.
460 * @set_export_options: set the options.
461 *
462 * File saver base class.
463 **/
464
465 static void
go_file_saver_init(GOFileSaver * fs)466 go_file_saver_init (GOFileSaver *fs)
467 {
468 fs->id = NULL;
469 fs->extension = NULL;
470 fs->mime_type = NULL;
471 fs->description = NULL;
472 fs->overwrite_files = TRUE;
473 fs->interactive_only = FALSE;
474 fs->format_level = GO_FILE_FL_NEW;
475 fs->save_scope = GO_FILE_SAVE_WORKBOOK;
476 fs->save_func = NULL;
477 }
478
479 static void
go_file_saver_finalize(GObject * obj)480 go_file_saver_finalize (GObject *obj)
481 {
482 GOFileSaver *fs;
483
484 g_return_if_fail (GO_IS_FILE_SAVER (obj));
485
486 fs = GO_FILE_SAVER (obj);
487 g_free (fs->id);
488 g_free (fs->extension);
489 g_free (fs->description);
490 g_free (fs->mime_type);
491
492 G_OBJECT_CLASS (g_type_class_peek (G_TYPE_OBJECT))->finalize (obj);
493 }
494
495 enum {
496 FS_PROP_0,
497 FS_PROP_ID,
498 FS_PROP_MIME_TYPE,
499 FS_PROP_EXTENSION,
500 FS_PROP_DESCRIPTION,
501 FS_PROP_OVERWRITE,
502 FS_PROP_INTERACTIVE_ONLY,
503 FS_PROP_FORMAT_LEVEL,
504 FS_PROP_SCOPE,
505 FS_PROP_SHEET_SELECTION
506 };
507
508 enum {
509 FS_SET_EXPORT_OPTIONS,
510 FS_LAST_SIGNAL
511 };
512
513 static guint fs_signals[FS_LAST_SIGNAL];
514
515 static void
go_file_saver_set_property(GObject * object,guint property_id,GValue const * value,GParamSpec * pspec)516 go_file_saver_set_property (GObject *object, guint property_id,
517 GValue const *value, GParamSpec *pspec)
518 {
519 GOFileSaver *fs = (GOFileSaver *)object;
520
521 switch (property_id) {
522 case FS_PROP_ID: {
523 char *s = g_value_dup_string (value);
524 g_free (fs->id);
525 fs->id = s;
526 break;
527 }
528 case FS_PROP_MIME_TYPE: {
529 char *s = g_value_dup_string (value);
530 g_free (fs->mime_type);
531 fs->mime_type = s;
532 break;
533 }
534 case FS_PROP_EXTENSION: {
535 char *s = g_value_dup_string (value);
536 g_free (fs->extension);
537 fs->extension = s;
538 break;
539 }
540 case FS_PROP_DESCRIPTION: {
541 char *s = g_value_dup_string (value);
542 g_free (fs->description);
543 fs->description = s;
544 break;
545 }
546 case FS_PROP_OVERWRITE:
547 fs->overwrite_files = g_value_get_boolean (value);
548 break;
549 case FS_PROP_INTERACTIVE_ONLY:
550 fs->interactive_only = g_value_get_boolean (value);
551 break;
552 case FS_PROP_FORMAT_LEVEL:
553 fs->format_level = g_value_get_enum (value);
554 break;
555 case FS_PROP_SCOPE:
556 fs->save_scope = g_value_get_enum (value);
557 break;
558 case FS_PROP_SHEET_SELECTION:
559 fs->sheet_selection = g_value_get_boolean (value);
560 break;
561 default:
562 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
563 break;
564 }
565 }
566
567 static void
go_file_saver_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)568 go_file_saver_get_property (GObject *object, guint property_id,
569 GValue *value, GParamSpec *pspec)
570 {
571 GOFileSaver *fs = (GOFileSaver *)object;
572
573 switch (property_id) {
574 case FS_PROP_ID:
575 g_value_set_string (value, fs->id);
576 break;
577 case FS_PROP_MIME_TYPE:
578 g_value_set_string (value, fs->mime_type);
579 break;
580 case FS_PROP_EXTENSION:
581 g_value_set_string (value, fs->extension);
582 break;
583 case FS_PROP_DESCRIPTION:
584 g_value_set_string (value, fs->description);
585 break;
586 case FS_PROP_OVERWRITE:
587 g_value_set_boolean (value, fs->overwrite_files);
588 break;
589 case FS_PROP_INTERACTIVE_ONLY:
590 g_value_set_boolean (value, fs->interactive_only);
591 break;
592 case FS_PROP_FORMAT_LEVEL:
593 g_value_set_enum (value, fs->format_level);
594 break;
595 case FS_PROP_SCOPE:
596 g_value_set_enum (value, fs->save_scope);
597 break;
598 case FS_PROP_SHEET_SELECTION:
599 g_value_set_boolean (value, fs->sheet_selection);
600 break;
601 default:
602 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
603 break;
604 }
605 }
606
607 static void
go_file_saver_save_real(GOFileSaver const * fs,GOIOContext * io_context,GoView const * view,GsfOutput * output)608 go_file_saver_save_real (GOFileSaver const *fs, GOIOContext *io_context,
609 GoView const *view, GsfOutput *output)
610 {
611 if (fs->save_func == NULL) {
612 go_io_error_unknown (io_context);
613 return;
614 }
615
616 fs->save_func (fs, io_context, view, output);
617 }
618
619 static void
go_file_saver_class_init(GOFileSaverClass * klass)620 go_file_saver_class_init (GOFileSaverClass *klass)
621 {
622 GObjectClass *goc = G_OBJECT_CLASS (klass);
623
624 goc->finalize = go_file_saver_finalize;
625 goc->set_property = go_file_saver_set_property;
626 goc->get_property = go_file_saver_get_property;
627
628 g_object_class_install_property
629 (goc,
630 FS_PROP_ID,
631 g_param_spec_string ("id",
632 _("ID"),
633 _("The identifier of the saver."),
634 NULL,
635 GSF_PARAM_STATIC |
636 G_PARAM_CONSTRUCT_ONLY |
637 G_PARAM_READWRITE));
638
639 g_object_class_install_property
640 (goc,
641 FS_PROP_MIME_TYPE,
642 g_param_spec_string ("mime-type",
643 _("MIME type"),
644 _("The MIME type of the saver."),
645 NULL,
646 GSF_PARAM_STATIC |
647 G_PARAM_READWRITE));
648
649 g_object_class_install_property
650 (goc,
651 FS_PROP_EXTENSION,
652 g_param_spec_string ("extension",
653 _("Extension"),
654 _("The standard file name extension of the saver."),
655 NULL,
656 GSF_PARAM_STATIC |
657 G_PARAM_READWRITE));
658
659 g_object_class_install_property
660 (goc,
661 FS_PROP_DESCRIPTION,
662 g_param_spec_string ("description",
663 _("Description"),
664 _("The description of the saver."),
665 NULL,
666 GSF_PARAM_STATIC |
667 G_PARAM_READWRITE));
668
669 g_object_class_install_property
670 (goc,
671 FS_PROP_OVERWRITE,
672 g_param_spec_boolean ("overwrite",
673 _("Overwrite"),
674 _("Whether the saver will overwrite files."),
675 TRUE,
676 GSF_PARAM_STATIC |
677 G_PARAM_READWRITE));
678
679 g_object_class_install_property
680 (goc,
681 FS_PROP_INTERACTIVE_ONLY,
682 g_param_spec_boolean ("interactive-only",
683 _("Interactive only"),
684 _("TRUE if this saver requires interaction"),
685 FALSE,
686 GSF_PARAM_STATIC |
687 G_PARAM_READWRITE));
688
689 /* What a load of crap! */
690 g_object_class_install_property
691 (goc,
692 FS_PROP_FORMAT_LEVEL,
693 g_param_spec_enum ("format-level",
694 _("Format Level"),
695 "?",
696 GO_TYPE_FILE_FORMAT_LEVEL,
697 GO_FILE_FL_NEW,
698 GSF_PARAM_STATIC |
699 G_PARAM_READWRITE));
700
701 g_object_class_install_property
702 (goc,
703 FS_PROP_SCOPE,
704 g_param_spec_enum ("scope",
705 _("Scope"),
706 _("How much of a document is saved"),
707 GO_TYPE_FILE_SAVE_SCOPE,
708 GO_FILE_SAVE_WORKBOOK,
709 GSF_PARAM_STATIC |
710 G_PARAM_READWRITE));
711
712 g_object_class_install_property
713 (goc,
714 FS_PROP_SHEET_SELECTION,
715 g_param_spec_boolean ("sheet-selection",
716 _("Sheet Selection"),
717 _("TRUE if this saver supports saving a subset of all sheet"),
718 FALSE,
719 GSF_PARAM_STATIC |
720 G_PARAM_READWRITE));
721
722 klass->save = go_file_saver_save_real;
723
724 /* class signals */
725 fs_signals[FS_SET_EXPORT_OPTIONS] =
726 g_signal_new ("set-export-options",
727 G_TYPE_FROM_CLASS (klass),
728 G_SIGNAL_RUN_LAST,
729 G_STRUCT_OFFSET (GOFileSaverClass, set_export_options),
730 NULL, NULL,
731 go__BOOLEAN__OBJECT_STRING_POINTER,
732 G_TYPE_BOOLEAN,
733 3, GO_TYPE_DOC, G_TYPE_STRING, G_TYPE_POINTER);
734 }
735
GSF_CLASS(GOFileSaver,go_file_saver,go_file_saver_class_init,go_file_saver_init,G_TYPE_OBJECT)736 GSF_CLASS (GOFileSaver, go_file_saver,
737 go_file_saver_class_init, go_file_saver_init,
738 G_TYPE_OBJECT)
739
740 /**
741 * go_file_saver_new:
742 * @id: (nullable): ID of the saver
743 * @extension: (nullable): Default extension of saved files
744 * @description: Description of supported file format
745 * @level: File format level
746 * @save_func: (scope async): Pointer to "save" function
747 *
748 * Creates new #GOFileSaver object. Optional @id will be used
749 * after registering it with go_file_saver_register or
750 * go_file_saver_register_as_default function.
751 *
752 * Returns: newly created #GOFileSaver object.
753 */
754 GOFileSaver *
755 go_file_saver_new (gchar const *id,
756 gchar const *extension,
757 gchar const *description,
758 GOFileFormatLevel level,
759 GOFileSaverSaveFunc save_func)
760 {
761 GOFileSaver *fs;
762
763 g_return_val_if_fail (description != NULL, NULL);
764
765 fs = GO_FILE_SAVER (g_object_new (GO_TYPE_FILE_SAVER,
766 "id", id,
767 "extension", extension,
768 "description", description,
769 "format-level", level,
770 NULL));
771 fs->save_func = save_func;
772
773 return fs;
774 }
775
776 void
go_file_saver_set_save_scope(GOFileSaver * fs,GOFileSaveScope scope)777 go_file_saver_set_save_scope (GOFileSaver *fs, GOFileSaveScope scope)
778 {
779 g_return_if_fail (GO_IS_FILE_SAVER (fs));
780 g_return_if_fail (scope < GO_FILE_SAVE_LAST);
781
782 fs->save_scope = scope;
783 }
784
785 /**
786 * go_file_saver_get_save_scope:
787 * @fs: #GOFileSaver to query
788 *
789 * Returns: The save scope of @fs.
790 */
791 GOFileSaveScope
go_file_saver_get_save_scope(GOFileSaver const * fs)792 go_file_saver_get_save_scope (GOFileSaver const *fs)
793 {
794 g_return_val_if_fail (GO_IS_FILE_SAVER (fs), GO_FILE_SAVE_WORKBOOK);
795
796 return fs->save_scope;
797 }
798
799 /**
800 * go_file_saver_get_id:
801 * @fs: #GOFileSaver to query
802 *
803 * Returns: (nullable) (transfer none): The id of @fs.
804 */
805 gchar const *
go_file_saver_get_id(GOFileSaver const * fs)806 go_file_saver_get_id (GOFileSaver const *fs)
807 {
808 g_return_val_if_fail (GO_IS_FILE_SAVER (fs), NULL);
809
810 return fs->id;
811 }
812
813 /**
814 * go_file_saver_get_mime_type:
815 * @fs: #GOFileSaver to query
816 *
817 * Returns: (nullable) (transfer none): The mime type of the @fs.
818 */
819 gchar const *
go_file_saver_get_mime_type(GOFileSaver const * fs)820 go_file_saver_get_mime_type (GOFileSaver const *fs)
821 {
822 g_return_val_if_fail (GO_IS_FILE_SAVER (fs), NULL);
823
824 return fs->mime_type;
825 }
826
827 /**
828 * go_file_saver_get_extension:
829 * @fs: #GOFileSaver to query
830 *
831 * Returns: (nullable) (transfer none): The default extensions for files saved
832 * by @fs.
833 */
834 gchar const *
go_file_saver_get_extension(GOFileSaver const * fs)835 go_file_saver_get_extension (GOFileSaver const *fs)
836 {
837 g_return_val_if_fail (GO_IS_FILE_SAVER (fs), NULL);
838
839 return fs->extension;
840 }
841
842 /**
843 * go_file_saver_get_description:
844 * @fs: #GOFileSaver to query
845 *
846 * Returns: (transfer none): The description of @fs.
847 */
848 gchar const *
go_file_saver_get_description(GOFileSaver const * fs)849 go_file_saver_get_description (GOFileSaver const *fs)
850 {
851 g_return_val_if_fail (GO_IS_FILE_SAVER (fs), NULL);
852
853 return fs->description;
854 }
855
856 /**
857 * go_file_saver_get_format_level:
858 * @fs: #GOFileSaver to query
859 *
860 * Returns: The format level of @fs.
861 */
862 GOFileFormatLevel
go_file_saver_get_format_level(GOFileSaver const * fs)863 go_file_saver_get_format_level (GOFileSaver const *fs)
864 {
865 g_return_val_if_fail (GO_IS_FILE_SAVER (fs), GO_FILE_FL_NEW);
866
867 return fs->format_level;
868 }
869
870 gboolean
go_file_saver_set_export_options(GOFileSaver * fs,GODoc * doc,const char * options,GError ** err)871 go_file_saver_set_export_options (GOFileSaver *fs,
872 GODoc *doc,
873 const char *options,
874 GError **err)
875 {
876 gboolean res = TRUE;
877
878 g_signal_emit (G_OBJECT (fs), fs_signals[FS_SET_EXPORT_OPTIONS], 0,
879 doc, options, err, &res);
880 return res;
881 }
882
883 /**
884 * go_file_saver_save:
885 * @fs: GOFileSaver object
886 * @io_context: Context for i/o operation
887 * @view: #GoView
888 * @output: Output stream
889 *
890 * Saves @wbv and the workbook it is attached to into @output stream.
891 * Results are reported using @io_context object, use
892 * go_io_error_occurred to find out if operation was successful.
893 * It's possible that @file_name is created and contain some data if
894 * operation fails, you should remove the file in that case.
895 */
896 void
go_file_saver_save(GOFileSaver const * fs,GOIOContext * io_context,GoView * view,GsfOutput * output)897 go_file_saver_save (GOFileSaver const *fs, GOIOContext *io_context,
898 GoView *view, GsfOutput *output)
899 {
900 g_return_if_fail (GO_IS_FILE_SAVER (fs));
901 g_return_if_fail (GSF_IS_OUTPUT (output));
902
903 if (GSF_IS_OUTPUT_STDIO (output)) {
904 const char *name = gsf_output_name (output);
905 /*
906 * This is mostly bogus. We need a proper source for the file
907 * name, not something double-converted.
908 */
909 char *file_name = name
910 ? g_filename_from_utf8 (name, -1, NULL, NULL, NULL)
911 : NULL;
912
913 /* If we don't have a filename, silently skip the test. */
914 if (!fs->overwrite_files &&
915 file_name &&
916 g_file_test (file_name, G_FILE_TEST_EXISTS)) {
917 GOErrorInfo *save_error;
918 const char *msg = _("Saving over old files of this type is disabled for safety.");
919
920 if (!gsf_output_error (output))
921 gsf_output_set_error (output, 0, "%s", msg);
922
923 g_free (file_name);
924
925 save_error = go_error_info_new_str_with_details (
926 msg,
927 go_error_info_new_str (
928 _("You can turn this safety feature off by editing appropriate plugin.xml file.")));
929 go_io_error_info_set (io_context, save_error);
930 return;
931 }
932
933 g_free (file_name);
934 }
935
936 GO_FILE_SAVER_METHOD (fs, save) (fs, io_context, view, output);
937 }
938
939 /**
940 * go_file_saver_set_overwrite_files:
941 * @fs: #GOFileSaver object
942 * @overwrite: A boolean value saying whether the saver should overwrite
943 * existing files.
944 *
945 * Changes behaviour of the saver when saving a file. If @overwrite is set
946 * to TRUE, existing file will be overwritten. Otherwise, the saver will
947 * report an error without saving anything.
948 */
949 void
go_file_saver_set_overwrite_files(GOFileSaver * fs,gboolean overwrite)950 go_file_saver_set_overwrite_files (GOFileSaver *fs, gboolean overwrite)
951 {
952 g_return_if_fail (GO_IS_FILE_SAVER (fs));
953
954 fs->overwrite_files = overwrite;
955 }
956
957 GType
go_file_format_level_get_type(void)958 go_file_format_level_get_type (void)
959 {
960 static GType etype = 0;
961 if (etype == 0) {
962 static GEnumValue values[] = {
963 { GO_FILE_FL_NONE, (char*)"GO_FILE_FL_NONE", (char*)"none" },
964 { GO_FILE_FL_WRITE_ONLY, (char*)"GO_FILE_FL_WRITE_ONLY", (char*)"write_only" },
965 { GO_FILE_FL_NEW, (char*)"GO_FILE_FL_NEW", (char*)"new" },
966 { GO_FILE_FL_MANUAL, (char*)"GO_FILE_FL_MANUAL", (char*)"manual" },
967 { GO_FILE_FL_MANUAL_REMEMBER, (char*)"GO_FL_MANUAL_REMEMBER", (char*)"manual_remember" },
968 { GO_FILE_FL_AUTO, (char*)"GO_FILE_FL_AUTO", (char*)"auto" },
969 { 0, NULL, NULL }
970 };
971 etype = g_enum_register_static ("GOFileFormatLevel", values);
972 }
973 return etype;
974 }
975
976 GType
go_file_save_scope_get_type(void)977 go_file_save_scope_get_type (void)
978 {
979 static GType etype = 0;
980 if (etype == 0) {
981 static GEnumValue values[] = {
982 { GO_FILE_SAVE_WORKBOOK, (char*)"GO_FILE_SAVE_WORKBOOK", (char*)"workbook" },
983 { GO_FILE_SAVE_SHEET, (char*)"GO_FILE_SAVE_SHEET", (char*)"sheet" },
984 { GO_FILE_SAVE_RANGE, (char*)"GO_FILE_SAVE_RANGE", (char*)"range" },
985 { 0, NULL, NULL }
986 };
987 etype = g_enum_register_static ("GOFileSaveScope", values);
988 }
989 return etype;
990 }
991
992 /*-------------------------------------------------------------------------- */
993
994
995 /*
996 * ------
997 */
998
999 typedef struct {
1000 gint priority;
1001 GOFileSaver *saver;
1002 } DefaultFileSaver;
1003
1004 static GHashTable *file_opener_id_hash = NULL,
1005 *file_saver_id_hash = NULL;
1006 static GList *file_opener_list = NULL, *file_opener_priority_list = NULL;
1007 static GList *file_saver_list = NULL, *default_file_saver_list = NULL;
1008
1009 static gint
cmp_int_less_than(gconstpointer list_i,gconstpointer i)1010 cmp_int_less_than (gconstpointer list_i, gconstpointer i)
1011 {
1012 return !(GPOINTER_TO_INT (list_i) < GPOINTER_TO_INT (i));
1013 }
1014
1015 /**
1016 * go_file_opener_register:
1017 * @fo: GOFileOpener object
1018 * @priority: Opener's priority
1019 *
1020 * Adds @fo opener to the list of available file openers, making it
1021 * available for Gnumeric i/o routines. The opener is registered with given
1022 * @priority. The priority is used to determine the order in which openers
1023 * will be tried when reading a file. The higher the priority, the sooner it
1024 * will be tried. Default XML-based Gnumeric file opener is registered at
1025 * priority 50. Recommended range for @priority is [0, 100].
1026 * Reference count for the opener is incremented inside the function, but
1027 * you don't have to (and shouldn't) call g_object_unref on it if it's
1028 * floating object (for example, when you pass object newly created with
1029 * go_file_opener_new and not referenced anywhere).
1030 */
1031 void
go_file_opener_register(GOFileOpener * fo,gint priority)1032 go_file_opener_register (GOFileOpener *fo, gint priority)
1033 {
1034 gint pos;
1035 gchar const *id;
1036
1037 g_return_if_fail (GO_IS_FILE_OPENER (fo));
1038 g_return_if_fail (priority >=0 && priority <= 100);
1039
1040 pos = go_list_index_custom (file_opener_priority_list,
1041 GINT_TO_POINTER (priority),
1042 cmp_int_less_than);
1043 file_opener_priority_list = g_list_insert (
1044 file_opener_priority_list,
1045 GINT_TO_POINTER (priority), pos);
1046 file_opener_list = g_list_insert (file_opener_list, fo, pos);
1047 g_object_ref (fo);
1048
1049 id = go_file_opener_get_id (fo);
1050 if (id != NULL) {
1051 if (file_opener_id_hash == NULL)
1052 file_opener_id_hash = g_hash_table_new (&g_str_hash, &g_str_equal);
1053 g_hash_table_insert (file_opener_id_hash, (gpointer) id, fo);
1054 }
1055 }
1056
1057 /**
1058 * go_file_opener_unregister:
1059 * @fo: #GOFileOpener object previously registered using
1060 * go_file_opener_register
1061 *
1062 * Removes @fo opener from list of available file openers. Reference count
1063 * for the opener is decremented inside the function.
1064 */
1065 void
go_file_opener_unregister(GOFileOpener * fo)1066 go_file_opener_unregister (GOFileOpener *fo)
1067 {
1068 gint pos;
1069 GList *l;
1070 gchar const *id;
1071
1072 g_return_if_fail (GO_IS_FILE_OPENER (fo));
1073
1074 pos = g_list_index (file_opener_list, fo);
1075 g_return_if_fail (pos != -1);
1076 l = g_list_nth (file_opener_list, pos);
1077 file_opener_list = g_list_remove_link (file_opener_list, l);
1078 g_list_free_1 (l);
1079 l = g_list_nth (file_opener_priority_list, pos);
1080 file_opener_priority_list = g_list_remove_link (file_opener_priority_list, l);
1081 g_list_free_1 (l);
1082
1083 id = go_file_opener_get_id (fo);
1084 if (id != NULL) {
1085 g_hash_table_remove (file_opener_id_hash, (gpointer) id);
1086 if (g_hash_table_size (file_opener_id_hash) == 0) {
1087 g_hash_table_destroy (file_opener_id_hash);
1088 file_opener_id_hash = NULL;
1089 }
1090 }
1091
1092 g_object_unref (fo);
1093 }
1094
1095 static gint
default_file_saver_cmp_priority(gconstpointer a,gconstpointer b)1096 default_file_saver_cmp_priority (gconstpointer a, gconstpointer b)
1097 {
1098 DefaultFileSaver const *dfs_a = a, *dfs_b = b;
1099
1100 return dfs_b->priority - dfs_a->priority;
1101 }
1102
1103 /**
1104 * go_file_saver_register:
1105 * @fs: #GOFileSaver object
1106 *
1107 * Adds @fs saver to the list of available file savers, making it
1108 * available for the user when selecting file format for save.
1109 */
1110 void
go_file_saver_register(GOFileSaver * fs)1111 go_file_saver_register (GOFileSaver *fs)
1112 {
1113 gchar const *id;
1114
1115 g_return_if_fail (GO_IS_FILE_SAVER (fs));
1116
1117 file_saver_list = g_list_prepend (file_saver_list, fs);
1118 g_object_ref (fs);
1119
1120 id = go_file_saver_get_id (fs);
1121 if (id != NULL) {
1122 if (file_saver_id_hash == NULL)
1123 file_saver_id_hash = g_hash_table_new (&g_str_hash,
1124 &g_str_equal);
1125 g_hash_table_insert (file_saver_id_hash, (gpointer) id, fs);
1126 }
1127 }
1128
1129 /**
1130 * go_file_saver_register_as_default:
1131 * @fs: GOFileSaver object
1132 * @priority: Saver's priority
1133 *
1134 * Adds @fs saver to the list of available file savers, making it
1135 * available for the user when selecting file format for save.
1136 * The saver is also marked as default saver with given priority.
1137 * When Gnumeric needs default file saver, it chooses the one with the
1138 * highest priority. Recommended range for @priority is [0, 100].
1139 */
1140 void
go_file_saver_register_as_default(GOFileSaver * fs,gint priority)1141 go_file_saver_register_as_default (GOFileSaver *fs, gint priority)
1142 {
1143 DefaultFileSaver *dfs;
1144
1145 g_return_if_fail (GO_IS_FILE_SAVER (fs));
1146 g_return_if_fail (priority >=0 && priority <= 100);
1147
1148 go_file_saver_register (fs);
1149
1150 dfs = g_new (DefaultFileSaver, 1);
1151 dfs->priority = priority;
1152 dfs->saver = fs;
1153 default_file_saver_list = g_list_insert_sorted (
1154 default_file_saver_list, dfs,
1155 default_file_saver_cmp_priority);
1156 }
1157
1158 /**
1159 * go_file_saver_unregister:
1160 * @fs: #GOFileSaver object previously registered using
1161 * go_file_saver_register or go_file_saver_register_as_default
1162 *
1163 * Removes @fs saver from list of available file savers. Reference count
1164 * for the saver is decremented inside the function.
1165 **/
1166 void
go_file_saver_unregister(GOFileSaver * fs)1167 go_file_saver_unregister (GOFileSaver *fs)
1168 {
1169 GList *l;
1170 gchar const *id;
1171
1172 g_return_if_fail (GO_IS_FILE_SAVER (fs));
1173
1174 l = g_list_find (file_saver_list, fs);
1175 g_return_if_fail (l != NULL);
1176 file_saver_list = g_list_remove_link (file_saver_list, l);
1177 g_list_free_1 (l);
1178
1179 id = go_file_saver_get_id (fs);
1180 if (id != NULL) {
1181 g_hash_table_remove (file_saver_id_hash, (gpointer) id);
1182 if (g_hash_table_size (file_saver_id_hash) == 0) {
1183 g_hash_table_destroy (file_saver_id_hash);
1184 file_saver_id_hash = NULL;
1185 }
1186 }
1187
1188 for (l = default_file_saver_list; l != NULL; l = l->next) {
1189 if (((DefaultFileSaver *) l->data)->saver == fs) {
1190 default_file_saver_list = g_list_remove_link (default_file_saver_list, l);
1191 g_free (l->data);
1192 g_list_free_1 (l);
1193 break;
1194 }
1195 }
1196
1197 g_object_unref (fs);
1198 }
1199
1200 /**
1201 * go_file_saver_get_default:
1202 *
1203 * Finds file saver registered as default saver with the highest priority.
1204 * Reference count for the saver is NOT incremented.
1205 *
1206 * Returns: (transfer none) (nullable): #GOFileSaver for the highest priority
1207 * default saver.
1208 **/
1209 GOFileSaver *
go_file_saver_get_default(void)1210 go_file_saver_get_default (void)
1211 {
1212 if (default_file_saver_list == NULL)
1213 return NULL;
1214
1215 return ((DefaultFileSaver *) default_file_saver_list->data)->saver;
1216 }
1217
1218 /**
1219 * go_file_saver_for_mime_type:
1220 * @mime_type: A mime type
1221 *
1222 * Returns: (transfer none) (nullable): A #GOFileSaver object associated with
1223 * @mime_type.
1224 **/
1225 GOFileSaver *
go_file_saver_for_mime_type(gchar const * mime_type)1226 go_file_saver_for_mime_type (gchar const *mime_type)
1227 {
1228 GList *l;
1229
1230 g_return_val_if_fail (mime_type != NULL, NULL);
1231
1232 for (l = default_file_saver_list ; l != NULL; l = l->next) {
1233 DefaultFileSaver *dfs = l->data;
1234 GOFileSaver *fs = dfs->saver;
1235 const char *this_type = go_file_saver_get_mime_type (fs);
1236 if (this_type && !strcmp (this_type, mime_type))
1237 return fs;
1238 }
1239
1240 for (l = file_saver_list; l != NULL; l = l->next) {
1241 GOFileSaver *fs = l->data;
1242 const char *this_type = go_file_saver_get_mime_type (fs);
1243 if (this_type && !strcmp (this_type, mime_type))
1244 return fs;
1245 }
1246
1247 return NULL;
1248 }
1249
1250 /**
1251 * go_file_saver_for_file_name:
1252 * @file_name: name
1253 *
1254 * Searches for file saver matching the given @file_name, registered using
1255 * go_file_saver_register.
1256 *
1257 * Returns: (transfer none) (nullable): #GOFileSaver for @file_name
1258 **/
1259 GOFileSaver *
go_file_saver_for_file_name(char const * file_name)1260 go_file_saver_for_file_name (char const *file_name)
1261 {
1262 GList *l;
1263 GOFileSaver *best_saver = NULL;
1264 char const *extension = gsf_extension_pointer (file_name);
1265
1266 for (l = default_file_saver_list; l != NULL; l = l->next) {
1267 DefaultFileSaver *def_saver = l->data;
1268 GOFileSaver *saver = def_saver->saver;
1269 if (g_strcmp0 (go_file_saver_get_extension (saver), extension))
1270 continue;
1271 return saver;
1272 }
1273
1274 for (l = file_saver_list; l != NULL; l = l->next) {
1275 GOFileSaver *saver = l->data;
1276 if (g_strcmp0 (go_file_saver_get_extension (saver), extension))
1277 continue;
1278
1279 if (!best_saver ||
1280 (saver->save_scope < best_saver->save_scope))
1281 best_saver = saver;
1282 }
1283
1284 return best_saver;
1285 }
1286
1287 /**
1288 * go_file_opener_for_id:
1289 * @id: File opener's ID
1290 *
1291 * Searches for file opener with given @id, registered using
1292 * go_file_opener_register
1293 *
1294 * Returns: (transfer none) (nullable): #GOFileOpener with given id.
1295 **/
1296 GOFileOpener *
go_file_opener_for_id(gchar const * id)1297 go_file_opener_for_id (gchar const *id)
1298 {
1299 g_return_val_if_fail (id != NULL, NULL);
1300
1301 if (file_opener_id_hash == NULL)
1302 return NULL;
1303 return GO_FILE_OPENER (g_hash_table_lookup (file_opener_id_hash, id));
1304 }
1305
1306 /**
1307 * go_file_saver_for_id:
1308 * @id: File saver's ID
1309 *
1310 * Searches for file saver with given @id, registered using
1311 * go_file_saver_register or register_file_opener_as_default.
1312 *
1313 * Returns: (transfer none) (nullable): #GOFileSaver with given id.
1314 **/
1315 GOFileSaver *
go_file_saver_for_id(gchar const * id)1316 go_file_saver_for_id (gchar const *id)
1317 {
1318 g_return_val_if_fail (id != NULL, NULL);
1319
1320 if (file_saver_id_hash == NULL)
1321 return NULL;
1322 return GO_FILE_SAVER (g_hash_table_lookup (file_saver_id_hash, id));
1323 }
1324
1325 /**
1326 * go_get_file_savers:
1327 *
1328 * Returns: (element-type GOFileSaver) (transfer none): list of known
1329 * #GOFileSaver types (do not modify list)
1330 */
1331 GList *
go_get_file_savers(void)1332 go_get_file_savers (void)
1333 {
1334 return file_saver_list;
1335 }
1336
1337 /**
1338 * go_get_file_openers:
1339 *
1340 * Returns: (element-type GOFileOpener) (transfer none): list of known
1341 * #GOFileOpener types (do not modify list)
1342 **/
1343 GList *
go_get_file_openers(void)1344 go_get_file_openers (void)
1345 {
1346 return file_opener_list;
1347 }
1348