]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilesystem.c
Finish monitoring, add a TODO, fill in details in README, fix some missing finalizati...
[~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 "gtkfilesystem.h"
22
23 struct _GtkFileInfo
24 {
25   GtkFileTime modification_time;
26   gint64 size;
27   gchar *display_name;
28   gchar *display_key;
29   gchar *mime_type;
30   GdkPixbuf *icon;
31   guint is_folder : 1;
32   guint is_hidden : 1;
33 };
34
35 static void gtk_file_system_base_init (gpointer g_class);
36 static void gtk_file_folder_base_init (gpointer g_class);
37
38 GQuark
39 gtk_file_system_error_quark (void)
40 {
41   static GQuark quark = 0;
42   if (quark == 0)
43     quark = g_quark_from_static_string ("gtk-file-system-error-quark");
44   return quark;
45 }
46
47 /*****************************************
48  *             GtkFileInfo               *
49  *****************************************/
50 GType
51 gtk_file_info_get_type (void)
52 {
53   static GType our_type = 0;
54   
55   if (our_type == 0)
56     our_type = g_boxed_type_register_static ("GtkFileInfo",
57                                              (GBoxedCopyFunc) gtk_file_info_copy,
58                                              (GBoxedFreeFunc) gtk_file_info_free);
59
60   return our_type;
61 }
62
63 GtkFileInfo *
64 gtk_file_info_new  (void)
65 {
66   GtkFileInfo *info;
67   
68   info = g_new0 (GtkFileInfo, 1);
69
70   return info;
71 }
72
73 GtkFileInfo *
74 gtk_file_info_copy (GtkFileInfo *info)
75 {
76   GtkFileInfo *new_info;
77
78   g_return_val_if_fail (info != NULL, NULL);
79
80   new_info = g_memdup (info, sizeof (GtkFileInfo));
81   if (new_info->display_name)
82     new_info->display_name = g_strdup (new_info->display_name);
83   if (new_info->mime_type)
84     new_info->mime_type = g_strdup (new_info->mime_type);
85   if (new_info->icon)
86     g_object_ref (new_info->icon);
87
88   return new_info;
89 }
90
91 void
92 gtk_file_info_free (GtkFileInfo *info)
93 {
94   g_return_if_fail (info != NULL);
95
96   if (info->display_name)
97     g_free (info->display_name);
98   if (info->mime_type)
99     g_free (info->mime_type);
100   if (info->icon)
101     g_object_unref (info->icon);
102 }
103
104 G_CONST_RETURN gchar *
105 gtk_file_info_get_display_name (const GtkFileInfo *info)
106 {
107   g_return_val_if_fail (info != NULL, NULL);
108   
109   return info->display_name;
110 }
111
112 /**
113  * gtk_file_info_get_display_key:
114  * @info: a #GtkFileInfo
115  * 
116  * Returns results of g_utf8_collate_key() on the display name
117  * for @info. This is useful when sorting a bunch of #GtkFileInfo
118  * structures since the collate key will be only computed once.
119  * 
120  * Return value: The collate key for the display name, or %NULL
121  *   if the display name hasn't been set.
122  **/
123 G_CONST_RETURN gchar *
124 gtk_file_info_get_display_key (const GtkFileInfo *info)
125 {
126   g_return_val_if_fail (info != NULL, NULL);
127
128   if (!info->display_key && info->display_name)
129     {
130       /* Since info->display_key is only a cache, we cast off the const
131        */
132       ((GtkFileInfo *)info)->display_key = g_utf8_collate_key (info->display_name, -1);
133     }
134         
135   
136   return info->display_key;
137 }
138
139 void
140 gtk_file_info_set_display_name (GtkFileInfo *info,
141                                 const gchar *display_name)
142 {
143   g_return_if_fail (info != NULL);
144
145   if (info->display_name)
146     g_free (info->display_name);
147   if (info->display_key)
148     {
149       g_free (info->display_key);
150       info->display_key = NULL;
151     }
152
153   info->display_name = g_strdup (display_name);
154 }
155
156 gboolean
157 gtk_file_info_get_is_folder (const GtkFileInfo *info)
158 {
159   g_return_val_if_fail (info != NULL, FALSE);
160   
161   return info->is_folder;
162 }
163
164 void
165 gtk_file_info_set_is_folder (GtkFileInfo *info,
166                              gboolean     is_folder)
167 {
168   g_return_if_fail (info != NULL);
169
170   info->is_folder = is_folder != FALSE;
171 }
172
173 gboolean
174 gtk_file_info_get_is_hidden (const GtkFileInfo *info)
175 {
176   g_return_val_if_fail (info != NULL, FALSE);
177   
178   return info->is_hidden;
179 }
180
181 void
182 gtk_file_info_set_is_hidden (GtkFileInfo *info,
183                              gboolean     is_hidden)
184 {
185   g_return_if_fail (info != NULL);
186
187   info->is_hidden = is_hidden != FALSE;
188 }
189
190 G_CONST_RETURN gchar *
191 gtk_file_info_get_mime_type (const GtkFileInfo *info)
192 {
193   g_return_val_if_fail (info != NULL, NULL);
194   
195   return info->mime_type;
196 }
197
198 void
199 gtk_file_info_set_mime_type (GtkFileInfo *info,
200                              const gchar *mime_type)
201 {
202   g_return_if_fail (info != NULL);
203   
204   if (info->mime_type)
205     g_free (info->mime_type);
206
207   info->mime_type = g_strdup (mime_type);
208 }
209
210 GtkFileTime
211 gtk_file_info_get_modification_time (const GtkFileInfo *info)
212 {
213   g_return_val_if_fail (info != NULL, 0);
214
215   return info->modification_time;
216 }
217
218 void
219 gtk_file_info_set_modification_time (GtkFileInfo *info,
220                                      GtkFileTime  modification_time)
221 {
222   g_return_if_fail (info != NULL);
223   
224   info->modification_time = modification_time;
225 }
226
227 gint64
228 gtk_file_info_get_size (const GtkFileInfo *info)
229 {
230   g_return_val_if_fail (info != NULL, 0);
231   
232   return info->size;
233 }
234
235 void
236 gtk_file_info_set_size (GtkFileInfo *info,
237                         gint64       size)
238 {
239   g_return_if_fail (info != NULL);
240   g_return_if_fail (size >= 0);
241   
242   info->size = size;
243 }
244
245 GdkPixbuf *
246 gtk_file_info_get_icon (const GtkFileInfo *info)
247 {
248   g_return_val_if_fail (info != NULL, NULL);
249
250   return info->icon;
251 }
252
253 void
254 gtk_file_info_set_icon (GtkFileInfo *info,
255                         GdkPixbuf   *icon)
256 {
257   g_return_if_fail (info != NULL);
258   g_return_if_fail (icon == NULL || GDK_IS_PIXBUF (icon));
259
260   if (icon != info->icon)
261     {
262       if (info->icon)
263         g_object_unref (info->icon);
264
265       info->icon = icon;
266       
267       if (info->icon)
268         g_object_ref (info->icon);
269     }
270 }
271
272 /*****************************************
273  *             GtkFileSystem             *
274  *****************************************/
275 GType
276 gtk_file_system_get_type (void)
277 {
278   static GType file_system_type = 0;
279
280   if (!file_system_type)
281     {
282       static const GTypeInfo file_system_info =
283       {
284         sizeof (GtkFileSystemIface),  /* class_size */
285         gtk_file_system_base_init,    /* base_init */
286         NULL,                         /* base_finalize */
287       };
288
289       file_system_type = g_type_register_static (G_TYPE_INTERFACE,
290                                                  "GtkFileSystem",
291                                                  &file_system_info, 0);
292
293       g_type_interface_add_prerequisite (file_system_type, G_TYPE_OBJECT);
294     }
295
296   return file_system_type;
297 }
298
299 static void
300 gtk_file_system_base_init (gpointer g_class)
301 {
302   static gboolean initialized = FALSE;
303   
304   if (!initialized)
305     {
306       GType iface_type = G_TYPE_FROM_INTERFACE (g_class);
307
308       g_signal_new ("roots_changed",
309                     iface_type,
310                     G_SIGNAL_RUN_LAST,
311                     G_STRUCT_OFFSET (GtkFileSystemIface, roots_changed),
312                     NULL, NULL,
313                     g_cclosure_marshal_VOID__VOID,
314                     G_TYPE_NONE, 0);
315
316       initialized = TRUE;
317     }
318 }
319
320 GSList *
321 gtk_file_system_list_roots (GtkFileSystem  *file_system)
322 {
323   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
324
325   return GTK_FILE_SYSTEM_GET_IFACE (file_system)->list_roots (file_system);
326 }
327
328 GtkFileInfo *
329 gtk_file_system_get_root_info  (GtkFileSystem    *file_system,
330                                 const gchar      *uri,
331                                 GtkFileInfoType   types,
332                                 GError          **error)
333 {
334   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
335   g_return_val_if_fail (uri != NULL, NULL);
336   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
337
338   return GTK_FILE_SYSTEM_GET_IFACE (file_system)->get_root_info (file_system, uri, types, error);
339 }
340
341 GtkFileFolder *
342 gtk_file_system_get_folder (GtkFileSystem    *file_system,
343                             const gchar      *uri,
344                             GtkFileInfoType   types,
345                             GError          **error)
346 {
347   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
348   g_return_val_if_fail (uri != NULL, NULL);
349   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
350
351   return GTK_FILE_SYSTEM_GET_IFACE (file_system)->get_folder (file_system, uri, types, error);
352 }
353
354 gboolean
355 gtk_file_system_create_folder(GtkFileSystem    *file_system,
356                               const gchar      *uri,
357                               GError          **error)
358 {
359   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE);
360   g_return_val_if_fail (uri != NULL, FALSE);
361   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
362
363   return GTK_FILE_SYSTEM_GET_IFACE (file_system)->create_folder (file_system, uri, error);
364 }
365
366 gboolean
367 gtk_file_system_get_parent (GtkFileSystem *file_system,
368                             const gchar   *uri,
369                             gchar         **parent,
370                             GError        **error)
371 {
372   gchar *tmp_parent = NULL;
373   gboolean result;
374   
375   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE);
376   g_return_val_if_fail (uri != NULL, FALSE);
377   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
378
379   result = GTK_FILE_SYSTEM_GET_IFACE (file_system)->get_parent (file_system, uri, &tmp_parent, error);
380   g_assert (result || tmp_parent == NULL);
381
382   if (parent)
383     *parent = tmp_parent;
384   else
385     g_free (tmp_parent);
386   
387   return result;
388 }
389
390 gchar *
391 gtk_file_system_make_uri (GtkFileSystem    *file_system,
392                           const gchar      *base_uri,
393                           const gchar      *display_name,
394                           GError          **error)
395 {
396   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), NULL);
397   g_return_val_if_fail (base_uri != NULL, NULL);
398   g_return_val_if_fail (display_name != NULL, NULL);
399   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
400
401   return GTK_FILE_SYSTEM_GET_IFACE (file_system)->make_uri (file_system, base_uri, display_name, error);
402 }
403
404 /**
405  * gtk_file_system_parse:
406  * @file_system: a #GtkFileSystem
407  * @base_uri: reference folder with respect to which relative
408  *            paths should be interpreted.
409  * @str: the string to parse
410  * @folder: location to store folder portion of result, or %NULL
411  * @file_part: location to store file portion of result, or %NULL
412  * @error: location to store error, or %NULL
413  * 
414  * Given a string entered by a user, parse it (possibly using
415  * heuristics) into a folder URI and a UTF-8 encoded
416  * filename part. (Suitable for passing to gtk_file_system_make_uri())
417  *
418  * Note that the returned filename point may point to a subfolder
419  * of the returned folder. Adding a trailing path separator is needed
420  * to enforce the interpretation as a folder name.
421  *
422  * If parsing fails because the syntax of @str is not understood,
423  * and error of type GTK_FILE_SYSTEM_ERROR_BAD_FILENAME will
424  * be set in @error and %FALSE returned.
425  *
426  * If parsing fails because a path was encountered that doesn't
427  * exist on the filesystem, then an error of type
428  * %GTK_FILE_SYSTEM_ERROR_NONEXISTANT will be set in @error
429  * and %FALSE returned. (This only applies to parsing relative paths,
430  * not to interpretation of @file_part. No check is made as
431  * to whether @file_part exists.)
432  *
433  * Return value: %TRUE if the parsing succeeds, otherwise, %FALSE.
434  **/
435 gboolean
436 gtk_file_system_parse (GtkFileSystem   *file_system,
437                        const gchar     *base_uri,
438                        const gchar     *str,
439                        gchar          **folder,
440                        gchar          **file_part,
441                        GError         **error)
442 {
443   gchar *tmp_folder = NULL;
444   gchar *tmp_file_part = NULL;
445   gboolean result;
446
447   g_return_val_if_fail (GTK_IS_FILE_SYSTEM (file_system), FALSE);
448   g_return_val_if_fail (base_uri != NULL, FALSE);
449   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
450
451
452   result = GTK_FILE_SYSTEM_GET_IFACE (file_system)->parse (file_system, base_uri, str,
453                                                             &tmp_folder, &tmp_file_part,
454                                                             error);
455   g_assert (result || (tmp_folder == NULL && tmp_file_part == NULL));
456
457   if (folder)
458     *folder = tmp_folder;
459   else
460     g_free (tmp_folder);
461
462   if (file_part)
463     *file_part = tmp_file_part;
464   else
465     g_free (tmp_file_part);
466
467   return result;
468 }
469
470 /*****************************************
471  *             GtkFileFolder             *
472  *****************************************/
473 GType
474 gtk_file_folder_get_type (void)
475 {
476   static GType file_folder_type = 0;
477
478   if (!file_folder_type)
479     {
480       static const GTypeInfo file_folder_info =
481       {
482         sizeof (GtkFileFolderIface),  /* class_size */
483         gtk_file_folder_base_init,    /* base_init */
484         NULL,                         /* base_finalize */
485       };
486
487       file_folder_type = g_type_register_static (G_TYPE_INTERFACE,
488                                                  "GtkFileFolder",
489                                                  &file_folder_info, 0);
490       
491       g_type_interface_add_prerequisite (file_folder_type, G_TYPE_OBJECT);
492     }
493
494   return file_folder_type;
495 }
496
497 static void
498 gtk_file_folder_base_init (gpointer g_class)
499 {
500   static gboolean initialized = FALSE;
501   
502   if (!initialized)
503     {
504       GType iface_type = G_TYPE_FROM_INTERFACE (g_class);
505
506       g_signal_new ("deleted",
507                     iface_type,
508                     G_SIGNAL_RUN_LAST,
509                     G_STRUCT_OFFSET (GtkFileFolderIface, deleted),
510                     NULL, NULL,
511                     g_cclosure_marshal_VOID__VOID,
512                     G_TYPE_NONE, 0);
513       g_signal_new ("files_added",
514                     iface_type,
515                     G_SIGNAL_RUN_LAST,
516                     G_STRUCT_OFFSET (GtkFileFolderIface, files_added),
517                     NULL, NULL,
518                     g_cclosure_marshal_VOID__POINTER,
519                     G_TYPE_NONE, 1,
520                     G_TYPE_POINTER);
521       g_signal_new ("files_changed",
522                     iface_type,
523                     G_SIGNAL_RUN_LAST,
524                     G_STRUCT_OFFSET (GtkFileFolderIface, files_changed),
525                     NULL, NULL,
526                     g_cclosure_marshal_VOID__POINTER,
527                     G_TYPE_NONE, 1,
528                     G_TYPE_POINTER);
529       g_signal_new ("files_removed",
530                     iface_type,
531                     G_SIGNAL_RUN_LAST,
532                     G_STRUCT_OFFSET (GtkFileFolderIface, files_removed),
533                     NULL, NULL,
534                     g_cclosure_marshal_VOID__POINTER,
535                     G_TYPE_NONE, 1,
536                     G_TYPE_POINTER);
537
538       initialized = TRUE;
539     }
540 }
541
542 gboolean
543 gtk_file_folder_list_children (GtkFileFolder    *folder,
544                                GSList          **children,
545                                GError          **error)
546 {
547   gboolean result;
548   GSList *tmp_children = NULL;
549   
550   g_return_val_if_fail (GTK_IS_FILE_FOLDER (folder), FALSE);
551   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
552
553   result = GTK_FILE_FOLDER_GET_IFACE (folder)->list_children (folder, &tmp_children, error);
554   g_assert (result || tmp_children == NULL);
555
556   if (children)
557     *children = tmp_children;
558   else
559     {
560       g_slist_foreach (tmp_children, (GFunc)g_free, NULL);
561       g_slist_free (tmp_children);
562     }
563
564   return result;
565 }
566
567 GtkFileInfo *
568 gtk_file_folder_get_info (GtkFileFolder    *folder,
569                           const gchar      *uri,
570                           GError          **error)
571 {
572   g_return_val_if_fail (GTK_IS_FILE_FOLDER (folder), NULL);
573   g_return_val_if_fail (uri != NULL, NULL);
574   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
575
576   return GTK_FILE_FOLDER_GET_IFACE (folder)->get_info (folder, uri, error);
577 }