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