]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilesystemwin32.c
gtk/gtkfilechooserdefault.c (shortcuts_append_home,
[~andy/gtk] / gtk / gtkfilesystemwin32.c
1 /* GTK - The GIMP Toolkit
2  * gtkfilesystemwin32.c: Default implementation of GtkFileSystem for Windows
3  * Copyright (C) 2003, Red Hat, Inc.
4  * Copyright (C) 2004, Hans Breuer
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #include <config.h>
23 #include "gtkalias.h"
24 #include "gtkfilesystem.h"
25 #include "gtkfilesystemwin32.h"
26 #include "gtkintl.h"
27 #include "gtkstock.h"
28 #include "gtkiconfactory.h"
29
30 #include <errno.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <ctype.h>
34 #include <sys/types.h>
35
36 #ifdef G_OS_WIN32
37 #define WIN32_LEAN_AND_MEAN
38 #include <windows.h>
39 #include <shellapi.h> /* ExtractAssociatedIcon */
40 #include <direct.h>
41 #include <io.h>
42 #define mkdir(p,m) _mkdir(p)
43 #include <gdk/win32/gdkwin32.h> /* gdk_win32_hdc_get */
44 #else
45 #error "The implementation is win32 only."
46 #endif /* G_OS_WIN32 */
47
48 #ifndef G_IS_DIR_SEPARATOR
49 #define G_IS_DIR_SEPARATOR(c) ((c) == G_DIR_SEPARATOR || (c) == '/')
50 #endif
51
52 typedef struct _GtkFileSystemWin32Class GtkFileSystemWin32Class;
53
54 #define GTK_FILE_SYSTEM_WIN32_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_SYSTEM_WIN32, GtkFileSystemWin32Class))
55 #define GTK_IS_FILE_SYSTEM_WIN32_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_SYSTEM_WIN32))
56 #define GTK_FILE_SYSTEM_WIN32_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_SYSTEM_WIN32, GtkFileSystemWin32Class))
57
58 struct _GtkFileSystemWin32Class
59 {
60   GObjectClass parent_class;
61 };
62
63 struct _GtkFileSystemWin32
64 {
65   GObject parent_instance;
66
67   GHashTable *folder_hash;
68 };
69
70 #define GTK_TYPE_FILE_FOLDER_WIN32             (gtk_file_folder_win32_get_type ())
71 #define GTK_FILE_FOLDER_WIN32(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_FILE_FOLDER_WIN32, GtkFileFolderWin32))
72 #define GTK_IS_FILE_FOLDER_WIN32(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_FILE_FOLDER_WIN32))
73 #define GTK_FILE_FOLDER_WIN32_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_FOLDER_WIN32, GtkFileFolderWin32Class))
74 #define GTK_IS_FILE_FOLDER_WIN32_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_FOLDER_WIN32))
75 #define GTK_FILE_FOLDER_WIN32_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_FOLDER_WIN32, GtkFileFolderWin32Class))
76
77 typedef struct _GtkFileFolderWin32      GtkFileFolderWin32;
78 typedef struct _GtkFileFolderWin32Class GtkFileFolderWin32Class;
79
80 struct _GtkFileFolderWin32Class
81 {
82   GObjectClass parent_class;
83 };
84
85 struct _GtkFileFolderWin32
86 {
87   GObject parent_instance;
88
89   GtkFileSystemWin32 *system_win32;
90   GtkFileInfoType types;
91   gchar *filename;
92 };
93
94 static GObjectClass *system_parent_class;
95 static GObjectClass *folder_parent_class;
96
97 static void           gtk_file_system_win32_class_init       (GtkFileSystemWin32Class  *class);
98 static void           gtk_file_system_win32_iface_init       (GtkFileSystemIface       *iface);
99 static void           gtk_file_system_win32_init             (GtkFileSystemWin32       *impl);
100 static void           gtk_file_system_win32_finalize         (GObject                  *object);
101
102 static GSList *       gtk_file_system_win32_list_volumes     (GtkFileSystem      *file_system);
103 static GtkFileSystemVolume *gtk_file_system_win32_get_volume_for_path (GtkFileSystem     *file_system,
104                                                                        const GtkFilePath *path);
105
106 static GtkFileFolder *gtk_file_system_win32_get_folder       (GtkFileSystem            *file_system,
107                                                               const GtkFilePath        *path,
108                                                               GtkFileInfoType           types,
109                                                               GError                  **error);
110 static gboolean       gtk_file_system_win32_create_folder    (GtkFileSystem            *file_system,
111                                                               const GtkFilePath        *path,
112                                                               GError                  **error);
113
114 static void         gtk_file_system_win32_volume_free             (GtkFileSystem       *file_system,
115                                                                    GtkFileSystemVolume *volume);
116 static GtkFilePath *gtk_file_system_win32_volume_get_base_path    (GtkFileSystem       *file_system,
117                                                                    GtkFileSystemVolume *volume);
118 static gboolean     gtk_file_system_win32_volume_get_is_mounted   (GtkFileSystem       *file_system,
119                                                                    GtkFileSystemVolume *volume);
120 static gboolean     gtk_file_system_win32_volume_mount            (GtkFileSystem       *file_system,
121                                                                    GtkFileSystemVolume *volume,
122                                                                    GError             **error);
123 static gchar *      gtk_file_system_win32_volume_get_display_name (GtkFileSystem       *file_system,
124                                                                    GtkFileSystemVolume *volume);
125 static GdkPixbuf *  gtk_file_system_win32_volume_render_icon      (GtkFileSystem        *file_system,
126                                                                    GtkFileSystemVolume  *volume,
127                                                                    GtkWidget            *widget,
128                                                                    gint                  pixel_size,
129                                                                    GError              **error);
130
131 static gboolean       gtk_file_system_win32_get_parent       (GtkFileSystem            *file_system,
132                                                               const GtkFilePath        *path,
133                                                               GtkFilePath             **parent,
134                                                               GError                  **error);
135 static GtkFilePath *  gtk_file_system_win32_make_path        (GtkFileSystem            *file_system,
136                                                               const GtkFilePath        *base_path,
137                                                               const gchar              *display_name,
138                                                               GError                  **error);
139 static gboolean       gtk_file_system_win32_parse            (GtkFileSystem            *file_system,
140                                                               const GtkFilePath        *base_path,
141                                                               const gchar              *str,
142                                                               GtkFilePath             **folder,
143                                                               gchar                   **file_part,
144                                                               GError                  **error);
145 static gchar *        gtk_file_system_win32_path_to_uri      (GtkFileSystem            *file_system,
146                                                               const GtkFilePath        *path);
147 static gchar *        gtk_file_system_win32_path_to_filename (GtkFileSystem            *file_system,
148                                                               const GtkFilePath        *path);
149 static GtkFilePath *  gtk_file_system_win32_uri_to_path      (GtkFileSystem            *file_system,
150                                                               const gchar              *uri);
151 static GtkFilePath *  gtk_file_system_win32_filename_to_path (GtkFileSystem            *file_system,
152                                                               const gchar              *filename);
153 static GdkPixbuf *gtk_file_system_win32_render_icon (GtkFileSystem     *file_system,
154                                                      const GtkFilePath *path,
155                                                      GtkWidget         *widget,
156                                                      gint               pixel_size,
157                                                      GError           **error);
158
159 static gboolean       gtk_file_system_win32_insert_bookmark  (GtkFileSystem            *file_system,
160                                                               const GtkFilePath        *path,
161                                                               gint               position,
162                                                               GError                  **error);
163 static gboolean       gtk_file_system_win32_remove_bookmark  (GtkFileSystem            *file_system,
164                                                               const GtkFilePath        *path,
165                                                               GError                  **error);
166 static GSList *       gtk_file_system_win32_list_bookmarks   (GtkFileSystem            *file_system);
167 static GType          gtk_file_folder_win32_get_type         (void);
168 static void           gtk_file_folder_win32_class_init       (GtkFileFolderWin32Class  *class);
169 static void           gtk_file_folder_win32_iface_init       (GtkFileFolderIface       *iface);
170 static void           gtk_file_folder_win32_init             (GtkFileFolderWin32       *impl);
171 static void           gtk_file_folder_win32_finalize         (GObject                  *object);
172 static GtkFileInfo *  gtk_file_folder_win32_get_info         (GtkFileFolder            *folder,
173                                                               const GtkFilePath        *path,
174                                                               GError                  **error);
175 static gboolean       gtk_file_folder_win32_list_children    (GtkFileFolder            *folder,
176                                                               GSList                  **children,
177                                                               GError                  **error);
178
179 static gchar *        filename_from_path                     (const GtkFilePath        *path);
180 static GtkFilePath *  filename_to_path                       (const gchar              *filename);
181
182 static gboolean       filename_is_drive_root                 (const char               *filename);
183 static gboolean       filename_is_some_root                  (const char               *filename);
184 static GtkFileInfo *  filename_get_info                      (const gchar              *filename,
185                                                               GtkFileInfoType           types,
186                                                               GError                  **error);
187
188 /* some info kept together for volumes */
189 struct _GtkFileSystemVolume
190 {
191   gchar    *drive;
192   gboolean  is_mounted;
193 };
194
195 /*
196  * GtkFileSystemWin32
197  */
198 GType
199 gtk_file_system_win32_get_type (void)
200 {
201   static GType file_system_win32_type = 0;
202
203   if (!file_system_win32_type)
204     {
205       static const GTypeInfo file_system_win32_info =
206       {
207         sizeof (GtkFileSystemWin32Class),
208         NULL,           /* base_init */
209         NULL,           /* base_finalize */
210         (GClassInitFunc) gtk_file_system_win32_class_init,
211         NULL,           /* class_finalize */
212         NULL,           /* class_data */
213         sizeof (GtkFileSystemWin32),
214         0,              /* n_preallocs */
215         (GInstanceInitFunc) gtk_file_system_win32_init,
216       };
217       
218       static const GInterfaceInfo file_system_info =
219       {
220         (GInterfaceInitFunc) gtk_file_system_win32_iface_init, /* interface_init */
221         NULL,                                                  /* interface_finalize */
222         NULL                                                   /* interface_data */
223       };
224
225       file_system_win32_type = g_type_register_static (G_TYPE_OBJECT,
226                                                        "GtkFileSystemWin32",
227                                                        &file_system_win32_info, 0);
228       g_type_add_interface_static (file_system_win32_type,
229                                    GTK_TYPE_FILE_SYSTEM,
230                                    &file_system_info);
231     }
232
233   return file_system_win32_type;
234 }
235
236 /**
237  * gtk_file_system_win32_new:
238  * 
239  * Creates a new #GtkFileSystemWin32 object. #GtkFileSystemWin32
240  * implements the #GtkFileSystem interface with direct access to
241  * the filesystem using Windows API calls
242  * 
243  * Return value: the new #GtkFileSystemWin32 object
244  **/
245 GtkFileSystem *
246 gtk_file_system_win32_new (void)
247 {
248   return g_object_new (GTK_TYPE_FILE_SYSTEM_WIN32, NULL);
249 }
250
251 static void
252 gtk_file_system_win32_class_init (GtkFileSystemWin32Class *class)
253 {
254   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
255
256   system_parent_class = g_type_class_peek_parent (class);
257   
258   gobject_class->finalize = gtk_file_system_win32_finalize;
259 }
260
261 static void
262 gtk_file_system_win32_iface_init (GtkFileSystemIface *iface)
263 {
264   iface->list_volumes = gtk_file_system_win32_list_volumes;
265   iface->get_volume_for_path = gtk_file_system_win32_get_volume_for_path;
266   iface->get_folder = gtk_file_system_win32_get_folder;
267   iface->create_folder = gtk_file_system_win32_create_folder;
268   iface->volume_free = gtk_file_system_win32_volume_free;
269   iface->volume_get_base_path = gtk_file_system_win32_volume_get_base_path;
270   iface->volume_get_is_mounted = gtk_file_system_win32_volume_get_is_mounted;
271   iface->volume_mount = gtk_file_system_win32_volume_mount;
272   iface->volume_get_display_name = gtk_file_system_win32_volume_get_display_name;
273   iface->volume_render_icon = gtk_file_system_win32_volume_render_icon;
274   iface->get_parent = gtk_file_system_win32_get_parent;
275   iface->make_path = gtk_file_system_win32_make_path;
276   iface->parse = gtk_file_system_win32_parse;
277   iface->path_to_uri = gtk_file_system_win32_path_to_uri;
278   iface->path_to_filename = gtk_file_system_win32_path_to_filename;
279   iface->uri_to_path = gtk_file_system_win32_uri_to_path;
280   iface->filename_to_path = gtk_file_system_win32_filename_to_path;
281   iface->render_icon = gtk_file_system_win32_render_icon;
282   iface->insert_bookmark = gtk_file_system_win32_insert_bookmark;
283   iface->remove_bookmark = gtk_file_system_win32_remove_bookmark;
284   iface->list_bookmarks = gtk_file_system_win32_list_bookmarks;
285 }
286
287 static void
288 gtk_file_system_win32_init (GtkFileSystemWin32 *system_win32)
289 {
290   system_win32->folder_hash = g_hash_table_new (g_str_hash, g_str_equal);
291 }
292
293 static void
294 gtk_file_system_win32_finalize (GObject *object)
295 {
296   GtkFileSystemWin32 *system_win32;
297
298   system_win32 = GTK_FILE_SYSTEM_WIN32 (object);
299
300   /* FIXME: assert that the hash is empty? */
301   g_hash_table_destroy (system_win32->folder_hash);
302
303   system_parent_class->finalize (object);
304 }
305
306 static GSList *
307 gtk_file_system_win32_list_volumes (GtkFileSystem *file_system)
308 {
309   DWORD   drives;
310   gchar   drive[4] = "A:\\";
311   GSList *list = NULL;
312
313   drives = GetLogicalDrives();
314
315   if (!drives)
316     g_warning ("GetLogicalDrives failed.");
317
318   while (drives && drive[0] <= 'Z')
319     {
320       if (drives & 1)
321       {
322         GtkFileSystemVolume *vol = g_new0 (GtkFileSystemVolume, 1);
323         if (drive[0] == 'A' || drive[0] == 'B')
324           vol->is_mounted = FALSE; /* skip floppy */
325         else
326           vol->is_mounted = TRUE; /* handle other removable drives special, too? */
327
328         vol->drive = g_strdup (drive);
329         list = g_slist_append (list, vol);
330       }
331       drives >>= 1;
332       drive[0]++;
333     }
334   return list;
335 }
336
337 static GtkFileSystemVolume *
338 gtk_file_system_win32_get_volume_for_path (GtkFileSystem     *file_system,
339                                            const GtkFilePath *path)
340 {
341   GtkFileSystemVolume *vol = g_new0 (GtkFileSystemVolume, 1);
342   gchar* p = g_strndup (gtk_file_path_get_string (path), 3);
343
344   g_return_val_if_fail (p != NULL, NULL);
345
346   /*FIXME: gtk_file_path_compare() is case sensitive, we are not*/
347   p[0] = g_ascii_toupper (p[0]);
348   vol->drive = p;
349   vol->is_mounted = (p[0] != 'A' && p[0] != 'B');
350
351   return vol;
352 }
353
354 static GtkFileFolder *
355 gtk_file_system_win32_get_folder (GtkFileSystem    *file_system,
356                                  const GtkFilePath *path,
357                                  GtkFileInfoType    types,
358                                  GError           **error)
359 {
360   GtkFileSystemWin32 *system_win32;
361   GtkFileFolderWin32 *folder_win32;
362   gchar *filename;
363
364   system_win32 = GTK_FILE_SYSTEM_WIN32 (file_system);
365
366   filename = filename_from_path (path);
367   g_return_val_if_fail (filename != NULL, NULL);
368
369   folder_win32 = g_hash_table_lookup (system_win32->folder_hash, filename);
370
371   if (folder_win32)
372     return g_object_ref (folder_win32);
373
374   if (!g_file_test (filename, G_FILE_TEST_IS_DIR))
375     {
376       int save_errno = errno;
377       gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
378
379       /* If g_file_test() returned FALSE but not due to an error, it means
380        * that the filename is not a directory.
381        */
382       if (save_errno == 0)
383         /* ENOTDIR */
384         g_set_error (error,
385                      GTK_FILE_SYSTEM_ERROR,
386                      GTK_FILE_SYSTEM_ERROR_NOT_FOLDER,
387                      _("%s: %s"),
388                      filename_utf8 ? filename_utf8 : "???",
389                      g_strerror (ENOTDIR));
390       else
391         g_set_error (error,
392                      GTK_FILE_SYSTEM_ERROR,
393                      GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
394                      _("error getting information for '%s': %s"),
395                      filename_utf8 ? filename_utf8 : "???",
396                      g_strerror (save_errno));
397
398       g_free (filename_utf8);
399       return NULL;
400     }
401
402   folder_win32 = g_object_new (GTK_TYPE_FILE_FOLDER_WIN32, NULL);
403   folder_win32->system_win32 = system_win32;
404   folder_win32->filename = filename;
405   folder_win32->types = types;
406
407   g_hash_table_insert (system_win32->folder_hash, folder_win32->filename, folder_win32);
408
409   return GTK_FILE_FOLDER (folder_win32);
410 }
411
412 static gboolean
413 gtk_file_system_win32_create_folder (GtkFileSystem     *file_system,
414                                      const GtkFilePath *path,
415                                      GError           **error)
416 {
417   GtkFileSystemWin32 *system_win32;
418   gchar *filename;
419   gboolean result;
420   char *parent;
421
422   system_win32 = GTK_FILE_SYSTEM_WIN32 (file_system);
423
424   filename = filename_from_path (path);
425   g_return_val_if_fail (filename != NULL, FALSE);
426
427   result = mkdir (filename, 0777) == 0;
428
429   if (!result)
430     {
431       gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
432       g_set_error (error,
433                    GTK_FILE_SYSTEM_ERROR,
434                    GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
435                    _("error creating directory '%s': %s"),
436                    filename_utf8 ? filename_utf8 : "???",
437                    g_strerror (errno));
438       g_free (filename_utf8);
439     }
440   else if (!filename_is_drive_root (filename))
441     {
442       parent = g_path_get_dirname (filename);
443       if (parent)
444         {
445           GtkFileFolderWin32 *folder_win32;
446
447           folder_win32 = g_hash_table_lookup (system_win32->folder_hash, parent);
448           if (folder_win32)
449             {
450               GSList *paths;
451
452               paths = g_slist_append (NULL, (GtkFilePath *) path);
453               g_signal_emit_by_name (folder_win32, "files-added", paths);
454               g_slist_free (paths);
455             }
456           g_free(parent);
457         }
458     }
459
460   g_free (filename);
461
462   return result;
463 }
464
465 static void
466 gtk_file_system_win32_volume_free (GtkFileSystem        *file_system,
467                                    GtkFileSystemVolume  *volume)
468 {
469   g_free (volume->drive);
470   g_free (volume);
471 }
472
473 static GtkFilePath *
474 gtk_file_system_win32_volume_get_base_path (GtkFileSystem        *file_system,
475                                             GtkFileSystemVolume  *volume)
476 {
477   return (GtkFilePath *) g_strdup (volume->drive);
478 }
479
480 static gboolean
481 gtk_file_system_win32_volume_get_is_mounted (GtkFileSystem        *file_system,
482                                              GtkFileSystemVolume  *volume)
483 {
484   return volume->is_mounted;
485 }
486
487 static gboolean
488 gtk_file_system_win32_volume_mount (GtkFileSystem        *file_system, 
489                                     GtkFileSystemVolume  *volume,
490                                     GError              **error)
491 {
492   g_set_error (error,
493                GTK_FILE_SYSTEM_ERROR,
494                GTK_FILE_SYSTEM_ERROR_FAILED,
495                _("This file system does not support mounting"));
496   return FALSE;
497 }
498
499 static gchar *
500 gtk_file_system_win32_volume_get_display_name (GtkFileSystem       *file_system,
501                                               GtkFileSystemVolume *volume)
502 {
503   gchar *real_display_name;
504   gunichar2 *wdrive = g_utf8_to_utf16 (volume->drive, -1, NULL, NULL, NULL);
505   gunichar2  wname[80];
506
507   g_return_val_if_fail (wdrive != NULL, NULL);
508
509   if (GetVolumeInformationW (wdrive,
510                             wname, G_N_ELEMENTS(wname), 
511                             NULL, /* serial number */
512                             NULL, /* max. component length */
513                             NULL, /* fs flags */
514                             NULL, 0) /* fs type like FAT, NTFS */
515                             && wname[0])
516     {
517       gchar *name = g_utf16_to_utf8 (wname, -1, NULL, NULL, NULL);
518       real_display_name = g_strconcat (name, " (", volume->drive, ")", NULL);
519       g_free (name);
520     }
521   else
522     real_display_name = g_strdup (volume->drive);
523
524   g_free (wdrive);
525
526   return real_display_name;
527 }
528
529 static GdkPixbuf *
530 gtk_file_system_win32_volume_render_icon (GtkFileSystem        *file_system,
531                                          GtkFileSystemVolume  *volume,
532                                          GtkWidget            *widget,
533                                          gint                  pixel_size,
534                                          GError              **error)
535 {
536   GtkIconSet *icon_set = NULL;
537   DWORD dt = GetDriveType (volume->drive);
538
539   switch (dt)
540     {
541     case DRIVE_REMOVABLE :
542       icon_set = gtk_style_lookup_icon_set (widget->style, GTK_STOCK_FLOPPY);
543       break;
544     case DRIVE_CDROM :
545       icon_set = gtk_style_lookup_icon_set (widget->style, GTK_STOCK_CDROM);
546       break;
547     case DRIVE_REMOTE :
548       icon_set = gtk_style_lookup_icon_set (widget->style, GTK_STOCK_NETWORK);
549       break;
550     case DRIVE_FIXED :
551       icon_set = gtk_style_lookup_icon_set (widget->style, GTK_STOCK_HARDDISK);
552       break;
553     case DRIVE_RAMDISK :
554       /*FIXME: need a ram stock icon
555       gtk_file_info_set_icon_type (info, GTK_STOCK_OPEN);*/
556       break;
557     default :
558       g_assert_not_reached ();
559     }
560
561   return gtk_icon_set_render_icon (icon_set, 
562                                    widget->style,
563                                    gtk_widget_get_direction (widget),
564                                    GTK_STATE_NORMAL,
565                                    GTK_ICON_SIZE_BUTTON,
566                                    widget, NULL); 
567 }
568
569 static gboolean
570 gtk_file_system_win32_get_parent (GtkFileSystem     *file_system,
571                                   const GtkFilePath *path,
572                                   GtkFilePath      **parent,
573                                   GError           **error)
574 {
575   const char *filename;
576
577   filename = gtk_file_path_get_string (path);
578   g_return_val_if_fail (filename != NULL, FALSE);
579   g_return_val_if_fail (g_path_is_absolute (filename), FALSE);
580
581   if (filename_is_some_root (filename))
582     {
583       *parent = NULL;
584     }
585   else
586     {
587       gchar *parent_filename = g_path_get_dirname (filename);
588       *parent = filename_to_path (parent_filename);
589       g_free (parent_filename);
590     }
591
592   return TRUE;
593 }
594
595 static GtkFilePath *
596 gtk_file_system_win32_make_path (GtkFileSystem     *file_system,
597                                  const GtkFilePath *base_path,
598                                  const gchar       *display_name,
599                                  GError           **error)
600 {
601   const char *base_filename;
602   gchar *filename;
603   gchar *full_filename;
604   GError *tmp_error = NULL;
605   GtkFilePath *result;
606   
607   base_filename = gtk_file_path_get_string (base_path);
608   g_return_val_if_fail (base_filename != NULL, NULL);
609   g_return_val_if_fail (g_path_is_absolute (base_filename), NULL);
610
611   filename = g_filename_from_utf8 (display_name, -1, NULL, NULL, &tmp_error);
612   if (!filename)
613     {
614       g_set_error (error,
615                    GTK_FILE_SYSTEM_ERROR,
616                    GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
617                    "%s",
618                    tmp_error->message);
619       
620       g_error_free (tmp_error);
621
622       return NULL;
623     }
624     
625   full_filename = g_build_filename (base_filename, filename, NULL);
626   result = filename_to_path (full_filename);
627   g_free (filename);
628   g_free (full_filename);
629   
630   return result;
631 }
632
633 /* If this was a publically exported function, it should return
634  * a dup'ed result, but we make it modify-in-place for efficiency
635  * here, and because it works for us.
636  */
637 static void
638 canonicalize_filename (gchar *filename)
639 {
640   gchar *p, *q;
641   gchar *past_root;
642   gboolean last_was_slash = FALSE;
643
644 #if 0
645   printf("canonicalize_filename: %s ", filename);
646 #endif
647
648   p = filename;
649   q = filename;
650
651   if (g_ascii_isalpha (*filename) &&
652       filename[1] == ':' &&
653       G_IS_DIR_SEPARATOR (filename[2]))
654     past_root = filename + 3;
655   else
656     past_root = filename + 1;
657   
658
659   while (*p)
660     {
661       if (G_IS_DIR_SEPARATOR (*p))
662         {
663           if (!last_was_slash)
664             *q++ = G_DIR_SEPARATOR;
665
666           last_was_slash = TRUE;
667         }
668       else
669         {
670           if (last_was_slash && *p == '.')
671             {
672               if (G_IS_DIR_SEPARATOR (*(p + 1)) ||
673                   *(p + 1) == '\0')
674                 {
675                   if (*(p + 1) == '\0')
676                     break;
677                   
678                   p += 1;
679                 }
680               else if (*(p + 1) == '.' &&
681                        (G_IS_DIR_SEPARATOR (*(p + 2)) ||
682                         *(p + 2) == '\0'))
683                 {
684                   if (q > past_root)
685                     {
686                       q--;
687                       while (q > past_root &&
688                              !G_IS_DIR_SEPARATOR (*(q - 1)))
689                         q--;
690                     }
691
692                   if (*(p + 2) == '\0')
693                     break;
694                   
695                   p += 2;
696                 }
697               else
698                 {
699                   *q++ = *p;
700                   last_was_slash = FALSE;
701                 }
702             }
703           else
704             {
705               *q++ = *p;
706               last_was_slash = FALSE;
707             }
708         }
709
710       p++;
711     }
712
713   if (q > past_root && G_IS_DIR_SEPARATOR (*(q - 1)))
714     q--;
715
716   *q = '\0';
717 #if 0
718   printf(" => %s\n", filename);
719 #endif
720 }
721
722 static gboolean
723 gtk_file_system_win32_parse (GtkFileSystem     *file_system,
724                              const GtkFilePath *base_path,
725                              const gchar       *str,
726                              GtkFilePath      **folder,
727                              gchar            **file_part,
728                              GError           **error)
729 {
730   const char *base_filename;
731   gchar *last_backslash, *last_slash;
732   gboolean result = FALSE;
733
734 #if 0
735   printf("gtk_file_system_win32_parse: base_path=%s str=%s\n",(char*)base_path,str);
736 #endif
737
738   base_filename = gtk_file_path_get_string (base_path);
739   g_return_val_if_fail (base_filename != NULL, FALSE);
740   g_return_val_if_fail (g_path_is_absolute (base_filename), FALSE);
741   
742   last_backslash = strrchr (str, G_DIR_SEPARATOR);
743   last_slash = strrchr (str, '/');
744   if (last_slash == NULL ||
745       (last_backslash != NULL && last_backslash > last_slash))
746     last_slash = last_backslash;
747
748   if (!last_slash)
749     {
750       *folder = gtk_file_path_copy (base_path);
751       *file_part = g_strdup (str);
752       result = TRUE;
753     }
754   else
755     {
756       gchar *folder_part;
757       gchar *folder_path;
758       GError *tmp_error = NULL;
759
760       if (last_slash == str)
761         {
762           if (g_ascii_isalpha (base_filename[0]) &&
763               base_filename[1] == ':')
764             folder_part = g_strdup_printf ("%c:%c", base_filename[0],
765                                            G_DIR_SEPARATOR);
766           else
767             folder_part = g_strdup (G_DIR_SEPARATOR_S);
768         }
769       else if (g_ascii_isalpha (str[0]) &&
770                str[1] == ':' &&
771                G_IS_DIR_SEPARATOR (str[2]))
772         folder_part = g_filename_from_utf8 (str, last_slash - str + 1,
773                                             NULL, NULL, &tmp_error);
774       else
775         folder_part = g_filename_from_utf8 (str, last_slash - str,
776                                             NULL, NULL, &tmp_error);
777
778       if (!folder_part)
779         {
780           g_set_error (error,
781                        GTK_FILE_SYSTEM_ERROR,
782                        GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
783                        "%s",
784                        tmp_error->message);
785           g_error_free (tmp_error);
786         }
787       else
788         {
789           if (g_path_is_absolute (folder_part))
790             folder_path = folder_part;
791           else
792             {
793               folder_path = g_build_filename (base_filename, folder_part, NULL);
794               g_free (folder_part);
795             }
796
797           canonicalize_filename (folder_path);
798           
799           *folder = filename_to_path (folder_path);
800           *file_part = g_strdup (last_slash + 1);
801
802           g_free (folder_path);
803
804           result = TRUE;
805         }
806     }
807
808 #if 0
809   printf("gtk_file_system_win32_parse:returning folder=%s file_part=%s\n",(*folder?(char*)*folder:"NULL"),*file_part);
810 #endif
811
812   return result;
813 }
814
815 static gchar *
816 gtk_file_system_win32_path_to_uri (GtkFileSystem     *file_system,
817                                    const GtkFilePath *path)
818 {
819   return g_filename_to_uri (gtk_file_path_get_string (path), NULL, NULL);
820 }
821
822 static gchar *
823 gtk_file_system_win32_path_to_filename (GtkFileSystem     *file_system,
824                                         const GtkFilePath *path)
825 {
826   return g_strdup (gtk_file_path_get_string (path));
827 }
828
829 static GtkFilePath *
830 gtk_file_system_win32_uri_to_path (GtkFileSystem     *file_system,
831                                    const gchar       *uri)
832 {
833   GtkFilePath *path = NULL;
834   gchar *filename = g_filename_from_uri (uri, NULL, NULL);
835   if (filename)
836     {
837       path = filename_to_path (filename);
838       g_free (filename);
839     }
840
841   return path;
842 }
843
844 static GtkFilePath *
845 gtk_file_system_win32_filename_to_path (GtkFileSystem *file_system,
846                                         const gchar   *filename)
847 {
848   return filename_to_path (filename);
849 }
850
851 static gboolean
852 bookmarks_serialize (GSList  **bookmarks,
853                      gchar    *uri,
854                      gboolean  add,
855                      gint      position,
856                      GError  **error)
857 {
858   gchar   *filename;
859   gboolean ok = TRUE;
860   GSList  *list = *bookmarks;
861
862   filename = g_build_filename (g_get_home_dir (), ".gtk-bookmarks", NULL);
863
864   if (filename)
865     {
866       gchar *contents = NULL;
867       gsize  len = 0;
868       GSList *entry;
869       FILE  *f;   
870        
871       if (g_file_test (filename, G_FILE_TEST_EXISTS))
872         {
873           if (g_file_get_contents (filename, &contents, &len, error))
874             {
875               gchar **lines = g_strsplit (contents, "\n", -1);
876               gint    i;
877
878               for (i = 0; lines[i] != NULL; i++)
879                 {
880                   if (lines[i][0] && !g_slist_find_custom (list, lines[i], (GCompareFunc) strcmp))
881                     list = g_slist_append (list, g_strdup (lines[i]));
882                 }
883               g_strfreev (lines);
884             }
885           else
886             ok = FALSE;
887         }
888       if (ok && (f = fopen (filename, "wb")) != NULL)
889         {
890           entry = g_slist_find_custom (list, uri, (GCompareFunc) strcmp);
891           if (add)
892             {
893               /* g_slist_insert() and our insert semantics are 
894                * compatible, but maybe we should check for 
895                * positon > length ?
896                * 
897                */
898               if (!entry)
899                 list = g_slist_insert (list, g_strdup (uri), position);
900               else
901                 {
902                   g_set_error (error,
903                                GTK_FILE_SYSTEM_ERROR,
904                                GTK_FILE_SYSTEM_ERROR_ALREADY_EXISTS,
905                                "%s already exists in the bookmarks list",
906                                uri);
907                   ok = FALSE;
908                 }
909             }
910           else
911             {
912               /* to remove the given uri */
913               if (entry)
914                 list = g_slist_delete_link(list, entry);
915               for (entry = list; entry != NULL; entry = entry->next)
916                 {
917                   fputs (entry->data, f);
918                   fputs ("\n", f);
919                 }
920             }
921           fclose (f);
922         }
923       else if (ok && error)
924         {
925           g_set_error (error,
926                        GTK_FILE_SYSTEM_ERROR,
927                        GTK_FILE_SYSTEM_ERROR_FAILED,
928                        _("Bookmark saving failed (%s)"),
929                        g_strerror (errno));
930         }
931     }
932   *bookmarks = list;
933   return ok;
934 }
935
936 static GdkPixbuf*
937 extract_icon (const char* filename)
938 {
939   GdkPixbuf *pixbuf = NULL;
940   WORD iicon;
941   HICON hicon;
942   char filename_copy[MAX_PATH];
943   
944   if (!filename || !filename[0])
945     return NULL;
946
947   /* the ugly ExtractAssociatedIcon modifies filename in place - at least on win98 */
948   strcpy(filename_copy, filename);
949   hicon = ExtractAssociatedIcon (GetModuleHandle (NULL), filename_copy, &iicon);
950   if (hicon > (HICON)1)
951     {
952       ICONINFO ii;
953
954       if (GetIconInfo (hicon, &ii))
955         {
956           struct
957           {
958             BITMAPINFOHEADER bi;
959             RGBQUAD colors[2];
960           } bmi;
961           HDC hdc;
962
963           memset (&bmi, 0, sizeof (bmi));
964           bmi.bi.biSize = sizeof (bmi.bi);
965           hdc = CreateCompatibleDC (NULL);
966
967           if (GetDIBits (hdc, ii.hbmColor, 0, 1, NULL, (BITMAPINFO *)&bmi, DIB_RGB_COLORS))
968             {
969                 gchar *pixels, *bits;
970                 gint    rowstride, x, y, w = bmi.bi.biWidth, h = bmi.bi.biHeight;
971
972                 bmi.bi.biBitCount = 24;
973                 bmi.bi.biCompression = BI_RGB;
974                 bmi.bi.biHeight = -h;
975                 pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, w, h);
976                 bits = g_malloc (4 * w * h);
977
978                 /* color data */
979                 if (!GetDIBits (hdc, ii.hbmColor, 0, h, bits, (BITMAPINFO *)&bmi, DIB_RGB_COLORS))
980                   g_warning(G_STRLOC ": Failed to get dibits");
981
982                 pixels = gdk_pixbuf_get_pixels (pixbuf);
983                 rowstride = gdk_pixbuf_get_rowstride (pixbuf);
984                 for (y = 0; y < h; y++)
985                   {
986                     for (x = 0; x < w; x++)
987                       {
988                           pixels[2] = bits[(x+y*w) * 3];
989                           pixels[1] = bits[(x+y*w) * 3 + 1];
990                           pixels[0] = bits[(x+y*w) * 3 + 2];
991                           pixels += 4;
992                       }
993                     pixels += (w * 4 - rowstride);
994                   }
995                 /* transparency */
996                 if (!GetDIBits (hdc, ii.hbmMask, 0, h, bits, (BITMAPINFO *)&bmi, DIB_RGB_COLORS))
997                   g_warning(G_STRLOC ": Failed to get dibits");
998                 pixels = gdk_pixbuf_get_pixels (pixbuf);
999                 for (y = 0; y < h; y++)
1000                   {
1001                     for (x = 0; x < w; x++)
1002                       {
1003                           pixels[3] = 255 - bits[(x + y * w) * 3];
1004                           pixels += 4;
1005                       }
1006                     pixels += (w * 4 - rowstride);
1007                   }
1008                 
1009                 /* release temporary resources */
1010                 g_free (bits);
1011                 if (!DeleteObject (ii.hbmColor) || !DeleteObject (ii.hbmMask))
1012                   g_warning(G_STRLOC ": Leaking Icon Bitmaps ?");
1013             }
1014           else
1015             g_warning(G_STRLOC ": GetDIBits () failed, %s", g_win32_error_message (GetLastError ()));
1016
1017           DeleteDC (hdc);
1018         }
1019       else
1020         g_warning(G_STRLOC ": GetIconInfo failed: %s\n", g_win32_error_message (GetLastError ())); 
1021
1022       if (!DestroyIcon (hicon))
1023         g_warning(G_STRLOC ": DestroyIcon failed");
1024     }
1025   else
1026     g_print ("ExtractAssociatedIcon(%s) failed: %s\n", filename, g_win32_error_message (GetLastError ()));
1027
1028   return pixbuf;
1029 }
1030
1031 static GtkIconSet *
1032 win32_pseudo_mime_lookup (const char* name)
1033 {
1034   static GHashTable *mime_hash = NULL;
1035   GtkIconSet *is = NULL;
1036   char *p = strrchr(name, '.');
1037   char *extension = p ? g_ascii_strdown (p, -1) : g_strdup ("");
1038
1039   if (!mime_hash)
1040     mime_hash = g_hash_table_new (g_str_hash, g_str_equal);
1041
1042   /* do we already have it ? */
1043   is = g_hash_table_lookup (mime_hash, extension);
1044   if (is)
1045     {
1046       g_free (extension);
1047       return is;
1048     }
1049   /* create icon and set */
1050   {
1051     GdkPixbuf *pixbuf = extract_icon (name);
1052     if (pixbuf)
1053       {
1054         GtkIconSource* source = gtk_icon_source_new ();
1055
1056         is = gtk_icon_set_new_from_pixbuf (pixbuf);
1057         gtk_icon_source_set_pixbuf (source, pixbuf);
1058         gtk_icon_set_add_source (is, source);
1059
1060         gtk_icon_source_free (source);
1061       }
1062
1063     g_hash_table_insert (mime_hash, extension, is);
1064     return is;
1065   }
1066 }
1067
1068 static GdkPixbuf *
1069 gtk_file_system_win32_render_icon (GtkFileSystem     *file_system,
1070                                    const GtkFilePath *path,
1071                                    GtkWidget         *widget,
1072                                    gint               pixel_size,
1073                                    GError           **error)
1074 {
1075   GtkIconSet *icon_set = NULL;
1076   const char* filename = gtk_file_path_get_string (path);
1077
1078   /* handle drives with stock icons */
1079   if (filename_is_drive_root (filename))
1080     {
1081       gchar filename2[] = "?:\\";
1082       DWORD dt;
1083
1084       filename2[0] = filename[0];
1085       dt = GetDriveType (filename2);
1086
1087       switch (dt)
1088         {
1089         case DRIVE_REMOVABLE :
1090           icon_set = gtk_style_lookup_icon_set (widget->style, GTK_STOCK_FLOPPY);
1091           break;
1092         case DRIVE_CDROM :
1093           icon_set = gtk_style_lookup_icon_set (widget->style, GTK_STOCK_CDROM);
1094           break;
1095         case DRIVE_FIXED :
1096           icon_set = gtk_style_lookup_icon_set (widget->style, GTK_STOCK_HARDDISK);
1097           break;
1098         default :
1099           break;
1100         }
1101     }
1102   else if (g_file_test (filename, G_FILE_TEST_IS_DIR))
1103     {
1104       const gchar *home = g_get_home_dir ();
1105       if (home != NULL && 0 == strcmp (home, filename))
1106         icon_set = gtk_style_lookup_icon_set (widget->style, GTK_STOCK_HOME);
1107       else
1108         icon_set = gtk_style_lookup_icon_set (widget->style, GTK_STOCK_DIRECTORY);
1109     }
1110   else if (g_file_test (filename, G_FILE_TEST_IS_EXECUTABLE))
1111     {
1112       /* don't lookup all executable icons */
1113       icon_set = gtk_style_lookup_icon_set (widget->style, GTK_STOCK_EXECUTE);
1114     }
1115   else if (g_file_test (filename, G_FILE_TEST_EXISTS))
1116     {
1117       icon_set = win32_pseudo_mime_lookup (filename);
1118     }
1119
1120   if (!icon_set)
1121     {
1122        g_set_error (error,
1123                   GTK_FILE_SYSTEM_ERROR,
1124                   GTK_FILE_SYSTEM_ERROR_FAILED,
1125                   _("This file system does not support icons for everything"));
1126        return NULL;
1127     }
1128
1129   // FIXME : I'd like to get from pixel_size (=20) back to
1130   // icon size, which is an index, but there appears to be no way ?
1131   return gtk_icon_set_render_icon (icon_set, 
1132                                    widget->style,
1133                                    gtk_widget_get_direction (widget),
1134                                    GTK_STATE_NORMAL,
1135                                    GTK_ICON_SIZE_BUTTON,
1136                                    widget, NULL); 
1137 }
1138
1139 static GSList *_bookmarks = NULL;
1140
1141 static gboolean
1142 gtk_file_system_win32_insert_bookmark (GtkFileSystem     *file_system,
1143                                     const GtkFilePath *path,
1144                                     gint               position,
1145                                     GError           **error)
1146 {
1147   gchar *uri = gtk_file_system_win32_path_to_uri (file_system, path);
1148   gboolean ret = bookmarks_serialize (&_bookmarks, uri, TRUE, position, error);
1149   if (ret)
1150     g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
1151   g_free (uri);
1152   return ret;
1153                                
1154 }
1155
1156 static gboolean
1157 gtk_file_system_win32_remove_bookmark (GtkFileSystem     *file_system,
1158                                        const GtkFilePath *path,
1159                                        GError           **error)
1160 {
1161   gchar *uri = gtk_file_system_win32_path_to_uri (file_system, path);
1162   gboolean ret = bookmarks_serialize (&_bookmarks, uri, FALSE, 0, error);
1163   if (ret)
1164     g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
1165   g_free (uri);
1166   return ret;
1167 }
1168
1169 static GSList *
1170 gtk_file_system_win32_list_bookmarks (GtkFileSystem *file_system)
1171 {
1172   GSList *list = NULL;
1173   GSList *entry;
1174
1175
1176   if (bookmarks_serialize (&_bookmarks, "", FALSE, 0, NULL))
1177     {
1178       for (entry = _bookmarks; entry != NULL; entry = entry->next)
1179         {
1180           GtkFilePath *path = gtk_file_system_win32_uri_to_path (
1181                                 file_system, (gchar *)entry->data);
1182
1183           list = g_slist_append (list, path);
1184         }
1185     }
1186
1187   return list;
1188 }
1189
1190 /*
1191  * GtkFileFolderWin32
1192  */
1193 static GType
1194 gtk_file_folder_win32_get_type (void)
1195 {
1196   static GType file_folder_win32_type = 0;
1197
1198   if (!file_folder_win32_type)
1199     {
1200       static const GTypeInfo file_folder_win32_info =
1201       {
1202         sizeof (GtkFileFolderWin32Class),
1203         NULL,           /* base_init */
1204         NULL,           /* base_finalize */
1205         (GClassInitFunc) gtk_file_folder_win32_class_init,
1206         NULL,           /* class_finalize */
1207         NULL,           /* class_data */
1208         sizeof (GtkFileFolderWin32),
1209         0,              /* n_preallocs */
1210         (GInstanceInitFunc) gtk_file_folder_win32_init,
1211       };
1212       
1213       static const GInterfaceInfo file_folder_info =
1214       {
1215         (GInterfaceInitFunc) gtk_file_folder_win32_iface_init, /* interface_init */
1216         NULL,                                                 /* interface_finalize */
1217         NULL                                                  /* interface_data */
1218       };
1219
1220       file_folder_win32_type = g_type_register_static (G_TYPE_OBJECT,
1221                                                       "GtkFileFolderWin32",
1222                                                       &file_folder_win32_info, 0);
1223       g_type_add_interface_static (file_folder_win32_type,
1224                                    GTK_TYPE_FILE_FOLDER,
1225                                    &file_folder_info);
1226     }
1227
1228   return file_folder_win32_type;
1229 }
1230
1231 static void
1232 gtk_file_folder_win32_class_init (GtkFileFolderWin32Class *class)
1233 {
1234   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
1235
1236   folder_parent_class = g_type_class_peek_parent (class);
1237
1238   gobject_class->finalize = gtk_file_folder_win32_finalize;
1239 }
1240
1241 static void
1242 gtk_file_folder_win32_iface_init (GtkFileFolderIface *iface)
1243 {
1244   iface->get_info = gtk_file_folder_win32_get_info;
1245   iface->list_children = gtk_file_folder_win32_list_children;
1246 }
1247
1248 static void
1249 gtk_file_folder_win32_init (GtkFileFolderWin32 *impl)
1250 {
1251 }
1252
1253 static void
1254 gtk_file_folder_win32_finalize (GObject *object)
1255 {
1256   GtkFileFolderWin32 *folder_win32 = GTK_FILE_FOLDER_WIN32 (object);
1257
1258   g_hash_table_remove (folder_win32->system_win32->folder_hash, folder_win32->filename);
1259
1260   g_free (folder_win32->filename);
1261   
1262   folder_parent_class->finalize (object);
1263 }
1264
1265 static GtkFileInfo *
1266 gtk_file_folder_win32_get_info (GtkFileFolder     *folder,
1267                                 const GtkFilePath *path,
1268                                 GError           **error)
1269 {
1270   GtkFileFolderWin32 *folder_win32 = GTK_FILE_FOLDER_WIN32 (folder);
1271   GtkFileInfo *info;
1272   gchar *dirname;
1273   gchar *filename;
1274   
1275   if (!path)
1276     {
1277       g_return_val_if_fail (filename_is_some_root (folder_win32->filename), NULL);
1278
1279       /* ??? */
1280       info = filename_get_info (folder_win32->filename, folder_win32->types, error);
1281
1282       return info;
1283     }
1284
1285   filename = filename_from_path (path);
1286   g_return_val_if_fail (filename != NULL, NULL);
1287
1288 #if 0
1289   dirname = g_path_get_dirname (filename);
1290   g_return_val_if_fail (strcmp (dirname, folder_win32->filename) == 0, NULL);
1291   g_free (dirname);
1292 #endif
1293
1294   info = filename_get_info (filename, folder_win32->types, error);
1295
1296   g_free (filename);
1297
1298   return info;
1299 }
1300
1301 static gboolean
1302 gtk_file_folder_win32_list_children (GtkFileFolder  *folder,
1303                                      GSList        **children,
1304                                      GError        **error)
1305 {
1306   GtkFileFolderWin32 *folder_win32 = GTK_FILE_FOLDER_WIN32 (folder);
1307   GError *tmp_error = NULL;
1308   GDir *dir;
1309
1310   *children = NULL;
1311
1312   dir = g_dir_open (folder_win32->filename, 0, &tmp_error);
1313   if (!dir)
1314     {
1315       g_set_error (error,
1316                    GTK_FILE_SYSTEM_ERROR,
1317                    GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
1318                    "%s",
1319                    tmp_error->message);
1320       
1321       g_error_free (tmp_error);
1322
1323       return FALSE;
1324     }
1325
1326   while (TRUE)
1327     {
1328       const gchar *filename = g_dir_read_name (dir);
1329       gchar *fullname;
1330
1331       if (!filename)
1332         break;
1333
1334       fullname = g_build_filename (folder_win32->filename, filename, NULL);
1335       *children = g_slist_prepend (*children, filename_to_path (fullname));
1336       g_free (fullname);
1337     }
1338
1339   g_dir_close (dir);
1340
1341   *children = g_slist_reverse (*children);
1342   
1343   return TRUE;
1344 }
1345
1346 static GtkFileInfo *
1347 filename_get_info (const gchar     *filename,
1348                    GtkFileInfoType  types,
1349                    GError         **error)
1350 {
1351   GtkFileInfo *info;
1352 #if 0 /* it's dead in GtkFileSystemUnix.c, too */
1353   GtkFileIconType icon_type = GTK_FILE_ICON_REGULAR;
1354 #endif
1355   WIN32_FILE_ATTRIBUTE_DATA wfad;
1356
1357   if (!GetFileAttributesEx (filename, GetFileExInfoStandard, &wfad))
1358     {
1359       gchar *filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
1360       g_set_error (error,
1361                    GTK_FILE_SYSTEM_ERROR,
1362                    GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
1363                    _("error getting information for '%s': %s"),
1364                    filename_utf8 ? filename_utf8 : "???",
1365                    g_win32_error_message (GetLastError ()));
1366       g_free (filename_utf8);
1367       
1368       return NULL;
1369     }
1370
1371   info = gtk_file_info_new ();
1372
1373   if (filename_is_some_root (filename))
1374     {
1375       if (types & GTK_FILE_INFO_DISPLAY_NAME)
1376         gtk_file_info_set_display_name (info, filename);
1377       
1378       if (types & GTK_FILE_INFO_IS_HIDDEN)
1379         gtk_file_info_set_is_hidden (info, FALSE);
1380     }
1381   else
1382     {
1383       gchar *basename = g_path_get_basename (filename);
1384   
1385       if (types & GTK_FILE_INFO_DISPLAY_NAME)
1386         {
1387           gchar *display_name = g_filename_to_utf8 (basename, -1, NULL, NULL, NULL);
1388           if (!display_name)
1389             display_name = g_strescape (basename, NULL);
1390           
1391           gtk_file_info_set_display_name (info, display_name);
1392           
1393           g_free (display_name);
1394         }
1395       
1396       if (types & GTK_FILE_INFO_IS_HIDDEN)
1397         {
1398             /* win32 convention ... */
1399             gboolean is_hidden = basename[0] == '.';
1400             /* ... _and_ windoze attribute */
1401             is_hidden = is_hidden || !!(wfad.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN);
1402           gtk_file_info_set_is_hidden (info, is_hidden);
1403         }
1404
1405       g_free (basename);
1406     }
1407
1408   if (types & GTK_FILE_INFO_IS_FOLDER)
1409     {
1410       gtk_file_info_set_is_folder (info, !!(wfad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY));
1411    }
1412
1413 #if 0 /* it's dead in GtkFileSystemUnix.c, too */
1414   if (types & GTK_FILE_INFO_ICON)
1415     {
1416       if (wfad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1417         icon_type = GTK_FILE_ICON_DIRECTORY;
1418
1419       gtk_file_info_set_icon_type (info, icon_type);
1420     }
1421 #endif
1422
1423   if ((types & GTK_FILE_INFO_MIME_TYPE)
1424 #if 0 /* it's dead in GtkFileSystemUnix.c, too */
1425       || ((types & GTK_FILE_INFO_ICON) && icon_type == GTK_FILE_ICON_REGULAR)
1426 #endif
1427      )
1428     {
1429 #if 0
1430       const char *mime_type = xdg_mime_get_mime_type_for_file (filename);
1431       gtk_file_info_set_mime_type (info, mime_type);
1432
1433       if ((types & GTK_FILE_INFO_ICON) && icon_type == GTK_FILE_ICON_REGULAR &&
1434           (statbuf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) &&
1435           (strcmp (mime_type, XDG_MIME_TYPE_UNKNOWN) == 0 ||
1436            strcmp (mime_type, "application/x-executable") == 0 ||
1437            strcmp (mime_type, "application/x-shellscript") == 0))
1438         gtk_file_info_set_icon_type (info, GTK_FILE_ICON_EXECUTABLE);
1439 #endif
1440     }
1441
1442   if (types & GTK_FILE_INFO_MODIFICATION_TIME)
1443     {
1444       GtkFileTime time = wfad.ftLastWriteTime.dwLowDateTime 
1445                        | ((guint64)wfad.ftLastWriteTime.dwHighDateTime) << 32;
1446       /* 100-nanosecond intervals since January 1, 1601, urgh! */
1447       time /= G_GINT64_CONSTANT (10000000); /* now seconds */
1448       time -= G_GINT64_CONSTANT (134774) * 24 * 3600; /* good old Unix time */
1449       gtk_file_info_set_modification_time (info, time);
1450     }
1451
1452   if (types & GTK_FILE_INFO_SIZE)
1453     {
1454       gint64 size = wfad.nFileSizeLow | ((guint64)wfad.nFileSizeHigh) << 32;
1455       gtk_file_info_set_size (info, size);
1456     }
1457   
1458   return info;
1459 }
1460
1461 static gchar *
1462 filename_from_path (const GtkFilePath *path)
1463 {
1464   return g_strdup (gtk_file_path_get_string (path));
1465 }
1466
1467 static GtkFilePath *
1468 filename_to_path (const char *filename)
1469 {
1470   return gtk_file_path_new_dup (filename);
1471 }
1472
1473 static gboolean
1474 filename_is_drive_root (const char *filename)
1475 {
1476   guint len = strlen (filename);
1477
1478   /* accept both forms */
1479
1480   return (len == 3 && filename[1] == ':' && G_IS_DIR_SEPARATOR (filename[2]));
1481 }
1482
1483 static gboolean
1484 filename_is_some_root (const char *filename)
1485 {
1486   return (G_IS_DIR_SEPARATOR (filename[0]) && filename[1] == '\0') ||
1487          filename_is_drive_root (filename);
1488 }