1 /* GTK - The GIMP Toolkit
2 * gtkfilefilter.c: Filters for selecting a file subset
3 * Copyright (C) 2003, Red Hat, Inc.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /**
20 * SECTION:gtkfilefilter
21 * @Short_description: A filter for selecting a file subset
22 * @Title: GtkFileFilter
23 * @see_also: #GtkFileChooser
24 *
25 * A GtkFileFilter can be used to restrict the files being shown in a
26 * #GtkFileChooser. Files can be filtered based on their name (with
27 * gtk_file_filter_add_pattern()), on their mime type (with
28 * gtk_file_filter_add_mime_type()), or by a custom filter function
29 * (with gtk_file_filter_add_custom()).
30 *
31 * Filtering by mime types handles aliasing and subclassing of mime
32 * types; e.g. a filter for text/plain also matches a file with mime
33 * type application/rtf, since application/rtf is a subclass of
34 * text/plain. Note that #GtkFileFilter allows wildcards for the
35 * subtype of a mime type, so you can e.g. filter for image/\*.
36 *
37 * Normally, filters are used by adding them to a #GtkFileChooser,
38 * see gtk_file_chooser_add_filter(), but it is also possible
39 * to manually use a filter on a file with gtk_file_filter_filter().
40 *
41 * # GtkFileFilter as GtkBuildable
42 *
43 * The GtkFileFilter implementation of the GtkBuildable interface
44 * supports adding rules using the `<mime-types>`, `<patterns>` and
45 * `<applications>` elements and listing the rules within. Specifying
46 * a `<mime-type>` or `<pattern>` has the same effect as as calling
47 * gtk_file_filter_add_mime_type() or gtk_file_filter_add_pattern().
48 *
49 * An example of a UI definition fragment specifying GtkFileFilter
50 * rules:
51 *
52 * |[<!-- language="xml" -->
53 * <object class="GtkFileFilter">
54 * <mime-types>
55 * <mime-type>text/plain</mime-type>
56 * <mime-type>image/ *</mime-type>
57 * </mime-types>
58 * <patterns>
59 * <pattern>*.txt</pattern>
60 * <pattern>*.png</pattern>
61 * </patterns>
62 * </object>
63 * ]|
64 */
65
66 #include "config.h"
67 #include <string.h>
68
69 #include <gdk-pixbuf/gdk-pixbuf.h>
70
71 #include "gtkfilefilterprivate.h"
72 #include "gtkbuildable.h"
73 #include "gtkbuilderprivate.h"
74 #include "gtkintl.h"
75 #include "gtkprivate.h"
76
77 typedef struct _GtkFileFilterClass GtkFileFilterClass;
78 typedef struct _FilterRule FilterRule;
79
80 #define GTK_FILE_FILTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_FILTER, GtkFileFilterClass))
81 #define GTK_IS_FILE_FILTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_FILTER))
82 #define GTK_FILE_FILTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_FILTER, GtkFileFilterClass))
83
84 typedef enum {
85 FILTER_RULE_PATTERN,
86 FILTER_RULE_MIME_TYPE,
87 FILTER_RULE_PIXBUF_FORMATS,
88 FILTER_RULE_CUSTOM
89 } FilterRuleType;
90
91 struct _GtkFileFilterClass
92 {
93 GInitiallyUnownedClass parent_class;
94 };
95
96 struct _GtkFileFilter
97 {
98 GInitiallyUnowned parent_instance;
99
100 gchar *name;
101 GSList *rules;
102
103 GtkFileFilterFlags needed;
104 };
105
106 struct _FilterRule
107 {
108 FilterRuleType type;
109 GtkFileFilterFlags needed;
110
111 union {
112 gchar *pattern;
113 gchar *mime_type;
114 GSList *pixbuf_formats;
115 struct {
116 GtkFileFilterFunc func;
117 gpointer data;
118 GDestroyNotify notify;
119 } custom;
120 } u;
121 };
122
123 static void gtk_file_filter_finalize (GObject *object);
124
125
126 static void gtk_file_filter_buildable_init (GtkBuildableIface *iface);
127 static void gtk_file_filter_buildable_set_name (GtkBuildable *buildable,
128 const gchar *name);
129 static const gchar* gtk_file_filter_buildable_get_name (GtkBuildable *buildable);
130
131
132 static gboolean gtk_file_filter_buildable_custom_tag_start (GtkBuildable *buildable,
133 GtkBuilder *builder,
134 GObject *child,
135 const gchar *tagname,
136 GMarkupParser *parser,
137 gpointer *data);
138 static void gtk_file_filter_buildable_custom_tag_end (GtkBuildable *buildable,
139 GtkBuilder *builder,
140 GObject *child,
141 const gchar *tagname,
142 gpointer *data);
143
G_DEFINE_TYPE_WITH_CODE(GtkFileFilter,gtk_file_filter,G_TYPE_INITIALLY_UNOWNED,G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,gtk_file_filter_buildable_init))144 G_DEFINE_TYPE_WITH_CODE (GtkFileFilter, gtk_file_filter, G_TYPE_INITIALLY_UNOWNED,
145 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
146 gtk_file_filter_buildable_init))
147
148 static void
149 gtk_file_filter_init (GtkFileFilter *object)
150 {
151 }
152
153 static void
gtk_file_filter_class_init(GtkFileFilterClass * class)154 gtk_file_filter_class_init (GtkFileFilterClass *class)
155 {
156 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
157
158 gobject_class->finalize = gtk_file_filter_finalize;
159 }
160
161 static void
filter_rule_free(FilterRule * rule)162 filter_rule_free (FilterRule *rule)
163 {
164 switch (rule->type)
165 {
166 case FILTER_RULE_MIME_TYPE:
167 g_free (rule->u.mime_type);
168 break;
169 case FILTER_RULE_PATTERN:
170 g_free (rule->u.pattern);
171 break;
172 case FILTER_RULE_CUSTOM:
173 if (rule->u.custom.notify)
174 rule->u.custom.notify (rule->u.custom.data);
175 break;
176 case FILTER_RULE_PIXBUF_FORMATS:
177 g_slist_free (rule->u.pixbuf_formats);
178 break;
179 default:
180 g_assert_not_reached ();
181 }
182
183 g_slice_free (FilterRule, rule);
184 }
185
186 static void
gtk_file_filter_finalize(GObject * object)187 gtk_file_filter_finalize (GObject *object)
188 {
189 GtkFileFilter *filter = GTK_FILE_FILTER (object);
190
191 g_slist_free_full (filter->rules, (GDestroyNotify)filter_rule_free);
192
193 g_free (filter->name);
194
195 G_OBJECT_CLASS (gtk_file_filter_parent_class)->finalize (object);
196 }
197
198 /*
199 * GtkBuildable implementation
200 */
201 static void
gtk_file_filter_buildable_init(GtkBuildableIface * iface)202 gtk_file_filter_buildable_init (GtkBuildableIface *iface)
203 {
204 iface->custom_tag_start = gtk_file_filter_buildable_custom_tag_start;
205 iface->custom_tag_end = gtk_file_filter_buildable_custom_tag_end;
206 iface->set_name = gtk_file_filter_buildable_set_name;
207 iface->get_name = gtk_file_filter_buildable_get_name;
208 }
209
210 static void
gtk_file_filter_buildable_set_name(GtkBuildable * buildable,const gchar * name)211 gtk_file_filter_buildable_set_name (GtkBuildable *buildable,
212 const gchar *name)
213 {
214 gtk_file_filter_set_name (GTK_FILE_FILTER (buildable), name);
215 }
216
217 static const gchar *
gtk_file_filter_buildable_get_name(GtkBuildable * buildable)218 gtk_file_filter_buildable_get_name (GtkBuildable *buildable)
219 {
220 return gtk_file_filter_get_name (GTK_FILE_FILTER (buildable));
221 }
222
223 typedef enum {
224 PARSE_MIME_TYPES,
225 PARSE_PATTERNS
226 } ParserType;
227
228 typedef struct {
229 GtkFileFilter *filter;
230 GtkBuilder *builder;
231 ParserType type;
232 GString *string;
233 gboolean parsing;
234 } SubParserData;
235
236 static void
parser_start_element(GMarkupParseContext * context,const gchar * element_name,const gchar ** names,const gchar ** values,gpointer user_data,GError ** error)237 parser_start_element (GMarkupParseContext *context,
238 const gchar *element_name,
239 const gchar **names,
240 const gchar **values,
241 gpointer user_data,
242 GError **error)
243 {
244 SubParserData *data = (SubParserData*)user_data;
245
246 if (!g_markup_collect_attributes (element_name, names, values, error,
247 G_MARKUP_COLLECT_INVALID, NULL, NULL,
248 G_MARKUP_COLLECT_INVALID))
249 {
250 _gtk_builder_prefix_error (data->builder, context, error);
251 return;
252 }
253
254 if (strcmp (element_name, "mime-types") == 0 ||
255 strcmp (element_name, "patterns") == 0)
256 {
257 if (!_gtk_builder_check_parent (data->builder, context, "object", error))
258 return;
259 }
260 else if (strcmp (element_name, "mime-type") == 0)
261 {
262 if (!_gtk_builder_check_parent (data->builder, context, "mime-types", error))
263 return;
264
265 data->parsing = TRUE;
266 }
267 else if (strcmp (element_name, "pattern") == 0)
268 {
269 if (!_gtk_builder_check_parent (data->builder, context, "patterns", error))
270 return;
271
272 data->parsing = TRUE;
273 }
274 else
275 {
276 _gtk_builder_error_unhandled_tag (data->builder, context,
277 "GtkFileFilter", element_name,
278 error);
279 }
280 }
281
282 static void
parser_text_element(GMarkupParseContext * context,const gchar * text,gsize text_len,gpointer user_data,GError ** error)283 parser_text_element (GMarkupParseContext *context,
284 const gchar *text,
285 gsize text_len,
286 gpointer user_data,
287 GError **error)
288 {
289 SubParserData *data = (SubParserData*)user_data;
290
291 if (data->parsing)
292 g_string_append_len (data->string, text, text_len);
293 }
294
295 static void
parser_end_element(GMarkupParseContext * context,const gchar * element_name,gpointer user_data,GError ** error)296 parser_end_element (GMarkupParseContext *context,
297 const gchar *element_name,
298 gpointer user_data,
299 GError **error)
300 {
301 SubParserData *data = (SubParserData*)user_data;
302
303 if (data->string != NULL && data->string->len != 0)
304 {
305 switch (data->type)
306 {
307 case PARSE_MIME_TYPES:
308 gtk_file_filter_add_mime_type (data->filter, data->string->str);
309 break;
310 case PARSE_PATTERNS:
311 gtk_file_filter_add_pattern (data->filter, data->string->str);
312 break;
313 default:
314 break;
315 }
316 }
317
318 g_string_set_size (data->string, 0);
319 data->parsing = FALSE;
320 }
321
322 static const GMarkupParser sub_parser =
323 {
324 parser_start_element,
325 parser_end_element,
326 parser_text_element,
327 };
328
329 static gboolean
gtk_file_filter_buildable_custom_tag_start(GtkBuildable * buildable,GtkBuilder * builder,GObject * child,const gchar * tagname,GMarkupParser * parser,gpointer * parser_data)330 gtk_file_filter_buildable_custom_tag_start (GtkBuildable *buildable,
331 GtkBuilder *builder,
332 GObject *child,
333 const gchar *tagname,
334 GMarkupParser *parser,
335 gpointer *parser_data)
336 {
337 SubParserData *data = NULL;
338
339 if (strcmp (tagname, "mime-types") == 0)
340 {
341 data = g_slice_new0 (SubParserData);
342 data->string = g_string_new ("");
343 data->type = PARSE_MIME_TYPES;
344 data->filter = GTK_FILE_FILTER (buildable);
345 data->builder = builder;
346
347 *parser = sub_parser;
348 *parser_data = data;
349 }
350 else if (strcmp (tagname, "patterns") == 0)
351 {
352 data = g_slice_new0 (SubParserData);
353 data->string = g_string_new ("");
354 data->type = PARSE_PATTERNS;
355 data->filter = GTK_FILE_FILTER (buildable);
356 data->builder = builder;
357
358 *parser = sub_parser;
359 *parser_data = data;
360 }
361
362 return data != NULL;
363 }
364
365 static void
gtk_file_filter_buildable_custom_tag_end(GtkBuildable * buildable,GtkBuilder * builder,GObject * child,const gchar * tagname,gpointer * user_data)366 gtk_file_filter_buildable_custom_tag_end (GtkBuildable *buildable,
367 GtkBuilder *builder,
368 GObject *child,
369 const gchar *tagname,
370 gpointer *user_data)
371 {
372 if (strcmp (tagname, "mime-types") == 0 ||
373 strcmp (tagname, "patterns") == 0)
374 {
375 SubParserData *data = (SubParserData*)user_data;
376
377 g_string_free (data->string, TRUE);
378 g_slice_free (SubParserData, data);
379 }
380 }
381
382
383 /**
384 * gtk_file_filter_new:
385 *
386 * Creates a new #GtkFileFilter with no rules added to it.
387 * Such a filter doesn’t accept any files, so is not
388 * particularly useful until you add rules with
389 * gtk_file_filter_add_mime_type(), gtk_file_filter_add_pattern(),
390 * or gtk_file_filter_add_custom(). To create a filter
391 * that accepts any file, use:
392 * |[<!-- language="C" -->
393 * GtkFileFilter *filter = gtk_file_filter_new ();
394 * gtk_file_filter_add_pattern (filter, "*");
395 * ]|
396 *
397 * Returns: a new #GtkFileFilter
398 *
399 * Since: 2.4
400 **/
401 GtkFileFilter *
gtk_file_filter_new(void)402 gtk_file_filter_new (void)
403 {
404 return g_object_new (GTK_TYPE_FILE_FILTER, NULL);
405 }
406
407 /**
408 * gtk_file_filter_set_name:
409 * @filter: a #GtkFileFilter
410 * @name: (allow-none): the human-readable-name for the filter, or %NULL
411 * to remove any existing name.
412 *
413 * Sets the human-readable name of the filter; this is the string
414 * that will be displayed in the file selector user interface if
415 * there is a selectable list of filters.
416 *
417 * Since: 2.4
418 **/
419 void
gtk_file_filter_set_name(GtkFileFilter * filter,const gchar * name)420 gtk_file_filter_set_name (GtkFileFilter *filter,
421 const gchar *name)
422 {
423 g_return_if_fail (GTK_IS_FILE_FILTER (filter));
424
425 g_free (filter->name);
426
427 filter->name = g_strdup (name);
428 }
429
430 /**
431 * gtk_file_filter_get_name:
432 * @filter: a #GtkFileFilter
433 *
434 * Gets the human-readable name for the filter. See gtk_file_filter_set_name().
435 *
436 * Returns: (nullable): The human-readable name of the filter,
437 * or %NULL. This value is owned by GTK+ and must not
438 * be modified or freed.
439 *
440 * Since: 2.4
441 **/
442 const gchar *
gtk_file_filter_get_name(GtkFileFilter * filter)443 gtk_file_filter_get_name (GtkFileFilter *filter)
444 {
445 g_return_val_if_fail (GTK_IS_FILE_FILTER (filter), NULL);
446
447 return filter->name;
448 }
449
450 static void
file_filter_add_rule(GtkFileFilter * filter,FilterRule * rule)451 file_filter_add_rule (GtkFileFilter *filter,
452 FilterRule *rule)
453 {
454 filter->needed |= rule->needed;
455 filter->rules = g_slist_append (filter->rules, rule);
456 }
457
458 /**
459 * gtk_file_filter_add_mime_type:
460 * @filter: A #GtkFileFilter
461 * @mime_type: name of a MIME type
462 *
463 * Adds a rule allowing a given mime type to @filter.
464 *
465 * Since: 2.4
466 **/
467 void
gtk_file_filter_add_mime_type(GtkFileFilter * filter,const gchar * mime_type)468 gtk_file_filter_add_mime_type (GtkFileFilter *filter,
469 const gchar *mime_type)
470 {
471 FilterRule *rule;
472
473 g_return_if_fail (GTK_IS_FILE_FILTER (filter));
474 g_return_if_fail (mime_type != NULL);
475
476 rule = g_slice_new (FilterRule);
477 rule->type = FILTER_RULE_MIME_TYPE;
478 rule->needed = GTK_FILE_FILTER_MIME_TYPE;
479 rule->u.mime_type = g_strdup (mime_type);
480
481 file_filter_add_rule (filter, rule);
482 }
483
484 /**
485 * gtk_file_filter_add_pattern:
486 * @filter: a #GtkFileFilter
487 * @pattern: a shell style glob
488 *
489 * Adds a rule allowing a shell style glob to a filter.
490 *
491 * Since: 2.4
492 **/
493 void
gtk_file_filter_add_pattern(GtkFileFilter * filter,const gchar * pattern)494 gtk_file_filter_add_pattern (GtkFileFilter *filter,
495 const gchar *pattern)
496 {
497 FilterRule *rule;
498
499 g_return_if_fail (GTK_IS_FILE_FILTER (filter));
500 g_return_if_fail (pattern != NULL);
501
502 rule = g_slice_new (FilterRule);
503 rule->type = FILTER_RULE_PATTERN;
504 rule->needed = GTK_FILE_FILTER_DISPLAY_NAME;
505 rule->u.pattern = g_strdup (pattern);
506
507 file_filter_add_rule (filter, rule);
508 }
509
510 /**
511 * gtk_file_filter_add_pixbuf_formats:
512 * @filter: a #GtkFileFilter
513 *
514 * Adds a rule allowing image files in the formats supported
515 * by GdkPixbuf.
516 *
517 * Since: 2.6
518 **/
519 void
gtk_file_filter_add_pixbuf_formats(GtkFileFilter * filter)520 gtk_file_filter_add_pixbuf_formats (GtkFileFilter *filter)
521 {
522 FilterRule *rule;
523
524 g_return_if_fail (GTK_IS_FILE_FILTER (filter));
525
526 rule = g_slice_new (FilterRule);
527 rule->type = FILTER_RULE_PIXBUF_FORMATS;
528 rule->needed = GTK_FILE_FILTER_MIME_TYPE;
529 rule->u.pixbuf_formats = gdk_pixbuf_get_formats ();
530 file_filter_add_rule (filter, rule);
531 }
532
533
534 /**
535 * gtk_file_filter_add_custom:
536 * @filter: a #GtkFileFilter
537 * @needed: bitfield of flags indicating the information that the custom
538 * filter function needs.
539 * @func: callback function; if the function returns %TRUE, then
540 * the file will be displayed.
541 * @data: data to pass to @func
542 * @notify: function to call to free @data when it is no longer needed.
543 *
544 * Adds rule to a filter that allows files based on a custom callback
545 * function. The bitfield @needed which is passed in provides information
546 * about what sorts of information that the filter function needs;
547 * this allows GTK+ to avoid retrieving expensive information when
548 * it isn’t needed by the filter.
549 *
550 * Since: 2.4
551 **/
552 void
gtk_file_filter_add_custom(GtkFileFilter * filter,GtkFileFilterFlags needed,GtkFileFilterFunc func,gpointer data,GDestroyNotify notify)553 gtk_file_filter_add_custom (GtkFileFilter *filter,
554 GtkFileFilterFlags needed,
555 GtkFileFilterFunc func,
556 gpointer data,
557 GDestroyNotify notify)
558 {
559 FilterRule *rule;
560
561 g_return_if_fail (GTK_IS_FILE_FILTER (filter));
562 g_return_if_fail (func != NULL);
563
564 rule = g_slice_new (FilterRule);
565 rule->type = FILTER_RULE_CUSTOM;
566 rule->needed = needed;
567 rule->u.custom.func = func;
568 rule->u.custom.data = data;
569 rule->u.custom.notify = notify;
570
571 file_filter_add_rule (filter, rule);
572 }
573
574 /**
575 * gtk_file_filter_get_needed:
576 * @filter: a #GtkFileFilter
577 *
578 * Gets the fields that need to be filled in for the #GtkFileFilterInfo
579 * passed to gtk_file_filter_filter()
580 *
581 * This function will not typically be used by applications; it
582 * is intended principally for use in the implementation of
583 * #GtkFileChooser.
584 *
585 * Returns: bitfield of flags indicating needed fields when
586 * calling gtk_file_filter_filter()
587 *
588 * Since: 2.4
589 **/
590 GtkFileFilterFlags
gtk_file_filter_get_needed(GtkFileFilter * filter)591 gtk_file_filter_get_needed (GtkFileFilter *filter)
592 {
593 return filter->needed;
594 }
595
596 #ifdef GDK_WINDOWING_QUARTZ
597
598 #import <Foundation/Foundation.h>
599
_gtk_file_filter_get_as_pattern_nsstrings(GtkFileFilter * filter)600 NSArray * _gtk_file_filter_get_as_pattern_nsstrings (GtkFileFilter *filter)
601 {
602 NSMutableArray *array = [[NSMutableArray alloc] init];
603 GSList *tmp_list;
604
605 for (tmp_list = filter->rules; tmp_list; tmp_list = tmp_list->next)
606 {
607 FilterRule *rule = tmp_list->data;
608
609 switch (rule->type)
610 {
611 case FILTER_RULE_CUSTOM:
612 [array release];
613 return NULL;
614 break;
615 case FILTER_RULE_MIME_TYPE:
616 {
617 // convert mime-types to UTI
618 NSString *mime_type_nsstring = [NSString stringWithUTF8String: rule->u.mime_type];
619 NSString *uti_nsstring = (NSString *) UTTypeCreatePreferredIdentifierForTag (kUTTagClassMIMEType, (CFStringRef) mime_type_nsstring, NULL);
620 if (uti_nsstring == NULL)
621 {
622 [array release];
623 return NULL;
624 }
625 [array addObject:uti_nsstring];
626 }
627 break;
628 case FILTER_RULE_PATTERN:
629 {
630 // patterns will need to be stripped of their leading *.
631 GString *pattern = g_string_new (rule->u.pattern);
632 if (strncmp (pattern->str, "*.", 2) == 0)
633 {
634 pattern = g_string_erase (pattern, 0, 2);
635 }
636 else if (strncmp (pattern->str, "*", 1) == 0)
637 {
638 pattern = g_string_erase (pattern, 0, 1);
639 }
640 gchar *pattern_c = g_string_free (pattern, FALSE);
641 NSString *pattern_nsstring = [NSString stringWithUTF8String:pattern_c];
642 g_free (pattern_c);
643 [pattern_nsstring retain];
644 [array addObject:pattern_nsstring];
645 }
646 break;
647 case FILTER_RULE_PIXBUF_FORMATS:
648 {
649 GSList *list;
650
651 for (list = rule->u.pixbuf_formats; list; list = list->next)
652 {
653 int i;
654 gchar **extensions;
655
656 extensions = gdk_pixbuf_format_get_extensions (list->data);
657
658 for (i = 0; extensions[i] != NULL; i++)
659 {
660 NSString *extension = [NSString stringWithUTF8String: extensions[i]];
661 [extension retain];
662 [array addObject:extension];
663 }
664 g_strfreev (extensions);
665 }
666 break;
667 }
668 }
669 }
670 return array;
671 }
672 #endif
673
674 char **
_gtk_file_filter_get_as_patterns(GtkFileFilter * filter)675 _gtk_file_filter_get_as_patterns (GtkFileFilter *filter)
676 {
677 GPtrArray *array;
678 GSList *tmp_list;
679
680 array = g_ptr_array_new_with_free_func (g_free);
681
682 for (tmp_list = filter->rules; tmp_list; tmp_list = tmp_list->next)
683 {
684 FilterRule *rule = tmp_list->data;
685
686 switch (rule->type)
687 {
688 case FILTER_RULE_CUSTOM:
689 case FILTER_RULE_MIME_TYPE:
690 g_ptr_array_free (array, TRUE);
691 return NULL;
692 break;
693 case FILTER_RULE_PATTERN:
694 g_ptr_array_add (array, g_strdup (rule->u.pattern));
695 break;
696 case FILTER_RULE_PIXBUF_FORMATS:
697 {
698 GSList *list;
699
700 for (list = rule->u.pixbuf_formats; list; list = list->next)
701 {
702 int i;
703 gchar **extensions;
704
705 extensions = gdk_pixbuf_format_get_extensions (list->data);
706
707 for (i = 0; extensions[i] != NULL; i++)
708 g_ptr_array_add (array, g_strdup_printf ("*.%s", extensions[i]));
709
710 g_strfreev (extensions);
711 }
712 break;
713 }
714 }
715 }
716
717 g_ptr_array_add (array, NULL); /* Null terminate */
718 return (char **)g_ptr_array_free (array, FALSE);
719 }
720
721 /**
722 * gtk_file_filter_filter:
723 * @filter: a #GtkFileFilter
724 * @filter_info: a #GtkFileFilterInfo containing information
725 * about a file.
726 *
727 * Tests whether a file should be displayed according to @filter.
728 * The #GtkFileFilterInfo @filter_info should include
729 * the fields returned from gtk_file_filter_get_needed().
730 *
731 * This function will not typically be used by applications; it
732 * is intended principally for use in the implementation of
733 * #GtkFileChooser.
734 *
735 * Returns: %TRUE if the file should be displayed
736 *
737 * Since: 2.4
738 **/
739 gboolean
gtk_file_filter_filter(GtkFileFilter * filter,const GtkFileFilterInfo * filter_info)740 gtk_file_filter_filter (GtkFileFilter *filter,
741 const GtkFileFilterInfo *filter_info)
742 {
743 GSList *tmp_list;
744
745 for (tmp_list = filter->rules; tmp_list; tmp_list = tmp_list->next)
746 {
747 FilterRule *rule = tmp_list->data;
748
749 if ((filter_info->contains & rule->needed) != rule->needed)
750 continue;
751
752 switch (rule->type)
753 {
754 case FILTER_RULE_MIME_TYPE:
755 if (filter_info->mime_type != NULL)
756 {
757 gchar *filter_content_type, *rule_content_type;
758 gboolean match;
759
760 filter_content_type = g_content_type_from_mime_type (filter_info->mime_type);
761 rule_content_type = g_content_type_from_mime_type (rule->u.mime_type);
762 match = filter_content_type != NULL &&
763 rule_content_type != NULL &&
764 g_content_type_is_a (filter_content_type, rule_content_type);
765 g_free (filter_content_type);
766 g_free (rule_content_type);
767
768 if (match)
769 return TRUE;
770 }
771 break;
772 case FILTER_RULE_PATTERN:
773 if (filter_info->display_name != NULL &&
774 _gtk_fnmatch (rule->u.pattern, filter_info->display_name, FALSE))
775 return TRUE;
776 break;
777 case FILTER_RULE_PIXBUF_FORMATS:
778 {
779 GSList *list;
780
781 if (!filter_info->mime_type)
782 break;
783
784 for (list = rule->u.pixbuf_formats; list; list = list->next)
785 {
786 int i;
787 gchar **mime_types;
788
789 mime_types = gdk_pixbuf_format_get_mime_types (list->data);
790
791 for (i = 0; mime_types[i] != NULL; i++)
792 {
793 if (strcmp (mime_types[i], filter_info->mime_type) == 0)
794 {
795 g_strfreev (mime_types);
796 return TRUE;
797 }
798 }
799
800 g_strfreev (mime_types);
801 }
802 break;
803 }
804 case FILTER_RULE_CUSTOM:
805 if (rule->u.custom.func (filter_info, rule->u.custom.data))
806 return TRUE;
807 break;
808 }
809 }
810
811 return FALSE;
812 }
813
814 /**
815 * gtk_file_filter_to_gvariant:
816 * @filter: a #GtkFileFilter
817 *
818 * Serialize a file filter to an a{sv} variant.
819 *
820 * Returns: (transfer none): a new, floating, #GVariant
821 *
822 * Since: 3.22
823 */
824 GVariant *
gtk_file_filter_to_gvariant(GtkFileFilter * filter)825 gtk_file_filter_to_gvariant (GtkFileFilter *filter)
826 {
827 GVariantBuilder builder;
828 GSList *l;
829
830 g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(us)"));
831 for (l = filter->rules; l; l = l->next)
832 {
833 FilterRule *rule = l->data;
834
835 switch (rule->type)
836 {
837 case FILTER_RULE_PATTERN:
838 g_variant_builder_add (&builder, "(us)", 0, rule->u.pattern);
839 break;
840 case FILTER_RULE_MIME_TYPE:
841 g_variant_builder_add (&builder, "(us)", 1, rule->u.mime_type);
842 break;
843 case FILTER_RULE_PIXBUF_FORMATS:
844 {
845 GSList *f;
846
847 for (f = rule->u.pixbuf_formats; f; f = f->next)
848 {
849 GdkPixbufFormat *fmt = f->data;
850 gchar **mime_types;
851 int i;
852
853 mime_types = gdk_pixbuf_format_get_mime_types (fmt);
854 for (i = 0; mime_types[i]; i++)
855 g_variant_builder_add (&builder, "(us)", 1, mime_types[i]);
856 g_strfreev (mime_types);
857 }
858 }
859 break;
860 case FILTER_RULE_CUSTOM:
861 default:
862 break;
863 }
864 }
865
866 return g_variant_new ("(s@a(us))", filter->name, g_variant_builder_end (&builder));
867 }
868
869 /**
870 * gtk_file_filter_new_from_gvariant:
871 * @variant: an a{sv} #GVariant
872 *
873 * Deserialize a file filter from an a{sv} variant in
874 * the format produced by gtk_file_filter_to_gvariant().
875 *
876 * Returns: (transfer full): a new #GtkFileFilter object
877 *
878 * Since: 3.22
879 */
880 GtkFileFilter *
gtk_file_filter_new_from_gvariant(GVariant * variant)881 gtk_file_filter_new_from_gvariant (GVariant *variant)
882 {
883 GtkFileFilter *filter;
884 GVariantIter *iter;
885 const char *name;
886 int type;
887 char *tmp;
888
889 filter = gtk_file_filter_new ();
890
891 g_variant_get (variant, "(&sa(us))", &name, &iter);
892
893 gtk_file_filter_set_name (filter, name);
894
895 while (g_variant_iter_next (iter, "(u&s)", &type, &tmp))
896 {
897 switch (type)
898 {
899 case 0:
900 gtk_file_filter_add_pattern (filter, tmp);
901 break;
902 case 1:
903 gtk_file_filter_add_mime_type (filter, tmp);
904 break;
905 default:
906 break;
907 }
908 }
909 g_variant_iter_free (iter);
910
911 return filter;
912 }
913