]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilefilter.c
Change FSF Address
[~andy/gtk] / gtk / gtkfilefilter.c
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  *
24  * A GtkFileFilter can be used to restrict the files being shown in a
25  * #GtkFileChooser. Files can be filtered based on their name (with
26  * gtk_file_filter_add_pattern()), on their mime type (with
27  * gtk_file_filter_add_mime_type()), or by a custom filter function
28  * (with gtk_file_filter_add_custom()).
29  *
30  * Filtering by mime types handles aliasing and subclassing of mime
31  * types; e.g. a filter for text/plain also matches a file with mime
32  * type application/rtf, since application/rtf is a subclass of
33  * text/plain. Note that #GtkFileFilter allows wildcards for the
34  * subtype of a mime type, so you can e.g. filter for image/&ast;.
35  *
36  * Normally, filters are used by adding them to a #GtkFileChooser,
37  * see gtk_file_chooser_add_filter(), but it is also possible
38  * to manually use a filter on a file with gtk_file_filter_filter().
39  *
40  * <refsect2 id="GtkFileFilter-BUILDER-UI">
41  * <title>GtkFileFilter as GtkBuildable</title>
42  * <para>
43  * The GtkFileFilter implementation of the GtkBuildable interface
44  * supports adding rules using the &lt;mime-types&gt;, &lt;patterns&gt; and
45  * &lt;applications&gt; elements and listing the rules within. Specifying
46  * a &lt;mime-type&gt; or &lt;pattern&gt; is the same
47  * as calling gtk_recent_filter_add_mime_type() or gtk_recent_filter_add_pattern()
48  *
49  * <example>
50  * <title>A UI definition fragment specifying GtkFileFilter rules</title>
51  * <programlisting><![CDATA[
52  * <object class="GtkFileFilter">
53  *   <mime-types>
54  *     <mime-type>text/plain</mime-type>
55  *     <mime-type>image/&ast;</mime-type>
56  *   </mime-types>
57  *   <patterns>
58  *     <pattern>*.txt</pattern>
59  *     <pattern>*.png</pattern>
60  *   </patterns>
61  * </object>
62  * ]]></programlisting>
63  * </example>
64  * </para>
65  * </refsect2>
66  *
67  * @see_also: #GtkFileChooser
68  */
69
70 #include "config.h"
71 #include <string.h>
72
73 #include <gdk-pixbuf/gdk-pixbuf.h>
74
75 #include "gtkfilefilter.h"
76 #include "gtkbuildable.h"
77 #include "gtkintl.h"
78 #include "gtkprivate.h"
79
80 typedef struct _GtkFileFilterClass GtkFileFilterClass;
81 typedef struct _FilterRule FilterRule;
82
83 #define GTK_FILE_FILTER_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_FILTER, GtkFileFilterClass))
84 #define GTK_IS_FILE_FILTER_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_FILTER))
85 #define GTK_FILE_FILTER_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_FILTER, GtkFileFilterClass))
86
87 typedef enum {
88   FILTER_RULE_PATTERN,
89   FILTER_RULE_MIME_TYPE,
90   FILTER_RULE_PIXBUF_FORMATS,
91   FILTER_RULE_CUSTOM
92 } FilterRuleType;
93
94 struct _GtkFileFilterClass
95 {
96   GInitiallyUnownedClass parent_class;
97 };
98
99 struct _GtkFileFilter
100 {
101   GInitiallyUnowned parent_instance;
102
103   gchar *name;
104   GSList *rules;
105
106   GtkFileFilterFlags needed;
107 };
108
109 struct _FilterRule
110 {
111   FilterRuleType type;
112   GtkFileFilterFlags needed;
113   
114   union {
115     gchar *pattern;
116     gchar *mime_type;
117     GSList *pixbuf_formats;
118     struct {
119       GtkFileFilterFunc func;
120       gpointer data;
121       GDestroyNotify notify;
122     } custom;
123   } u;
124 };
125
126 static void gtk_file_filter_finalize   (GObject            *object);
127
128
129 static void     gtk_file_filter_buildable_init                 (GtkBuildableIface *iface);
130 static gboolean gtk_file_filter_buildable_custom_tag_start     (GtkBuildable  *buildable,
131                                                                 GtkBuilder    *builder,
132                                                                 GObject       *child,
133                                                                 const gchar   *tagname,
134                                                                 GMarkupParser *parser,
135                                                                 gpointer      *data);
136 static void     gtk_file_filter_buildable_custom_tag_end       (GtkBuildable  *buildable,
137                                                                 GtkBuilder    *builder,
138                                                                 GObject       *child,
139                                                                 const gchar   *tagname,
140                                                                 gpointer      *data);
141
142 G_DEFINE_TYPE_WITH_CODE (GtkFileFilter, gtk_file_filter, G_TYPE_INITIALLY_UNOWNED,
143                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
144                                                 gtk_file_filter_buildable_init))
145
146 static void
147 gtk_file_filter_init (GtkFileFilter *object)
148 {
149 }
150
151 static void
152 gtk_file_filter_class_init (GtkFileFilterClass *class)
153 {
154   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
155
156   gobject_class->finalize = gtk_file_filter_finalize;
157 }
158
159 static void
160 filter_rule_free (FilterRule *rule)
161 {
162   switch (rule->type)
163     {
164     case FILTER_RULE_MIME_TYPE:
165       g_free (rule->u.mime_type);
166       break;
167     case FILTER_RULE_PATTERN:
168       g_free (rule->u.pattern);
169       break;
170     case FILTER_RULE_CUSTOM:
171       if (rule->u.custom.notify)
172         rule->u.custom.notify (rule->u.custom.data);
173       break;
174     case FILTER_RULE_PIXBUF_FORMATS:
175       g_slist_free (rule->u.pixbuf_formats);
176       break;
177     default:
178       g_assert_not_reached ();
179     }
180
181   g_slice_free (FilterRule, rule);
182 }
183
184 static void
185 gtk_file_filter_finalize (GObject  *object)
186 {
187   GtkFileFilter *filter = GTK_FILE_FILTER (object);
188
189   g_slist_foreach (filter->rules, (GFunc)filter_rule_free, NULL);
190   g_slist_free (filter->rules);
191
192   g_free (filter->name);
193
194   G_OBJECT_CLASS (gtk_file_filter_parent_class)->finalize (object);
195 }
196
197 /*
198  * GtkBuildable implementation
199  */
200 static void
201 gtk_file_filter_buildable_init (GtkBuildableIface *iface)
202 {
203   iface->custom_tag_start = gtk_file_filter_buildable_custom_tag_start;
204   iface->custom_tag_end = gtk_file_filter_buildable_custom_tag_end;
205 }
206
207 typedef enum {
208   PARSE_MIME_TYPES,
209   PARSE_PATTERNS
210 } ParserType;
211
212 typedef struct {
213   GtkFileFilter *filter;
214   ParserType     type;
215   GString       *string;
216   gboolean       parsing;
217 } SubParserData;
218
219 static void
220 parser_start_element (GMarkupParseContext *context,
221                       const gchar         *element_name,
222                       const gchar        **names,
223                       const gchar        **values,
224                       gpointer             user_data,
225                       GError             **error)
226 {
227   SubParserData *parser_data = (SubParserData*)user_data;
228
229   if (strcmp (element_name, "mime-types") == 0)
230     return;
231   else if (strcmp (element_name, "mime-type") == 0)
232     {
233       parser_data->parsing = TRUE;
234       return;
235     }
236   else if (strcmp (element_name, "patterns") == 0)
237     return;
238   else if (strcmp (element_name, "pattern") == 0)
239     {
240       parser_data->parsing = TRUE;
241       return;
242     }
243   else
244     g_warning ("Unsupported tag for GtkFileFilter: %s\n", element_name);
245 }
246
247 static void
248 parser_text_element (GMarkupParseContext *context,
249                      const gchar         *text,
250                      gsize                text_len,
251                      gpointer             user_data,
252                      GError             **error)
253 {
254   SubParserData *parser_data = (SubParserData*)user_data;
255
256   if (parser_data->parsing)
257     g_string_append_len (parser_data->string, text, text_len);
258 }
259
260 static void
261 parser_end_element (GMarkupParseContext *context,
262                     const gchar         *element_name,
263                     gpointer             user_data,
264                     GError             **error)
265 {
266   SubParserData *parser_data = (SubParserData*)user_data;
267
268   if (parser_data->string)
269     {
270       switch (parser_data->type)
271         {
272         case PARSE_MIME_TYPES:
273           gtk_file_filter_add_mime_type (parser_data->filter, parser_data->string->str);
274           break;
275         case PARSE_PATTERNS:
276           gtk_file_filter_add_pattern (parser_data->filter, parser_data->string->str);
277           break;
278         default:
279           break;
280         }
281     }
282
283   g_string_set_size (parser_data->string, 0);
284   parser_data->parsing = FALSE;
285 }
286
287 static const GMarkupParser sub_parser =
288   {
289     parser_start_element,
290     parser_end_element,
291     parser_text_element,
292   };
293
294 static gboolean
295 gtk_file_filter_buildable_custom_tag_start (GtkBuildable  *buildable,
296                                               GtkBuilder    *builder,
297                                               GObject       *child,
298                                               const gchar   *tagname,
299                                               GMarkupParser *parser,
300                                               gpointer      *data)
301 {
302   SubParserData *parser_data = NULL;
303
304   if (strcmp (tagname, "mime-types") == 0)
305     {
306       parser_data         = g_slice_new0 (SubParserData);
307       parser_data->string = g_string_new ("");
308       parser_data->type   = PARSE_MIME_TYPES;
309       parser_data->filter = GTK_FILE_FILTER (buildable);
310
311       *parser = sub_parser;
312       *data = parser_data;
313     }
314   else if (strcmp (tagname, "patterns") == 0)
315     {
316       parser_data         = g_slice_new0 (SubParserData);
317       parser_data->string = g_string_new ("");
318       parser_data->type   = PARSE_PATTERNS;
319       parser_data->filter = GTK_FILE_FILTER (buildable);
320
321       *parser = sub_parser;
322       *data = parser_data;
323     }
324
325   return parser_data != NULL;
326 }
327
328 static void
329 gtk_file_filter_buildable_custom_tag_end (GtkBuildable *buildable,
330                                           GtkBuilder   *builder,
331                                           GObject      *child,
332                                           const gchar  *tagname,
333                                           gpointer     *data)
334 {
335   if (strcmp (tagname, "mime-types") == 0 ||
336       strcmp (tagname, "patterns") == 0)
337     {
338       SubParserData *parser_data = (SubParserData*)data;
339
340       g_string_free (parser_data->string, TRUE);
341       g_slice_free (SubParserData, parser_data);
342     }
343 }
344
345
346 /**
347  * gtk_file_filter_new:
348  * 
349  * Creates a new #GtkFileFilter with no rules added to it.
350  * Such a filter doesn't accept any files, so is not
351  * particularly useful until you add rules with
352  * gtk_file_filter_add_mime_type(), gtk_file_filter_add_pattern(),
353  * or gtk_file_filter_add_custom(). To create a filter
354  * that accepts any file, use:
355  * |[
356  * GtkFileFilter *filter = gtk_file_filter_new ();
357  * gtk_file_filter_add_pattern (filter, "*");
358  * ]|
359  * 
360  * Return value: a new #GtkFileFilter
361  * 
362  * Since: 2.4
363  **/
364 GtkFileFilter *
365 gtk_file_filter_new (void)
366 {
367   return g_object_new (GTK_TYPE_FILE_FILTER, NULL);
368 }
369
370 /**
371  * gtk_file_filter_set_name:
372  * @filter: a #GtkFileFilter
373  * @name: (allow-none): the human-readable-name for the filter, or %NULL
374  *   to remove any existing name.
375  * 
376  * Sets the human-readable name of the filter; this is the string
377  * that will be displayed in the file selector user interface if
378  * there is a selectable list of filters.
379  * 
380  * Since: 2.4
381  **/
382 void
383 gtk_file_filter_set_name (GtkFileFilter *filter,
384                           const gchar   *name)
385 {
386   g_return_if_fail (GTK_IS_FILE_FILTER (filter));
387   
388   g_free (filter->name);
389
390   filter->name = g_strdup (name);
391 }
392
393 /**
394  * gtk_file_filter_get_name:
395  * @filter: a #GtkFileFilter
396  * 
397  * Gets the human-readable name for the filter. See gtk_file_filter_set_name().
398  * 
399  * Return value: The human-readable name of the filter,
400  *   or %NULL. This value is owned by GTK+ and must not
401  *   be modified or freed.
402  * 
403  * Since: 2.4
404  **/
405 const gchar *
406 gtk_file_filter_get_name (GtkFileFilter *filter)
407 {
408   g_return_val_if_fail (GTK_IS_FILE_FILTER (filter), NULL);
409   
410   return filter->name;
411 }
412
413 static void
414 file_filter_add_rule (GtkFileFilter *filter,
415                       FilterRule    *rule)
416 {
417   filter->needed |= rule->needed;
418   filter->rules = g_slist_append (filter->rules, rule);
419 }
420
421 /**
422  * gtk_file_filter_add_mime_type:
423  * @filter: A #GtkFileFilter
424  * @mime_type: name of a MIME type
425  * 
426  * Adds a rule allowing a given mime type to @filter.
427  * 
428  * Since: 2.4
429  **/
430 void
431 gtk_file_filter_add_mime_type (GtkFileFilter *filter,
432                                const gchar   *mime_type)
433 {
434   FilterRule *rule;
435   
436   g_return_if_fail (GTK_IS_FILE_FILTER (filter));
437   g_return_if_fail (mime_type != NULL);
438
439   rule = g_slice_new (FilterRule);
440   rule->type = FILTER_RULE_MIME_TYPE;
441   rule->needed = GTK_FILE_FILTER_MIME_TYPE;
442   rule->u.mime_type = g_strdup (mime_type);
443
444   file_filter_add_rule (filter, rule);
445 }
446
447 /**
448  * gtk_file_filter_add_pattern:
449  * @filter: a #GtkFileFilter
450  * @pattern: a shell style glob
451  * 
452  * Adds a rule allowing a shell style glob to a filter.
453  * 
454  * Since: 2.4
455  **/
456 void
457 gtk_file_filter_add_pattern (GtkFileFilter *filter,
458                              const gchar   *pattern)
459 {
460   FilterRule *rule;
461   
462   g_return_if_fail (GTK_IS_FILE_FILTER (filter));
463   g_return_if_fail (pattern != NULL);
464
465   rule = g_slice_new (FilterRule);
466   rule->type = FILTER_RULE_PATTERN;
467   rule->needed = GTK_FILE_FILTER_DISPLAY_NAME;
468   rule->u.pattern = g_strdup (pattern);
469
470   file_filter_add_rule (filter, rule);
471 }
472
473 /**
474  * gtk_file_filter_add_pixbuf_formats:
475  * @filter: a #GtkFileFilter
476  * 
477  * Adds a rule allowing image files in the formats supported
478  * by GdkPixbuf.
479  * 
480  * Since: 2.6
481  **/
482 void
483 gtk_file_filter_add_pixbuf_formats (GtkFileFilter *filter)
484 {
485   FilterRule *rule;
486   
487   g_return_if_fail (GTK_IS_FILE_FILTER (filter));
488
489   rule = g_slice_new (FilterRule);
490   rule->type = FILTER_RULE_PIXBUF_FORMATS;
491   rule->needed = GTK_FILE_FILTER_MIME_TYPE;
492   rule->u.pixbuf_formats = gdk_pixbuf_get_formats ();
493   file_filter_add_rule (filter, rule);
494 }
495
496
497 /**
498  * gtk_file_filter_add_custom:
499  * @filter: a #GtkFileFilter
500  * @needed: bitfield of flags indicating the information that the custom
501  *          filter function needs.
502  * @func: callback function; if the function returns %TRUE, then
503  *   the file will be displayed.
504  * @data: data to pass to @func
505  * @notify: function to call to free @data when it is no longer needed.
506  * 
507  * Adds rule to a filter that allows files based on a custom callback
508  * function. The bitfield @needed which is passed in provides information
509  * about what sorts of information that the filter function needs;
510  * this allows GTK+ to avoid retrieving expensive information when
511  * it isn't needed by the filter.
512  * 
513  * Since: 2.4
514  **/
515 void
516 gtk_file_filter_add_custom (GtkFileFilter         *filter,
517                             GtkFileFilterFlags     needed,
518                             GtkFileFilterFunc      func,
519                             gpointer               data,
520                             GDestroyNotify         notify)
521 {
522   FilterRule *rule;
523   
524   g_return_if_fail (GTK_IS_FILE_FILTER (filter));
525   g_return_if_fail (func != NULL);
526
527   rule = g_slice_new (FilterRule);
528   rule->type = FILTER_RULE_CUSTOM;
529   rule->needed = needed;
530   rule->u.custom.func = func;
531   rule->u.custom.data = data;
532   rule->u.custom.notify = notify;
533
534   file_filter_add_rule (filter, rule);
535 }
536
537 /**
538  * gtk_file_filter_get_needed:
539  * @filter: a #GtkFileFilter
540  * 
541  * Gets the fields that need to be filled in for the structure
542  * passed to gtk_file_filter_filter()
543  * 
544  * This function will not typically be used by applications; it
545  * is intended principally for use in the implementation of
546  * #GtkFileChooser.
547  * 
548  * Return value: bitfield of flags indicating needed fields when
549  *   calling gtk_file_filter_filter()
550  * 
551  * Since: 2.4
552  **/
553 GtkFileFilterFlags
554 gtk_file_filter_get_needed (GtkFileFilter *filter)
555 {
556   return filter->needed;
557 }
558
559 /**
560  * gtk_file_filter_filter:
561  * @filter: a #GtkFileFilter
562  * @filter_info: a #GtkFileFilterInfo structure containing information
563  *  about a file.
564  * 
565  * Tests whether a file should be displayed according to @filter.
566  * The #GtkFileFilterInfo structure @filter_info should include
567  * the fields returned from gtk_file_filter_get_needed().
568  *
569  * This function will not typically be used by applications; it
570  * is intended principally for use in the implementation of
571  * #GtkFileChooser.
572  * 
573  * Return value: %TRUE if the file should be displayed
574  * 
575  * Since: 2.4
576  **/
577 gboolean
578 gtk_file_filter_filter (GtkFileFilter           *filter,
579                         const GtkFileFilterInfo *filter_info)
580 {
581   GSList *tmp_list;
582
583   for (tmp_list = filter->rules; tmp_list; tmp_list = tmp_list->next)
584     {
585       FilterRule *rule = tmp_list->data;
586
587       if ((filter_info->contains & rule->needed) != rule->needed)
588         continue;
589       
590       switch (rule->type)
591         {
592         case FILTER_RULE_MIME_TYPE:
593           if (filter_info->mime_type != NULL &&
594               g_content_type_is_a (filter_info->mime_type, rule->u.mime_type))
595             return TRUE;
596           break;
597         case FILTER_RULE_PATTERN:
598           if (filter_info->display_name != NULL &&
599               _gtk_fnmatch (rule->u.pattern, filter_info->display_name, FALSE))
600             return TRUE;
601           break;
602         case FILTER_RULE_PIXBUF_FORMATS:
603           {
604             GSList *list;
605
606             if (!filter_info->mime_type)
607               break;
608
609             for (list = rule->u.pixbuf_formats; list; list = list->next)
610               {
611                 int i;
612                 gchar **mime_types;
613
614                 mime_types = gdk_pixbuf_format_get_mime_types (list->data);
615
616                 for (i = 0; mime_types[i] != NULL; i++)
617                   {
618                     if (strcmp (mime_types[i], filter_info->mime_type) == 0)
619                       {
620                         g_strfreev (mime_types);
621                         return TRUE;
622                       }
623                   }
624
625                 g_strfreev (mime_types);
626               }
627             break;
628           }
629         case FILTER_RULE_CUSTOM:
630           if (rule->u.custom.func (filter_info, rule->u.custom.data))
631             return TRUE;
632           break;
633         }
634     }
635
636   return FALSE;
637 }