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