]> Pileus Git - ~andy/gtk/blob - gtk/gtkrecentfilter.c
Merge branch 'windows_list'
[~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 #include "config.h"
22 #include <string.h>
23
24 #include "gtkrecentfilter.h"
25 #include "gtkintl.h"
26 #include "gtkprivate.h"
27
28 typedef struct _GtkRecentFilterClass GtkRecentFilterClass;
29 typedef struct _FilterRule FilterRule;
30
31 typedef enum {
32   FILTER_RULE_URI,
33   FILTER_RULE_DISPLAY_NAME,
34   FILTER_RULE_MIME_TYPE,
35   FILTER_RULE_PIXBUF_FORMATS,
36   FILTER_RULE_APPLICATION,
37   FILTER_RULE_AGE,
38   FILTER_RULE_GROUP,
39   FILTER_RULE_CUSTOM
40 } FilterRuleType;
41
42 struct _GtkRecentFilter
43 {
44   GtkObject parent_instance;
45   
46   gchar *name;
47   GSList *rules;
48   
49   GtkRecentFilterFlags needed;
50 };
51
52 struct _GtkRecentFilterClass
53 {
54   GtkObjectClass parent_class;
55 };
56
57 struct _FilterRule
58 {
59   FilterRuleType type;
60   GtkRecentFilterFlags needed;
61   
62   union {
63     gchar *uri;
64     gchar *pattern;
65     gchar *mime_type;
66     GSList *pixbuf_formats;
67     gchar *application;
68     gchar *group;
69     gint age;
70     struct {
71       GtkRecentFilterFunc func;
72       gpointer data;
73       GDestroyNotify data_destroy;
74     } custom;
75   } u;
76 };
77
78 G_DEFINE_TYPE (GtkRecentFilter, gtk_recent_filter, GTK_TYPE_OBJECT)
79
80
81 static void
82 filter_rule_free (FilterRule *rule)
83 {
84   switch (rule->type)
85     {
86     case FILTER_RULE_MIME_TYPE:
87       g_free (rule->u.mime_type);
88       break;
89     case FILTER_RULE_URI:
90       g_free (rule->u.uri);
91       break;
92     case FILTER_RULE_DISPLAY_NAME:
93       g_free (rule->u.pattern);
94       break;
95     case FILTER_RULE_PIXBUF_FORMATS:
96       g_slist_free (rule->u.pixbuf_formats);
97       break;
98     case FILTER_RULE_AGE:
99       break;
100     case FILTER_RULE_APPLICATION:
101       g_free (rule->u.application);
102       break;
103     case FILTER_RULE_GROUP:
104       g_free (rule->u.group);
105       break;
106     case FILTER_RULE_CUSTOM:
107       if (rule->u.custom.data_destroy)
108         rule->u.custom.data_destroy (rule->u.custom.data);
109       break;
110     default:
111       g_assert_not_reached ();
112       break;
113     }
114   
115   g_free (rule);
116 }
117
118 static void
119 gtk_recent_filter_finalize (GObject *object)
120 {
121   GtkRecentFilter *filter = GTK_RECENT_FILTER (object);
122   
123   g_free (filter->name);
124   
125   if (filter->rules)
126     {
127       g_slist_foreach (filter->rules,
128                        (GFunc) filter_rule_free,
129                        NULL);
130       g_slist_free (filter->rules);
131     }
132   
133   G_OBJECT_CLASS (gtk_recent_filter_parent_class)->finalize (object);
134 }
135
136 static void
137 gtk_recent_filter_class_init (GtkRecentFilterClass *klass)
138 {
139   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
140   
141   gobject_class->finalize = gtk_recent_filter_finalize;
142 }
143
144 static void
145 gtk_recent_filter_init (GtkRecentFilter *filter)
146 {
147
148 }
149
150 /*
151  * Public API
152  */
153  
154 /**
155  * gtk_recent_filter_new:
156  *
157  * Creates a new #GtkRecentFilter with no rules added to it.
158  * Such filter does not accept any recently used resources, so is not
159  * particularly useful until you add rules with
160  * gtk_recent_filter_add_pattern(), gtk_recent_filter_add_mime_type(),
161  * gtk_recent_filter_add_application(), gtk_recent_filter_add_age().
162  * To create a filter that accepts any recently used resource, use:
163  * |[
164  * GtkRecentFilter *filter = gtk_recent_filter_new ();
165  * gtk_recent_filter_add_pattern (filter, "*");
166  * ]|
167  *
168  * Return value: a new #GtkRecentFilter
169  *
170  * Since: 2.10
171  */
172 GtkRecentFilter *
173 gtk_recent_filter_new (void)
174 {
175   return g_object_new (GTK_TYPE_RECENT_FILTER, NULL);
176 }
177
178 /**
179  * gtk_recent_filter_set_name:
180  * @filter: a #GtkRecentFilter
181  * @name: then human readable name of @filter
182  *
183  * Sets the human-readable name of the filter; this is the string
184  * that will be displayed in the recently used resources selector
185  * user interface if there is a selectable list of filters.
186  *
187  * Since: 2.10
188  */
189 void
190 gtk_recent_filter_set_name (GtkRecentFilter *filter,
191                             const gchar     *name)
192 {
193   g_return_if_fail (GTK_IS_RECENT_FILTER (filter));
194   
195   g_free (filter->name);
196   
197   if (name)
198     filter->name = g_strdup (name);
199 }
200
201 /**
202  * gtk_recent_filter_get_name:
203  * @filter: a #GtkRecentFilter
204  *
205  * Gets the human-readable name for the filter.
206  * See gtk_recent_filter_set_name().
207  *
208  * Return value: the name of the filter, or %NULL.  The returned string
209  *   is owned by the filter object and should not be freed.
210  *
211  * Since: 2.10
212  */
213 G_CONST_RETURN gchar *
214 gtk_recent_filter_get_name (GtkRecentFilter *filter)
215 {
216   g_return_val_if_fail (GTK_IS_RECENT_FILTER (filter), NULL);
217   
218   return filter->name;
219 }
220
221 /**
222  * gtk_recent_filter_get_needed:
223  * @filter: a #GtkRecentFilter
224  *
225  * Gets the fields that need to be filled in for the structure
226  * passed to gtk_recent_filter_filter()
227  * 
228  * This function will not typically be used by applications; it
229  * is intended principally for use in the implementation of
230  * #GtkRecentChooser.
231  * 
232  * Return value: bitfield of flags indicating needed fields when
233  *   calling gtk_recent_filter_filter()
234  *
235  * Since: 2.10
236  */
237 GtkRecentFilterFlags
238 gtk_recent_filter_get_needed (GtkRecentFilter *filter)
239 {
240   return filter->needed;
241 }
242
243 static void
244 recent_filter_add_rule (GtkRecentFilter *filter,
245                         FilterRule      *rule)
246 {
247   filter->needed |= rule->needed;
248   filter->rules = g_slist_append (filter->rules, rule);
249 }
250
251 /**
252  * gtk_recent_filter_add_mime_type:
253  * @filter: a #GtkRecentFilter
254  * @mime_type: a MIME type
255  *
256  * Adds a rule that allows resources based on their registered MIME type.
257  *
258  * Since: 2.10
259  */
260 void
261 gtk_recent_filter_add_mime_type (GtkRecentFilter *filter,
262                                  const gchar     *mime_type)
263 {
264   FilterRule *rule;
265   
266   g_return_if_fail (GTK_IS_RECENT_FILTER (filter));
267   g_return_if_fail (mime_type != NULL);
268   
269   rule = g_new0 (FilterRule, 1);
270   rule->type = FILTER_RULE_MIME_TYPE;
271   rule->needed = GTK_RECENT_FILTER_MIME_TYPE;
272   rule->u.mime_type = g_strdup (mime_type);
273   
274   recent_filter_add_rule (filter, rule);
275 }
276
277 /**
278  * gtk_recent_filter_add_pattern:
279  * @filter: a #GtkRecentFilter
280  * @pattern: a file pattern
281  *
282  * Adds a rule that allows resources based on a pattern matching their
283  * display name.
284  *
285  * Since: 2.10
286  */
287 void
288 gtk_recent_filter_add_pattern (GtkRecentFilter *filter,
289                                const gchar     *pattern)
290 {
291   FilterRule *rule;
292   
293   g_return_if_fail (GTK_IS_RECENT_FILTER (filter));
294   g_return_if_fail (pattern != NULL);
295   
296   rule = g_new0 (FilterRule, 1);
297   rule->type = FILTER_RULE_DISPLAY_NAME;
298   rule->needed = GTK_RECENT_FILTER_DISPLAY_NAME;
299   rule->u.pattern = g_strdup (pattern);
300   
301   recent_filter_add_rule (filter, rule);
302 }
303
304 /**
305  * gtk_recent_filter_add_pixbuf_formats:
306  * @filter: a #GtkRecentFilter
307  *
308  * Adds a rule allowing image files in the formats supported
309  * by GdkPixbuf.
310  *
311  * Since: 2.10
312  */
313 void
314 gtk_recent_filter_add_pixbuf_formats (GtkRecentFilter *filter)
315 {
316   FilterRule *rule;
317
318   g_return_if_fail (GTK_IS_RECENT_FILTER (filter));
319
320   rule = g_new0 (FilterRule, 1);
321   rule->type = FILTER_RULE_PIXBUF_FORMATS;
322   rule->needed = GTK_RECENT_FILTER_MIME_TYPE;
323   rule->u.pixbuf_formats = gdk_pixbuf_get_formats ();
324   
325   recent_filter_add_rule (filter, rule);
326 }
327
328 /**
329  * gtk_recent_filter_add_application:
330  * @filter: a #GtkRecentFilter
331  * @application: an application name
332  *
333  * Adds a rule that allows resources based on the name of the application
334  * that has registered them.
335  *
336  * Since: 2.10
337  */
338 void
339 gtk_recent_filter_add_application (GtkRecentFilter *filter,
340                                    const gchar     *application)
341 {
342   FilterRule *rule;
343   
344   g_return_if_fail (GTK_IS_RECENT_FILTER (filter));
345   g_return_if_fail (application != NULL);
346   
347   rule = g_new0 (FilterRule, 1);
348   rule->type = FILTER_RULE_APPLICATION;
349   rule->needed = GTK_RECENT_FILTER_APPLICATION;
350   rule->u.application = g_strdup (application);
351   
352   recent_filter_add_rule (filter, rule);
353 }
354
355 /**
356  * gtk_recent_filter_add_group:
357  * @filter: a #GtkRecentFilter
358  * @group: a group name
359  *
360  * Adds a rule that allows resources based on the name of the group
361  * to which they belong
362  *
363  * Since: 2.10
364  */
365 void
366 gtk_recent_filter_add_group (GtkRecentFilter *filter,
367                              const gchar     *group)
368 {
369   FilterRule *rule;
370   
371   g_return_if_fail (GTK_IS_RECENT_FILTER (filter));
372   g_return_if_fail (group != NULL);
373   
374   rule = g_new0 (FilterRule, 1);
375   rule->type = FILTER_RULE_GROUP;
376   rule->needed = GTK_RECENT_FILTER_GROUP;
377   rule->u.group = g_strdup (group);
378   
379   recent_filter_add_rule (filter, rule);
380 }
381
382 /**
383  * gtk_recent_filter_add_age:
384  * @filter: a #GtkRecentFilter
385  * @days: number of days
386  *
387  * Adds a rule that allows resources based on their age - that is, the number
388  * of days elapsed since they were last modified.
389  *
390  * Since: 2.10
391  */
392 void
393 gtk_recent_filter_add_age (GtkRecentFilter *filter,
394                            gint             days)
395 {
396   FilterRule *rule;
397   
398   g_return_if_fail (GTK_IS_RECENT_FILTER (filter));
399   
400   rule = g_new0 (FilterRule, 1);
401   rule->type = FILTER_RULE_AGE;
402   rule->needed = GTK_RECENT_FILTER_AGE;
403   rule->u.age = days;
404   
405   recent_filter_add_rule (filter, rule);
406 }
407
408 /**
409  * gtk_recent_filter_add_custom:
410  * @filter: a #GtkRecentFilter
411  * @needed: bitfield of flags indicating the information that the custom
412  *          filter function needs.
413  * @func: callback function; if the function returns %TRUE, then
414  *   the file will be displayed.
415  * @data: data to pass to @func
416  * @data_destroy: function to call to free @data when it is no longer needed.
417  * 
418  * Adds a rule to a filter that allows resources based on a custom callback
419  * function. The bitfield @needed which is passed in provides information
420  * about what sorts of information that the filter function needs;
421  * this allows GTK+ to avoid retrieving expensive information when
422  * it isn't needed by the filter.
423  * 
424  * Since: 2.10
425  **/
426 void
427 gtk_recent_filter_add_custom (GtkRecentFilter      *filter,
428                               GtkRecentFilterFlags  needed,
429                               GtkRecentFilterFunc   func,
430                               gpointer              data,
431                               GDestroyNotify        data_destroy)
432 {
433   FilterRule *rule;
434   
435   g_return_if_fail (GTK_IS_RECENT_FILTER (filter));
436   g_return_if_fail (func != NULL);
437
438   rule = g_new0 (FilterRule, 1);
439   rule->type = FILTER_RULE_CUSTOM;
440   rule->needed = needed;
441   rule->u.custom.func = func;
442   rule->u.custom.data = data;
443   rule->u.custom.data_destroy = data_destroy;
444
445   recent_filter_add_rule (filter, rule);
446 }
447
448
449 /**
450  * gtk_recent_filter_filter:
451  * @filter: a #GtkRecentFilter
452  * @filter_info: a #GtkRecentFilterInfo structure containing information
453  *   about a recently used resource
454  *
455  * Tests whether a file should be displayed according to @filter.
456  * The #GtkRecentFilterInfo structure @filter_info should include
457  * the fields returned from gtk_recent_filter_get_needed().
458  *
459  * This function will not typically be used by applications; it
460  * is intended principally for use in the implementation of
461  * #GtkRecentChooser.
462  * 
463  * Return value: %TRUE if the file should be displayed
464  *
465  * Since: 2.10
466  */
467 gboolean
468 gtk_recent_filter_filter (GtkRecentFilter           *filter,
469                           const GtkRecentFilterInfo *filter_info)
470 {
471   GSList *l;
472   
473   g_return_val_if_fail (GTK_IS_RECENT_FILTER (filter), FALSE);
474   g_return_val_if_fail (filter_info != NULL, FALSE);
475   
476   for (l = filter->rules; l != NULL; l = l->next)
477     {
478       FilterRule *rule = (FilterRule *) l->data;
479
480       if ((filter_info->contains & rule->needed) != rule->needed)
481         continue;
482
483       switch (rule->type)
484         {
485         case FILTER_RULE_MIME_TYPE:
486           if (filter_info->mime_type != NULL &&
487               g_content_type_is_a (filter_info->mime_type, rule->u.mime_type))
488             return TRUE;
489           break;
490         case FILTER_RULE_APPLICATION:
491           if (filter_info->applications)
492             {
493               gint i;
494               
495               for (i = 0; filter_info->applications[i] != NULL; i++)
496                 {
497                   if (strcmp (filter_info->applications[i], rule->u.application) == 0)
498                     return TRUE;
499                 }
500             }
501           break;
502         case FILTER_RULE_GROUP:
503           if (filter_info->groups)
504             {
505               gint i;
506
507               for (i = 0; filter_info->groups[i] != NULL; i++)
508                 {
509                   if (strcmp (filter_info->groups[i], rule->u.group) == 0)
510                     return TRUE;
511                 }
512             }
513           break;
514         case FILTER_RULE_PIXBUF_FORMATS:
515           {
516             GSList *list;
517             if (!filter_info->mime_type)
518               break;
519
520             for (list = rule->u.pixbuf_formats; list; list = list->next)
521               {
522                 gint i;
523                 gchar **mime_types;
524
525                 mime_types = gdk_pixbuf_format_get_mime_types (list->data);
526
527                 for (i = 0; mime_types[i] != NULL; i++)
528                   {
529                     if (strcmp (mime_types[i], filter_info->mime_type) == 0)
530                       {
531                         g_strfreev (mime_types);
532                         return TRUE;
533                       }
534                   }
535
536                 g_strfreev (mime_types);
537               }
538             break;
539           }
540         case FILTER_RULE_URI:
541           if ((filter_info->uri != NULL) &&
542               _gtk_fnmatch (rule->u.uri, filter_info->uri, FALSE))
543             return TRUE;
544           break;
545         case FILTER_RULE_DISPLAY_NAME:
546           if ((filter_info->display_name != NULL) &&
547               _gtk_fnmatch (rule->u.pattern, filter_info->display_name, FALSE))
548             return TRUE;
549           break;
550         case FILTER_RULE_AGE:
551           if ((filter_info->age != -1) &&
552               (filter_info->age < rule->u.age))
553             return TRUE;
554           break;
555         case FILTER_RULE_CUSTOM:
556           if (rule->u.custom.func (filter_info, rule->u.custom.data))
557             return TRUE;
558           break;
559         }
560     }
561   
562   return FALSE;
563 }