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