1 /*
2 * This file is part of gedit
3 *
4 * Copyright (C) 2020 Sébastien Wilmet <swilmet@gnome.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (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, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "gedit-file-chooser.h"
21 #include <glib/gi18n.h>
22 #include "gedit-settings.h"
23
24 /* Common code between the different GeditFileChooser's. */
25
26 struct _GeditFileChooserPrivate
27 {
28 GtkFileChooser *gtk_chooser;
29 };
30
31 enum
32 {
33 SIGNAL_DONE,
34 N_SIGNALS
35 };
36
37 static guint signals[N_SIGNALS];
38
G_DEFINE_TYPE_WITH_PRIVATE(GeditFileChooser,_gedit_file_chooser,G_TYPE_OBJECT)39 G_DEFINE_TYPE_WITH_PRIVATE (GeditFileChooser, _gedit_file_chooser, G_TYPE_OBJECT)
40
41 #define ALL_FILES _("All Files")
42 #define ALL_TEXT_FILES _("All Text Files")
43
44 /* Whether to use GtkFileChooserNative or GtkFileChooserDialog. */
45 gboolean
46 _gedit_file_chooser_is_native (void)
47 {
48 /* TODO: finish the implementation of the native variants. */
49 return FALSE;
50 }
51
52 static gboolean
mime_types_are_supported(void)53 mime_types_are_supported (void)
54 {
55 /* Note that the #ifdef could be moved to where this function is called, to have
56 * much more code between the #ifdef/#else/#endif. The goal is to always compile
57 * all the code on all platforms, to catch compilation problems earlier.
58 */
59 #ifdef G_OS_WIN32
60 /* See the GtkFileChooserNative documentation, a GtkFileFilter with
61 * mime-types is not supported on Windows.
62 */
63 return FALSE;
64 #else
65 return TRUE;
66 #endif
67 }
68
69 /* Returns: (transfer none) (element-type utf8): a list containing "text/plain"
70 * first and then the list of mime-types unrelated to "text/plain" that
71 * GtkSourceView supports for the syntax highlighting.
72 */
73 static GSList *
get_supported_mime_types(void)74 get_supported_mime_types (void)
75 {
76 static GSList *supported_mime_types = NULL;
77 static gboolean initialized = FALSE;
78
79 GtkSourceLanguageManager *language_manager;
80 const gchar * const *language_ids;
81 gint language_num;
82
83 if (initialized)
84 {
85 return supported_mime_types;
86 }
87
88 language_manager = gtk_source_language_manager_get_default ();
89 language_ids = gtk_source_language_manager_get_language_ids (language_manager);
90 for (language_num = 0; language_ids != NULL && language_ids[language_num] != NULL; language_num++)
91 {
92 const gchar *cur_language_id = language_ids[language_num];
93 GtkSourceLanguage *language;
94 gchar **mime_types;
95 gint mime_type_num;
96
97 language = gtk_source_language_manager_get_language (language_manager, cur_language_id);
98 mime_types = gtk_source_language_get_mime_types (language);
99
100 if (mime_types == NULL)
101 {
102 continue;
103 }
104
105 for (mime_type_num = 0; mime_types[mime_type_num] != NULL; mime_type_num++)
106 {
107 const gchar *cur_mime_type = mime_types[mime_type_num];
108
109 if (!g_content_type_is_a (cur_mime_type, "text/plain"))
110 {
111 //g_message ("Mime-type '%s' is not related to 'text/plain'", cur_mime_type);
112 supported_mime_types = g_slist_prepend (supported_mime_types,
113 g_strdup (cur_mime_type));
114 }
115 }
116
117 g_strfreev (mime_types);
118 }
119
120 // Note that all "text/*" mime-types are subclasses of "text/plain", see:
121 // https://specifications.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html#subclassing
122 supported_mime_types = g_slist_prepend (supported_mime_types, g_strdup ("text/plain"));
123
124 initialized = TRUE;
125 return supported_mime_types;
126 }
127
128 static const gchar * const *
get_supported_globs(void)129 get_supported_globs (void)
130 {
131 /* List generated with Tepl's shared-mime-info-list-text-plain-globs
132 * tool.
133 *
134 * TODO: can be improved by including globs from GtkSourceLanguage's.
135 */
136 static const gchar * const supported_globs[] =
137 {
138 "*.abw",
139 "*.adb",
140 "*.ads",
141 "*.al",
142 "*.asc",
143 "*.asp",
144 "*.ass",
145 "*.atom",
146 "*.automount",
147 "*.awk",
148 "*.bib",
149 "*.build",
150 "*.c",
151 "*.c++",
152 "*.cbl",
153 "*.cc",
154 "*.ccmx",
155 "*.cl",
156 "*.cls",
157 "*.cmake",
158 "*.cob",
159 "*.coffee",
160 "*.cpp",
161 "*.cs",
162 "*.csh",
163 "*.css",
164 "*.csv",
165 "*.csvs",
166 "*.cue",
167 "*.cxx",
168 "*.d",
169 "*.dbk",
170 "*.dcl",
171 "*.desktop",
172 "*.device",
173 "*.di",
174 "*.dia",
175 "*.diff",
176 "*.docbook",
177 "*.dot",
178 "*.dsl",
179 "*.dtd",
180 "*.dtx",
181 "*.e",
182 "*.eif",
183 "*.el",
184 "*.eml",
185 "*.ent",
186 "*.eps",
187 "*.epsf",
188 "*.epsi",
189 "*.erl",
190 "*.es",
191 "*.etx",
192 "*.f",
193 "*.f90",
194 "*.f95",
195 "*.fb2",
196 "*.feature",
197 "*.fl",
198 "*.flatpakref",
199 "*.flatpakrepo",
200 "*.fo",
201 "*.fodg",
202 "*.fodp",
203 "*.fods",
204 "*.fodt",
205 "*.for",
206 "*.gcode",
207 "*.gcrd",
208 "*.geojson",
209 "*.glade",
210 "*.gml",
211 "*.gnuplot",
212 "*.go",
213 "*.gp",
214 "*.gpg",
215 "*.gplt",
216 "*.gpx",
217 "*.gradle",
218 "*.groovy",
219 "*.gs",
220 "*.gsf",
221 "*.gsh",
222 "*.gv",
223 "*.gvp",
224 "*.gvy",
225 "*.gy",
226 "*.h",
227 "*.h++",
228 "*.hh",
229 "*.hp",
230 "*.hpp",
231 "*.hs",
232 "*.htm",
233 "*.html",
234 "*.hxx",
235 "*.ica",
236 "*.ics",
237 "*.idl",
238 "*.iges",
239 "*.igs",
240 "*.ime",
241 "*.imy",
242 "*.ins",
243 "*.iptables",
244 "*.ipynb",
245 "*.it87",
246 "*.jad",
247 "*.java",
248 "*.jnlp",
249 "*.jrd",
250 "*.js",
251 "*.jsm",
252 "*.json",
253 "*.jsonld",
254 "*.json-patch",
255 "*.kdelnk",
256 "*.key",
257 "*.kino",
258 "*.kml",
259 "*.la",
260 "*.latex",
261 "*.ldif",
262 "*.lhs",
263 "*.log",
264 "*.ltx",
265 "*.lua",
266 "*.ly",
267 "*.lyx",
268 "*.m",
269 "*.m1u",
270 "*.m3u",
271 "*.m3u8",
272 "*.m4",
273 "*.m4u",
274 "*.mab",
275 "*.mak",
276 "*.man",
277 "*.manifest",
278 "*.markdown",
279 "*.mbox",
280 "*.mc2",
281 "*.md",
282 "*.me",
283 "*.meta4",
284 "*.metalink",
285 "*.mgp",
286 "*.mjs",
287 "*.mk",
288 "*.mkd",
289 "*.ml",
290 "*.mli",
291 "*.mm",
292 "*.mml",
293 "*.mo",
294 "*.moc",
295 "*.mof",
296 "*.mount",
297 "*.mrl",
298 "*.mrml",
299 "*.ms",
300 "*.mup",
301 "*.mxu",
302 "*.nb",
303 "*.nfo",
304 "*.not",
305 "*.nzb",
306 "*.ocl",
307 "*.ooc",
308 "*.opml",
309 "*.owl",
310 "*.owx",
311 "*.p",
312 "*.p7s",
313 "*.pas",
314 "*.patch",
315 "*.path",
316 "*.perl",
317 "*.pfa",
318 "*.pfb",
319 "*.pgn",
320 "*.pgp",
321 "*.php",
322 "*.php3",
323 "*.php4",
324 "*.php5",
325 "*.phps",
326 "*.pkr",
327 "*.pl",
328 "*.pm",
329 "*.po",
330 "*.pod",
331 "*.pot",
332 "*.ps",
333 "*.py",
334 "*.py3",
335 "*.py3x",
336 "*.pyx",
337 "*.qml",
338 "*.qmlproject",
339 "*.qmltypes",
340 "*.qti",
341 "*.raml",
342 "*.rb",
343 "*.rdf",
344 "*.rdfs",
345 "*.rej",
346 "*.rnc",
347 "*.rng",
348 "*.roff",
349 "*.rs",
350 "*.rss",
351 "*.rst",
352 "*.rt",
353 "*.rtf",
354 "*.rtx",
355 "*.sami",
356 "*.sass",
357 "*.scala",
358 "*.scm",
359 "*.scope",
360 "*.scss",
361 "*.sdp",
362 "*.service",
363 "*.sgf",
364 "*.sgm",
365 "*.sgml",
366 "*.sh",
367 "*.shape",
368 "*.sig",
369 "*.siv",
370 "*.skr",
371 "*.slice",
372 "*.slk",
373 "*.smi",
374 "*.smil",
375 "*.sml",
376 "*.socket",
377 "*.spec",
378 "*.sql",
379 "*.src",
380 "*.srt",
381 "*.ss",
382 "*.ssa",
383 "*.sty",
384 "*.sub",
385 "*.sv",
386 "*.svg",
387 "*.svh",
388 "*.swap",
389 "*.sylk",
390 "*.t",
391 "*.t2t",
392 "*.target",
393 "*.tcl",
394 "*.tex",
395 "*.texi",
396 "*.texinfo",
397 "*.theme",
398 "*.timer",
399 "*.tk",
400 "*.toc",
401 "*.tr",
402 "*.trig",
403 "*.ts",
404 "*.tsv",
405 "*.ttl",
406 "*.ttx",
407 "*.twig",
408 "*.txt",
409 "*.ufraw",
410 "*.ui",
411 "*.uil",
412 "*.uue",
413 "*.v",
414 "*.vala",
415 "*.vapi",
416 "*.vcard",
417 "*.vcf",
418 "*.vcs",
419 "*.vct",
420 "*.vhd",
421 "*.vhdl",
422 "*.vlc",
423 "*.vrm",
424 "*.vrml",
425 "*.vtt",
426 "*.wml",
427 "*.wmls",
428 "*.wrl",
429 "*.wsgi",
430 "*.xbel",
431 "*.xbl",
432 "*.xht",
433 "*.xhtml",
434 "*.xlf",
435 "*.xliff",
436 "*.xmi",
437 "*.xml",
438 "*.xsd",
439 "*.xsl",
440 "*.xslfo",
441 "*.xslt",
442 "*.xspf",
443 "*.xul",
444 "*.yaml",
445 "*.yml",
446 "*.zabw",
447 NULL
448 };
449
450 return supported_globs;
451 }
452
453 static GtkFileFilter *
create_all_text_files_filter(void)454 create_all_text_files_filter (void)
455 {
456 GtkFileFilter *filter;
457
458 filter = gtk_file_filter_new ();
459 gtk_file_filter_set_name (filter, ALL_TEXT_FILES);
460
461 if (mime_types_are_supported ())
462 {
463 GSList *supported_mime_types;
464 GSList *l;
465
466 supported_mime_types = get_supported_mime_types ();
467 for (l = supported_mime_types; l != NULL; l = l->next)
468 {
469 const gchar *mime_type = l->data;
470
471 gtk_file_filter_add_mime_type (filter, mime_type);
472 }
473
474 /* FIXME: use globs too - Paolo (Aug. 27, 2007).
475 *
476 * Yes, use globs too - swilmet (June 2020). Get the list of
477 * globs from GtkSourceLanguage's, and add them to the filter
478 * (only those that are not covered by a previously added
479 * mime-type). Note that the list of extra globs here must be
480 * computed differently than the list of extra globs for
481 * get_supported_globs() (see the TODO comment there).
482 */
483 }
484 else
485 {
486 const gchar * const *supported_globs;
487 gint i;
488
489 supported_globs = get_supported_globs ();
490 for (i = 0; supported_globs != NULL && supported_globs[i] != NULL; i++)
491 {
492 const gchar *glob = supported_globs[i];
493
494 gtk_file_filter_add_pattern (filter, glob);
495 }
496 }
497
498 return filter;
499 }
500
501 static void
notify_filter_cb(GtkFileChooser * gtk_chooser,GParamSpec * pspec,gpointer user_data)502 notify_filter_cb (GtkFileChooser *gtk_chooser,
503 GParamSpec *pspec,
504 gpointer user_data)
505 {
506 GtkFileFilter *filter;
507 const gchar *name;
508 gint id = 0;
509 GeditSettings *settings;
510 GSettings *file_chooser_state_settings;
511
512 /* Remember the selected filter. */
513
514 filter = gtk_file_chooser_get_filter (gtk_chooser);
515 if (filter == NULL)
516 {
517 return;
518 }
519
520 name = gtk_file_filter_get_name (filter);
521 if (g_strcmp0 (name, ALL_FILES) == 0)
522 {
523 id = 1;
524 }
525
526 settings = _gedit_settings_get_singleton ();
527 file_chooser_state_settings = _gedit_settings_peek_file_chooser_state_settings (settings);
528 g_settings_set_int (file_chooser_state_settings, GEDIT_SETTINGS_ACTIVE_FILE_FILTER, id);
529 }
530
531 static void
setup_filters(GeditFileChooser * chooser)532 setup_filters (GeditFileChooser *chooser)
533 {
534 GeditSettings *settings;
535 GSettings *file_chooser_state_settings;
536 gint active_filter;
537 GtkFileFilter *filter;
538
539 settings = _gedit_settings_get_singleton ();
540 file_chooser_state_settings = _gedit_settings_peek_file_chooser_state_settings (settings);
541 active_filter = g_settings_get_int (file_chooser_state_settings, GEDIT_SETTINGS_ACTIVE_FILE_FILTER);
542
543 /* "All Text Files" filter */
544 filter = create_all_text_files_filter ();
545
546 g_object_ref_sink (filter);
547 gtk_file_chooser_add_filter (chooser->priv->gtk_chooser, filter);
548 if (active_filter != 1)
549 {
550 /* Use this filter if set by user and as default. */
551 gtk_file_chooser_set_filter (chooser->priv->gtk_chooser, filter);
552 }
553 g_object_unref (filter);
554
555 /* "All Files" filter */
556 filter = gtk_file_filter_new ();
557 gtk_file_filter_set_name (filter, ALL_FILES);
558 gtk_file_filter_add_pattern (filter, "*");
559
560 g_object_ref_sink (filter);
561 gtk_file_chooser_add_filter (chooser->priv->gtk_chooser, filter);
562 if (active_filter == 1)
563 {
564 /* Use this filter if set by user. */
565 gtk_file_chooser_set_filter (chooser->priv->gtk_chooser, filter);
566 }
567 g_object_unref (filter);
568
569 g_signal_connect (chooser->priv->gtk_chooser,
570 "notify::filter",
571 G_CALLBACK (notify_filter_cb),
572 NULL);
573 }
574
575 /* Set the dialog as modal. It's a workaround for this bug:
576 * https://gitlab.gnome.org/GNOME/gtk/issues/2824
577 * "GtkNativeDialog: non-modal and gtk_native_dialog_show(), doesn't present the
578 * window"
579 *
580 * - For opening files: drag-and-drop files from the file chooser to the
581 * GeditWindow: OK, it still works.
582 * - Other main windows not being "blocked"/insensitive (GtkWindowGroup): OK,
583 * calling gtk_native_dialog_set_transient_for() does the right thing.
584 *
585 * Even if the above GTK bug is fixed, the file chooser can be kept modal,
586 * except if there was a good reason for not being modal (what reason(s)?).
587 */
588 static void
set_modal(GeditFileChooser * chooser)589 set_modal (GeditFileChooser *chooser)
590 {
591 if (_gedit_file_chooser_is_native ())
592 {
593 gtk_native_dialog_set_modal (GTK_NATIVE_DIALOG (chooser->priv->gtk_chooser), TRUE);
594 }
595 else
596 {
597 gtk_window_set_modal (GTK_WINDOW (chooser->priv->gtk_chooser), TRUE);
598 }
599 }
600
601 static void
response_cb(GtkFileChooser * gtk_chooser,gint response_id,GeditFileChooser * chooser)602 response_cb (GtkFileChooser *gtk_chooser,
603 gint response_id,
604 GeditFileChooser *chooser)
605 {
606 gboolean accept;
607
608 accept = response_id == GTK_RESPONSE_ACCEPT;
609 g_signal_emit (chooser, signals[SIGNAL_DONE], 0, accept);
610 }
611
612 static void
_gedit_file_chooser_constructed(GObject * object)613 _gedit_file_chooser_constructed (GObject *object)
614 {
615 GeditFileChooser *chooser = GEDIT_FILE_CHOOSER (object);
616 GeditFileChooserClass *klass = GEDIT_FILE_CHOOSER_GET_CLASS (chooser);
617
618 if (G_OBJECT_CLASS (_gedit_file_chooser_parent_class)->constructed != NULL)
619 {
620 G_OBJECT_CLASS (_gedit_file_chooser_parent_class)->constructed (object);
621 }
622
623 if (klass->create_gtk_file_chooser != NULL)
624 {
625 g_return_if_fail (chooser->priv->gtk_chooser == NULL);
626 chooser->priv->gtk_chooser = klass->create_gtk_file_chooser (chooser);
627
628 setup_filters (chooser);
629 set_modal (chooser);
630 gtk_file_chooser_set_local_only (chooser->priv->gtk_chooser, FALSE);
631
632 g_signal_connect_object (chooser->priv->gtk_chooser,
633 "response",
634 G_CALLBACK (response_cb),
635 chooser,
636 0);
637 }
638 }
639
640 static void
_gedit_file_chooser_dispose(GObject * object)641 _gedit_file_chooser_dispose (GObject *object)
642 {
643 GeditFileChooser *chooser = GEDIT_FILE_CHOOSER (object);
644
645 if (chooser->priv->gtk_chooser != NULL)
646 {
647 if (_gedit_file_chooser_is_native ())
648 {
649 g_object_unref (chooser->priv->gtk_chooser);
650 }
651 else
652 {
653 gtk_widget_destroy (GTK_WIDGET (chooser->priv->gtk_chooser));
654 }
655
656 chooser->priv->gtk_chooser = NULL;
657 }
658
659 G_OBJECT_CLASS (_gedit_file_chooser_parent_class)->dispose (object);
660 }
661
662 static void
_gedit_file_chooser_class_init(GeditFileChooserClass * klass)663 _gedit_file_chooser_class_init (GeditFileChooserClass *klass)
664 {
665 GObjectClass *object_class = G_OBJECT_CLASS (klass);
666
667 object_class->constructed = _gedit_file_chooser_constructed;
668 object_class->dispose = _gedit_file_chooser_dispose;
669
670 /*
671 * GeditFileChooser::done:
672 * @chooser: the #GeditFileChooser emitting the signal.
673 * @accept: whether the response code is %GTK_RESPONSE_ACCEPT.
674 */
675 signals[SIGNAL_DONE] =
676 g_signal_new ("done",
677 G_TYPE_FROM_CLASS (klass),
678 G_SIGNAL_RUN_FIRST,
679 0, NULL, NULL, NULL,
680 G_TYPE_NONE,
681 1, G_TYPE_BOOLEAN);
682 }
683
684 static void
_gedit_file_chooser_init(GeditFileChooser * chooser)685 _gedit_file_chooser_init (GeditFileChooser *chooser)
686 {
687 chooser->priv = _gedit_file_chooser_get_instance_private (chooser);
688 }
689
690 GeditFileChooser *
_gedit_file_chooser_new(void)691 _gedit_file_chooser_new (void)
692 {
693 return g_object_new (GEDIT_TYPE_FILE_CHOOSER, NULL);
694 }
695
696 /* TODO: this function will go away. */
697 void
_gedit_file_chooser_set_gtk_file_chooser(GeditFileChooser * chooser,GtkFileChooser * gtk_chooser)698 _gedit_file_chooser_set_gtk_file_chooser (GeditFileChooser *chooser,
699 GtkFileChooser *gtk_chooser)
700 {
701 g_return_if_fail (GEDIT_IS_FILE_CHOOSER (chooser));
702 g_return_if_fail (GTK_IS_FILE_CHOOSER (gtk_chooser));
703 g_return_if_fail (chooser->priv->gtk_chooser == NULL);
704
705 chooser->priv->gtk_chooser = g_object_ref_sink (gtk_chooser);
706 setup_filters (chooser);
707 }
708
709 GtkFileChooser *
_gedit_file_chooser_get_gtk_file_chooser(GeditFileChooser * chooser)710 _gedit_file_chooser_get_gtk_file_chooser (GeditFileChooser *chooser)
711 {
712 g_return_val_if_fail (GEDIT_IS_FILE_CHOOSER (chooser), NULL);
713 return chooser->priv->gtk_chooser;
714 }
715
716 void
_gedit_file_chooser_set_transient_for(GeditFileChooser * chooser,GtkWindow * parent)717 _gedit_file_chooser_set_transient_for (GeditFileChooser *chooser,
718 GtkWindow *parent)
719 {
720 g_return_if_fail (GEDIT_IS_FILE_CHOOSER (chooser));
721 g_return_if_fail (parent == NULL || GTK_IS_WINDOW (parent));
722
723 if (_gedit_file_chooser_is_native ())
724 {
725 gtk_native_dialog_set_transient_for (GTK_NATIVE_DIALOG (chooser->priv->gtk_chooser), parent);
726 }
727 else
728 {
729 gtk_window_set_transient_for (GTK_WINDOW (chooser->priv->gtk_chooser), parent);
730
731 if (parent != NULL)
732 {
733 gtk_window_set_destroy_with_parent (GTK_WINDOW (chooser->priv->gtk_chooser), TRUE);
734 }
735 }
736 }
737
738 void
_gedit_file_chooser_show(GeditFileChooser * chooser)739 _gedit_file_chooser_show (GeditFileChooser *chooser)
740 {
741 g_return_if_fail (GEDIT_IS_FILE_CHOOSER (chooser));
742
743 if (_gedit_file_chooser_is_native ())
744 {
745 gtk_native_dialog_show (GTK_NATIVE_DIALOG (chooser->priv->gtk_chooser));
746 }
747 else
748 {
749 gtk_window_present (GTK_WINDOW (chooser->priv->gtk_chooser));
750 }
751 }
752
753 gchar *
_gedit_file_chooser_get_current_folder_uri(GeditFileChooser * chooser)754 _gedit_file_chooser_get_current_folder_uri (GeditFileChooser *chooser)
755 {
756 g_return_val_if_fail (GEDIT_IS_FILE_CHOOSER (chooser), NULL);
757
758 return gtk_file_chooser_get_current_folder_uri (chooser->priv->gtk_chooser);
759 }
760
761 void
_gedit_file_chooser_set_current_folder_uri(GeditFileChooser * chooser,const gchar * uri)762 _gedit_file_chooser_set_current_folder_uri (GeditFileChooser *chooser,
763 const gchar *uri)
764 {
765 g_return_if_fail (GEDIT_IS_FILE_CHOOSER (chooser));
766
767 gtk_file_chooser_set_current_folder_uri (chooser->priv->gtk_chooser, uri);
768 }
769
770 const GtkSourceEncoding *
_gedit_file_chooser_get_encoding(GeditFileChooser * chooser)771 _gedit_file_chooser_get_encoding (GeditFileChooser *chooser)
772 {
773 GeditFileChooserClass *klass;
774
775 g_return_val_if_fail (GEDIT_IS_FILE_CHOOSER (chooser), NULL);
776
777 klass = GEDIT_FILE_CHOOSER_GET_CLASS (chooser);
778 g_return_val_if_fail (klass->get_encoding != NULL, NULL);
779
780 return klass->get_encoding (chooser);
781 }
782