]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilesystem.c
Added a render_icon virtual method.
[~andy/gtk] / gtk / gtkfilesystem.c
1 /* GTK - The GIMP Toolkit
2  * gtkfilesystem.c: Abstract file system interfaces
3  * Copyright (C) 2003, Red Hat, Inc.
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 #include "gtkfilesystem.h"
23 #include "gtkicontheme.h"
24
25 #include <string.h>
26
27 struct _GtkFileInfo
28 {
29   GtkFileTime modification_time;
30   gint64 size;
31   gchar *display_name;
32   gchar *display_key;
33   gchar *mime_type;
34 #if 0
35   GtkFileIconType icon_type : 4;
36 #endif
37   guint is_folder : 1;
38   guint is_hidden : 1;
39 };
40
41 static void gtk_file_system_base_init (gpointer g_class);
42 static void gtk_file_folder_base_init (gpointer g_class);
43
44 GQuark
45 gtk_file_system_error_quark (void)
46 {
47   static GQuark quark = 0;
48   if (quark == 0)
49     quark = g_quark_from_static_string ("gtk-file-system-error-quark");
50   return quark;
51 }
52
53 /*****************************************
54  *             GtkFileInfo               *
55  *****************************************/
56 GType
57 gtk_file_info_get_type (void)
58 {
59   static GType our_type = 0;
60   
61   if (our_type == 0)
62     our_type = g_boxed_type_register_static ("GtkFileInfo",
63                                              (GBoxedCopyFunc) gtk_file_info_copy,
64                                              (GBoxedFreeFunc) gtk_file_info_free);
65
66   return our_type;
67 }
68
69 GtkFileInfo *
70 gtk_file_info_new  (void)
71 {
72   GtkFileInfo *info;
73   
74   info = g_new0 (GtkFileInfo, 1);
75
76   return info;
77 }
78
79 GtkFileInfo *
80 gtk_file_info_copy (GtkFileInfo *info)
81 {
82   GtkFileInfo *new_info;
83
84   g_return_val_if_fail (info != NULL, NULL);
85
86   new_info = g_memdup (info, sizeof (GtkFileInfo));
87   if (new_info->display_name)
88     new_info->display_name = g_strdup (new_info->display_name);
89   if (new_info->mime_type)
90     new_info->mime_type = g_strdup (new_info->mime_type);
91
92   return new_info;
93 }
94
95 void
96 gtk_file_info_free (GtkFileInfo *info)
97 {
98   g_return_if_fail (info != NULL);
99
100   if (info->display_name)
101     g_free (info->display_name);
102   if (info->mime_type)
103     g_free (info->mime_type);
104   if (info->display_key)
105     g_free (info->display_key);
106
107   g_free (info);
108 }
109
110 G_CONST_RETURN gchar *
111 gtk_file_info_get_display_name (const GtkFileInfo *info)
112 {
113   g_return_val_if_fail (info != NULL, NULL);
114   
115   return info->display_name;
116 }
117
118 /**
119  * gtk_file_info_get_display_key:
120  * @info: a #GtkFileInfo
121  * 
122  * Returns results of g_utf8_collate_key() on the display name
123  * for @info. This is useful when sorting a bunch of #GtkFileInfo
124  * structures since the collate key will be only computed once.
125  * 
126  * Return value: The collate key for the display name, or %NULL
127  *   if the display name hasn't been set.
128  **/
129 G_CONST_RETURN gchar *
130 gtk_file_info_get_display_key (const GtkFileInfo *info)
131 {
132   g_return_val_if_fail (info != NULL, NULL);
133
134   if (!info->display_key && info->display_name)
135     {
136       /* Since info->display_key is only a cache, we cast off the const
137        */
138       ((GtkFileInfo *)info)->display_key = g_utf8_collate_key (info->display_name, -1);
139     }
140         
141   return info->display_key;
142 }
143
144 void
145 gtk_file_info_set_display_name (GtkFileInfo *info,
146                                 const gchar *display_name)
147 {
148   g_return_if_fail (info != NULL);
149
150   if (info->display_name)
151     g_free (info->display_name);
152   if (info->display_key)
153     {
154       g_free (info->display_key);
155       info->display_key = NULL;
156     }
157
158   info->display_name = g_strdup (display_name);
159 }
160
161 gboolean
162 gtk_file_info_get_is_folder (const GtkFileInfo *info)
163 {
164   g_return_val_if_fail (info != NULL, FALSE);
165   
166   return info->is_folder;
167 }
168
169 void
170 gtk_file_info_set_is_folder (GtkFileInfo *info,
171                              gboolean     is_folder)
172 {
173   g_return_if_fail (info != NULL);
174
175   info->is_folder = is_folder != FALSE;
176 }
177
178 gboolean
179 gtk_file_info_get_is_hidden (const GtkFileInfo *info)
180 {
181   g_return_val_if_fail (info != NULL, FALSE);
182   
183   return info->is_hidden;
184 }
185
186 void
187 gtk_file_info_set_is_hidden (GtkFileInfo *info,
188                              gboolean     is_hidden)
189 {
190   g_return_if_fail (info != NULL);
191
192   info->is_hidden = is_hidden != FALSE;
193 }
194
195 G_CONST_RETURN gchar *
196 gtk_file_info_get_mime_type (const GtkFileInfo *info)
197 {
198   g_return_val_if_fail (info != NULL, NULL);
199   
200   return info->mime_type;
201 }
202
203 void
204 gtk_file_info_set_mime_type (GtkFileInfo *info,
205                              const gchar *mime_type)
206 {
207   g_return_if_fail (info != NULL);
208   
209   if (info->mime_type)
210     g_free (info->mime_type);
211
212   info->mime_type = g_strdup (mime_type);
213 }
214
215 GtkFileTime
216 gtk_file_info_get_modification_time (const GtkFileInfo *info)
217 {
218   g_return_val_if_fail (info != NULL, 0);
219
220   return info->modification_time;
221 }
222
223 void
224 gtk_file_info_set_modification_time (GtkFileInfo *info,
225                                      GtkFileTime  modification_time)
226 {
227   g_return_if_fail (info != NULL);
228   
229   info->modification_time = modification_time;
230 }
231
232 gint64
233 gtk_file_info_get_size (const GtkFileInfo *info)
234 {
235   g_return_val_if_fail (info != NULL, 0);
236   
237   return info->size;
238 }
239
240 void
241 gtk_file_info_set_size (GtkFileInfo *info,
242                         gint64       size)
243 {
244   g_return_if_fail (info != NULL);
245   g_return_if_fail (size >= 0);
246   
247   info->size = size;
248 }
249
250 #if 0
251 void
252 gtk_file_info_set_icon_type  (GtkFileInfo      *info,
253                               GtkFileIconType   icon_type)
254 {
255   g_return_if_fail (info != NULL);
256
257   info->icon_type = icon_type;
258 }
259
260 GtkFileIconType
261 gtk_file_info_get_icon_type (const GtkFileInfo *info)
262 {
263   g_return_val_if_fail (info != NULL, GTK_FILE_ICON_REGULAR);
264
265   return info->icon_type;
266 }
267
268 typedef struct _IconCacheElement IconCacheElement;
269
270 struct _IconCacheElement
271 {
272   gint size;
273   GdkPixbuf *pixbuf;
274 };
275
276 static void
277 icon_cache_element_free (IconCacheElement *element)
278 {
279   if (element->pixbuf)
280     g_object_unref (element->pixbuf);
281   g_free (element);
282 }
283
284 static void
285 icon_theme_changed (GtkIconTheme *icon_theme)
286 {
287   GHashTable *cache;
288   
289   /* Difference from the initial creation is that we don't
290    * reconnect the signal
291    */
292   cache = g_hash_table_new_full (g_str_hash, g_str_equal,
293                                  (GDestroyNotify)g_free,
294                                  (GDestroyNotify)icon_cache_element_free);
295   g_object_set_data_full (G_OBJECT (icon_theme), "gtk-file-icon-cache",
296                           cache, (GDestroyNotify)g_hash_table_destroy);
297 }
298
299 static GdkPixbuf *
300 get_cached_icon (GtkWidget   *widget,
301                  const gchar *name,
302                  gint         pixel_size)
303 {
304   GtkIconTheme *icon_theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (widget));
305   GHashTable *cache = g_object_get_data (G_OBJECT (icon_theme), "gtk-file-icon-cache");
306   IconCacheElement *element;
307   
308   if (!cache)
309     {
310       cache = g_hash_table_new_full (g_str_hash, g_str_equal,
311                                      (GDestroyNotify)g_free,
312                                      (GDestroyNotify)icon_cache_element_free);
313       
314       g_object_set_data_full (G_OBJECT (icon_theme), "gtk-file-icon-cache",
315                               cache, (GDestroyNotify)g_hash_table_destroy);
316       g_signal_connect (icon_theme, "changed",
317                         G_CALLBACK (icon_theme_changed), NULL);
318     }
319
320   element = g_hash_table_lookup (cache, name);
321   if (!element)
322     {
323       element = g_new0 (IconCacheElement, 1);
324       g_hash_table_insert (cache, g_strdup (name), element);
325     }
326
327   if (element->size != pixel_size)
328     {
329       if (element->pixbuf)
330         g_object_unref (element->pixbuf);
331       element->size = pixel_size;
332       element->pixbuf = gtk_icon_theme_load_icon (icon_theme, name,
333                                                   pixel_size, 0, NULL);
334     }
335
336   return element->pixbuf ? g_object_ref (element->pixbuf) : NULL;
337 }
338                  
339
340 GdkPixbuf *
341 gtk_file_info_render_icon (const GtkFileInfo *info,
342                            GtkWidget         *widget,
343                            gint               pixel_size)
344 {
345   const gchar *separator;
346   GdkPixbuf *pixbuf;
347   GString *icon_name;
348
349   g_return_val_if_fail (info != NULL, NULL);
350   g_return_val_if_fail (widget != NULL, NULL);
351   g_return_val_if_fail (pixel_size > 0, NULL);
352
353   if (info->icon_type != GTK_FILE_ICON_REGULAR)
354     {
355       const char *name = NULL;  /* Quiet gcc */
356       
357       switch (info->icon_type)
358         {
359         case GTK_FILE_ICON_BLOCK_DEVICE:
360           name ="gnome-fs-blockdev";
361           break;
362         case GTK_FILE_ICON_BROKEN_SYMBOLIC_LINK:
363           name = "gnome-fs-symlink";
364           break;
365         case GTK_FILE_ICON_CHARACTER_DEVICE:
366           name = "gnome-fs-chardev";
367           break;
368         case GTK_FILE_ICON_DIRECTORY:
369           name = "gnome-fs-directory";
370           break;
371         case GTK_FILE_ICON_EXECUTABLE:
372           name ="gnome-fs-executable";
373           break;
374         case GTK_FILE_ICON_FIFO:
375           name = "gnome-fs-fifo";
376           break;
377         case GTK_FILE_ICON_SOCKET:
378           name = "gnome-fs-socket";
379           break;
380         case GTK_FILE_ICON_REGULAR:
381           g_assert_not_reached ();
382         }
383
384       return get_cached_icon (widget, name, pixel_size);
385     }
386   
387   if (!info->mime_type)
388     return NULL;
389
390   separator = strchr (info->mime_type, '/');
391   if (!separator)
392     return NULL;
393
394   icon_name = g_string_new ("gnome-mime-");
395   g_string_append_len (icon_name, info->mime_type, separator - info->mime_type);
396   g_string_append_c (icon_name, '-');
397   g_string_append (icon_name, separator + 1);
398   pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
399   g_string_free (icon_name, TRUE);
400   if (pixbuf)
401     return pixbuf;
402
403   icon_name = g_string_new ("gnome-mime-");
404   g_string_append_len (icon_name, info->mime_type, separator - info->mime_type);
405   pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
406   g_string_free (icon_name, TRUE);
407   if (pixbuf)
408     return pixbuf;
409
410   return get_cached_icon (widget, "gnome-fs-regular", pixel_size);
411 }
412 #endif
413
414 /*****************************************
415  *             GtkFileSystem             *
416  *****************************************/
417 GType
418 gtk_file_system_get_type (void)
419 {
420   static GType file_system_type = 0;
421
422   if (!file_system_type)
423     {
424       static const GTypeInfo file_system_info =
425       {
426         sizeof (GtkFileSystemIface),  /* class_size */
427         gtk_file_system_base_init,    /* base_init */
428         NULL,                         /* base_finalize */
429       };
430
431       file_system_type = g_type_register_static (G_TYPE_INTERFACE,
432                                                  "GtkFileSystem",
433                                                  &file_system_info, 0);
434
435       g_type_interface_add_prerequisite (file_system_type, G_TYPE_OBJECT);
436     }
437
438   return file_system_type;
439 }
440
441 static void
442 gtk_file_system_base_init (gpointer g_class)
443 {
444   static gboolean initialized = FALSE;
445   
446   if (!initialized)
447     {
448       GType iface_type = G_TYPE_FROM_INTERFACE (g_class);
449
450       g_signal_new ("roots-changed",
451                     iface_type,
452                     G_SIGNAL_RUN_LAST,
453                     G_STRUCT_OFFSET (GtkFileSystemIface, roots_changed),
454                     NULL, NULL,
455                     g_cclosure_marshal_VOID__VOID,
456                     G_TYPE_NONE, 0);
457       g_signal_new ("bookmarks-changed",
458                     iface_type,
459                     G_SIGNAL_RUN_LAST,
460                     G_STRUCT_OFFSET (GtkFileSystemIface, bookmarks_changed),
461                     NULL, NULL,
462                     g_cclosure_marshal_VOID__VOID,
463                     G_TYPE_NONE, 0);
464
465       initialized = TRUE;
466     }
467 }
468
469 GSList *
470 gtk_file_system_list_roots (GtkFileSystem  *file_system)
471 {
472   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
473
474   return GTK_FILE_SYSTEM_GET_IFACE (file_system)->list_roots (file_system);
475 }
476
477 GtkFileInfo *
478 gtk_file_system_get_root_info  (GtkFileSystem     *file_system,
479                                 const GtkFilePath *path,
480                                 GtkFileInfoType    types,
481                                 GError           **error)
482 {
483   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
484   g_return_val_if_fail (path != NULL, NULL);
485   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
486
487   return GTK_FILE_SYSTEM_GET_IFACE (file_system)->get_root_info (file_system, path, types, error);
488 }
489
490 GtkFileFolder *
491 gtk_file_system_get_folder (GtkFileSystem     *file_system,
492                             const GtkFilePath *path,
493                             GtkFileInfoType    types,
494                             GError           **error)
495 {
496   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
497   g_return_val_if_fail (path != NULL, NULL);
498   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
499
500   return GTK_FILE_SYSTEM_GET_IFACE (file_system)->get_folder (file_system, path, types, error);
501 }
502
503 gboolean
504 gtk_file_system_create_folder(GtkFileSystem     *file_system,
505                               const GtkFilePath *path,
506                               GError           **error)
507 {
508   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE);
509   g_return_val_if_fail (path != NULL, FALSE);
510   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
511
512   return GTK_FILE_SYSTEM_GET_IFACE (file_system)->create_folder (file_system, path, error);
513 }
514
515 /**
516  * gtk_file_system_get_parent:
517  * @file_system: a #GtkFileSystem
518  * @path: base path name
519  * @parent: location to store parent path name
520  * @error: location to store error, or %NULL
521  * 
522  * Gets the name of the parent folder of a file.
523  * 
524  * Return value: TRUE if the operation was successful; note that in this case @parent
525  * can be returned as %NULL if the base @path has no parent folder (i.e. if it is
526  * already a file system root).  If the operation fails, this function returns FALSE
527  * and sets the @error value if it is specified.
528  **/
529 gboolean
530 gtk_file_system_get_parent (GtkFileSystem     *file_system,
531                             const GtkFilePath *path,
532                             GtkFilePath      **parent,
533                             GError           **error)
534 {
535   GtkFilePath *tmp_parent = NULL;
536   gboolean result;
537   
538   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE);
539   g_return_val_if_fail (path != NULL, FALSE);
540   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
541
542   result = GTK_FILE_SYSTEM_GET_IFACE (file_system)->get_parent (file_system, path, &tmp_parent, error);
543   g_assert (result || tmp_parent == NULL);
544
545   if (parent)
546     *parent = tmp_parent;
547   else
548     gtk_file_path_free (tmp_parent);
549   
550   return result;
551 }
552
553 GtkFilePath *
554 gtk_file_system_make_path (GtkFileSystem    *file_system,
555                           const GtkFilePath *base_path,
556                           const gchar       *display_name,
557                           GError           **error)
558 {
559   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
560   g_return_val_if_fail (base_path != NULL, NULL);
561   g_return_val_if_fail (display_name != NULL, NULL);
562   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
563
564   return GTK_FILE_SYSTEM_GET_IFACE (file_system)->make_path (file_system, base_path, display_name, error);
565 }
566
567 /**
568  * gtk_file_system_parse:
569  * @file_system: a #GtkFileSystem
570  * @base_path: reference folder with respect to which relative
571  *            paths should be interpreted.
572  * @str: the string to parse
573  * @folder: location to store folder portion of result, or %NULL
574  * @file_part: location to store file portion of result, or %NULL
575  * @error: location to store error, or %NULL
576  * 
577  * Given a string entered by a user, parse it (possibly using
578  * heuristics) into a folder path and a UTF-8 encoded
579  * filename part. (Suitable for passing to gtk_file_system_make_path())
580  *
581  * Note that the returned filename point may point to a subfolder
582  * of the returned folder. Adding a trailing path separator is needed
583  * to enforce the interpretation as a folder name.
584  *
585  * If parsing fails because the syntax of @str is not understood,
586  * and error of type GTK_FILE_SYSTEM_ERROR_BAD_FILENAME will
587  * be set in @error and %FALSE returned.
588  *
589  * If parsing fails because a path was encountered that doesn't
590  * exist on the filesystem, then an error of type
591  * %GTK_FILE_SYSTEM_ERROR_NONEXISTENT will be set in @error
592  * and %FALSE returned. (This only applies to parsing relative paths,
593  * not to interpretation of @file_part. No check is made as
594  * to whether @file_part exists.)
595  *
596  * Return value: %TRUE if the parsing succeeds, otherwise, %FALSE.
597  **/
598 gboolean
599 gtk_file_system_parse (GtkFileSystem     *file_system,
600                        const GtkFilePath *base_path,
601                        const gchar       *str,
602                        GtkFilePath      **folder,
603                        gchar            **file_part,
604                        GError           **error)
605 {
606   GtkFilePath *tmp_folder = NULL;
607   gchar *tmp_file_part = NULL;
608   gboolean result;
609
610   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE);
611   g_return_val_if_fail (base_path != NULL, FALSE);
612   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
613
614
615   result = GTK_FILE_SYSTEM_GET_IFACE (file_system)->parse (file_system, base_path, str,
616                                                            &tmp_folder, &tmp_file_part,
617                                                            error);
618   g_assert (result || (tmp_folder == NULL && tmp_file_part == NULL));
619
620   if (folder)
621     *folder = tmp_folder;
622   else
623     gtk_file_path_free (tmp_folder);
624
625   if (file_part)
626     *file_part = tmp_file_part;
627   else
628     g_free (tmp_file_part);
629
630   return result;
631 }
632
633
634 gchar *
635 gtk_file_system_path_to_uri (GtkFileSystem     *file_system,
636                              const GtkFilePath *path)
637 {
638   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
639   g_return_val_if_fail (path != NULL, NULL);
640   
641   return GTK_FILE_SYSTEM_GET_IFACE (file_system)->path_to_uri (file_system, path);
642 }
643
644 gchar *
645 gtk_file_system_path_to_filename (GtkFileSystem     *file_system,
646                                   const GtkFilePath *path)
647 {
648   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
649   g_return_val_if_fail (path != NULL, NULL);
650   
651   return GTK_FILE_SYSTEM_GET_IFACE (file_system)->path_to_filename (file_system, path);
652 }
653
654 GtkFilePath *
655 gtk_file_system_uri_to_path (GtkFileSystem *file_system,
656                              const gchar    *uri)
657 {
658   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
659   g_return_val_if_fail (uri != NULL, NULL);
660   
661   return GTK_FILE_SYSTEM_GET_IFACE (file_system)->uri_to_path (file_system, uri);
662 }
663
664 GtkFilePath *
665 gtk_file_system_filename_to_path (GtkFileSystem *file_system,
666                                   const gchar   *filename)
667 {
668   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
669   g_return_val_if_fail (filename != NULL, NULL);
670
671   return GTK_FILE_SYSTEM_GET_IFACE (file_system)->filename_to_path (file_system, filename);
672 }
673
674 GdkPixbuf *
675 gtk_file_system_render_icon (GtkFileSystem      *file_system,
676                              const GtkFilePath  *path,
677                              GtkWidget          *widget,
678                              gint                pixel_size,
679                              GError            **error)
680 {
681   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
682   g_return_val_if_fail (path != NULL, NULL);
683   g_return_val_if_fail (widget != NULL, NULL);
684   g_return_val_if_fail (pixel_size > 0, NULL);
685
686   return GTK_FILE_SYSTEM_GET_IFACE (file_system)->render_icon (file_system, path, widget, pixel_size, error);
687 }
688
689 /**
690  * gtk_file_system_add_bookmark:
691  * @file_system: a #GtkFileSystem
692  * @bookmark: path of the bookmark to add
693  * @error: location to store error, or %NULL
694  * 
695  * Adds a bookmark folder to the user's bookmarks list.  If the operation succeeds,
696  * the "bookmarks_changed" signal will be emitted.
697  * 
698  * Return value: TRUE if the operation succeeds, FALSE otherwise.  In the latter case,
699  * the @error value will be set.
700  **/
701 gboolean
702 gtk_file_system_add_bookmark (GtkFileSystem     *file_system,
703                               const GtkFilePath *path,
704                               GError           **error)
705 {
706   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE);
707   g_return_val_if_fail (path != NULL, FALSE);
708
709   return GTK_FILE_SYSTEM_GET_IFACE (file_system)->add_bookmark (file_system, path, error);
710 }
711
712 /**
713  * gtk_file_system_remove_bookmark:
714  * @file_system: a #GtkFileSystem
715  * @bookmark: path of the bookmark to remove
716  * @error: location to store error, or %NULL
717  * 
718  * Removes a bookmark folder from the user's bookmarks list.  If the operation
719  * succeeds, the "bookmarks_changed" signal will be emitted.
720  * 
721  * Return value: TRUE if the operation succeeds, FALSE otherwise.  In the latter
722  * case, the @error value will be set.
723  **/
724 gboolean
725 gtk_file_system_remove_bookmark (GtkFileSystem     *file_system,
726                                  const GtkFilePath *path,
727                                  GError           **error)
728 {
729   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE);
730   g_return_val_if_fail (path != NULL, FALSE);
731
732   return GTK_FILE_SYSTEM_GET_IFACE (file_system)->remove_bookmark (file_system, path, error);
733 }
734
735 /**
736  * gtk_file_system_list_bookmarks:
737  * @file_system: a #GtkFileSystem
738  * 
739  * Queries the list of bookmarks in the file system.
740  * 
741  * Return value: A list of #GtkFilePath, or NULL if there are no configured
742  * bookmarks.  You should use gtk_file_paths_free() to free this list.
743  *
744  * See also: gtk_file_system_get_supports_bookmarks()
745  **/
746 GSList *
747 gtk_file_system_list_bookmarks (GtkFileSystem *file_system)
748 {
749   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
750
751   return GTK_FILE_SYSTEM_GET_IFACE (file_system)->list_bookmarks (file_system);
752 }
753
754 /*****************************************
755  *             GtkFileFolder             *
756  *****************************************/
757 GType
758 gtk_file_folder_get_type (void)
759 {
760   static GType file_folder_type = 0;
761
762   if (!file_folder_type)
763     {
764       static const GTypeInfo file_folder_info =
765       {
766         sizeof (GtkFileFolderIface),  /* class_size */
767         gtk_file_folder_base_init,    /* base_init */
768         NULL,                         /* base_finalize */
769       };
770
771       file_folder_type = g_type_register_static (G_TYPE_INTERFACE,
772                                                  "GtkFileFolder",
773                                                  &file_folder_info, 0);
774       
775       g_type_interface_add_prerequisite (file_folder_type, G_TYPE_OBJECT);
776     }
777
778   return file_folder_type;
779 }
780
781 static void
782 gtk_file_folder_base_init (gpointer g_class)
783 {
784   static gboolean initialized = FALSE;
785   
786   if (!initialized)
787     {
788       GType iface_type = G_TYPE_FROM_INTERFACE (g_class);
789
790       g_signal_new ("deleted",
791                     iface_type,
792                     G_SIGNAL_RUN_LAST,
793                     G_STRUCT_OFFSET (GtkFileFolderIface, deleted),
794                     NULL, NULL,
795                     g_cclosure_marshal_VOID__VOID,
796                     G_TYPE_NONE, 0);
797       g_signal_new ("files-added",
798                     iface_type,
799                     G_SIGNAL_RUN_LAST,
800                     G_STRUCT_OFFSET (GtkFileFolderIface, files_added),
801                     NULL, NULL,
802                     g_cclosure_marshal_VOID__POINTER,
803                     G_TYPE_NONE, 1,
804                     G_TYPE_POINTER);
805       g_signal_new ("files-changed",
806                     iface_type,
807                     G_SIGNAL_RUN_LAST,
808                     G_STRUCT_OFFSET (GtkFileFolderIface, files_changed),
809                     NULL, NULL,
810                     g_cclosure_marshal_VOID__POINTER,
811                     G_TYPE_NONE, 1,
812                     G_TYPE_POINTER);
813       g_signal_new ("files-removed",
814                     iface_type,
815                     G_SIGNAL_RUN_LAST,
816                     G_STRUCT_OFFSET (GtkFileFolderIface, files_removed),
817                     NULL, NULL,
818                     g_cclosure_marshal_VOID__POINTER,
819                     G_TYPE_NONE, 1,
820                     G_TYPE_POINTER);
821
822       initialized = TRUE;
823     }
824 }
825
826 gboolean
827 gtk_file_folder_list_children (GtkFileFolder    *folder,
828                                GSList          **children,
829                                GError          **error)
830 {
831   gboolean result;
832   GSList *tmp_children = NULL;
833   
834   g_return_val_if_fail (GTK_IS_FILE_FOLDER (folder), FALSE);
835   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
836
837   result = GTK_FILE_FOLDER_GET_IFACE (folder)->list_children (folder, &tmp_children, error);
838   g_assert (result || tmp_children == NULL);
839
840   if (children)
841     *children = tmp_children;
842   else
843     gtk_file_paths_free (tmp_children);
844
845   return result;
846 }
847
848 GtkFileInfo *
849 gtk_file_folder_get_info (GtkFileFolder     *folder,
850                           const GtkFilePath *path,
851                           GError           **error)
852 {
853   g_return_val_if_fail (GTK_IS_FILE_FOLDER (folder), NULL);
854   g_return_val_if_fail (path != NULL, NULL);
855   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
856
857   return GTK_FILE_FOLDER_GET_IFACE (folder)->get_info (folder, path, error);
858 }
859
860 GSList *
861 gtk_file_paths_sort (GSList *paths)
862 {
863   return g_slist_sort (paths, (GCompareFunc)strcmp);
864 }
865
866 /**
867  * gtk_file_paths_copy:
868  * @paths: A #GSList of 3GtkFilePath structures.
869  * 
870  * Copies a list of #GtkFilePath structures.
871  * 
872  * Return value: A copy of @paths.  Since the contents of the list are copied as
873  * well, you should use gtk_file_paths_free() to free the result.
874  **/
875 GSList *
876 gtk_file_paths_copy (GSList *paths)
877 {
878   GSList *head, *tail, *l;
879
880   head = tail = NULL;
881
882   for (l = paths; l; l = l->next)
883     {
884       GtkFilePath *path;
885       GSList *node;
886
887       path = l->data;
888       node = g_slist_alloc ();
889
890       if (tail)
891         tail->next = node;
892       else
893         head = node;
894
895       node->data = gtk_file_path_copy (path);
896       tail = node;
897     }
898
899   return head;
900 }
901
902 void
903 gtk_file_paths_free (GSList *paths)
904 {
905   GSList *tmp_list;
906
907   for (tmp_list = paths; tmp_list; tmp_list = tmp_list->next)
908     gtk_file_path_free (tmp_list->data);
909
910   g_slist_free (paths);
911 }