]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilesystem.c
Fix some problems reported by Morten Welinder:
[~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 #include <config.h>
22 #include <gmodule.h>
23 #include "gtkalias.h"
24 #include "gtkfilesystem.h"
25 #include "gtkicontheme.h"
26 #include "gtkmodules.h"
27
28 #include <string.h>
29
30 struct _GtkFileInfo
31 {
32   GtkFileTime modification_time;
33   gint64 size;
34   gchar *display_name;
35   gchar *display_key;
36   gchar *mime_type;
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 (display_name == info->display_name)
151     return;
152
153   if (info->display_name)
154     g_free (info->display_name);
155   if (info->display_key)
156     {
157       g_free (info->display_key);
158       info->display_key = NULL;
159     }
160
161   info->display_name = g_strdup (display_name);
162 }
163
164 gboolean
165 gtk_file_info_get_is_folder (const GtkFileInfo *info)
166 {
167   g_return_val_if_fail (info != NULL, FALSE);
168   
169   return info->is_folder;
170 }
171
172 void
173 gtk_file_info_set_is_folder (GtkFileInfo *info,
174                              gboolean     is_folder)
175 {
176   g_return_if_fail (info != NULL);
177
178   info->is_folder = is_folder != FALSE;
179 }
180
181 gboolean
182 gtk_file_info_get_is_hidden (const GtkFileInfo *info)
183 {
184   g_return_val_if_fail (info != NULL, FALSE);
185   
186   return info->is_hidden;
187 }
188
189 void
190 gtk_file_info_set_is_hidden (GtkFileInfo *info,
191                              gboolean     is_hidden)
192 {
193   g_return_if_fail (info != NULL);
194
195   info->is_hidden = is_hidden != FALSE;
196 }
197
198 G_CONST_RETURN gchar *
199 gtk_file_info_get_mime_type (const GtkFileInfo *info)
200 {
201   g_return_val_if_fail (info != NULL, NULL);
202   
203   return info->mime_type;
204 }
205
206 void
207 gtk_file_info_set_mime_type (GtkFileInfo *info,
208                              const gchar *mime_type)
209 {
210   g_return_if_fail (info != NULL);
211   
212   if (info->mime_type)
213     g_free (info->mime_type);
214
215   info->mime_type = g_strdup (mime_type);
216 }
217
218 GtkFileTime
219 gtk_file_info_get_modification_time (const GtkFileInfo *info)
220 {
221   g_return_val_if_fail (info != NULL, 0);
222
223   return info->modification_time;
224 }
225
226 void
227 gtk_file_info_set_modification_time (GtkFileInfo *info,
228                                      GtkFileTime  modification_time)
229 {
230   g_return_if_fail (info != NULL);
231   
232   info->modification_time = modification_time;
233 }
234
235 gint64
236 gtk_file_info_get_size (const GtkFileInfo *info)
237 {
238   g_return_val_if_fail (info != NULL, 0);
239   
240   return info->size;
241 }
242
243 void
244 gtk_file_info_set_size (GtkFileInfo *info,
245                         gint64       size)
246 {
247   g_return_if_fail (info != NULL);
248   g_return_if_fail (size >= 0);
249   
250   info->size = size;
251 }
252
253
254 /*****************************************
255  *             GtkFileSystem             *
256  *****************************************/
257 GType
258 gtk_file_system_get_type (void)
259 {
260   static GType file_system_type = 0;
261
262   if (!file_system_type)
263     {
264       static const GTypeInfo file_system_info =
265       {
266         sizeof (GtkFileSystemIface),  /* class_size */
267         gtk_file_system_base_init,    /* base_init */
268         NULL,                         /* base_finalize */
269       };
270
271       file_system_type = g_type_register_static (G_TYPE_INTERFACE,
272                                                  "GtkFileSystem",
273                                                  &file_system_info, 0);
274
275       g_type_interface_add_prerequisite (file_system_type, G_TYPE_OBJECT);
276     }
277
278   return file_system_type;
279 }
280
281 static void
282 gtk_file_system_base_init (gpointer g_class)
283 {
284   static gboolean initialized = FALSE;
285   
286   if (!initialized)
287     {
288       GType iface_type = G_TYPE_FROM_INTERFACE (g_class);
289
290       g_signal_new ("volumes-changed",
291                     iface_type,
292                     G_SIGNAL_RUN_LAST,
293                     G_STRUCT_OFFSET (GtkFileSystemIface, volumes_changed),
294                     NULL, NULL,
295                     g_cclosure_marshal_VOID__VOID,
296                     G_TYPE_NONE, 0);
297       g_signal_new ("bookmarks-changed",
298                     iface_type,
299                     G_SIGNAL_RUN_LAST,
300                     G_STRUCT_OFFSET (GtkFileSystemIface, bookmarks_changed),
301                     NULL, NULL,
302                     g_cclosure_marshal_VOID__VOID,
303                     G_TYPE_NONE, 0);
304
305       initialized = TRUE;
306     }
307 }
308
309 GSList *
310 gtk_file_system_list_volumes (GtkFileSystem  *file_system)
311 {
312   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
313
314   return GTK_FILE_SYSTEM_GET_IFACE (file_system)->list_volumes (file_system);
315 }
316
317 GtkFileFolder *
318 gtk_file_system_get_folder (GtkFileSystem     *file_system,
319                             const GtkFilePath *path,
320                             GtkFileInfoType    types,
321                             GError           **error)
322 {
323   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
324   g_return_val_if_fail (path != NULL, NULL);
325   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
326
327   return GTK_FILE_SYSTEM_GET_IFACE (file_system)->get_folder (file_system, path, types, error);
328 }
329
330 gboolean
331 gtk_file_system_create_folder(GtkFileSystem     *file_system,
332                               const GtkFilePath *path,
333                               GError           **error)
334 {
335   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE);
336   g_return_val_if_fail (path != NULL, FALSE);
337   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
338
339   return GTK_FILE_SYSTEM_GET_IFACE (file_system)->create_folder (file_system, path, error);
340 }
341
342 /**
343  * gtk_file_system_get_volume_for_path:
344  * @file_system: a #GtkFileSystem
345  * @path: a #GtkFilePath
346  * 
347  * Queries the file system volume that corresponds to a specific path.
348  * There might not be a volume for all paths (consinder for instance remote
349  * shared), so this can return NULL.
350  * 
351  * Return value: the #GtkFileSystemVolume that corresponds to the specified
352  * @path, or NULL if there is no such volume. You should free this value with
353  * gtk_file_system_volume_free().
354  **/
355 GtkFileSystemVolume *
356 gtk_file_system_get_volume_for_path (GtkFileSystem     *file_system,
357                                      const GtkFilePath *path)
358 {
359   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
360   g_return_val_if_fail (path != NULL, NULL);
361
362   return GTK_FILE_SYSTEM_GET_IFACE (file_system)->get_volume_for_path (file_system, path);
363 }
364
365 /**
366  * gtk_file_system_volume_free:
367  * @file_system: a #GtkFileSystem
368  * @volume: a #GtkFileSystemVolume
369  * 
370  * Frees a #GtkFileSystemVolume structure as returned by
371  * gtk_file_system_list_volumes().
372  **/
373 void
374 gtk_file_system_volume_free (GtkFileSystem       *file_system,
375                              GtkFileSystemVolume *volume)
376 {
377   g_return_if_fail (GTK_IS_FILE_SYSTEM (file_system));
378   g_return_if_fail (volume != NULL);
379
380   GTK_FILE_SYSTEM_GET_IFACE (file_system)->volume_free (file_system, volume);
381 }
382
383 /**
384  * gtk_file_system_volume_get_base_path:
385  * @file_system: a #GtkFileSystem
386  * @volume: a #GtkFileSystemVolume
387  * 
388  * Queries the base path for a volume.  For example, a CD-ROM device may yield a
389  * path of "/mnt/cdrom".
390  * 
391  * Return value: a #GtkFilePath with the base mount path of the specified
392  * @volume.
393  **/
394 GtkFilePath *
395 gtk_file_system_volume_get_base_path (GtkFileSystem       *file_system,
396                                       GtkFileSystemVolume *volume)
397 {
398   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
399   g_return_val_if_fail (volume != NULL, NULL);
400
401   return GTK_FILE_SYSTEM_GET_IFACE (file_system)->volume_get_base_path (file_system, volume);
402 }
403
404 /**
405  * gtk_file_system_volume_get_is_mounted:
406  * @file_system: a #GtkFileSystem
407  * @volume: a #GtkFileSystemVolume
408  * 
409  * Queries whether a #GtkFileSystemVolume is mounted or not.  If it is not, it
410  * can be mounted with gtk_file_system_volume_mount().
411  * 
412  * Return value: TRUE if the @volume is mounted, FALSE otherwise.
413  **/
414 gboolean
415 gtk_file_system_volume_get_is_mounted (GtkFileSystem       *file_system,
416                                        GtkFileSystemVolume *volume)
417 {
418   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE);
419   g_return_val_if_fail (volume != NULL, FALSE);
420
421   return GTK_FILE_SYSTEM_GET_IFACE (file_system)->volume_get_is_mounted (file_system, volume);
422 }
423
424 /**
425  * gtk_file_system_volume_mount:
426  * @file_system: a #GtkFileSystem
427  * @volume: a #GtkFileSystemVolume
428  * @error: location to store error, or %NULL
429  * 
430  * Tries to mount an unmounted volume.  This may cause the "volumes-changed"
431  * signal in the @file_system to be emitted.
432  * 
433  * Return value: TRUE if the @volume was mounted successfully, FALSE otherwise.
434  **/
435 gboolean
436 gtk_file_system_volume_mount (GtkFileSystem        *file_system, 
437                               GtkFileSystemVolume  *volume,
438                               GError              **error)
439 {
440   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE);
441   g_return_val_if_fail (volume != NULL, FALSE);
442   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
443
444   return GTK_FILE_SYSTEM_GET_IFACE (file_system)->volume_mount (file_system, volume, error);
445 }
446
447 /**
448  * gtk_file_system_volume_get_display_name:
449  * @file_system: a #GtkFileSystem
450  * @volume: a #GtkFileSystemVolume
451  * 
452  * Queries the human-readable name for a @volume.  This string can be displayed
453  * in a list of volumes that can be accessed, for example.
454  * 
455  * Return value: A string with the human-readable name for a #GtkFileSystemVolume.
456  **/
457 char *
458 gtk_file_system_volume_get_display_name (GtkFileSystem        *file_system, 
459                                          GtkFileSystemVolume  *volume)
460 {
461   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
462   g_return_val_if_fail (volume != NULL, NULL);
463
464   return GTK_FILE_SYSTEM_GET_IFACE (file_system)->volume_get_display_name (file_system, volume);
465 }
466
467 /**
468  * gtk_file_system_volume_render_icon:
469  * @file_system: a #GtkFileSystem
470  * @volume: a #GtkFileSystemVolume
471  * @widget: Reference widget to render icons.
472  * @pixel_size: Size of the icon.
473  * @error: location to store error, or %NULL
474  * 
475  * Renders an icon suitable for a file #GtkFileSystemVolume.
476  * 
477  * Return value: A #GdkPixbuf containing an icon, or NULL if the icon could not
478  * be rendered.  In the latter case, the @error value will be set as
479  * appropriate.
480  **/
481 GdkPixbuf *
482 gtk_file_system_volume_render_icon (GtkFileSystem        *file_system,
483                                     GtkFileSystemVolume  *volume,
484                                     GtkWidget            *widget,
485                                     gint                  pixel_size,
486                                     GError              **error)
487 {
488   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
489   g_return_val_if_fail (volume != NULL, NULL);
490   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
491   g_return_val_if_fail (pixel_size > 0, NULL);
492   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
493
494   return GTK_FILE_SYSTEM_GET_IFACE (file_system)->volume_render_icon (file_system,
495                                                                       volume,
496                                                                       widget,
497                                                                       pixel_size,
498                                                                       error);
499 }
500
501 /**
502  * gtk_file_system_get_parent:
503  * @file_system: a #GtkFileSystem
504  * @path: base path name
505  * @parent: location to store parent path name
506  * @error: location to store error, or %NULL
507  * 
508  * Gets the name of the parent folder of a path.  If the path has no parent, as when
509  * you request the parent of a file system root, then @parent will be set to %NULL.
510  * 
511  * Return value: %TRUE if the operation was successful:  @parent will be set to
512  * the name of the @path's parent, or to %NULL if @path is already a file system
513  * root.  If the operation fails, this function returns %FALSE, sets @parent to
514  * NULL, and sets the @error value if it is specified.
515  **/
516 gboolean
517 gtk_file_system_get_parent (GtkFileSystem     *file_system,
518                             const GtkFilePath *path,
519                             GtkFilePath      **parent,
520                             GError           **error)
521 {
522   gboolean result;
523   
524   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE);
525   g_return_val_if_fail (path != NULL, FALSE);
526   g_return_val_if_fail (parent != NULL, FALSE);
527   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
528
529   *parent = NULL;
530
531   result = GTK_FILE_SYSTEM_GET_IFACE (file_system)->get_parent (file_system, path, parent, error);
532   g_assert (result || *parent == NULL);
533
534   return result;
535 }
536
537 GtkFilePath *
538 gtk_file_system_make_path (GtkFileSystem    *file_system,
539                           const GtkFilePath *base_path,
540                           const gchar       *display_name,
541                           GError           **error)
542 {
543   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
544   g_return_val_if_fail (base_path != NULL, NULL);
545   g_return_val_if_fail (display_name != NULL, NULL);
546   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
547
548   return GTK_FILE_SYSTEM_GET_IFACE (file_system)->make_path (file_system, base_path, display_name, error);
549 }
550
551 /**
552  * gtk_file_system_parse:
553  * @file_system: a #GtkFileSystem
554  * @base_path: reference folder with respect to which relative
555  *            paths should be interpreted.
556  * @str: the string to parse
557  * @folder: location to store folder portion of result, or %NULL
558  * @file_part: location to store file portion of result, or %NULL
559  * @error: location to store error, or %NULL
560  * 
561  * Given a string entered by a user, parse it (possibly using
562  * heuristics) into a folder path and a UTF-8 encoded
563  * filename part. (Suitable for passing to gtk_file_system_make_path())
564  *
565  * Note that the returned filename point may point to a subfolder
566  * of the returned folder. Adding a trailing path separator is needed
567  * to enforce the interpretation as a folder name.
568  *
569  * If parsing fails because the syntax of @str is not understood,
570  * and error of type GTK_FILE_SYSTEM_ERROR_BAD_FILENAME will
571  * be set in @error and %FALSE returned.
572  *
573  * If parsing fails because a path was encountered that doesn't
574  * exist on the filesystem, then an error of type
575  * %GTK_FILE_SYSTEM_ERROR_NONEXISTENT will be set in @error
576  * and %FALSE returned. (This only applies to parsing relative paths,
577  * not to interpretation of @file_part. No check is made as
578  * to whether @file_part exists.)
579  *
580  * Return value: %TRUE if the parsing succeeds, otherwise, %FALSE.
581  **/
582 gboolean
583 gtk_file_system_parse (GtkFileSystem     *file_system,
584                        const GtkFilePath *base_path,
585                        const gchar       *str,
586                        GtkFilePath      **folder,
587                        gchar            **file_part,
588                        GError           **error)
589 {
590   GtkFilePath *tmp_folder = NULL;
591   gchar *tmp_file_part = NULL;
592   gboolean result;
593
594   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE);
595   g_return_val_if_fail (base_path != NULL, FALSE);
596   g_return_val_if_fail (str != NULL, FALSE);
597   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
598
599   result = GTK_FILE_SYSTEM_GET_IFACE (file_system)->parse (file_system, base_path, str,
600                                                            &tmp_folder, &tmp_file_part,
601                                                            error);
602   g_assert (result || (tmp_folder == NULL && tmp_file_part == NULL));
603
604   if (folder)
605     *folder = tmp_folder;
606   else
607     gtk_file_path_free (tmp_folder);
608
609   if (file_part)
610     *file_part = tmp_file_part;
611   else
612     g_free (tmp_file_part);
613
614   return result;
615 }
616
617
618 gchar *
619 gtk_file_system_path_to_uri (GtkFileSystem     *file_system,
620                              const GtkFilePath *path)
621 {
622   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
623   g_return_val_if_fail (path != NULL, NULL);
624   
625   return GTK_FILE_SYSTEM_GET_IFACE (file_system)->path_to_uri (file_system, path);
626 }
627
628 gchar *
629 gtk_file_system_path_to_filename (GtkFileSystem     *file_system,
630                                   const GtkFilePath *path)
631 {
632   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
633   g_return_val_if_fail (path != NULL, NULL);
634   
635   return GTK_FILE_SYSTEM_GET_IFACE (file_system)->path_to_filename (file_system, path);
636 }
637
638 GtkFilePath *
639 gtk_file_system_uri_to_path (GtkFileSystem *file_system,
640                              const gchar    *uri)
641 {
642   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
643   g_return_val_if_fail (uri != NULL, NULL);
644   
645   return GTK_FILE_SYSTEM_GET_IFACE (file_system)->uri_to_path (file_system, uri);
646 }
647
648 GtkFilePath *
649 gtk_file_system_filename_to_path (GtkFileSystem *file_system,
650                                   const gchar   *filename)
651 {
652   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
653   g_return_val_if_fail (filename != NULL, NULL);
654
655   return GTK_FILE_SYSTEM_GET_IFACE (file_system)->filename_to_path (file_system, filename);
656 }
657
658 /**
659  * gtk_file_system_path_is_local:
660  * @filesystem: a #GtkFileSystem
661  * @path: A #GtkFilePath from that filesystem
662  * 
663  * Checks whether a #GtkFilePath is local; that is whether
664  * gtk_file_system_path_to_filename would return non-%NULL.
665  * 
666  * Return value: %TRUE if the path is loca
667  **/
668 gboolean
669 gtk_file_system_path_is_local (GtkFileSystem     *file_system,
670                                const GtkFilePath *path)
671 {
672   gchar *filename;
673   gboolean result;
674     
675   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE);
676   g_return_val_if_fail (path != NULL, FALSE);
677
678   filename = gtk_file_system_path_to_filename (file_system, path);
679   result = filename != NULL;
680   g_free (filename);
681
682   return result;
683 }
684
685 GdkPixbuf *
686 gtk_file_system_render_icon (GtkFileSystem      *file_system,
687                              const GtkFilePath  *path,
688                              GtkWidget          *widget,
689                              gint                pixel_size,
690                              GError            **error)
691 {
692   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
693   g_return_val_if_fail (path != NULL, NULL);
694   g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
695   g_return_val_if_fail (pixel_size > 0, NULL);
696
697   return GTK_FILE_SYSTEM_GET_IFACE (file_system)->render_icon (file_system, path, widget, pixel_size, error);
698 }
699
700 /**
701  * gtk_file_system_insert_bookmark:
702  * @file_system: a #GtkFileSystem
703  * @path: path of the bookmark to add
704  * @position: index in the bookmarks list at which the @path should be inserted; use 0
705  * for the beginning, and -1 or the number of bookmarks itself for the end of the list.
706  * @error: location to store error, or %NULL
707  * 
708  * Adds a path for a folder to the user's bookmarks list.  If the operation
709  * succeeds, the "bookmarks_changed" signal will be emitted.  Bookmark paths are
710  * unique; if you try to insert a @path that already exists, the operation will
711  * fail and the @error will be set to #GTK_FILE_SYSTEM_ERROR_ALREADY_EXISTS.  To
712  * reorder the list of bookmarks, use gtk_file_system_remove_bookmark() to
713  * remove the path in question, and call gtk_file_system_insert_bookmark() with
714  * the new position for the path.
715  * 
716  * Return value: TRUE if the operation succeeds, FALSE otherwise.  In the latter case,
717  * the @error value will be set.
718  **/
719 gboolean
720 gtk_file_system_insert_bookmark (GtkFileSystem     *file_system,
721                                  const GtkFilePath *path,
722                                  gint               position,
723                                  GError           **error)
724 {
725   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE);
726   g_return_val_if_fail (path != NULL, FALSE);
727
728   return GTK_FILE_SYSTEM_GET_IFACE (file_system)->insert_bookmark (file_system, path, position, error);
729 }
730
731 /**
732  * gtk_file_system_remove_bookmark:
733  * @file_system: a #GtkFileSystem
734  * @path: path of the bookmark to remove
735  * @error: location to store error, or %NULL
736  * 
737  * Removes a bookmark folder from the user's bookmarks list.  If the operation
738  * succeeds, the "bookmarks_changed" signal will be emitted.  If you try to remove
739  * a @path which does not exist in the bookmarks list, the operation will fail
740  * and the @error will be set to GTK_FILE_SYSTEM_ERROR_NONEXISTENT.
741  * 
742  * Return value: TRUE if the operation succeeds, FALSE otherwise.  In the latter
743  * case, the @error value will be set.
744  **/
745 gboolean
746 gtk_file_system_remove_bookmark (GtkFileSystem     *file_system,
747                                  const GtkFilePath *path,
748                                  GError           **error)
749 {
750   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE);
751   g_return_val_if_fail (path != NULL, FALSE);
752
753   return GTK_FILE_SYSTEM_GET_IFACE (file_system)->remove_bookmark (file_system, path, error);
754 }
755
756 /**
757  * gtk_file_system_list_bookmarks:
758  * @file_system: a #GtkFileSystem
759  * 
760  * Queries the list of bookmarks in the file system.
761  * 
762  * Return value: A list of #GtkFilePath, or NULL if there are no configured
763  * bookmarks.  You should use gtk_file_paths_free() to free this list.
764  *
765  * See also: gtk_file_system_get_supports_bookmarks()
766  **/
767 GSList *
768 gtk_file_system_list_bookmarks (GtkFileSystem *file_system)
769 {
770   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
771
772   return GTK_FILE_SYSTEM_GET_IFACE (file_system)->list_bookmarks (file_system);
773 }
774
775 /*****************************************
776  *             GtkFileFolder             *
777  *****************************************/
778 GType
779 gtk_file_folder_get_type (void)
780 {
781   static GType file_folder_type = 0;
782
783   if (!file_folder_type)
784     {
785       static const GTypeInfo file_folder_info =
786       {
787         sizeof (GtkFileFolderIface),  /* class_size */
788         gtk_file_folder_base_init,    /* base_init */
789         NULL,                         /* base_finalize */
790       };
791
792       file_folder_type = g_type_register_static (G_TYPE_INTERFACE,
793                                                  "GtkFileFolder",
794                                                  &file_folder_info, 0);
795       
796       g_type_interface_add_prerequisite (file_folder_type, G_TYPE_OBJECT);
797     }
798
799   return file_folder_type;
800 }
801
802 static void
803 gtk_file_folder_base_init (gpointer g_class)
804 {
805   static gboolean initialized = FALSE;
806   
807   if (!initialized)
808     {
809       GType iface_type = G_TYPE_FROM_INTERFACE (g_class);
810
811       g_signal_new ("deleted",
812                     iface_type,
813                     G_SIGNAL_RUN_LAST,
814                     G_STRUCT_OFFSET (GtkFileFolderIface, deleted),
815                     NULL, NULL,
816                     g_cclosure_marshal_VOID__VOID,
817                     G_TYPE_NONE, 0);
818       g_signal_new ("files-added",
819                     iface_type,
820                     G_SIGNAL_RUN_LAST,
821                     G_STRUCT_OFFSET (GtkFileFolderIface, files_added),
822                     NULL, NULL,
823                     g_cclosure_marshal_VOID__POINTER,
824                     G_TYPE_NONE, 1,
825                     G_TYPE_POINTER);
826       g_signal_new ("files-changed",
827                     iface_type,
828                     G_SIGNAL_RUN_LAST,
829                     G_STRUCT_OFFSET (GtkFileFolderIface, files_changed),
830                     NULL, NULL,
831                     g_cclosure_marshal_VOID__POINTER,
832                     G_TYPE_NONE, 1,
833                     G_TYPE_POINTER);
834       g_signal_new ("files-removed",
835                     iface_type,
836                     G_SIGNAL_RUN_LAST,
837                     G_STRUCT_OFFSET (GtkFileFolderIface, files_removed),
838                     NULL, NULL,
839                     g_cclosure_marshal_VOID__POINTER,
840                     G_TYPE_NONE, 1,
841                     G_TYPE_POINTER);
842       g_signal_new ("finished-loading",
843                     iface_type,
844                     G_SIGNAL_RUN_LAST,
845                     G_STRUCT_OFFSET (GtkFileFolderIface, finished_loading),
846                     NULL, NULL,
847                     g_cclosure_marshal_VOID__VOID,
848                     G_TYPE_NONE, 0);
849
850       initialized = TRUE;
851     }
852 }
853
854 gboolean
855 gtk_file_folder_list_children (GtkFileFolder    *folder,
856                                GSList          **children,
857                                GError          **error)
858 {
859   gboolean result;
860   GSList *tmp_children = NULL;
861   
862   g_return_val_if_fail (GTK_IS_FILE_FOLDER (folder), FALSE);
863   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
864
865   result = GTK_FILE_FOLDER_GET_IFACE (folder)->list_children (folder, &tmp_children, error);
866   g_assert (result || tmp_children == NULL);
867
868   if (children)
869     *children = tmp_children;
870   else
871     gtk_file_paths_free (tmp_children);
872
873   return result;
874 }
875
876 GtkFileInfo *
877 gtk_file_folder_get_info (GtkFileFolder     *folder,
878                           const GtkFilePath *path,
879                           GError           **error)
880 {
881   g_return_val_if_fail (GTK_IS_FILE_FOLDER (folder), NULL);
882   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
883
884   return GTK_FILE_FOLDER_GET_IFACE (folder)->get_info (folder, path, error);
885 }
886
887 gboolean
888 gtk_file_folder_is_finished_loading (GtkFileFolder *folder)
889 {
890   g_return_val_if_fail (GTK_IS_FILE_FOLDER (folder), TRUE);
891
892   if (!GTK_FILE_FOLDER_GET_IFACE (folder)->is_finished_loading)
893     return TRUE;
894   else
895     return GTK_FILE_FOLDER_GET_IFACE (folder)->is_finished_loading (folder);
896 }
897
898
899 /*****************************************
900  *         GtkFilePath modules           *
901  *****************************************/
902
903 /* We make these real functions in case either copy or free are implemented as macros
904  */
905 static gpointer
906 gtk_file_path_real_copy (gpointer boxed)
907 {
908   return gtk_file_path_copy ((GtkFilePath *) boxed);
909 }
910
911 static void
912 gtk_file_path_real_free (gpointer boxed)
913 {
914   gtk_file_path_free (boxed);
915 }
916
917 GType
918 gtk_file_path_get_type (void)
919 {
920   static GType our_type = 0;
921   
922   if (our_type == 0)
923     our_type = g_boxed_type_register_static ("GtkFilePath",
924                                              (GBoxedCopyFunc) gtk_file_path_real_copy,
925                                              (GBoxedFreeFunc) gtk_file_path_real_free);
926
927   return our_type;
928 }
929
930
931 GSList *
932 gtk_file_paths_sort (GSList *paths)
933 {
934   return g_slist_sort (paths, (GCompareFunc)strcmp);
935 }
936
937 /**
938  * gtk_file_paths_copy:
939  * @paths: A #GSList of #GtkFilePath structures.
940  * 
941  * Copies a list of #GtkFilePath structures.
942  * 
943  * Return value: A copy of @paths.  Since the contents of the list are copied as
944  * well, you should use gtk_file_paths_free() to free the result.
945  **/
946 GSList *
947 gtk_file_paths_copy (GSList *paths)
948 {
949   GSList *head, *tail, *l;
950
951   head = tail = NULL;
952
953   for (l = paths; l; l = l->next)
954     {
955       GtkFilePath *path;
956       GSList *node;
957
958       path = l->data;
959       node = g_slist_alloc ();
960
961       if (tail)
962         tail->next = node;
963       else
964         head = node;
965
966       node->data = gtk_file_path_copy (path);
967       tail = node;
968     }
969
970   return head;
971 }
972
973 void
974 gtk_file_paths_free (GSList *paths)
975 {
976   GSList *tmp_list;
977
978   for (tmp_list = paths; tmp_list; tmp_list = tmp_list->next)
979     gtk_file_path_free (tmp_list->data);
980
981   g_slist_free (paths);
982 }
983
984 /*****************************************
985  *         GtkFileSystem modules         *
986  *****************************************/
987
988 typedef struct _GtkFileSystemModule GtkFileSystemModule;
989 typedef struct _GtkFileSystemModuleClass GtkFileSystemModuleClass;
990
991 struct _GtkFileSystemModule
992 {
993   GTypeModule parent_instance;
994   
995   GModule *library;
996
997   void            (*init)     (GTypeModule    *module);
998   void            (*exit)     (void);
999   GtkFileSystem * (*create)   (void);
1000
1001   gchar *path;
1002 };
1003
1004 struct _GtkFileSystemModuleClass
1005 {
1006   GTypeModuleClass parent_class;
1007 };
1008
1009 G_DEFINE_TYPE (GtkFileSystemModule, _gtk_file_system_module, G_TYPE_TYPE_MODULE);
1010 #define GTK_TYPE_FILE_SYSTEM_MODULE       (_gtk_file_system_module_get_type ())
1011 #define GTK_FILE_SYSTEM_MODULE(module)    (G_TYPE_CHECK_INSTANCE_CAST ((module), GTK_TYPE_FILE_SYSTEM_MODULE, GtkFileSystemModule))
1012
1013
1014 static GSList *loaded_file_systems;
1015
1016 static gboolean
1017 gtk_file_system_module_load (GTypeModule *module)
1018 {
1019   GtkFileSystemModule *fs_module = GTK_FILE_SYSTEM_MODULE (module);
1020   
1021   fs_module->library = g_module_open (fs_module->path, 0);
1022   if (!fs_module->library)
1023     {
1024       g_warning (g_module_error());
1025       return FALSE;
1026     }
1027   
1028   /* extract symbols from the lib */
1029   if (!g_module_symbol (fs_module->library, "fs_module_init",
1030                         (gpointer *)&fs_module->init) ||
1031       !g_module_symbol (fs_module->library, "fs_module_exit", 
1032                         (gpointer *)&fs_module->exit) ||
1033       !g_module_symbol (fs_module->library, "fs_module_create", 
1034                         (gpointer *)&fs_module->create))
1035     {
1036       g_warning (g_module_error());
1037       g_module_close (fs_module->library);
1038       
1039       return FALSE;
1040     }
1041             
1042   /* call the filesystems's init function to let it */
1043   /* setup anything it needs to set up. */
1044   fs_module->init (module);
1045
1046   return TRUE;
1047 }
1048
1049 static void
1050 gtk_file_system_module_unload (GTypeModule *module)
1051 {
1052   GtkFileSystemModule *fs_module = GTK_FILE_SYSTEM_MODULE (module);
1053   
1054   fs_module->exit();
1055
1056   g_module_close (fs_module->library);
1057   fs_module->library = NULL;
1058
1059   fs_module->init = NULL;
1060   fs_module->exit = NULL;
1061   fs_module->create = NULL;
1062 }
1063
1064 /* This only will ever be called if an error occurs during
1065  * initialization
1066  */
1067 static void
1068 gtk_file_system_module_finalize (GObject *object)
1069 {
1070   GtkFileSystemModule *module = GTK_FILE_SYSTEM_MODULE (object);
1071
1072   g_free (module->path);
1073
1074   G_OBJECT_CLASS (_gtk_file_system_module_parent_class)->finalize (object);
1075 }
1076
1077 static void
1078 _gtk_file_system_module_class_init (GtkFileSystemModuleClass *class)
1079 {
1080   GTypeModuleClass *module_class = G_TYPE_MODULE_CLASS (class);
1081   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
1082   
1083   module_class->load = gtk_file_system_module_load;
1084   module_class->unload = gtk_file_system_module_unload;
1085
1086   gobject_class->finalize = gtk_file_system_module_finalize;
1087 }
1088
1089 static void
1090 _gtk_file_system_module_init (GtkFileSystemModule *fs_module)
1091 {
1092 }
1093
1094
1095 static GtkFileSystem *
1096 _gtk_file_system_module_create (GtkFileSystemModule *fs_module)
1097 {
1098   GtkFileSystem *fs;
1099   
1100   if (g_type_module_use (G_TYPE_MODULE (fs_module)))
1101     {
1102       fs = fs_module->create ();
1103       g_type_module_unuse (G_TYPE_MODULE (fs_module));
1104       return fs;
1105     }
1106   return NULL;
1107 }
1108
1109
1110 GtkFileSystem *
1111 _gtk_file_system_create (const char *file_system_name)
1112 {
1113   GSList *l;
1114   char *module_path;
1115   GtkFileSystemModule *fs_module;
1116   GtkFileSystem *fs;
1117
1118   for (l = loaded_file_systems; l != NULL; l = l->next)
1119     {
1120       fs_module = l->data;
1121       
1122       if (strcmp (G_TYPE_MODULE (fs_module)->name, file_system_name) == 0)
1123         return _gtk_file_system_module_create (fs_module);
1124     }
1125
1126   fs = NULL;
1127   if (g_module_supported ())
1128     {
1129       module_path = _gtk_find_module (file_system_name, "filesystems");
1130
1131       if (module_path)
1132         {
1133           fs_module = g_object_new (GTK_TYPE_FILE_SYSTEM_MODULE, NULL);
1134
1135           g_type_module_set_name (G_TYPE_MODULE (fs_module), file_system_name);
1136           fs_module->path = g_strdup (module_path);
1137
1138           loaded_file_systems = g_slist_prepend (loaded_file_systems,
1139                                                  fs_module);
1140
1141           fs = _gtk_file_system_module_create (fs_module);
1142         }
1143       
1144       g_free (module_path);
1145     }
1146   
1147   return fs;
1148 }
1149
1150