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