]> Pileus Git - ~andy/gtk/blob - gtk/gtkrecentfilter.c
Merge branch 'master' into broadway
[~andy/gtk] / gtk / gtkrecentfilter.c
1 /* GTK - The GIMP Toolkit
2  * gtkrecentfilter.h - Filter object for recently used resources
3  * Copyright (C) 2006, Emmanuele Bassi
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, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 /**
22  * SECTION:gtkrecentfilter
23  * @Short_Description: A filter for selecting a subset of recently used files
24  * @Title: GtkRecentFilter
25  *
26  * A #GtkRecentFilter can be used to restrict the files being shown
27  * in a #GtkRecentChooser.  Files can be filtered based on their name
28  * (with gtk_recent_filter_add_pattern()), on their mime type (with
29  * gtk_file_filter_add_mime_type()), on the application that has
30  * registered them (with gtk_recent_filter_add_application()), or by
31  * a custom filter function (with gtk_recent_filter_add_custom()).
32  *
33  * Filtering by mime type handles aliasing and subclassing of mime
34  * types; e.g. a filter for text/plain also matches a file with mime
35  * type application/rtf, since application/rtf is a subclass of text/plain.
36  * Note that #GtkRecentFilter allows wildcards for the subtype of a
37  * mime type, so you can e.g. filter for image/<!-- -->*.
38  *
39  * Normally, filters are used by adding them to a #GtkRecentChooser,
40  * see gtk_recent_chooser_add_filter(), but it is also possible to
41  * manually use a filter on a file with gtk_recent_filter_filter().
42  *
43  * Recently used files are supported since GTK+ 2.10.
44  *
45  * <refsect2 id="GtkRecentFilter-BUILDER-UI">
46  * <title>GtkRecentFilter as GtkBuildable</title>
47  * <para>
48  * The GtkRecentFilter implementation of the GtkBuildable interface
49  * supports adding rules using the &lt;mime-types&gt, &lt;patterns&gt and
50  * &lt;applications&gt elements and listing the rules within. Specifying
51  * a &lt;mime-type&gt, &lt;pattern&gt or &lt;application&gt is the same
52  * as calling gtk_recent_filter_add_mime_type(), gtk_recent_filter_add_pattern()
53  * or gtk_recent_filter_add_application().
54  *
55  * <example>
56  * <title>A UI definition fragment specifying GtkRecentFilter rules</title>
57  * <programlisting><![CDATA[
58  * <object class="GtkRecentFilter">
59  *   <mime-types>
60  *     <mime-type>text/plain</mime-type>
61  *     <mime-type>image/png</mime-type>
62  *   </mime-types>
63  *   <patterns>
64  *     <pattern>*.txt</pattern>
65  *     <pattern>*.png</pattern>
66  *   </patterns>
67  *   <applications>
68  *     <application>gimp</application>
69  *     <application>gedit</application>
70  *     <application>glade</application>
71  *   </applications>
72  * </object>
73  * ]]></programlisting>
74  * </example>
75  * </para>
76  * </refsect2>
77  */
78
79 #include "config.h"
80 #include <string.h>
81
82 #include <gdk-pixbuf/gdk-pixbuf.h>
83
84 #include "gtkrecentfilter.h"
85 #include "gtkbuildable.h"
86 #include "gtkintl.h"
87 #include "gtkprivate.h"
88
89 static void     gtk_recent_filter_buildable_init                 (GtkBuildableIface *iface);
90 static gboolean gtk_recent_filter_buildable_custom_tag_start     (GtkBuildable  *buildable,
91                                                                   GtkBuilder    *builder,
92                                                                   GObject       *child,
93                                                                   const gchar   *tagname,
94                                                                   GMarkupParser *parser,
95                                                                   gpointer      *data);
96 static void     gtk_recent_filter_buildable_custom_tag_end       (GtkBuildable  *buildable,
97                                                                   GtkBuilder    *builder,
98                                                                   GObject       *child,
99                                                                   const gchar   *tagname,
100                                                                   gpointer      *data);
101
102 typedef struct _GtkRecentFilterClass GtkRecentFilterClass;
103 typedef struct _FilterRule FilterRule;
104
105 typedef enum {
106   FILTER_RULE_URI,
107   FILTER_RULE_DISPLAY_NAME,
108   FILTER_RULE_MIME_TYPE,
109   FILTER_RULE_PIXBUF_FORMATS,
110   FILTER_RULE_APPLICATION,
111   FILTER_RULE_AGE,
112   FILTER_RULE_GROUP,
113   FILTER_RULE_CUSTOM
114 } FilterRuleType;
115
116 struct _GtkRecentFilter
117 {
118   GInitiallyUnowned parent_instance;
119
120   gchar *name;
121   GSList *rules;
122   
123   GtkRecentFilterFlags needed;
124 };
125
126 struct _GtkRecentFilterClass
127 {
128   GInitiallyUnownedClass parent_class;
129 };
130
131 struct _FilterRule
132 {
133   FilterRuleType type;
134   GtkRecentFilterFlags needed;
135   
136   union {
137     gchar *uri;
138     gchar *pattern;
139     gchar *mime_type;
140     GSList *pixbuf_formats;
141     gchar *application;
142     gchar *group;
143     gint age;
144     struct {
145       GtkRecentFilterFunc func;
146       gpointer data;
147       GDestroyNotify data_destroy;
148     } custom;
149   } u;
150 };
151
152 G_DEFINE_TYPE_WITH_CODE (GtkRecentFilter, gtk_recent_filter, G_TYPE_INITIALLY_UNOWNED,
153                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
154                                                 gtk_recent_filter_buildable_init))
155
156
157 static void
158 filter_rule_free (FilterRule *rule)
159 {
160   switch (rule->type)
161     {
162     case FILTER_RULE_MIME_TYPE:
163       g_free (rule->u.mime_type);
164       break;
165     case FILTER_RULE_URI:
166       g_free (rule->u.uri);
167       break;
168     case FILTER_RULE_DISPLAY_NAME:
169       g_free (rule->u.pattern);
170       break;
171     case FILTER_RULE_PIXBUF_FORMATS:
172       g_slist_free (rule->u.pixbuf_formats);
173       break;
174     case FILTER_RULE_AGE:
175       break;
176     case FILTER_RULE_APPLICATION:
177       g_free (rule->u.application);
178       break;
179     case FILTER_RULE_GROUP:
180       g_free (rule->u.group);
181       break;
182     case FILTER_RULE_CUSTOM:
183       if (rule->u.custom.data_destroy)
184         rule->u.custom.data_destroy (rule->u.custom.data);
185       break;
186     default:
187       g_assert_not_reached ();
188       break;
189     }
190   
191   g_free (rule);
192 }
193
194 static void
195 gtk_recent_filter_finalize (GObject *object)
196 {
197   GtkRecentFilter *filter = GTK_RECENT_FILTER (object);
198   
199   g_free (filter->name);
200   
201   if (filter->rules)
202     {
203       g_slist_foreach (filter->rules,
204                        (GFunc) filter_rule_free,
205                        NULL);
206       g_slist_free (filter->rules);
207     }
208   
209   G_OBJECT_CLASS (gtk_recent_filter_parent_class)->finalize (object);
210 }
211
212 static void
213 gtk_recent_filter_class_init (GtkRecentFilterClass *klass)
214 {
215   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
216   
217   gobject_class->finalize = gtk_recent_filter_finalize;
218 }
219
220 static void
221 gtk_recent_filter_init (GtkRecentFilter *filter)
222 {
223
224 }
225
226
227 /*
228  * GtkBuildable implementation
229  */
230 static void
231 gtk_recent_filter_buildable_init (GtkBuildableIface *iface)
232 {
233   iface->custom_tag_start = gtk_recent_filter_buildable_custom_tag_start;
234   iface->custom_tag_end = gtk_recent_filter_buildable_custom_tag_end;
235 }
236
237
238 typedef enum {
239   PARSE_MIME_TYPES,
240   PARSE_PATTERNS,
241   PARSE_APPLICATIONS
242 } ParserType;
243
244 typedef struct {
245   GtkRecentFilter *filter;
246   ParserType       type;
247   GString         *string;
248   gboolean         parsing;
249 } SubParserData;
250
251 static void
252 parser_start_element (GMarkupParseContext *context,
253                       const gchar         *element_name,
254                       const gchar        **names,
255                       const gchar        **values,
256                       gpointer             user_data,
257                       GError             **error)
258 {
259   SubParserData *parser_data = (SubParserData*)user_data;
260
261   if (strcmp (element_name, "mime-types") == 0)
262     return;
263   else if (strcmp (element_name, "mime-type") == 0)
264     {
265       parser_data->parsing = TRUE;
266       return;
267     }
268   else if (strcmp (element_name, "patterns") == 0)
269     return;
270   else if (strcmp (element_name, "pattern") == 0)
271     {
272       parser_data->parsing = TRUE;
273       return;
274     }
275   else if (strcmp (element_name, "applications") == 0)
276     return;
277   else if (strcmp (element_name, "application") == 0)
278     {
279       parser_data->parsing = TRUE;
280       return;
281     }
282   else
283     g_warning ("Unsupported tag for GtkRecentFilter: %s\n", element_name);
284 }
285
286 static void
287 parser_text_element (GMarkupParseContext *context,
288                      const gchar         *text,
289                      gsize                text_len,
290                      gpointer             user_data,
291                      GError             **error)
292 {
293   SubParserData *parser_data = (SubParserData*)user_data;
294
295   if (parser_data->parsing)
296     g_string_append_len (parser_data->string, text, text_len);
297 }
298
299 static void
300 parser_end_element (GMarkupParseContext *context,
301                     const gchar         *element_name,
302                     gpointer             user_data,
303                     GError             **error)
304 {
305   SubParserData *parser_data = (SubParserData*)user_data;
306
307   if (parser_data->string)
308     {
309       switch (parser_data->type)
310         {
311         case PARSE_MIME_TYPES:
312           gtk_recent_filter_add_mime_type (parser_data->filter, parser_data->string->str);
313           break;
314         case PARSE_PATTERNS:
315           gtk_recent_filter_add_pattern (parser_data->filter, parser_data->string->str);
316           break;
317         case PARSE_APPLICATIONS:
318           gtk_recent_filter_add_application (parser_data->filter, parser_data->string->str);
319           break;
320         default:
321           break;
322         }
323     }
324
325   g_string_set_size (parser_data->string, 0);
326   parser_data->parsing = FALSE;
327 }
328
329 static const GMarkupParser sub_parser =
330   {
331     parser_start_element,
332     parser_end_element,
333     parser_text_element,
334   };
335
336 static gboolean
337 gtk_recent_filter_buildable_custom_tag_start (GtkBuildable  *buildable,
338                                               GtkBuilder    *builder,
339                                               GObject       *child,
340                                               const gchar   *tagname,
341                                               GMarkupParser *parser,
342                                               gpointer      *data)
343 {
344   SubParserData *parser_data = NULL;
345
346   if (strcmp (tagname, "mime-types") == 0)
347     {
348       parser_data         = g_slice_new0 (SubParserData);
349       parser_data->string = g_string_new ("");
350       parser_data->type   = PARSE_MIME_TYPES;
351       parser_data->filter = GTK_RECENT_FILTER (buildable);
352
353       *parser = sub_parser;
354       *data = parser_data;
355     }
356   else if (strcmp (tagname, "patterns") == 0)
357     {
358       parser_data         = g_slice_new0 (SubParserData);
359       parser_data->string = g_string_new ("");
360       parser_data->type   = PARSE_PATTERNS;
361       parser_data->filter = GTK_RECENT_FILTER (buildable);
362
363       *parser = sub_parser;
364       *data = parser_data;
365     }
366   else if (strcmp (tagname, "applications") == 0)
367     {
368       parser_data         = g_slice_new0 (SubParserData);
369       parser_data->string = g_string_new ("");
370       parser_data->type   = PARSE_APPLICATIONS;
371       parser_data->filter = GTK_RECENT_FILTER (buildable);
372
373       *parser = sub_parser;
374       *data = parser_data;
375     }
376
377   return parser_data != NULL;
378 }
379
380 static void
381 gtk_recent_filter_buildable_custom_tag_end (GtkBuildable *buildable,
382                                             GtkBuilder   *builder,
383                                             GObject      *child,
384                                             const gchar  *tagname,
385                                             gpointer     *data)
386 {
387   if (strcmp (tagname, "mime-types") == 0 ||
388       strcmp (tagname, "patterns") == 0 ||
389       strcmp (tagname, "applications") == 0)
390     {
391       SubParserData *parser_data = (SubParserData*)data;
392
393       g_string_free (parser_data->string, TRUE);
394       g_slice_free (SubParserData, parser_data);
395     }
396 }
397
398 /*
399  * Public API
400  */
401  
402 /**
403  * gtk_recent_filter_new:
404  *
405  * Creates a new #GtkRecentFilter with no rules added to it.
406  * Such filter does not accept any recently used resources, so is not
407  * particularly useful until you add rules with
408  * gtk_recent_filter_add_pattern(), gtk_recent_filter_add_mime_type(),
409  * gtk_recent_filter_add_application(), gtk_recent_filter_add_age().
410  * To create a filter that accepts any recently used resource, use:
411  * |[
412  * GtkRecentFilter *filter = gtk_recent_filter_new ();
413  * gtk_recent_filter_add_pattern (filter, "*");
414  * ]|
415  *
416  * Return value: a new #GtkRecentFilter
417  *
418  * Since: 2.10
419  */
420 GtkRecentFilter *
421 gtk_recent_filter_new (void)
422 {
423   return g_object_new (GTK_TYPE_RECENT_FILTER, NULL);
424 }
425
426 /**
427  * gtk_recent_filter_set_name:
428  * @filter: a #GtkRecentFilter
429  * @name: then human readable name of @filter
430  *
431  * Sets the human-readable name of the filter; this is the string
432  * that will be displayed in the recently used resources selector
433  * user interface if there is a selectable list of filters.
434  *
435  * Since: 2.10
436  */
437 void
438 gtk_recent_filter_set_name (GtkRecentFilter *filter,
439                             const gchar     *name)
440 {
441   g_return_if_fail (GTK_IS_RECENT_FILTER (filter));
442   
443   g_free (filter->name);
444   
445   if (name)
446     filter->name = g_strdup (name);
447 }
448
449 /**
450  * gtk_recent_filter_get_name:
451  * @filter: a #GtkRecentFilter
452  *
453  * Gets the human-readable name for the filter.
454  * See gtk_recent_filter_set_name().
455  *
456  * Return value: the name of the filter, or %NULL.  The returned string
457  *   is owned by the filter object and should not be freed.
458  *
459  * Since: 2.10
460  */
461 G_CONST_RETURN gchar *
462 gtk_recent_filter_get_name (GtkRecentFilter *filter)
463 {
464   g_return_val_if_fail (GTK_IS_RECENT_FILTER (filter), NULL);
465   
466   return filter->name;
467 }
468
469 /**
470  * gtk_recent_filter_get_needed:
471  * @filter: a #GtkRecentFilter
472  *
473  * Gets the fields that need to be filled in for the structure
474  * passed to gtk_recent_filter_filter()
475  * 
476  * This function will not typically be used by applications; it
477  * is intended principally for use in the implementation of
478  * #GtkRecentChooser.
479  * 
480  * Return value: bitfield of flags indicating needed fields when
481  *   calling gtk_recent_filter_filter()
482  *
483  * Since: 2.10
484  */
485 GtkRecentFilterFlags
486 gtk_recent_filter_get_needed (GtkRecentFilter *filter)
487 {
488   return filter->needed;
489 }
490
491 static void
492 recent_filter_add_rule (GtkRecentFilter *filter,
493                         FilterRule      *rule)
494 {
495   filter->needed |= rule->needed;
496   filter->rules = g_slist_append (filter->rules, rule);
497 }
498
499 /**
500  * gtk_recent_filter_add_mime_type:
501  * @filter: a #GtkRecentFilter
502  * @mime_type: a MIME type
503  *
504  * Adds a rule that allows resources based on their registered MIME type.
505  *
506  * Since: 2.10
507  */
508 void
509 gtk_recent_filter_add_mime_type (GtkRecentFilter *filter,
510                                  const gchar     *mime_type)
511 {
512   FilterRule *rule;
513   
514   g_return_if_fail (GTK_IS_RECENT_FILTER (filter));
515   g_return_if_fail (mime_type != NULL);
516   
517   rule = g_new0 (FilterRule, 1);
518   rule->type = FILTER_RULE_MIME_TYPE;
519   rule->needed = GTK_RECENT_FILTER_MIME_TYPE;
520   rule->u.mime_type = g_strdup (mime_type);
521   
522   recent_filter_add_rule (filter, rule);
523 }
524
525 /**
526  * gtk_recent_filter_add_pattern:
527  * @filter: a #GtkRecentFilter
528  * @pattern: a file pattern
529  *
530  * Adds a rule that allows resources based on a pattern matching their
531  * display name.
532  *
533  * Since: 2.10
534  */
535 void
536 gtk_recent_filter_add_pattern (GtkRecentFilter *filter,
537                                const gchar     *pattern)
538 {
539   FilterRule *rule;
540   
541   g_return_if_fail (GTK_IS_RECENT_FILTER (filter));
542   g_return_if_fail (pattern != NULL);
543   
544   rule = g_new0 (FilterRule, 1);
545   rule->type = FILTER_RULE_DISPLAY_NAME;
546   rule->needed = GTK_RECENT_FILTER_DISPLAY_NAME;
547   rule->u.pattern = g_strdup (pattern);
548   
549   recent_filter_add_rule (filter, rule);
550 }
551
552 /**
553  * gtk_recent_filter_add_pixbuf_formats:
554  * @filter: a #GtkRecentFilter
555  *
556  * Adds a rule allowing image files in the formats supported
557  * by GdkPixbuf.
558  *
559  * Since: 2.10
560  */
561 void
562 gtk_recent_filter_add_pixbuf_formats (GtkRecentFilter *filter)
563 {
564   FilterRule *rule;
565
566   g_return_if_fail (GTK_IS_RECENT_FILTER (filter));
567
568   rule = g_new0 (FilterRule, 1);
569   rule->type = FILTER_RULE_PIXBUF_FORMATS;
570   rule->needed = GTK_RECENT_FILTER_MIME_TYPE;
571   rule->u.pixbuf_formats = gdk_pixbuf_get_formats ();
572   
573   recent_filter_add_rule (filter, rule);
574 }
575
576 /**
577  * gtk_recent_filter_add_application:
578  * @filter: a #GtkRecentFilter
579  * @application: an application name
580  *
581  * Adds a rule that allows resources based on the name of the application
582  * that has registered them.
583  *
584  * Since: 2.10
585  */
586 void
587 gtk_recent_filter_add_application (GtkRecentFilter *filter,
588                                    const gchar     *application)
589 {
590   FilterRule *rule;
591   
592   g_return_if_fail (GTK_IS_RECENT_FILTER (filter));
593   g_return_if_fail (application != NULL);
594   
595   rule = g_new0 (FilterRule, 1);
596   rule->type = FILTER_RULE_APPLICATION;
597   rule->needed = GTK_RECENT_FILTER_APPLICATION;
598   rule->u.application = g_strdup (application);
599   
600   recent_filter_add_rule (filter, rule);
601 }
602
603 /**
604  * gtk_recent_filter_add_group:
605  * @filter: a #GtkRecentFilter
606  * @group: a group name
607  *
608  * Adds a rule that allows resources based on the name of the group
609  * to which they belong
610  *
611  * Since: 2.10
612  */
613 void
614 gtk_recent_filter_add_group (GtkRecentFilter *filter,
615                              const gchar     *group)
616 {
617   FilterRule *rule;
618   
619   g_return_if_fail (GTK_IS_RECENT_FILTER (filter));
620   g_return_if_fail (group != NULL);
621   
622   rule = g_new0 (FilterRule, 1);
623   rule->type = FILTER_RULE_GROUP;
624   rule->needed = GTK_RECENT_FILTER_GROUP;
625   rule->u.group = g_strdup (group);
626   
627   recent_filter_add_rule (filter, rule);
628 }
629
630 /**
631  * gtk_recent_filter_add_age:
632  * @filter: a #GtkRecentFilter
633  * @days: number of days
634  *
635  * Adds a rule that allows resources based on their age - that is, the number
636  * of days elapsed since they were last modified.
637  *
638  * Since: 2.10
639  */
640 void
641 gtk_recent_filter_add_age (GtkRecentFilter *filter,
642                            gint             days)
643 {
644   FilterRule *rule;
645   
646   g_return_if_fail (GTK_IS_RECENT_FILTER (filter));
647   
648   rule = g_new0 (FilterRule, 1);
649   rule->type = FILTER_RULE_AGE;
650   rule->needed = GTK_RECENT_FILTER_AGE;
651   rule->u.age = days;
652   
653   recent_filter_add_rule (filter, rule);
654 }
655
656 /**
657  * gtk_recent_filter_add_custom:
658  * @filter: a #GtkRecentFilter
659  * @needed: bitfield of flags indicating the information that the custom
660  *          filter function needs.
661  * @func: callback function; if the function returns %TRUE, then
662  *   the file will be displayed.
663  * @data: data to pass to @func
664  * @data_destroy: function to call to free @data when it is no longer needed.
665  * 
666  * Adds a rule to a filter that allows resources based on a custom callback
667  * function. The bitfield @needed which is passed in provides information
668  * about what sorts of information that the filter function needs;
669  * this allows GTK+ to avoid retrieving expensive information when
670  * it isn't needed by the filter.
671  * 
672  * Since: 2.10
673  **/
674 void
675 gtk_recent_filter_add_custom (GtkRecentFilter      *filter,
676                               GtkRecentFilterFlags  needed,
677                               GtkRecentFilterFunc   func,
678                               gpointer              data,
679                               GDestroyNotify        data_destroy)
680 {
681   FilterRule *rule;
682   
683   g_return_if_fail (GTK_IS_RECENT_FILTER (filter));
684   g_return_if_fail (func != NULL);
685
686   rule = g_new0 (FilterRule, 1);
687   rule->type = FILTER_RULE_CUSTOM;
688   rule->needed = needed;
689   rule->u.custom.func = func;
690   rule->u.custom.data = data;
691   rule->u.custom.data_destroy = data_destroy;
692
693   recent_filter_add_rule (filter, rule);
694 }
695
696
697 /**
698  * gtk_recent_filter_filter:
699  * @filter: a #GtkRecentFilter
700  * @filter_info: a #GtkRecentFilterInfo structure containing information
701  *   about a recently used resource
702  *
703  * Tests whether a file should be displayed according to @filter.
704  * The #GtkRecentFilterInfo structure @filter_info should include
705  * the fields returned from gtk_recent_filter_get_needed().
706  *
707  * This function will not typically be used by applications; it
708  * is intended principally for use in the implementation of
709  * #GtkRecentChooser.
710  * 
711  * Return value: %TRUE if the file should be displayed
712  *
713  * Since: 2.10
714  */
715 gboolean
716 gtk_recent_filter_filter (GtkRecentFilter           *filter,
717                           const GtkRecentFilterInfo *filter_info)
718 {
719   GSList *l;
720   
721   g_return_val_if_fail (GTK_IS_RECENT_FILTER (filter), FALSE);
722   g_return_val_if_fail (filter_info != NULL, FALSE);
723   
724   for (l = filter->rules; l != NULL; l = l->next)
725     {
726       FilterRule *rule = (FilterRule *) l->data;
727
728       if ((filter_info->contains & rule->needed) != rule->needed)
729         continue;
730
731       switch (rule->type)
732         {
733         case FILTER_RULE_MIME_TYPE:
734           if (filter_info->mime_type != NULL &&
735               g_content_type_is_a (filter_info->mime_type, rule->u.mime_type))
736             return TRUE;
737           break;
738         case FILTER_RULE_APPLICATION:
739           if (filter_info->applications)
740             {
741               gint i;
742               
743               for (i = 0; filter_info->applications[i] != NULL; i++)
744                 {
745                   if (strcmp (filter_info->applications[i], rule->u.application) == 0)
746                     return TRUE;
747                 }
748             }
749           break;
750         case FILTER_RULE_GROUP:
751           if (filter_info->groups)
752             {
753               gint i;
754
755               for (i = 0; filter_info->groups[i] != NULL; i++)
756                 {
757                   if (strcmp (filter_info->groups[i], rule->u.group) == 0)
758                     return TRUE;
759                 }
760             }
761           break;
762         case FILTER_RULE_PIXBUF_FORMATS:
763           {
764             GSList *list;
765             if (!filter_info->mime_type)
766               break;
767
768             for (list = rule->u.pixbuf_formats; list; list = list->next)
769               {
770                 gint i;
771                 gchar **mime_types;
772
773                 mime_types = gdk_pixbuf_format_get_mime_types (list->data);
774
775                 for (i = 0; mime_types[i] != NULL; i++)
776                   {
777                     if (strcmp (mime_types[i], filter_info->mime_type) == 0)
778                       {
779                         g_strfreev (mime_types);
780                         return TRUE;
781                       }
782                   }
783
784                 g_strfreev (mime_types);
785               }
786             break;
787           }
788         case FILTER_RULE_URI:
789           if ((filter_info->uri != NULL) &&
790               _gtk_fnmatch (rule->u.uri, filter_info->uri, FALSE))
791             return TRUE;
792           break;
793         case FILTER_RULE_DISPLAY_NAME:
794           if ((filter_info->display_name != NULL) &&
795               _gtk_fnmatch (rule->u.pattern, filter_info->display_name, FALSE))
796             return TRUE;
797           break;
798         case FILTER_RULE_AGE:
799           if ((filter_info->age != -1) &&
800               (filter_info->age < rule->u.age))
801             return TRUE;
802           break;
803         case FILTER_RULE_CUSTOM:
804           if (rule->u.custom.func (filter_info, rule->u.custom.data))
805             return TRUE;
806           break;
807         }
808     }
809   
810   return FALSE;
811 }