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