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