]> Pileus Git - ~andy/gtk/blob - gtk/gtkrecentchooserutils.c
stylecontext: Do invalidation on first resize container
[~andy/gtk] / gtk / gtkrecentchooserutils.c
1 /* gtkrecentchooserutils.h - Private utility functions for implementing a
2  *                           GtkRecentChooser interface
3  *
4  * Copyright (C) 2006 Emmanuele Bassi
5  *
6  * All rights reserved
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
20  *
21  * Based on gtkfilechooserutils.c:
22  *      Copyright (C) 2003 Red Hat, Inc.
23  */
24
25 #include "config.h"
26
27 #include "gtkrecentchooserutils.h"
28
29 /* Methods */
30 static void      delegate_set_sort_func              (GtkRecentChooser  *chooser,
31                                                       GtkRecentSortFunc  sort_func,
32                                                       gpointer           sort_data,
33                                                       GDestroyNotify     data_destroy);
34 static void      delegate_add_filter                 (GtkRecentChooser  *chooser,
35                                                       GtkRecentFilter   *filter);
36 static void      delegate_remove_filter              (GtkRecentChooser  *chooser,
37                                                       GtkRecentFilter   *filter);
38 static GSList   *delegate_list_filters               (GtkRecentChooser  *chooser);
39 static gboolean  delegate_select_uri                 (GtkRecentChooser  *chooser,
40                                                       const gchar       *uri,
41                                                       GError           **error);
42 static void      delegate_unselect_uri               (GtkRecentChooser  *chooser,
43                                                       const gchar       *uri);
44 static GList    *delegate_get_items                  (GtkRecentChooser  *chooser);
45 static GtkRecentManager *delegate_get_recent_manager (GtkRecentChooser  *chooser);
46 static void      delegate_select_all                 (GtkRecentChooser  *chooser);
47 static void      delegate_unselect_all               (GtkRecentChooser  *chooser);
48 static gboolean  delegate_set_current_uri            (GtkRecentChooser  *chooser,
49                                                       const gchar       *uri,
50                                                       GError           **error);
51 static gchar *   delegate_get_current_uri            (GtkRecentChooser  *chooser);
52
53 /* Signals */
54 static void      delegate_notify            (GObject          *object,
55                                              GParamSpec       *pspec,
56                                              gpointer          user_data);
57 static void      delegate_selection_changed (GtkRecentChooser *receiver,
58                                              gpointer          user_data);
59 static void      delegate_item_activated    (GtkRecentChooser *receiver,
60                                              gpointer          user_data);
61
62 /**
63  * _gtk_recent_chooser_install_properties:
64  * @klass: the class structure for a type deriving from #GObject
65  *
66  * Installs the necessary properties for a class implementing
67  * #GtkRecentChooser. A #GtkParamSpecOverride property is installed
68  * for each property, using the values from the #GtkRecentChooserProp
69  * enumeration. The caller must make sure itself that the enumeration
70  * values don't collide with some other property values they
71  * are using.
72  */
73 void
74 _gtk_recent_chooser_install_properties (GObjectClass *klass)
75 {
76   g_object_class_override_property (klass,
77                                     GTK_RECENT_CHOOSER_PROP_RECENT_MANAGER,
78                                     "recent-manager");                                      
79   g_object_class_override_property (klass,
80                                     GTK_RECENT_CHOOSER_PROP_SHOW_PRIVATE,
81                                     "show-private");
82   g_object_class_override_property (klass,
83                                     GTK_RECENT_CHOOSER_PROP_SHOW_TIPS,
84                                     "show-tips");
85   g_object_class_override_property (klass,
86                                     GTK_RECENT_CHOOSER_PROP_SHOW_ICONS,
87                                     "show-icons");
88   g_object_class_override_property (klass,
89                                     GTK_RECENT_CHOOSER_PROP_SHOW_NOT_FOUND,
90                                     "show-not-found");
91   g_object_class_override_property (klass,
92                                     GTK_RECENT_CHOOSER_PROP_SELECT_MULTIPLE,
93                                     "select-multiple");
94   g_object_class_override_property (klass,
95                                     GTK_RECENT_CHOOSER_PROP_LIMIT,
96                                     "limit");
97   g_object_class_override_property (klass,
98                                     GTK_RECENT_CHOOSER_PROP_LOCAL_ONLY,
99                                     "local-only");
100   g_object_class_override_property (klass,
101                                     GTK_RECENT_CHOOSER_PROP_SORT_TYPE,
102                                     "sort-type");
103   g_object_class_override_property (klass,
104                                     GTK_RECENT_CHOOSER_PROP_FILTER,
105                                     "filter");
106 }
107
108 /**
109  * _gtk_recent_chooser_delegate_iface_init:
110  * @iface: a #GtkRecentChooserIface
111  *
112  * An interface-initialization function for use in cases where
113  * an object is simply delegating the methods, signals of
114  * the #GtkRecentChooser interface to another object.
115  * _gtk_recent_chooser_set_delegate() must be called on each
116  * instance of the object so that the delegate object can
117  * be found.
118  */
119 void
120 _gtk_recent_chooser_delegate_iface_init (GtkRecentChooserIface *iface)
121 {
122   iface->set_current_uri = delegate_set_current_uri;
123   iface->get_current_uri = delegate_get_current_uri;
124   iface->select_uri = delegate_select_uri;
125   iface->unselect_uri = delegate_unselect_uri;
126   iface->select_all = delegate_select_all;
127   iface->unselect_all = delegate_unselect_all;
128   iface->get_items = delegate_get_items;
129   iface->get_recent_manager = delegate_get_recent_manager;
130   iface->set_sort_func = delegate_set_sort_func;
131   iface->add_filter = delegate_add_filter;
132   iface->remove_filter = delegate_remove_filter;
133   iface->list_filters = delegate_list_filters;
134 }
135
136 /**
137  * _gtk_recent_chooser_set_delegate:
138  * @receiver: a #GObject implementing #GtkRecentChooser
139  * @delegate: another #GObject implementing #GtkRecentChooser
140  *
141  * Establishes that calls on @receiver for #GtkRecentChooser
142  * methods should be delegated to @delegate, and that
143  * #GtkRecentChooser signals emitted on @delegate should be
144  * forwarded to @receiver. Must be used in conjunction with
145  * _gtk_recent_chooser_delegate_iface_init().
146  */
147 void
148 _gtk_recent_chooser_set_delegate (GtkRecentChooser *receiver,
149                                   GtkRecentChooser *delegate)
150 {
151   g_return_if_fail (GTK_IS_RECENT_CHOOSER (receiver));
152   g_return_if_fail (GTK_IS_RECENT_CHOOSER (delegate));
153   
154   g_object_set_data (G_OBJECT (receiver),
155                     "gtk-recent-chooser-delegate", delegate);
156   
157   g_signal_connect (delegate, "notify",
158                     G_CALLBACK (delegate_notify), receiver);
159   g_signal_connect (delegate, "selection-changed",
160                     G_CALLBACK (delegate_selection_changed), receiver);
161   g_signal_connect (delegate, "item-activated",
162                     G_CALLBACK (delegate_item_activated), receiver);
163 }
164
165 GQuark
166 _gtk_recent_chooser_delegate_get_quark (void)
167 {
168   static GQuark quark = 0;
169   
170   if (G_UNLIKELY (quark == 0))
171     quark = g_quark_from_static_string ("gtk-recent-chooser-delegate");
172   
173   return quark;
174 }
175
176 static GtkRecentChooser *
177 get_delegate (GtkRecentChooser *receiver)
178 {
179   return g_object_get_qdata (G_OBJECT (receiver),
180                              GTK_RECENT_CHOOSER_DELEGATE_QUARK);
181 }
182
183 static void
184 delegate_set_sort_func (GtkRecentChooser  *chooser,
185                         GtkRecentSortFunc  sort_func,
186                         gpointer           sort_data,
187                         GDestroyNotify     data_destroy)
188 {
189   gtk_recent_chooser_set_sort_func (get_delegate (chooser),
190                                     sort_func,
191                                     sort_data,
192                                     data_destroy);
193 }
194
195 static void
196 delegate_add_filter (GtkRecentChooser *chooser,
197                      GtkRecentFilter  *filter)
198 {
199   gtk_recent_chooser_add_filter (get_delegate (chooser), filter);
200 }
201
202 static void
203 delegate_remove_filter (GtkRecentChooser *chooser,
204                         GtkRecentFilter  *filter)
205 {
206   gtk_recent_chooser_remove_filter (get_delegate (chooser), filter);
207 }
208
209 static GSList *
210 delegate_list_filters (GtkRecentChooser *chooser)
211 {
212   return gtk_recent_chooser_list_filters (get_delegate (chooser));
213 }
214
215 static gboolean
216 delegate_select_uri (GtkRecentChooser  *chooser,
217                      const gchar       *uri,
218                      GError           **error)
219 {
220   return gtk_recent_chooser_select_uri (get_delegate (chooser), uri, error);
221 }
222
223 static void
224 delegate_unselect_uri (GtkRecentChooser *chooser,
225                        const gchar      *uri)
226 {
227  gtk_recent_chooser_unselect_uri (get_delegate (chooser), uri);
228 }
229
230 static GList *
231 delegate_get_items (GtkRecentChooser *chooser)
232 {
233   return gtk_recent_chooser_get_items (get_delegate (chooser));
234 }
235
236 static GtkRecentManager *
237 delegate_get_recent_manager (GtkRecentChooser *chooser)
238 {
239   return _gtk_recent_chooser_get_recent_manager (get_delegate (chooser));
240 }
241
242 static void
243 delegate_select_all (GtkRecentChooser *chooser)
244 {
245   gtk_recent_chooser_select_all (get_delegate (chooser));
246 }
247
248 static void
249 delegate_unselect_all (GtkRecentChooser *chooser)
250 {
251   gtk_recent_chooser_unselect_all (get_delegate (chooser));
252 }
253
254 static gboolean
255 delegate_set_current_uri (GtkRecentChooser  *chooser,
256                           const gchar       *uri,
257                           GError           **error)
258 {
259   return gtk_recent_chooser_set_current_uri (get_delegate (chooser), uri, error);
260 }
261
262 static gchar *
263 delegate_get_current_uri (GtkRecentChooser *chooser)
264 {
265   return gtk_recent_chooser_get_current_uri (get_delegate (chooser));
266 }
267
268 static void
269 delegate_notify (GObject    *object,
270                  GParamSpec *pspec,
271                  gpointer    user_data)
272 {
273   gpointer iface;
274
275   iface = g_type_interface_peek (g_type_class_peek (G_OBJECT_TYPE (object)),
276                                  gtk_recent_chooser_get_type ());
277   if (g_object_interface_find_property (iface, pspec->name))
278     g_object_notify (user_data, pspec->name);
279 }
280
281 static void
282 delegate_selection_changed (GtkRecentChooser *receiver,
283                             gpointer          user_data)
284 {
285   _gtk_recent_chooser_selection_changed (GTK_RECENT_CHOOSER (user_data));
286 }
287
288 static void
289 delegate_item_activated (GtkRecentChooser *receiver,
290                          gpointer          user_data)
291 {
292   _gtk_recent_chooser_item_activated (GTK_RECENT_CHOOSER (user_data));
293 }
294
295 static gint
296 sort_recent_items_mru (GtkRecentInfo *a,
297                        GtkRecentInfo *b,
298                        gpointer       unused)
299 {
300   g_assert (a != NULL && b != NULL);
301   
302   return gtk_recent_info_get_modified (b) - gtk_recent_info_get_modified (a);
303 }
304
305 static gint
306 sort_recent_items_lru (GtkRecentInfo *a,
307                        GtkRecentInfo *b,
308                        gpointer       unused)
309 {
310   g_assert (a != NULL && b != NULL);
311   
312   return -1 * (gtk_recent_info_get_modified (b) - gtk_recent_info_get_modified (a));
313 }
314
315 typedef struct
316 {
317   GtkRecentSortFunc func;
318   gpointer data;
319 } SortRecentData;
320
321 /* our proxy sorting function */
322 static gint
323 sort_recent_items_proxy (gpointer *a,
324                          gpointer *b,
325                          gpointer  user_data)
326 {
327   GtkRecentInfo *info_a = (GtkRecentInfo *) a;
328   GtkRecentInfo *info_b = (GtkRecentInfo *) b;
329   SortRecentData *sort_recent = user_data;
330
331   if (sort_recent->func)
332     return (* sort_recent->func) (info_a, info_b, sort_recent->data);
333   
334   /* fallback */
335   return 0;
336 }
337
338 static gboolean
339 get_is_recent_filtered (GtkRecentFilter *filter,
340                         GtkRecentInfo   *info)
341 {
342   GtkRecentFilterInfo filter_info;
343   GtkRecentFilterFlags needed;
344   gboolean retval;
345
346   g_assert (info != NULL);
347   
348   needed = gtk_recent_filter_get_needed (filter);
349   
350   filter_info.contains = GTK_RECENT_FILTER_URI | GTK_RECENT_FILTER_MIME_TYPE;
351   
352   filter_info.uri = gtk_recent_info_get_uri (info);
353   filter_info.mime_type = gtk_recent_info_get_mime_type (info);
354   
355   if (needed & GTK_RECENT_FILTER_DISPLAY_NAME)
356     {
357       filter_info.display_name = gtk_recent_info_get_display_name (info);
358       filter_info.contains |= GTK_RECENT_FILTER_DISPLAY_NAME;
359     }
360   else
361     filter_info.uri = NULL;
362   
363   if (needed & GTK_RECENT_FILTER_APPLICATION)
364     {
365       filter_info.applications = (const gchar **) gtk_recent_info_get_applications (info, NULL);
366       filter_info.contains |= GTK_RECENT_FILTER_APPLICATION;
367     }
368   else
369     filter_info.applications = NULL;
370
371   if (needed & GTK_RECENT_FILTER_GROUP)
372     {
373       filter_info.groups = (const gchar **) gtk_recent_info_get_groups (info, NULL);
374       filter_info.contains |= GTK_RECENT_FILTER_GROUP;
375     }
376   else
377     filter_info.groups = NULL;
378
379   if (needed & GTK_RECENT_FILTER_AGE)
380     {
381       filter_info.age = gtk_recent_info_get_age (info);
382       filter_info.contains |= GTK_RECENT_FILTER_AGE;
383     }
384   else
385     filter_info.age = -1;
386   
387   retval = gtk_recent_filter_filter (filter, &filter_info);
388   
389   /* these we own */
390   if (filter_info.applications)
391     g_strfreev ((gchar **) filter_info.applications);
392   if (filter_info.groups)
393     g_strfreev ((gchar **) filter_info.groups);
394   
395   return !retval;
396 }
397
398 /*
399  * _gtk_recent_chooser_get_items:
400  * @chooser: a #GtkRecentChooser
401  * @filter: a #GtkRecentFilter
402  * @sort_func: (allow-none): sorting function, or %NULL
403  * @sort_data: (allow-none): sorting function data, or %NULL
404  *
405  * Default implementation for getting the filtered, sorted and
406  * clamped list of recently used resources from a #GtkRecentChooser.
407  * This function should be used by implementations of the
408  * #GtkRecentChooser interface inside the GtkRecentChooser::get_items
409  * vfunc.
410  *
411  * Return value: a list of #GtkRecentInfo objects
412  */
413 GList *
414 _gtk_recent_chooser_get_items (GtkRecentChooser  *chooser,
415                                GtkRecentFilter   *filter,
416                                GtkRecentSortFunc  sort_func,
417                                gpointer           sort_data)
418 {
419   GtkRecentManager *manager;
420   gint limit;
421   GtkRecentSortType sort_type;
422   GList *items;
423   GCompareDataFunc compare_func;
424   gint length;
425
426   g_return_val_if_fail (GTK_IS_RECENT_CHOOSER (chooser), NULL);
427
428   manager = _gtk_recent_chooser_get_recent_manager (chooser);
429   if (!manager)
430     return NULL;
431
432   items = gtk_recent_manager_get_items (manager);
433   if (!items)
434     return NULL;
435
436   limit = gtk_recent_chooser_get_limit (chooser);
437   if (limit == 0)
438     return NULL;
439
440   if (filter)
441     {
442       GList *filter_items, *l;
443       gboolean local_only = FALSE;
444       gboolean show_private = FALSE;
445       gboolean show_not_found = FALSE;
446
447       g_object_get (G_OBJECT (chooser),
448                     "local-only", &local_only,
449                     "show-private", &show_private,
450                     "show-not-found", &show_not_found,
451                     NULL);
452
453       filter_items = NULL;
454       for (l = items; l != NULL; l = l->next)
455         {
456           GtkRecentInfo *info = l->data;
457           gboolean remove_item = FALSE;
458
459           if (get_is_recent_filtered (filter, info))
460             remove_item = TRUE;
461           
462           if (local_only && !gtk_recent_info_is_local (info))
463             remove_item = TRUE;
464
465           if (!show_private && gtk_recent_info_get_private_hint (info))
466             remove_item = TRUE;
467
468           if (!show_not_found && !gtk_recent_info_exists (info))
469             remove_item = TRUE;
470           
471           if (!remove_item)
472             filter_items = g_list_prepend (filter_items, info);
473           else
474             gtk_recent_info_unref (info);
475         }
476       
477       g_list_free (items);
478       items = filter_items;
479     }
480
481   if (!items)
482     return NULL;
483
484   sort_type = gtk_recent_chooser_get_sort_type (chooser);
485   switch (sort_type)
486     {
487     case GTK_RECENT_SORT_NONE:
488       compare_func = NULL;
489       break;
490     case GTK_RECENT_SORT_MRU:
491       compare_func = (GCompareDataFunc) sort_recent_items_mru;
492       break;
493     case GTK_RECENT_SORT_LRU:
494       compare_func = (GCompareDataFunc) sort_recent_items_lru;
495       break;
496     case GTK_RECENT_SORT_CUSTOM:
497       compare_func = (GCompareDataFunc) sort_recent_items_proxy;
498       break;
499     default:
500       g_assert_not_reached ();
501       break;
502     }
503
504   if (compare_func)
505     {
506       SortRecentData sort_recent;
507
508       sort_recent.func = sort_func;
509       sort_recent.data = sort_data;
510
511       items = g_list_sort_with_data (items, compare_func, &sort_recent);
512     }
513   
514   length = g_list_length (items);
515   if ((limit != -1) && (length > limit))
516     {
517       GList *clamp, *l;
518       
519       clamp = g_list_nth (items, limit - 1);
520       if (!clamp)
521         return items;
522       
523       l = clamp->next;
524       clamp->next = NULL;
525     
526       g_list_free_full (l, (GDestroyNotify) gtk_recent_info_unref);
527     }
528
529   return items;
530 }