]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilesystemwin32.c
No GDK_WINDOW_DISPLAY in the Win32 backend.
[~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 /* #define this if you want the program to crash when a file system gets
23  * finalized while async handles are still outstanding.
24  */
25 #undef HANDLE_ME_HARDER
26
27 #include <config.h>
28
29 #include "gtkfilesystem.h"
30 #include "gtkfilesystemwin32.h"
31 #include "gtkicontheme.h"
32 #include "gtkintl.h"
33 #include "gtkstock.h"
34 #include "gtkiconfactory.h"
35 #include "gtkalias.h"
36
37 #include <glib/gstdio.h>
38
39 #include <errno.h>
40 #include <stdio.h>
41 #include <string.h>
42 #include <ctype.h>
43 #include <sys/types.h>
44
45 #define WIN32_LEAN_AND_MEAN
46 #define STRICT
47 #include "gdk/win32/gdkwin32.h"
48 #undef STRICT
49 #include <shlobj.h>
50 #include <shellapi.h>
51
52 #define BOOKMARKS_FILENAME ".gtk-bookmarks"
53
54 #define FOLDER_CACHE_LIFETIME 2 /* seconds */
55
56 typedef struct _GtkFileSystemWin32Class GtkFileSystemWin32Class;
57
58 #define GTK_FILE_SYSTEM_WIN32_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_SYSTEM_WIN32, GtkFileSystemWin32Class))
59 #define GTK_IS_FILE_SYSTEM_WIN32_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_SYSTEM_WIN32))
60 #define GTK_FILE_SYSTEM_WIN32_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_SYSTEM_WIN32, GtkFileSystemWin32Class))
61
62 struct _GtkFileSystemWin32Class
63 {
64   GObjectClass parent_class;
65 };
66
67 struct _GtkFileSystemWin32
68 {
69   GObject parent_instance;
70
71   guint32 drives;               /* bitmask as returned by GetLogicalDrives() */
72   GHashTable *folder_hash;
73   guint timeout;
74
75   GHashTable *handles;
76
77   guint execute_callbacks_idle_id;
78   GSList *callbacks;
79 };
80
81 /* Icon type, supplemented by MIME type
82  */
83 typedef enum {
84   ICON_UNDECIDED, /* Only used while we have not yet computed the icon in a struct stat_info_entry */
85   ICON_NONE,      /* "Could not compute the icon type" */
86   ICON_REGULAR,   /* Use mime type for icon */
87   ICON_DIRECTORY,
88   ICON_EXECUTABLE
89 } IconType;
90
91
92 #define GTK_TYPE_FILE_FOLDER_WIN32             (_gtk_file_folder_win32_get_type ())
93 #define GTK_FILE_FOLDER_WIN32(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_FILE_FOLDER_WIN32, GtkFileFolderWin32))
94 #define GTK_IS_FILE_FOLDER_WIN32(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_FILE_FOLDER_WIN32))
95 #define GTK_FILE_FOLDER_WIN32_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_FOLDER_WIN32, GtkFileFolderWin32Class))
96 #define GTK_IS_FILE_FOLDER_WIN32_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_FOLDER_WIN32))
97 #define GTK_FILE_FOLDER_WIN32_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_FOLDER_WIN32, GtkFileFolderWin32Class))
98
99 typedef struct _GtkFileFolderWin32      GtkFileFolderWin32;
100 typedef struct _GtkFileFolderWin32Class GtkFileFolderWin32Class;
101
102 struct _GtkFileFolderWin32Class
103 {
104   GObjectClass parent_class;
105 };
106
107 struct _GtkFileFolderWin32
108 {
109   GObject parent_instance;
110
111   GtkFileSystemWin32 *system_win32;
112   GtkFileInfoType types;
113   gchar *filename;
114   GHashTable *stat_info;
115   guint load_folder_id;
116   guint have_stat : 1;
117   guint have_mime_type : 1;
118   guint is_network_dir : 1;
119   guint is_finished_loading : 1;
120   time_t asof;
121 };
122
123 struct stat_info_entry {
124   WIN32_FILE_ATTRIBUTE_DATA wfad;
125   char *mime_type;
126   IconType icon_type;
127   gboolean hidden;
128 };
129
130 static const GtkFileInfoType STAT_NEEDED_MASK = (GTK_FILE_INFO_IS_FOLDER |
131                                                  GTK_FILE_INFO_MODIFICATION_TIME |
132                                                  GTK_FILE_INFO_SIZE |
133                                                  GTK_FILE_INFO_ICON);
134
135 static void gtk_file_system_win32_iface_init  (GtkFileSystemIface      *iface);
136 static void gtk_file_system_win32_dispose     (GObject                 *object);
137 static void gtk_file_system_win32_finalize    (GObject                 *object);
138
139 static GSList *             gtk_file_system_win32_list_volumes        (GtkFileSystem     *file_system);
140 static GtkFileSystemVolume *gtk_file_system_win32_get_volume_for_path (GtkFileSystem     *file_system,
141                                                                        const GtkFilePath *path);
142
143 static GtkFileSystemHandle *gtk_file_system_win32_get_folder (GtkFileSystem                 *file_system,
144                                                               const GtkFilePath             *path,
145                                                               GtkFileInfoType                types,
146                                                               GtkFileSystemGetFolderCallback callback,
147                                                               gpointer                       data);
148 static GtkFileSystemHandle *gtk_file_system_win32_get_info (GtkFileSystem               *file_system,
149                                                             const GtkFilePath           *path,
150                                                             GtkFileInfoType              types,
151                                                             GtkFileSystemGetInfoCallback callback,
152                                                             gpointer                     data);
153 static GtkFileSystemHandle *gtk_file_system_win32_create_folder (GtkFileSystem                    *file_system,
154                                                                  const GtkFilePath                *path,
155                                                                  GtkFileSystemCreateFolderCallback callback,
156                                                                  gpointer data);
157 static void         gtk_file_system_win32_cancel_operation        (GtkFileSystemHandle *handle);
158
159 static void         gtk_file_system_win32_volume_free             (GtkFileSystem       *file_system,
160                                                                    GtkFileSystemVolume *volume);
161 static GtkFilePath *gtk_file_system_win32_volume_get_base_path    (GtkFileSystem       *file_system,
162                                                                    GtkFileSystemVolume *volume);
163 static gboolean     gtk_file_system_win32_volume_get_is_mounted   (GtkFileSystem       *file_system,
164                                                                    GtkFileSystemVolume *volume);
165 static GtkFileSystemHandle *gtk_file_system_win32_volume_mount    (GtkFileSystem       *file_system,
166                                                                    GtkFileSystemVolume *volume,
167                                                                    GtkFileSystemVolumeMountCallback callback,
168                                                                    gpointer data);
169 static gchar *      gtk_file_system_win32_volume_get_display_name (GtkFileSystem       *file_system,
170                                                                    GtkFileSystemVolume *volume);
171 static gchar *      gtk_file_system_win32_volume_get_icon_name    (GtkFileSystem        *file_system,
172                                                                    GtkFileSystemVolume *volume,
173                                                                    GError             **error);
174
175 static gboolean       gtk_file_system_win32_get_parent   (GtkFileSystem      *file_system,
176                                                           const GtkFilePath  *path,
177                                                           GtkFilePath       **parent,
178                                                           GError            **error);
179 static GtkFilePath *  gtk_file_system_win32_make_path    (GtkFileSystem      *file_system,
180                                                           const GtkFilePath  *base_path,
181                                                           const gchar        *display_name,
182                                                           GError            **error);
183 static gboolean       gtk_file_system_win32_parse        (GtkFileSystem      *file_system,
184                                                           const GtkFilePath  *base_path,
185                                                           const gchar        *str,
186                                                           GtkFilePath       **folder,
187                                                           gchar             **file_part,
188                                                           GError            **error);
189
190 static gchar *      gtk_file_system_win32_path_to_uri      (GtkFileSystem      *file_system,
191                                                             const GtkFilePath  *path);
192 static gchar *      gtk_file_system_win32_path_to_filename (GtkFileSystem      *file_system,
193                                                             const GtkFilePath  *path);
194 static GtkFilePath *gtk_file_system_win32_uri_to_path      (GtkFileSystem      *file_system,
195                                                             const gchar        *uri);
196 static GtkFilePath *gtk_file_system_win32_filename_to_path (GtkFileSystem      *file_system,
197                                                             const gchar        *filename);
198
199
200 static gboolean       gtk_file_system_win32_insert_bookmark  (GtkFileSystem     *file_system,
201                                                               const GtkFilePath *path,
202                                                               gint               position,
203                                                               GError           **error);
204 static gboolean       gtk_file_system_win32_remove_bookmark  (GtkFileSystem     *file_system,
205                                                               const GtkFilePath *path,
206                                                               GError           **error);
207 static GSList       * gtk_file_system_win32_list_bookmarks     (GtkFileSystem     *file_system);
208 static gchar        * gtk_file_system_win32_get_bookmark_label (GtkFileSystem     *file_system,
209                                                                 const GtkFilePath *path);
210 static void           gtk_file_system_win32_set_bookmark_label (GtkFileSystem     *file_system,
211                                                                 const GtkFilePath *path,
212                                                                 const gchar       *label);
213
214 static void           gtk_file_folder_win32_iface_init (GtkFileFolderIface       *iface);
215 static void           gtk_file_folder_win32_finalize   (GObject                  *object);
216
217 static GtkFileInfo   *gtk_file_folder_win32_get_info         (GtkFileFolder       *folder,
218                                                               const GtkFilePath   *path,
219                                                               GError             **error);
220 static gboolean       gtk_file_folder_win32_list_children    (GtkFileFolder       *folder,
221                                                               GSList             **children,
222                                                               GError             **error);
223
224 static gboolean       gtk_file_folder_win32_is_finished_loading (GtkFileFolder *folder);
225
226 static GtkFilePath *filename_to_path       (const gchar *filename);
227
228 static gboolean     filename_is_root       (const char *filename);
229
230 static gboolean     filename_is_drive_root (const char *filename);
231 static gboolean     filename_is_some_root  (const char *filename);
232
233 static gboolean     stat_with_error        (const char                *filename,
234                                             WIN32_FILE_ATTRIBUTE_DATA *wfad,
235                                             GError                   **error);
236 static GtkFileInfo *create_file_info       (GtkFileFolderWin32        *folder_win32,
237                                             const char                *filename,
238                                             GtkFileInfoType            types,
239                                             WIN32_FILE_ATTRIBUTE_DATA *wfad,
240                                             const char                *mime_type);
241
242 static gboolean execute_callbacks_idle (gpointer data);
243
244 static gboolean fill_in_names        (GtkFileFolderWin32  *folder_win32,
245                                       GError             **error);
246 static void     fill_in_stats        (GtkFileFolderWin32  *folder_win32);
247 static void     fill_in_mime_type    (GtkFileFolderWin32  *folder_win32);
248
249 static gboolean cb_fill_in_stats     (gpointer key,
250                                       gpointer value,
251                                       gpointer user_data);
252 static gboolean cb_fill_in_mime_type (gpointer key,
253                                       gpointer value,
254                                       gpointer user_data);
255
256 /* some info kept together for volumes */
257 struct _GtkFileSystemVolume
258 {
259   gchar *drive;
260   int drive_type;
261 };
262
263 /*
264  * GtkFileSystemWin32
265  */
266 G_DEFINE_TYPE_WITH_CODE (GtkFileSystemWin32, gtk_file_system_win32, G_TYPE_OBJECT,
267                          G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_SYSTEM,
268                                                 gtk_file_system_win32_iface_init))
269
270 /*
271  * GtkFileFolderWin32
272  */
273 G_DEFINE_TYPE_WITH_CODE (GtkFileFolderWin32, _gtk_file_folder_win32, G_TYPE_OBJECT,
274                          G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_FOLDER,
275                                                 gtk_file_folder_win32_iface_init))
276
277 /**
278  * gtk_file_system_win32_new:
279  * 
280  * Creates a new #GtkFileSystemWin32 object. #GtkFileSystemWin32
281  * implements the #GtkFileSystem interface with direct access to
282  * the filesystem using Windows API calls
283  * 
284  * Return value: the new #GtkFileSystemWin32 object
285  **/
286 GtkFileSystem *
287 gtk_file_system_win32_new (void)
288 {
289   return g_object_new (GTK_TYPE_FILE_SYSTEM_WIN32, NULL);
290 }
291
292 static void
293 gtk_file_system_win32_class_init (GtkFileSystemWin32Class *class)
294 {
295   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
296
297   gobject_class->dispose = gtk_file_system_win32_dispose;
298   gobject_class->finalize = gtk_file_system_win32_finalize;
299 }
300
301 static void
302 gtk_file_system_win32_iface_init (GtkFileSystemIface *iface)
303 {
304   iface->list_volumes = gtk_file_system_win32_list_volumes;
305   iface->get_volume_for_path = gtk_file_system_win32_get_volume_for_path;
306   iface->get_folder = gtk_file_system_win32_get_folder;
307   iface->get_info = gtk_file_system_win32_get_info;
308   iface->create_folder = gtk_file_system_win32_create_folder;
309   iface->cancel_operation = gtk_file_system_win32_cancel_operation;
310   iface->volume_free = gtk_file_system_win32_volume_free;
311   iface->volume_get_base_path = gtk_file_system_win32_volume_get_base_path;
312   iface->volume_get_is_mounted = gtk_file_system_win32_volume_get_is_mounted;
313   iface->volume_mount = gtk_file_system_win32_volume_mount;
314   iface->volume_get_display_name = gtk_file_system_win32_volume_get_display_name;
315   iface->volume_get_icon_name = gtk_file_system_win32_volume_get_icon_name;
316   iface->get_parent = gtk_file_system_win32_get_parent;
317   iface->make_path = gtk_file_system_win32_make_path;
318   iface->parse = gtk_file_system_win32_parse;
319   iface->path_to_uri = gtk_file_system_win32_path_to_uri;
320   iface->path_to_filename = gtk_file_system_win32_path_to_filename;
321   iface->uri_to_path = gtk_file_system_win32_uri_to_path;
322   iface->filename_to_path = gtk_file_system_win32_filename_to_path;
323   iface->insert_bookmark = gtk_file_system_win32_insert_bookmark;
324   iface->remove_bookmark = gtk_file_system_win32_remove_bookmark;
325   iface->list_bookmarks = gtk_file_system_win32_list_bookmarks;
326   iface->get_bookmark_label = gtk_file_system_win32_get_bookmark_label;
327   iface->set_bookmark_label = gtk_file_system_win32_set_bookmark_label;
328 }
329
330 static gboolean
331 check_volumes (gpointer data)
332 {
333   GtkFileSystemWin32 *system_win32 = GTK_FILE_SYSTEM_WIN32 (data);
334
335   g_return_val_if_fail (system_win32, FALSE);
336
337   if (system_win32->drives != GetLogicalDrives())
338     g_signal_emit_by_name (system_win32, "volumes-changed", 0);
339
340   return TRUE;
341 }
342
343 static guint
344 casefolded_hash (gconstpointer v)
345 {
346   const gchar *p = (const gchar *) v;
347   guint32 h = 0;
348
349   while (*p)
350     {
351       h = (h << 5) - h + g_unichar_toupper (g_utf8_get_char (p));
352       p = g_utf8_next_char (p);
353     }
354
355   return h;
356 }
357
358 static gboolean casefolded_equal (gconstpointer v1,
359                                   gconstpointer v2)
360 {
361   return (_gtk_file_system_win32_path_compare ((const gchar *) v1, (const gchar *) v2) == 0);
362 }
363
364 static void
365 gtk_file_system_win32_init (GtkFileSystemWin32 *system_win32)
366 {
367   system_win32->folder_hash = g_hash_table_new (casefolded_hash, casefolded_equal);
368
369   /* Set up an idle handler for volume changes. Once a second should
370    * be enough.
371    */
372   system_win32->timeout = g_timeout_add_full (0, 1000, check_volumes, system_win32, NULL);
373
374   system_win32->handles = g_hash_table_new (g_direct_hash, g_direct_equal);
375
376   system_win32->execute_callbacks_idle_id = 0;
377   system_win32->callbacks = NULL;
378 }
379
380 static void
381 check_handle_fn (gpointer key, gpointer value, gpointer data)
382 {
383   GtkFileSystemHandle *handle;
384   int *num_live_handles;
385
386   handle = key;
387   num_live_handles = data;
388
389   (*num_live_handles)++;
390
391   g_warning ("file_system_win32=%p still has handle=%p at finalization which is %s!",
392              handle->file_system,
393              handle,
394              handle->cancelled ? "CANCELLED" : "NOT CANCELLED");
395 }
396
397 static void
398 check_handles_at_finalization (GtkFileSystemWin32 *system_win32)
399 {
400   int num_live_handles;
401
402   num_live_handles = 0;
403
404   g_hash_table_foreach (system_win32->handles, check_handle_fn, &num_live_handles);
405 #ifdef HANDLE_ME_HARDER
406   g_assert (num_live_handles == 0);
407 #endif
408
409   g_hash_table_destroy (system_win32->handles);
410   system_win32->handles = NULL;
411 }
412
413 #define GTK_TYPE_FILE_SYSTEM_HANDLE_WIN32 (_gtk_file_system_handle_win32_get_type ())
414
415 typedef struct _GtkFileSystemHandle GtkFileSystemHandleWin32;
416 typedef struct _GtkFileSystemHandleClass GtkFileSystemHandleWin32Class;
417
418 G_DEFINE_TYPE (GtkFileSystemHandleWin32, _gtk_file_system_handle_win32, GTK_TYPE_FILE_SYSTEM_HANDLE)
419
420 static void
421 _gtk_file_system_handle_win32_init (GtkFileSystemHandleWin32 *handle)
422 {
423 }
424
425 static void 
426 _gtk_file_system_handle_win32_finalize (GObject *object)
427 {
428   GtkFileSystemHandleWin32 *handle;
429   GtkFileSystemWin32 *system_win32;
430
431   handle = (GtkFileSystemHandleWin32 *)object;
432
433   system_win32 = GTK_FILE_SYSTEM_WIN32 (GTK_FILE_SYSTEM_HANDLE (handle)->file_system);
434
435   g_assert (g_hash_table_lookup (system_win32->handles, handle) != NULL);
436   g_hash_table_remove (system_win32->handles, handle);
437
438   if (G_OBJECT_CLASS (_gtk_file_system_handle_win32_parent_class)->finalize)
439     G_OBJECT_CLASS (_gtk_file_system_handle_win32_parent_class)->finalize (object);
440 }
441
442 static void
443 _gtk_file_system_handle_win32_class_init (GtkFileSystemHandleWin32Class *class)
444 {
445   GObjectClass *gobject_class = (GObjectClass *) class;
446
447   gobject_class->finalize = _gtk_file_system_handle_win32_finalize;
448 }
449
450 static void
451 gtk_file_system_win32_dispose (GObject *object)
452 {
453   GtkFileSystemWin32 *system_win32;
454
455   system_win32 = GTK_FILE_SYSTEM_WIN32 (object);
456
457   if (system_win32->execute_callbacks_idle_id)
458     {
459       g_source_remove (system_win32->execute_callbacks_idle_id);
460       system_win32->execute_callbacks_idle_id = 0;
461
462       /* call pending callbacks */
463       execute_callbacks_idle (system_win32);
464     }
465
466   G_OBJECT_CLASS (gtk_file_system_win32_parent_class)->dispose (object);
467 }
468
469 static void
470 gtk_file_system_win32_finalize (GObject *object)
471 {
472   GtkFileSystemWin32 *system_win32;
473
474   system_win32 = GTK_FILE_SYSTEM_WIN32 (object);
475
476   g_source_remove (system_win32->timeout);
477
478   check_handles_at_finalization  (system_win32);
479
480   /* FIXME: assert that the hash is empty? */
481   g_hash_table_destroy (system_win32->folder_hash);
482
483   G_OBJECT_CLASS (gtk_file_system_win32_parent_class)->finalize (object);
484 }
485
486 /* Lifted from GLib */
487
488 static gchar *
489 get_special_folder (int csidl)
490 {
491   union {
492     char c[MAX_PATH+1];
493     wchar_t wc[MAX_PATH+1];
494   } path;
495   HRESULT hr;
496   LPITEMIDLIST pidl = NULL;
497   BOOL b;
498   gchar *retval = NULL;
499
500   hr = SHGetSpecialFolderLocation (NULL, csidl, &pidl);
501   if (hr == S_OK)
502     {
503       b = SHGetPathFromIDListW (pidl, path.wc);
504       if (b)
505         retval = g_utf16_to_utf8 (path.wc, -1, NULL, NULL, NULL);
506       CoTaskMemFree (pidl);
507     }
508   return retval;
509 }
510
511 gchar *
512 _gtk_file_system_win32_get_desktop (void)
513 {
514   return get_special_folder (CSIDL_DESKTOPDIRECTORY);
515 }
516
517 static GSList *
518 gtk_file_system_win32_list_volumes (GtkFileSystem *file_system)
519 {
520   DWORD   drives;
521   gchar   drive[4] = "A:\\";
522   GSList *list = NULL;
523   GtkFileSystemWin32 *system_win32 = (GtkFileSystemWin32 *)file_system;
524
525   drives = GetLogicalDrives();
526
527   system_win32->drives = drives;
528   if (!drives)
529     g_warning ("GetLogicalDrives failed.");
530
531   while (drives && drive[0] <= 'Z')
532     {
533       if (drives & 1)
534       {
535         GtkFileSystemVolume *vol = g_new0 (GtkFileSystemVolume, 1);
536         vol->drive = g_strdup (drive);
537         vol->drive_type = GetDriveType (drive);
538         list = g_slist_append (list, vol);
539       }
540       drives >>= 1;
541       drive[0]++;
542     }
543   return list;
544 }
545
546 static GtkFileSystemVolume *
547 gtk_file_system_win32_get_volume_for_path (GtkFileSystem     *file_system,
548                                            const GtkFilePath *path)
549 {
550   GtkFileSystemVolume *vol = g_new0 (GtkFileSystemVolume, 1);
551   const gchar *p;
552
553   g_return_val_if_fail (path != NULL, NULL);
554
555   p = gtk_file_path_get_string (path);
556
557   if (!g_path_is_absolute (p))
558     {
559       if (g_ascii_isalpha (p[0]) && p[1] == ':')
560         vol->drive = g_strdup_printf ("%c:\\", p[0]);
561       else
562         vol->drive = g_strdup ("\\");
563       vol->drive_type = GetDriveType (vol->drive);
564     }
565   else
566     {
567       const gchar *q = g_path_skip_root (p);
568       vol->drive = g_strndup (p, q - p);
569       if (!G_IS_DIR_SEPARATOR (q[-1]))
570         {
571           /* Make sure "drive" always ends in a slash */
572           gchar *tem = vol->drive;
573           vol->drive = g_strconcat (vol->drive, "\\", NULL);
574           g_free (tem);
575         }
576       
577       if (filename_is_drive_root (vol->drive))
578         {
579           vol->drive[0] = g_ascii_toupper (vol->drive[0]);
580           vol->drive_type = GetDriveType (vol->drive);
581         }
582       else
583         {
584           wchar_t *wdrive = g_utf8_to_utf16 (vol->drive, -1, NULL, NULL, NULL);
585           vol->drive_type = GetDriveTypeW (wdrive);
586           g_free (wdrive);
587         }
588     }
589   return vol;
590 }
591
592 static char *
593 remove_trailing_slash (const char *filename)
594 {
595   int root_len, len;
596
597   len = strlen (filename);
598
599   if (g_path_is_absolute (filename))
600     root_len = g_path_skip_root (filename) - filename;
601   else
602     root_len = 1;
603   if (len > root_len && G_IS_DIR_SEPARATOR (filename[len - 1]))
604     return g_strndup (filename, len - 1);
605   else
606     return g_memdup (filename, len + 1);
607 }
608
609 /* Delay callback dispatching
610  */
611
612 enum callback_types
613 {
614   CALLBACK_GET_INFO,
615   CALLBACK_GET_FOLDER,
616   CALLBACK_CREATE_FOLDER,
617   CALLBACK_VOLUME_MOUNT
618 };
619
620 static void queue_callback (GtkFileSystemWin32 *system_win32, enum callback_types type, gpointer data);
621
622 struct get_info_callback
623 {
624   GtkFileSystemGetInfoCallback callback;
625   GtkFileSystemHandle *handle;
626   GtkFileInfo *file_info;
627   GError *error;
628   gpointer data;
629 };
630
631 static inline void
632 dispatch_get_info_callback (struct get_info_callback *info)
633 {
634   (* info->callback) (info->handle, info->file_info, info->error, info->data);
635
636   if (info->file_info)
637     gtk_file_info_free (info->file_info);
638
639   if (info->error)
640     g_error_free (info->error);
641
642   g_object_unref (info->handle);
643
644   g_free (info);
645 }
646
647 static inline void
648 queue_get_info_callback (GtkFileSystemGetInfoCallback  callback,
649                          GtkFileSystemHandle          *handle,
650                          GtkFileInfo                  *file_info,
651                          GError                       *error,
652                          gpointer                      data)
653 {
654   struct get_info_callback *info;
655
656   info = g_new (struct get_info_callback, 1);
657   info->callback = callback;
658   info->handle = handle;
659   info->file_info = file_info;
660   info->error = error;
661   info->data = data;
662
663   queue_callback (GTK_FILE_SYSTEM_WIN32 (handle->file_system), CALLBACK_GET_INFO, info);
664 }
665
666
667 struct get_folder_callback
668 {
669   GtkFileSystemGetFolderCallback callback;
670   GtkFileSystemHandle *handle;
671   GtkFileFolder *folder;
672   GError *error;
673   gpointer data;
674 };
675
676 static inline void
677 dispatch_get_folder_callback (struct get_folder_callback *info)
678 {
679   (* info->callback) (info->handle, info->folder, info->error, info->data);
680
681   if (info->error)
682     g_error_free (info->error);
683
684   g_object_unref (info->handle);
685
686   g_free (info);
687 }
688
689 static inline void
690 queue_get_folder_callback (GtkFileSystemGetFolderCallback  callback,
691                            GtkFileSystemHandle            *handle,
692                            GtkFileFolder                  *folder,
693                            GError                         *error,
694                            gpointer                        data)
695 {
696   struct get_folder_callback *info;
697
698   info = g_new (struct get_folder_callback, 1);
699   info->callback = callback;
700   info->handle = handle;
701   info->folder = folder;
702   info->error = error;
703   info->data = data;
704
705   queue_callback (GTK_FILE_SYSTEM_WIN32 (handle->file_system), CALLBACK_GET_FOLDER, info);
706 }
707
708
709 struct create_folder_callback
710 {
711   GtkFileSystemCreateFolderCallback callback;
712   GtkFileSystemHandle *handle;
713   GtkFilePath *path;
714   GError *error;
715   gpointer data;
716 };
717
718 static inline void
719 dispatch_create_folder_callback (struct create_folder_callback *info)
720 {
721   (* info->callback) (info->handle, info->path, info->error, info->data);
722
723   if (info->error)
724     g_error_free (info->error);
725
726   if (info->path)
727     gtk_file_path_free (info->path);
728
729   g_object_unref (info->handle);
730
731   g_free (info);
732 }
733
734 static inline void
735 queue_create_folder_callback (GtkFileSystemCreateFolderCallback  callback,
736                               GtkFileSystemHandle               *handle,
737                               const GtkFilePath                 *path,
738                               GError                            *error,
739                               gpointer                           data)
740 {
741   struct create_folder_callback *info;
742
743   info = g_new (struct create_folder_callback, 1);
744   info->callback = callback;
745   info->handle = handle;
746   info->path = gtk_file_path_copy (path);
747   info->error = error;
748   info->data = data;
749
750   queue_callback (GTK_FILE_SYSTEM_WIN32 (handle->file_system), CALLBACK_CREATE_FOLDER, info);
751 }
752
753
754 struct volume_mount_callback
755 {
756   GtkFileSystemVolumeMountCallback callback;
757   GtkFileSystemHandle *handle;
758   GtkFileSystemVolume *volume;
759   GError *error;
760   gpointer data;
761 };
762
763 static inline void
764 dispatch_volume_mount_callback (struct volume_mount_callback *info)
765 {
766   (* info->callback) (info->handle, info->volume, info->error, info->data);
767
768   if (info->error)
769     g_error_free (info->error);
770
771   g_object_unref (info->handle);
772
773   g_free (info);
774 }
775
776 static inline void
777 queue_volume_mount_callback (GtkFileSystemVolumeMountCallback  callback,
778                              GtkFileSystemHandle              *handle,
779                              GtkFileSystemVolume              *volume,
780                              GError                           *error,
781                              gpointer                          data)
782 {
783   struct volume_mount_callback *info;
784
785   info = g_new (struct volume_mount_callback, 1);
786   info->callback = callback;
787   info->handle = handle;
788   info->volume = volume;
789   info->error = error;
790   info->data = data;
791
792   queue_callback (GTK_FILE_SYSTEM_WIN32 (handle->file_system), CALLBACK_VOLUME_MOUNT, info);
793 }
794
795
796 struct callback_info
797 {
798   enum callback_types type;
799
800   union
801   {
802     struct get_info_callback *get_info;
803     struct get_folder_callback *get_folder;
804     struct create_folder_callback *create_folder;
805     struct volume_mount_callback *volume_mount;
806   } info;
807 };
808
809
810
811 static gboolean
812 execute_callbacks_idle (gpointer data)
813 {
814   GSList *l;
815   gboolean unref_file_system = TRUE;
816   GtkFileSystemWin32 *system_win32 = GTK_FILE_SYSTEM_WIN32 (data);
817
818   GDK_THREADS_ENTER ();
819
820   if (!system_win32->execute_callbacks_idle_id)
821     unref_file_system = FALSE;
822   else
823     g_object_ref (system_win32);
824
825   for (l = system_win32->callbacks; l; l = l->next)
826     {
827       struct callback_info *info = l->data;
828
829       switch (info->type)
830         {
831           case CALLBACK_GET_INFO:
832             dispatch_get_info_callback (info->info.get_info);
833             break;
834
835           case CALLBACK_GET_FOLDER:
836             dispatch_get_folder_callback (info->info.get_folder);
837             break;
838
839           case CALLBACK_CREATE_FOLDER:
840             dispatch_create_folder_callback (info->info.create_folder);
841             break;
842
843           case CALLBACK_VOLUME_MOUNT:
844             dispatch_volume_mount_callback (info->info.volume_mount);
845             break;
846         }
847
848       g_free (info);
849     }
850
851   g_slist_free (system_win32->callbacks);
852   system_win32->callbacks = NULL;
853
854   if (unref_file_system)
855     g_object_unref (system_win32);
856
857   system_win32->execute_callbacks_idle_id = 0;
858
859   GDK_THREADS_LEAVE ();
860
861   return FALSE;
862 }
863
864 static void
865 queue_callback (GtkFileSystemWin32  *system_win32,
866                 enum callback_types  type,
867                 gpointer             data)
868 {
869   struct callback_info *info;
870
871   info = g_new (struct callback_info, 1);
872   info->type = type;
873
874   switch (type)
875     {
876       case CALLBACK_GET_INFO:
877         info->info.get_info = data;
878         break;
879
880       case CALLBACK_GET_FOLDER:
881         info->info.get_folder = data;
882         break;
883
884       case CALLBACK_CREATE_FOLDER:
885         info->info.create_folder = data;
886         break;
887
888       case CALLBACK_VOLUME_MOUNT:
889         info->info.volume_mount = data;
890         break;
891     }
892
893   system_win32->callbacks = g_slist_append (system_win32->callbacks, info);
894
895   if (!system_win32->execute_callbacks_idle_id)
896     system_win32->execute_callbacks_idle_id = g_idle_add (execute_callbacks_idle, system_win32);
897 }
898
899 static GtkFileSystemHandle *
900 create_handle (GtkFileSystem *file_system)
901 {
902   GtkFileSystemWin32 *system_win32;
903   GtkFileSystemHandle *handle;
904
905   system_win32 = GTK_FILE_SYSTEM_WIN32 (file_system);
906
907   handle = g_object_new (GTK_TYPE_FILE_SYSTEM_HANDLE_WIN32, NULL);
908   handle->file_system = file_system;
909
910   g_assert (g_hash_table_lookup (system_win32->handles, handle) == NULL);
911   g_hash_table_insert (system_win32->handles, handle, handle);
912
913   return handle;
914 }
915
916 static char *
917 get_mime_type_for_file (const char                      *filename,
918                         const WIN32_FILE_ATTRIBUTE_DATA *wfad)
919 {
920   const char *extension;
921   HKEY key = NULL;
922   DWORD type, nbytes = 0;
923   char *value = NULL;
924
925   if (wfad->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
926     return g_strdup ("x-directory/normal");
927
928   extension = strrchr (filename, '.');
929
930   if (extension != NULL &&
931       (stricmp (extension, ".exe") == 0 ||
932        stricmp (extension, ".com") == 0))
933     return g_strdup ("application/x-executable");
934
935   if (extension != NULL &&
936       extension[1] != '\0' &&
937       RegOpenKeyEx (HKEY_CLASSES_ROOT, extension, 0,
938                     KEY_QUERY_VALUE, &key) == ERROR_SUCCESS &&
939       RegQueryValueEx (key, "Content Type", 0,
940                        &type, NULL, &nbytes) == ERROR_SUCCESS &&
941       type == REG_SZ &&
942       (value = g_try_malloc (nbytes + 1)) &&
943       RegQueryValueEx (key, "Content Type", 0,
944                        &type, value, &nbytes) == ERROR_SUCCESS)
945     {
946       value[nbytes] = '\0';
947     }
948   else
949     value = g_strdup ("application/octet-stream");
950   if (key != NULL)
951     RegCloseKey (key);
952
953   return value;
954 }
955
956 static GtkFileSystemHandle *
957 gtk_file_system_win32_get_info (GtkFileSystem               *file_system,
958                                 const GtkFilePath           *path,
959                                 GtkFileInfoType              types,
960                                 GtkFileSystemGetInfoCallback callback,
961                                 gpointer                     data)
962 {
963   GError *error = NULL;
964   GtkFileSystemWin32 *system_win32;
965   GtkFileSystemHandle *handle;
966   const char *filename;
967   GtkFileInfo *info;
968   WIN32_FILE_ATTRIBUTE_DATA wfad;
969   const char *mime_type;
970
971   system_win32 = GTK_FILE_SYSTEM_WIN32 (file_system);
972   handle = create_handle (file_system);
973
974   filename = gtk_file_path_get_string (path);
975   g_return_val_if_fail (filename != NULL, FALSE);
976   g_return_val_if_fail (g_path_is_absolute (filename), FALSE);
977
978   if (!stat_with_error (filename, &wfad, &error))
979     {
980       g_object_ref (handle);
981       queue_get_info_callback (callback, handle, NULL, error, data);
982       return handle;
983     }
984
985   if ((types & GTK_FILE_INFO_MIME_TYPE) != 0)
986     mime_type = get_mime_type_for_file (filename, &wfad);
987   else
988     mime_type = NULL;
989
990   info = create_file_info (NULL, filename, types, &wfad, mime_type);
991   g_object_ref (handle);
992   queue_get_info_callback (callback, handle, info, NULL, data);
993
994   return handle;
995 }
996
997 static gboolean
998 load_folder (gpointer data)
999 {
1000   GtkFileFolderWin32 *folder_win32 = data;
1001   GSList *children;
1002
1003   GDK_THREADS_ENTER ();
1004
1005   if ((folder_win32->types & STAT_NEEDED_MASK) != 0)
1006     fill_in_stats (folder_win32);
1007
1008   if ((folder_win32->types & GTK_FILE_INFO_MIME_TYPE) != 0)
1009     fill_in_mime_type (folder_win32);
1010
1011   if (gtk_file_folder_win32_list_children (GTK_FILE_FOLDER (folder_win32), &children, NULL))
1012     {
1013       folder_win32->is_finished_loading = TRUE;
1014       g_signal_emit_by_name (folder_win32, "files-added", children);
1015       gtk_file_paths_free (children);
1016     }
1017
1018   folder_win32->load_folder_id = 0;
1019
1020   g_signal_emit_by_name (folder_win32, "finished-loading", 0);
1021
1022   GDK_THREADS_LEAVE ();
1023
1024   return FALSE;
1025 }
1026
1027 static GtkFileSystemHandle *
1028 gtk_file_system_win32_get_folder (GtkFileSystem                 *file_system,
1029                                   const GtkFilePath             *path,
1030                                   GtkFileInfoType                types,
1031                                   GtkFileSystemGetFolderCallback callback,
1032                                   gpointer                       data)
1033 {
1034   GError *error = NULL;
1035   GtkFileSystemWin32 *system_win32;
1036   GtkFileFolderWin32 *folder_win32;
1037   GtkFileSystemHandle *handle;
1038   const gchar *filename;
1039   char *filename_copy;
1040   gboolean set_asof = FALSE;
1041
1042   system_win32 = GTK_FILE_SYSTEM_WIN32 (file_system);
1043
1044   filename = gtk_file_path_get_string (path);
1045   g_return_val_if_fail (filename != NULL, NULL);
1046   g_return_val_if_fail (g_path_is_absolute (filename), NULL);
1047
1048   handle = create_handle (file_system);
1049
1050   filename_copy = remove_trailing_slash (filename);
1051   folder_win32 = g_hash_table_lookup (system_win32->folder_hash, filename_copy);
1052
1053   if (folder_win32)
1054     {
1055       g_free (filename_copy);
1056       if (folder_win32->stat_info &&
1057           time (NULL) - folder_win32->asof >= FOLDER_CACHE_LIFETIME)
1058         {
1059 #if 0
1060           g_print ("Cleaning out cached directory %s\n", filename);
1061 #endif
1062           g_hash_table_destroy (folder_win32->stat_info);
1063           folder_win32->stat_info = NULL;
1064           folder_win32->have_mime_type = FALSE;
1065           folder_win32->have_stat = FALSE;
1066           set_asof = TRUE;
1067         }
1068
1069       g_object_ref (folder_win32);
1070       folder_win32->types |= types;
1071       types = folder_win32->types;
1072     }
1073   else
1074     {
1075       WIN32_FILE_ATTRIBUTE_DATA wfad;
1076
1077       if (!stat_with_error (filename, &wfad, &error))
1078         {
1079           g_object_ref (handle);
1080           queue_get_folder_callback (callback, handle, NULL, error, data);
1081
1082           g_free (filename_copy);
1083           return handle;
1084         }
1085
1086       if (!wfad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1087         {
1088           gchar *display_name = g_filename_display_name (filename);
1089
1090           g_set_error (&error,
1091                        GTK_FILE_SYSTEM_ERROR,
1092                        GTK_FILE_SYSTEM_ERROR_NOT_FOLDER,
1093                        _("Path is not a folder: '%s'"),
1094                        display_name);
1095
1096           g_object_ref (handle);
1097           queue_get_folder_callback (callback, handle, NULL, error, data);
1098
1099           g_free (display_name);
1100           g_free (filename_copy);
1101           return handle;
1102         }
1103
1104       folder_win32 = g_object_new (GTK_TYPE_FILE_FOLDER_WIN32, NULL);
1105       folder_win32->system_win32 = system_win32;
1106       folder_win32->filename = filename_copy;
1107       folder_win32->types = types;
1108       folder_win32->stat_info = NULL;
1109       folder_win32->load_folder_id = 0;
1110       folder_win32->have_mime_type = FALSE;
1111       folder_win32->have_stat = FALSE;
1112       folder_win32->is_finished_loading = FALSE;
1113       set_asof = TRUE;
1114           
1115       /* Browsing for shares not yet implemented */
1116       folder_win32->is_network_dir = FALSE;
1117
1118       g_hash_table_insert (system_win32->folder_hash,
1119                            folder_win32->filename,
1120                            folder_win32);
1121     }
1122
1123   if (set_asof)
1124     folder_win32->asof = time (NULL);
1125
1126   g_object_ref (handle);
1127   queue_get_folder_callback (callback, handle, GTK_FILE_FOLDER (folder_win32), NULL, data);
1128
1129   /* Start loading the folder contents in an idle */
1130   if (!folder_win32->load_folder_id)
1131     folder_win32->load_folder_id =
1132       g_idle_add ((GSourceFunc) load_folder, folder_win32);
1133
1134   return handle;
1135 }
1136
1137 static GtkFileSystemHandle *
1138 gtk_file_system_win32_create_folder (GtkFileSystem                    *file_system,
1139                                      const GtkFilePath                *path,
1140                                      GtkFileSystemCreateFolderCallback callback,
1141                                      gpointer                          data)
1142 {
1143   GError *error = NULL;
1144   GtkFileSystemWin32 *system_win32;
1145   GtkFileSystemHandle *handle;
1146   const char *filename;
1147   gboolean result;
1148   char *parent, *tmp;
1149   int save_errno;
1150
1151   system_win32 = GTK_FILE_SYSTEM_WIN32 (file_system);
1152
1153   filename = gtk_file_path_get_string (path);
1154   g_return_val_if_fail (filename != NULL, FALSE);
1155   g_return_val_if_fail (g_path_is_absolute (filename), NULL);
1156
1157   handle = create_handle (file_system);
1158
1159   tmp = remove_trailing_slash (filename);
1160   result = g_mkdir (tmp, 0777) == 0;
1161   save_errno = errno;
1162   g_free (tmp);
1163
1164   if (!result)
1165     {
1166       gchar *display_filename = g_filename_display_name (filename);
1167       g_set_error (&error,
1168                    GTK_FILE_SYSTEM_ERROR,
1169                    GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
1170                    _("Error creating directory '%s': %s"),
1171                    display_filename,
1172                    g_strerror (save_errno));
1173       
1174       g_object_ref (handle);
1175       queue_create_folder_callback (callback, handle, path, error, data);
1176
1177       g_free (display_filename);
1178       return handle;
1179     }
1180
1181   if (!filename_is_some_root (filename))
1182     {
1183       parent = g_path_get_dirname (filename);
1184       if (parent)
1185         {
1186           GtkFileFolderWin32 *folder_win32;
1187
1188           folder_win32 = g_hash_table_lookup (system_win32->folder_hash, parent);
1189           if (folder_win32)
1190             {
1191               GSList *paths;
1192               char *basename;
1193               struct stat_info_entry *entry;
1194
1195               /* Make sure the new folder exists in the parent's folder */
1196               entry = g_new0 (struct stat_info_entry, 1);
1197               if (folder_win32->is_network_dir)
1198                 {
1199                   entry->wfad.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
1200                   entry->mime_type = g_strdup ("x-directory/normal");
1201                 }
1202               
1203               basename = g_path_get_basename (filename);
1204               g_hash_table_insert (folder_win32->stat_info,
1205                                    basename,
1206                                    entry);
1207               
1208               if (folder_win32->have_stat)
1209                 {
1210                   /* Cheating */
1211                   if ((folder_win32->types & STAT_NEEDED_MASK) != 0)
1212                     cb_fill_in_stats (basename, entry, folder_win32);
1213                   
1214                   if ((folder_win32->types & GTK_FILE_INFO_MIME_TYPE) != 0)
1215                     cb_fill_in_mime_type (basename, entry, folder_win32);
1216                 }
1217
1218               paths = g_slist_append (NULL, (GtkFilePath *) path);
1219               g_signal_emit_by_name (folder_win32, "files-added", paths);
1220               g_slist_free (paths);
1221             }
1222
1223           g_free(parent);
1224         }
1225     }
1226
1227   return handle;
1228 }
1229
1230 static void
1231 gtk_file_system_win32_cancel_operation (GtkFileSystemHandle *handle)
1232 {
1233   /* We don't set "cancelled" to TRUE here, since the actual operation
1234    * is executed in the function itself and not in a callback.  So
1235    * the operations can never be cancelled (since they will be already
1236    * completed at this point.
1237    */
1238 }
1239
1240 static void
1241 gtk_file_system_win32_volume_free (GtkFileSystem        *file_system,
1242                                    GtkFileSystemVolume  *volume)
1243 {
1244   GtkFilePath *path;
1245
1246   g_free (volume->drive);
1247   path = (GtkFilePath *) volume;
1248   gtk_file_path_free (path);
1249 }
1250
1251 static GtkFilePath *
1252 gtk_file_system_win32_volume_get_base_path (GtkFileSystem        *file_system,
1253                                             GtkFileSystemVolume  *volume)
1254 {
1255   return (GtkFilePath *) g_strdup (volume->drive);
1256 }
1257
1258 static gboolean
1259 gtk_file_system_win32_volume_get_is_mounted (GtkFileSystem        *file_system,
1260                                              GtkFileSystemVolume  *volume)
1261 {
1262   return TRUE;
1263 }
1264
1265 static GtkFileSystemHandle *
1266 gtk_file_system_win32_volume_mount (GtkFileSystem                   *file_system, 
1267                                     GtkFileSystemVolume             *volume,
1268                                     GtkFileSystemVolumeMountCallback callback,
1269                                     gpointer                         data)
1270 {
1271   GError *error = NULL;
1272   GtkFileSystemHandle *handle = create_handle (file_system);
1273
1274   g_set_error (&error,
1275                GTK_FILE_SYSTEM_ERROR,
1276                GTK_FILE_SYSTEM_ERROR_FAILED,
1277                _("This file system does not support mounting"));
1278
1279   g_object_ref (handle);
1280   queue_volume_mount_callback (callback, handle, volume, error, data);
1281
1282   return handle;
1283 }
1284
1285 static gchar *
1286 gtk_file_system_win32_volume_get_display_name (GtkFileSystem       *file_system,
1287                                                GtkFileSystemVolume *volume)
1288 {
1289   gchar *real_display_name;
1290
1291   g_return_val_if_fail (volume->drive != NULL, NULL);
1292
1293   if (filename_is_drive_root (volume->drive) &&
1294       volume->drive_type == DRIVE_REMOTE)
1295     real_display_name = g_strdup_printf (_("Network Drive (%s)"), volume->drive);
1296   else if ((filename_is_drive_root (volume->drive) && volume->drive[0] >= 'C') ||
1297       volume->drive_type != DRIVE_REMOVABLE)
1298     {
1299       gchar *name = NULL;
1300       gunichar2 *wdrive = g_utf8_to_utf16 (volume->drive, -1, NULL, NULL, NULL);
1301       gunichar2 wname[80];
1302
1303       if (GetVolumeInformationW (wdrive,
1304                                  wname, G_N_ELEMENTS(wname), 
1305                                  NULL, /* serial number */
1306                                  NULL, /* max. component length */
1307                                  NULL, /* fs flags */
1308                                  NULL, 0) /* fs type like FAT, NTFS */ &&
1309           wname[0])
1310         {
1311           name = g_utf16_to_utf8 (wname, -1, NULL, NULL, NULL);
1312         }
1313       g_free (wdrive);
1314
1315       if (name != NULL)
1316         {
1317           real_display_name = g_strdup_printf (_("%s (%s)"), name, volume->drive);
1318           g_free (name);
1319         }
1320       else
1321         {
1322           real_display_name = g_strdup (volume->drive);
1323         }
1324     }
1325   else
1326     real_display_name = g_strdup (volume->drive);
1327
1328   return real_display_name;
1329 }
1330
1331 static IconType
1332 get_icon_type_from_stat (WIN32_FILE_ATTRIBUTE_DATA *wfad)
1333 {
1334   if (wfad->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1335     return ICON_DIRECTORY;
1336   else
1337     return ICON_REGULAR;
1338 }
1339
1340 /* Renders a fallback icon from the stock system */
1341 static const gchar *
1342 get_fallback_icon_name (IconType icon_type)
1343 {
1344   const char *stock_name;
1345
1346   switch (icon_type)
1347     {
1348     case ICON_DIRECTORY:
1349       stock_name = GTK_STOCK_DIRECTORY;
1350       break;
1351
1352     case ICON_EXECUTABLE:
1353       stock_name = GTK_STOCK_EXECUTE;
1354       break;
1355
1356     default:
1357       stock_name = GTK_STOCK_FILE;
1358       break;
1359     }
1360
1361   return stock_name;
1362 }
1363
1364 static gchar *
1365 gtk_file_system_win32_volume_get_icon_name (GtkFileSystem        *file_system,
1366                                            GtkFileSystemVolume  *volume,
1367                                            GError              **error)
1368 {
1369   /* FIXME: maybe we just always want to return GTK_STOCK_HARDDISK here?
1370    * or the new tango icon name?
1371    */
1372   return g_strdup ("gnome-dev-harddisk");
1373 }
1374
1375 #if 0 /* Unused, see below */
1376
1377 static char *
1378 get_parent_dir (const char *filename)
1379 {
1380   int len;
1381
1382   len = strlen (filename);
1383
1384   /* Ignore trailing slashes */
1385   if (len > 1 && G_IS_DIR_SEPARATOR (filename[len - 1]))
1386     {
1387       char *tmp, *parent;
1388
1389       tmp = g_strndup (filename, len - 1);
1390
1391       parent = g_path_get_dirname (tmp);
1392       g_free (tmp);
1393
1394       return parent;
1395     }
1396   else
1397     return g_path_get_dirname (filename);
1398 }
1399
1400 #endif
1401
1402 static gboolean
1403 gtk_file_system_win32_get_parent (GtkFileSystem     *file_system,
1404                                   const GtkFilePath *path,
1405                                   GtkFilePath      **parent,
1406                                   GError           **error)
1407 {
1408   const char *filename;
1409
1410   filename = gtk_file_path_get_string (path);
1411   g_return_val_if_fail (filename != NULL, FALSE);
1412   g_return_val_if_fail (g_path_is_absolute (filename), FALSE);
1413
1414   if (filename_is_some_root (filename))
1415     {
1416       *parent = NULL;
1417     }
1418   else
1419     {
1420       gchar *parent_filename = g_path_get_dirname (filename);
1421       *parent = filename_to_path (parent_filename);
1422       g_free (parent_filename);
1423     }
1424 #if DEBUGGING_OUTPUT
1425   printf ("%s: %s => %s\n", __FUNCTION__, (char*)path, (*parent)?(char*)*parent:"NULL"), fflush(stdout);
1426 #endif
1427   return TRUE;
1428 }
1429
1430 static GtkFilePath *
1431 gtk_file_system_win32_make_path (GtkFileSystem     *file_system,
1432                                  const GtkFilePath *base_path,
1433                                  const gchar       *display_name,
1434                                  GError           **error)
1435 {
1436   const char *base_filename;
1437   gchar *full_filename;
1438   GtkFilePath *result;
1439   char *p;
1440   
1441   base_filename = gtk_file_path_get_string (base_path);
1442   g_return_val_if_fail (base_filename != NULL, NULL);
1443   g_return_val_if_fail (g_path_is_absolute (base_filename), NULL);
1444
1445   if ((p = strpbrk (display_name, "<>\"/\\|")))
1446     {
1447       gchar badchar[2];
1448
1449       badchar[0] = *p;          /* We know it is a single-byte char */
1450       badchar[1] = '\0';
1451       g_set_error (error,
1452                    GTK_FILE_SYSTEM_ERROR,
1453                    GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
1454                    _("The name \"%s\" is not valid because it contains the character \"%s\". "
1455                      "Please use a different name."),
1456                    display_name,
1457                    badchar);
1458       return NULL;
1459     }
1460
1461   full_filename = g_build_filename (base_filename, display_name, NULL);
1462   result = filename_to_path (full_filename);
1463   g_free (full_filename);
1464   
1465   return result;
1466 }
1467
1468 /* If this was a publically exported function, it should return
1469  * a dup'ed result, but we make it modify-in-place for efficiency
1470  * here, and because it works for us.
1471  */
1472 static void
1473 canonicalize_filename (gchar *filename)
1474 {
1475   gchar *p, *q;
1476   gchar *past_root;
1477   gboolean last_was_slash = FALSE;
1478
1479 #if DEBUGGING_OUTPUT
1480   printf("%s: %s ", __FUNCTION__, filename), fflush (stdout);
1481 #endif
1482
1483   past_root = (gchar *) g_path_skip_root (filename);
1484
1485   q = p = past_root;
1486
1487   while (*p)
1488     {
1489       if (G_IS_DIR_SEPARATOR (*p))
1490         {
1491           if (!last_was_slash)
1492             *q++ = '\\';
1493
1494           last_was_slash = TRUE;
1495         }
1496       else
1497         {
1498           if (last_was_slash && *p == '.')
1499             {
1500               if (G_IS_DIR_SEPARATOR (*(p + 1)) ||
1501                   *(p + 1) == '\0')
1502                 {
1503                   if (*(p + 1) == '\0')
1504                     break;
1505                   
1506                   p += 1;
1507                 }
1508               else if (*(p + 1) == '.' &&
1509                        (G_IS_DIR_SEPARATOR (*(p + 2)) ||
1510                         *(p + 2) == '\0'))
1511                 {
1512                   if (q > past_root)
1513                     {
1514                       q--;
1515                       while (q > past_root &&
1516                              !G_IS_DIR_SEPARATOR (*(q - 1)))
1517                         q--;
1518                     }
1519
1520                   if (*(p + 2) == '\0')
1521                     break;
1522                   
1523                   p += 2;
1524                 }
1525               else
1526                 {
1527                   *q++ = *p;
1528                   last_was_slash = FALSE;
1529                 }
1530             }
1531           else
1532             {
1533               *q++ = *p;
1534               last_was_slash = FALSE;
1535             }
1536         }
1537
1538       p++;
1539     }
1540
1541   if (q > past_root && G_IS_DIR_SEPARATOR (*(q - 1)))
1542     q--;
1543
1544   *q = '\0';
1545
1546 #if DEBUGGING_OUTPUT
1547   printf(" => %s\n", filename), fflush (stdout);
1548 #endif
1549 }
1550
1551 static gboolean
1552 gtk_file_system_win32_parse (GtkFileSystem     *file_system,
1553                              const GtkFilePath *base_path,
1554                              const gchar       *str,
1555                              GtkFilePath      **folder,
1556                              gchar            **file_part,
1557                              GError           **error)
1558 {
1559   const char *base_filename;
1560   gchar *last_backslash, *last_slash;
1561   gboolean result = FALSE;
1562
1563 #if DEBUGGING_OUTPUT
1564   printf("%s: base_path=%s str=%s\n",__FUNCTION__,(char*)base_path,str),fflush(stdout);
1565 #endif
1566
1567   base_filename = gtk_file_path_get_string (base_path);
1568   g_return_val_if_fail (base_filename != NULL, FALSE);
1569   g_return_val_if_fail (g_path_is_absolute (base_filename), FALSE);
1570   
1571   last_backslash = strrchr (str, '\\');
1572   last_slash = strrchr (str, '/');
1573   if (last_slash == NULL ||
1574       (last_backslash != NULL && last_backslash > last_slash))
1575     last_slash = last_backslash;
1576
1577   if (!last_slash)
1578     {
1579       *folder = gtk_file_path_copy (base_path);
1580       *file_part = g_strdup (str);
1581       result = TRUE;
1582     }
1583   else
1584     {
1585       gchar *folder_part;
1586       gchar *folder_path;
1587
1588       if (last_slash == str)
1589         {
1590           if (g_ascii_isalpha (base_filename[0]) &&
1591               base_filename[1] == ':')
1592             folder_part = g_strdup_printf ("%c:\\", base_filename[0]);
1593           else
1594             folder_part = g_strdup ("\\");
1595         }
1596       else if (g_ascii_isalpha (str[0]) &&
1597                str[1] == ':' &&
1598                last_slash == str + 2)
1599         folder_part = g_strndup (str, last_slash - str + 1);
1600 #if 0
1601       /* Hmm, what the heck was this case supposed to do? It splits up
1602        * \\server\share\foo\bar into folder_part
1603        * \\server\share\foo\bar and file_path bar. Not good. As far as
1604        * I can see, this isn't needed.
1605        */
1606       else if (G_IS_DIR_SEPARATOR (str[0]) &&
1607                G_IS_DIR_SEPARATOR (str[1]) &&
1608                (!str[2] || !G_IS_DIR_SEPARATOR (str[2])))
1609         folder_part = g_strdup (str);
1610 #endif
1611       else
1612         folder_part = g_strndup (str, last_slash - str);
1613
1614       g_assert (folder_part);
1615
1616       if (g_path_is_absolute (folder_part))
1617         folder_path = folder_part;
1618       else
1619         {
1620           folder_path = g_build_filename (base_filename, folder_part, NULL);
1621           g_free (folder_part);
1622         }
1623       
1624       canonicalize_filename (folder_path);
1625       
1626       *folder = filename_to_path (folder_path);
1627       *file_part = g_strdup (last_slash + 1);
1628       
1629       g_free (folder_path);
1630       
1631       result = TRUE;
1632     }
1633
1634 #if DEBUGGING_OUTPUT
1635   printf ("%s:returning folder=%s file_part=%s\n", __FUNCTION__, (*folder?(char*)*folder:"NULL"), *file_part), fflush(stdout);
1636 #endif
1637
1638   return result;
1639 }
1640
1641 static gchar *
1642 gtk_file_system_win32_path_to_uri (GtkFileSystem     *file_system,
1643                                    const GtkFilePath *path)
1644 {
1645   return g_filename_to_uri (gtk_file_path_get_string (path), NULL, NULL);
1646 }
1647
1648 static gchar *
1649 gtk_file_system_win32_path_to_filename (GtkFileSystem     *file_system,
1650                                         const GtkFilePath *path)
1651 {
1652   return g_strdup (gtk_file_path_get_string (path));
1653 }
1654
1655 static GtkFilePath *
1656 gtk_file_system_win32_uri_to_path (GtkFileSystem     *file_system,
1657                                    const gchar       *uri)
1658 {
1659   GtkFilePath *path;
1660   gchar *filename = g_filename_from_uri (uri, NULL, NULL);
1661
1662 #if DEBUGGING_OUTPUT
1663   printf ("%s: %s -> %s\n", __FUNCTION__, uri, filename?filename:"NULL"), fflush (stdout);
1664 #endif
1665
1666   if (filename)
1667     {
1668       path = filename_to_path (filename);
1669       g_free (filename);
1670     }
1671   else
1672     path = NULL;
1673
1674   return path;
1675 }
1676
1677 static GtkFilePath *
1678 gtk_file_system_win32_filename_to_path (GtkFileSystem *file_system,
1679                                         const gchar   *filename)
1680 {
1681   return filename_to_path (filename);
1682 }
1683
1684 #if 0
1685
1686 /* These are unused currently, hmm */
1687
1688 static GdkPixbuf*
1689 extract_icon (const char* filename)
1690 {
1691 #if 0
1692   WORD iicon;
1693   wchar_t *wfn;
1694   wchar_t filename_copy[MAX_PATH];
1695 #else
1696   SHFILEINFOW shfi;
1697   wchar_t *wfn = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
1698   int rc;
1699 #endif
1700
1701   GdkPixbuf *pixbuf = NULL;
1702   HICON hicon;
1703   
1704   if (!filename || !filename[0])
1705     return NULL;
1706
1707 #if 0
1708   /* ExtractAssociatedIconW() is about twice as slow as SHGetFileInfoW() */
1709
1710   /* The ugly ExtractAssociatedIcon modifies filename in place. It
1711    * doesn't even take any argument saying how large the buffer is?
1712    * Let's hope MAX_PATH will be large enough. What dork designed that
1713    * API?
1714    */
1715     
1716   wfn = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
1717   if (wcslen (wfn) >= MAX_PATH)
1718     {
1719       g_free (wfn);
1720       return NULL;
1721     }
1722   wcscpy (filename_copy, wfn);
1723   g_free (wfn);
1724   hicon = ExtractAssociatedIconW (GetModuleHandle (NULL), filename_copy, &iicon);
1725
1726   if (!hicon)
1727     {
1728       g_warning (G_STRLOC ":ExtractAssociatedIcon(%s) failed: %s", filename, g_win32_error_message (GetLastError ()));
1729       return NULL;
1730     }
1731 #else
1732   rc = (int) SHGetFileInfoW (wfn, 0, &shfi, sizeof (shfi),
1733                              SHGFI_ICON|SHGFI_LARGEICON);
1734   g_free (wfn);
1735   if (!rc)
1736     return NULL;
1737   hicon = shfi.hIcon;
1738 #endif
1739   
1740   pixbuf = gdk_win32_icon_to_pixbuf_libgtk_only (hicon);
1741   
1742   if (!DestroyIcon (hicon))
1743     g_warning (G_STRLOC ": DestroyIcon failed: %s\n", g_win32_error_message (GetLastError ()));
1744
1745   return pixbuf;
1746 }
1747
1748 static GtkIconSet *
1749 win32_pseudo_mime_lookup (const char* name)
1750 {
1751   gboolean use_cache = TRUE;
1752   static GHashTable *mime_hash = NULL;
1753   GtkIconSet *is = NULL;
1754   char *p = strrchr(name, '.');
1755   char *extension = p ? g_utf8_casefold (p, -1) : g_strdup ("");
1756   GdkPixbuf *pixbuf;
1757
1758   /* Don't cache icons for files that might have embedded icons */
1759   if (strcmp (extension, ".lnk") == 0 ||
1760       strcmp (extension, ".exe") == 0 ||
1761       strcmp (extension, ".dll") == 0)
1762     {
1763       use_cache = FALSE;
1764       g_free (extension);
1765     }
1766   else
1767     {
1768       if (!mime_hash)
1769         mime_hash = g_hash_table_new (g_str_hash, g_str_equal);
1770
1771       /* do we already have it ? */
1772       is = g_hash_table_lookup (mime_hash, extension);
1773       if (is)
1774         {
1775           g_free (extension);
1776           return is;
1777         }
1778     }
1779
1780   /* create icon and set */
1781   pixbuf = extract_icon (name);
1782   if (pixbuf)
1783     {
1784       GtkIconSource* source = gtk_icon_source_new ();
1785       
1786       is = gtk_icon_set_new_from_pixbuf (pixbuf);
1787       gtk_icon_source_set_pixbuf (source, pixbuf);
1788       gtk_icon_set_add_source (is, source);
1789       
1790       gtk_icon_source_free (source);
1791     }
1792   
1793   if (use_cache)
1794     g_hash_table_insert (mime_hash, extension, is);
1795
1796   return is;
1797 }
1798
1799 #endif
1800
1801 /* Returns the name of the icon to be used for a path which is known to be a
1802  * directory.  This can vary for Home, Desktop, etc.
1803  */
1804 static const char *
1805 get_icon_name_for_directory (const char *path)
1806 {
1807   static char *desktop_path = NULL;
1808
1809   if (!g_get_home_dir ())
1810     return "gnome-fs-directory";
1811
1812   if (!desktop_path)
1813       desktop_path = g_build_filename (g_get_home_dir (), "Desktop", NULL);
1814
1815   if (strcmp (g_get_home_dir (), path) == 0)
1816     return "gnome-fs-home";
1817   else if (strcmp (desktop_path, path) == 0)
1818     return "gnome-fs-desktop";
1819   else
1820     return "gnome-fs-directory";
1821
1822   return NULL;
1823 }
1824
1825 /* Computes our internal icon type based on a path name; also returns the MIME
1826  * type in case we come up with ICON_REGULAR.
1827  */
1828 static IconType
1829 get_icon_type_from_path (GtkFileFolderWin32        *folder_win32,
1830                          WIN32_FILE_ATTRIBUTE_DATA *wfad,
1831                          const char                *filename,
1832                          const char               **mime_type)
1833 {
1834   IconType icon_type;
1835
1836   *mime_type = NULL;
1837
1838   if (folder_win32 && folder_win32->have_stat)
1839     {
1840       char *basename;
1841       struct stat_info_entry *entry;
1842
1843       g_assert (folder_win32->stat_info != NULL);
1844
1845       basename = g_path_get_basename (filename);
1846       entry = g_hash_table_lookup (folder_win32->stat_info, basename);
1847       g_free (basename);
1848       if (entry)
1849         {
1850           if (entry->icon_type == ICON_UNDECIDED)
1851             {
1852               entry->icon_type = get_icon_type_from_stat (&entry->wfad);
1853               g_assert (entry->icon_type != ICON_UNDECIDED);
1854             }
1855           icon_type = entry->icon_type;
1856           if (icon_type == ICON_REGULAR)
1857             {
1858               fill_in_mime_type (folder_win32);
1859               *mime_type = entry->mime_type;
1860             }
1861
1862           return icon_type;
1863         }
1864     }
1865
1866   icon_type = get_icon_type_from_stat (wfad);
1867
1868   if (icon_type == ICON_REGULAR)
1869     *mime_type = get_mime_type_for_file (filename, wfad);
1870
1871   return icon_type;
1872 }
1873
1874 /* Renders an icon for a non-ICON_REGULAR file */
1875 static const gchar *
1876 get_special_icon_name (IconType           icon_type,
1877                        const gchar       *filename)
1878 {
1879   const char *name;
1880
1881   g_assert (icon_type != ICON_REGULAR);
1882
1883   switch (icon_type)
1884     {
1885     case ICON_DIRECTORY:
1886       /* get_icon_name_for_directory() returns a dupped string */
1887       return get_icon_name_for_directory (filename);
1888     case ICON_EXECUTABLE:
1889       name ="gnome-fs-executable";
1890       break;
1891     default:
1892       g_assert_not_reached ();
1893       return NULL;
1894     }
1895
1896   return name;
1897 }
1898
1899 static gchar *
1900 get_icon_name_for_mime_type (const char *mime_type)
1901 {
1902   char *name;
1903   const char *separator;
1904   GString *icon_name;
1905
1906   if (!mime_type)
1907     return NULL;
1908
1909   separator = strchr (mime_type, '/');
1910   if (!separator)
1911     return NULL; /* maybe we should return a GError with "invalid MIME-type" */
1912
1913   /* FIXME: we default to the gnome icon naming for now.  Some question
1914    * as below, how are we going to handle a second attempt?
1915    */
1916 #if 0
1917   icon_name = g_string_new ("");
1918   g_string_append_len (icon_name, mime_type, separator - mime_type);
1919   g_string_append_c (icon_name, '-');
1920   g_string_append (icon_name, separator + 1);
1921   pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
1922   g_string_free (icon_name, TRUE);
1923   if (pixbuf)
1924     return pixbuf;
1925
1926   icon_name = g_string_new ("");
1927   g_string_append_len (icon_name, mime_type, separator - mime_type);
1928   g_string_append (icon_name, "-x-generic");
1929   pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
1930   g_string_free (icon_name, TRUE);
1931   if (pixbuf)
1932     return pixbuf;
1933 #endif
1934
1935   icon_name = g_string_new ("gnome-mime-");
1936   g_string_append_len (icon_name, mime_type, separator - mime_type);
1937   g_string_append_c (icon_name, '-');
1938   g_string_append (icon_name, separator + 1);
1939   name = icon_name->str;
1940   g_string_free (icon_name, FALSE);
1941
1942   return name;
1943
1944   /* FIXME: how are we going to implement a second attempt? */
1945 #if 0
1946   if (pixbuf)
1947     return pixbuf;
1948
1949   icon_name = g_string_new ("gnome-mime-");
1950   g_string_append_len (icon_name, mime_type, separator - mime_type);
1951   pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
1952   g_string_free (icon_name, TRUE);
1953
1954   return pixbuf;
1955 #endif
1956 }
1957
1958 static void
1959 bookmark_list_free (GSList *list)
1960 {
1961   GSList *l;
1962
1963   for (l = list; l; l = l->next)
1964     g_free (l->data);
1965
1966   g_slist_free (list);
1967 }
1968
1969 /* Returns whether a URI is a local file:// */
1970 static gboolean
1971 is_local_uri (const char *uri)
1972 {
1973   char *filename;
1974   char *hostname;
1975   gboolean result;
1976
1977   /* This is rather crude, but hey */
1978   filename = g_filename_from_uri (uri, &hostname, NULL);
1979
1980   result = (filename && !hostname);
1981
1982   g_free (filename);
1983   g_free (hostname);
1984
1985   return result;
1986 }
1987
1988 static char *
1989 bookmark_get_filename (void)
1990 {
1991   char *filename;
1992
1993   filename = g_build_filename (g_get_home_dir (), 
1994                                BOOKMARKS_FILENAME, NULL);
1995   g_assert (filename != NULL);
1996   return filename;
1997 }
1998
1999 static gboolean
2000 bookmark_list_read (GSList **bookmarks, GError **error)
2001 {
2002   gchar *filename;
2003   gchar *contents;
2004   gboolean result = FALSE;
2005
2006   filename = bookmark_get_filename ();
2007   *bookmarks = NULL;
2008
2009   if (g_file_get_contents (filename, &contents, NULL, error))
2010     {
2011       gchar **lines = g_strsplit (contents, "\n", -1);
2012       int i;
2013       GHashTable *table;
2014
2015       table = g_hash_table_new (g_str_hash, g_str_equal);
2016
2017       for (i = 0; lines[i]; i++)
2018         {
2019           if (lines[i][0] && !g_hash_table_lookup (table, lines[i]))
2020             {
2021               *bookmarks = g_slist_prepend (*bookmarks, g_strdup (lines[i]));
2022               g_hash_table_insert (table, lines[i], lines[i]);
2023             }
2024         }
2025
2026       g_free (contents);
2027       g_hash_table_destroy (table);
2028       g_strfreev (lines);
2029
2030       *bookmarks = g_slist_reverse (*bookmarks);
2031       result = TRUE;
2032     }
2033
2034   g_free (filename);
2035
2036   return result;
2037 }
2038
2039 static gboolean
2040 bookmark_list_write (GSList  *bookmarks, 
2041                      GError **error)
2042 {
2043   GSList *l;
2044   GString *string;
2045   char *filename;
2046   GError *tmp_error = NULL;
2047   gboolean result;
2048
2049   string = g_string_new ("");
2050
2051   for (l = bookmarks; l; l = l->next)
2052     {
2053       g_string_append (string, l->data);
2054       g_string_append_c (string, '\n');
2055     }
2056
2057   filename = bookmark_get_filename ();
2058
2059   result = g_file_set_contents (filename, string->str, -1, &tmp_error);
2060
2061   g_free (filename);
2062   g_string_free (string, TRUE);
2063
2064   if (!result)
2065     {
2066       g_set_error (error,
2067                    GTK_FILE_SYSTEM_ERROR,
2068                    GTK_FILE_SYSTEM_ERROR_FAILED,
2069                    _("Bookmark saving failed: %s"),
2070                    tmp_error->message);
2071       
2072       g_error_free (tmp_error);
2073     }
2074
2075   return result;
2076 }
2077
2078 static gboolean
2079 gtk_file_system_win32_insert_bookmark (GtkFileSystem     *file_system,
2080                                        const GtkFilePath *path,
2081                                        gint               position,
2082                                        GError            **error)
2083 {
2084   GSList *bookmarks;
2085   int num_bookmarks;
2086   GSList *l;
2087   char *uri;
2088   gboolean result;
2089   GError *err;
2090
2091   err = NULL;
2092   if (!bookmark_list_read (&bookmarks, &err) && err->code != G_FILE_ERROR_NOENT)
2093     {
2094       g_propagate_error (error, err);
2095       return FALSE;
2096     }
2097
2098   num_bookmarks = g_slist_length (bookmarks);
2099   g_return_val_if_fail (position >= -1 && position <= num_bookmarks, FALSE);
2100
2101   result = FALSE;
2102
2103   uri = gtk_file_system_win32_path_to_uri (file_system, path);
2104
2105   for (l = bookmarks; l; l = l->next)
2106     {
2107       char *bookmark, *space;
2108
2109       bookmark = l->data;
2110       
2111       space = strchr (bookmark, ' ');
2112       if (space)
2113         *space = '\0';
2114       if (strcmp (bookmark, uri) != 0)
2115         {
2116           if (space)
2117             *space = ' ';
2118         }
2119       else
2120         {
2121           g_set_error (error,
2122                        GTK_FILE_SYSTEM_ERROR,
2123                        GTK_FILE_SYSTEM_ERROR_ALREADY_EXISTS,
2124                        _("'%s' already exists in the bookmarks list"),
2125                        uri);
2126           goto out;
2127         }
2128     }
2129
2130   bookmarks = g_slist_insert (bookmarks, g_strdup (uri), position);
2131   if (bookmark_list_write (bookmarks, error))
2132     {
2133       result = TRUE;
2134       g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
2135     }
2136
2137  out:
2138
2139   g_free (uri);
2140   bookmark_list_free (bookmarks);
2141
2142   return result;
2143 }
2144
2145 static gboolean
2146 gtk_file_system_win32_remove_bookmark (GtkFileSystem     *file_system,
2147                                        const GtkFilePath *path,
2148                                        GError           **error)
2149 {
2150   GSList *bookmarks;
2151   char *uri;
2152   GSList *l;
2153   gboolean result;
2154
2155   if (!bookmark_list_read (&bookmarks, error))
2156     return FALSE;
2157
2158   result = FALSE;
2159
2160   uri = gtk_file_system_path_to_uri (file_system, path);
2161
2162   for (l = bookmarks; l; l = l->next)
2163     {
2164       char *bookmark, *space;
2165
2166       bookmark = (char *)l->data;
2167       space = strchr (bookmark, ' ');
2168       if (space)
2169         *space = '\0';
2170
2171       if (strcmp (bookmark, uri) != 0)
2172         {
2173           if (space)
2174             *space = ' ';
2175         }
2176       else
2177         {
2178           g_free (l->data);
2179           bookmarks = g_slist_remove_link (bookmarks, l);
2180           g_slist_free_1 (l);
2181
2182           if (bookmark_list_write (bookmarks, error))
2183             {
2184               result = TRUE;
2185
2186               g_signal_emit_by_name (file_system, "bookmarks-changed", 0);            
2187             }
2188
2189           goto out;
2190         }
2191     }
2192
2193   g_set_error (error,
2194                GTK_FILE_SYSTEM_ERROR,
2195                GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
2196                _("'%s' does not exist in the bookmarks list"),
2197                uri);
2198
2199  out:
2200
2201   g_free (uri);
2202   bookmark_list_free (bookmarks);
2203
2204   return result;
2205 }
2206
2207 static GSList *
2208 gtk_file_system_win32_list_bookmarks (GtkFileSystem *file_system)
2209 {
2210   GSList *bookmarks;
2211   GSList *result;
2212   GSList *l;
2213
2214   if (!bookmark_list_read (&bookmarks, NULL))
2215     return NULL;
2216
2217   result = NULL;
2218
2219   for (l = bookmarks; l; l = l->next)
2220     {
2221       char *bookmark, *space;
2222
2223       bookmark = (char *)l->data;
2224       space = strchr (bookmark, ' ');
2225       if (space)
2226         *space = '\0';
2227
2228       if (is_local_uri (bookmark))
2229         result = g_slist_prepend (result, gtk_file_system_win32_uri_to_path (file_system, bookmark));
2230     }
2231
2232   bookmark_list_free (bookmarks);
2233
2234   result = g_slist_reverse (result);
2235   return result;
2236 }
2237
2238 static gchar *
2239 gtk_file_system_win32_get_bookmark_label (GtkFileSystem     *file_system,
2240                                          const GtkFilePath *path)
2241 {
2242   GSList *labels;
2243   gchar *label;
2244   GSList *l;
2245   char *bookmark, *space, *uri;
2246   
2247   labels = NULL;
2248   label = NULL;
2249
2250   uri = gtk_file_system_path_to_uri (file_system, path);
2251   bookmark_list_read (&labels, NULL);
2252
2253   for (l = labels; l && !label; l = l->next) 
2254     {
2255       bookmark = (char *)l->data;
2256       space = strchr (bookmark, ' ');
2257       if (!space)
2258         continue;
2259
2260       *space = '\0';
2261
2262       if (strcmp (uri, bookmark) == 0)
2263         label = g_strdup (space + 1);
2264     }
2265
2266   bookmark_list_free (labels);
2267   g_free (uri);
2268
2269   return label;
2270 }
2271
2272 static void
2273 gtk_file_system_win32_set_bookmark_label (GtkFileSystem     *file_system,
2274                                          const GtkFilePath *path,
2275                                          const gchar       *label)
2276 {
2277   GSList *labels;
2278   GSList *l;
2279   gchar *bookmark, *space, *uri;
2280   gboolean found;
2281
2282   labels = NULL;
2283
2284   uri = gtk_file_system_path_to_uri (file_system, path);
2285   bookmark_list_read (&labels, NULL);
2286
2287   found = FALSE;
2288   for (l = labels; l && !found; l = l->next) 
2289     {
2290       bookmark = (gchar *)l->data;
2291       space = strchr (bookmark, ' ');
2292       if (space)
2293         *space = '\0';
2294
2295       if (strcmp (bookmark, uri) != 0)
2296         {
2297           if (space)
2298             *space = ' ';
2299         }
2300       else
2301         {
2302           g_free (bookmark);
2303           
2304           if (label && *label)
2305             l->data = g_strdup_printf ("%s %s", uri, label);
2306           else
2307             l->data = g_strdup (uri);
2308
2309           found = TRUE;
2310           break;
2311         }
2312     }
2313
2314   if (found)
2315     {
2316       if (bookmark_list_write (labels, NULL))
2317         g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
2318     }
2319   
2320   bookmark_list_free (labels);
2321   g_free (uri);
2322 }
2323
2324 static void
2325 _gtk_file_folder_win32_class_init (GtkFileFolderWin32Class *class)
2326 {
2327   GObjectClass *gobject_class = G_OBJECT_CLASS (class);
2328
2329   gobject_class->finalize = gtk_file_folder_win32_finalize;
2330 }
2331
2332 static void
2333 gtk_file_folder_win32_iface_init (GtkFileFolderIface *iface)
2334 {
2335   iface->get_info = gtk_file_folder_win32_get_info;
2336   iface->list_children = gtk_file_folder_win32_list_children;
2337   iface->is_finished_loading = gtk_file_folder_win32_is_finished_loading;
2338 }
2339
2340 static void
2341 _gtk_file_folder_win32_init (GtkFileFolderWin32 *impl)
2342 {
2343 }
2344
2345 static void
2346 gtk_file_folder_win32_finalize (GObject *object)
2347 {
2348   GtkFileFolderWin32 *folder_win32 = GTK_FILE_FOLDER_WIN32 (object);
2349
2350   if (folder_win32->load_folder_id)
2351     {
2352       g_source_remove (folder_win32->load_folder_id);
2353       folder_win32->load_folder_id = 0;
2354     }
2355
2356   g_hash_table_remove (folder_win32->system_win32->folder_hash, folder_win32->filename);
2357
2358   if (folder_win32->stat_info)
2359     {
2360 #if 0
2361       g_print ("Releasing information for directory %s\n", folder_win32->filename);
2362 #endif
2363       g_hash_table_destroy (folder_win32->stat_info);
2364     }
2365
2366   g_free (folder_win32->filename);
2367
2368   G_OBJECT_CLASS (_gtk_file_folder_win32_parent_class)->finalize (object);
2369 }
2370
2371 /* Creates a GtkFileInfo for a volume root by stat()ing it */
2372 static GtkFileInfo *
2373 file_info_for_root_with_error (const char  *root_name,
2374                                GError     **error)
2375 {
2376   struct stat statbuf;
2377   GtkFileInfo *info;
2378
2379   if (g_stat (root_name, &statbuf) != 0)
2380     {
2381       int saved_errno;
2382       char *display_name;
2383
2384       saved_errno = errno; 
2385       display_name = g_filename_display_name (root_name);
2386       g_set_error (error,
2387                    GTK_FILE_SYSTEM_ERROR,
2388                    GTK_FILE_SYSTEM_ERROR_FAILED,
2389                    _("Error getting information for '%s': %s"),
2390                    display_name,
2391                    g_strerror (saved_errno));
2392
2393       g_free (display_name);
2394       return NULL;
2395     }
2396
2397   info = gtk_file_info_new ();
2398   gtk_file_info_set_display_name (info, root_name);
2399   gtk_file_info_set_is_folder (info, TRUE);
2400   gtk_file_info_set_is_hidden (info, FALSE);
2401   gtk_file_info_set_mime_type (info, "x-directory/normal");
2402   gtk_file_info_set_modification_time (info, statbuf.st_mtime);
2403   gtk_file_info_set_size (info, statbuf.st_size);
2404
2405   return info;
2406 }
2407
2408 static gboolean
2409 stat_with_error (const char                *filename,
2410                  WIN32_FILE_ATTRIBUTE_DATA *wfad,
2411                  GError                   **error)
2412 {
2413   wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, error);
2414   int rc;
2415
2416   if (wfilename == NULL)
2417     return FALSE;
2418
2419   rc = GetFileAttributesExW (wfilename, GetFileExInfoStandard, wfad);
2420
2421   if (rc == 0)
2422     {
2423       if (error)
2424         {
2425           int error_number = GetLastError ();
2426           char *emsg = g_win32_error_message (error_number);
2427           gchar *display_name = g_filename_display_name (filename);
2428           int code;
2429
2430           if (error_number == ERROR_FILE_NOT_FOUND ||
2431               error_number == ERROR_PATH_NOT_FOUND)
2432             code = GTK_FILE_SYSTEM_ERROR_NONEXISTENT;
2433           else
2434             code = GTK_FILE_SYSTEM_ERROR_FAILED;
2435           
2436           g_set_error (error,
2437                        GTK_FILE_SYSTEM_ERROR,
2438                        code,
2439                        _("Error getting information for '%s': %s"),
2440                        display_name,
2441                        emsg);
2442           g_free (display_name);
2443           g_free (wfilename);
2444           g_free (emsg);
2445         }
2446       return FALSE;
2447     }
2448     
2449   g_free (wfilename);
2450   return TRUE;
2451 }
2452
2453 /* Creates a new GtkFileInfo from the specified data */
2454 static GtkFileInfo *
2455 create_file_info (GtkFileFolderWin32        *folder_win32,
2456                   const char                *filename,
2457                   GtkFileInfoType            types,
2458                   WIN32_FILE_ATTRIBUTE_DATA *wfad,
2459                   const char                *mime_type)
2460 {
2461   GtkFileInfo *info;
2462
2463   info = gtk_file_info_new ();
2464   
2465   if (types & GTK_FILE_INFO_DISPLAY_NAME)
2466     {
2467       gchar *display_name;
2468
2469       if (filename_is_root (filename))
2470         display_name = g_filename_display_name (filename);
2471       else
2472         display_name = g_filename_display_basename (filename);
2473
2474       gtk_file_info_set_display_name (info, display_name);
2475       g_free (display_name);
2476     }
2477
2478   if (types & GTK_FILE_INFO_IS_HIDDEN)
2479     {
2480       gboolean is_hidden = !!(wfad->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN);
2481       gtk_file_info_set_is_hidden (info, is_hidden);
2482     }
2483
2484   if (types & GTK_FILE_INFO_IS_FOLDER)
2485     gtk_file_info_set_is_folder (info, !!(wfad->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY));
2486
2487   if (types & GTK_FILE_INFO_MIME_TYPE)
2488     gtk_file_info_set_mime_type (info, mime_type);
2489
2490   if (types & GTK_FILE_INFO_MODIFICATION_TIME)
2491     {
2492       GtkFileTime time = (wfad->ftLastWriteTime.dwLowDateTime 
2493                           | ((guint64)wfad->ftLastWriteTime.dwHighDateTime) << 32);
2494       /* 100-nanosecond intervals since January 1, 1601, urgh! */
2495       time /= G_GINT64_CONSTANT (10000000); /* now seconds */
2496       time -= G_GINT64_CONSTANT (134774) * 24 * 3600; /* good old Unix time */
2497       gtk_file_info_set_modification_time (info, time);
2498     }
2499
2500   if (types & GTK_FILE_INFO_SIZE)
2501     {
2502       gint64 size = wfad->nFileSizeLow | ((guint64)wfad->nFileSizeHigh) << 32;
2503       gtk_file_info_set_size (info, size);
2504     }
2505
2506   if (types & GTK_FILE_INFO_ICON)
2507     {
2508       IconType icon_type;
2509       gboolean free_icon_name = FALSE;
2510       const char *icon_name;
2511       const char *icon_mime_type;
2512
2513       icon_type = get_icon_type_from_path (folder_win32, wfad, filename, &icon_mime_type);
2514
2515       switch (icon_type)
2516         {
2517           case ICON_NONE:
2518             icon_name = get_fallback_icon_name (icon_type);
2519             break;
2520
2521           case ICON_REGULAR:
2522             free_icon_name = TRUE;
2523             if (icon_mime_type)
2524               icon_name = get_icon_name_for_mime_type (icon_mime_type);
2525             else
2526               icon_name = get_icon_name_for_mime_type (mime_type);
2527             break;
2528
2529           default:
2530             icon_name = get_special_icon_name (icon_type, filename);
2531             break;
2532         }
2533
2534       gtk_file_info_set_icon_name (info, icon_name);
2535
2536       if (free_icon_name)
2537         g_free ((char *) icon_name);
2538     }
2539
2540   return info;
2541 }
2542
2543 static struct stat_info_entry *
2544 create_stat_info_entry_and_emit_add (GtkFileFolderWin32        *folder_win32,
2545                                      const char                *filename,
2546                                      const char                *basename,
2547                                      WIN32_FILE_ATTRIBUTE_DATA *wfad)
2548 {
2549   GSList *paths;
2550   GtkFilePath *path;
2551   struct stat_info_entry *entry;
2552
2553   entry = g_new0 (struct stat_info_entry, 1);
2554
2555   if ((folder_win32->types & STAT_NEEDED_MASK) != 0)
2556     entry->wfad = *wfad;
2557
2558   if ((folder_win32->types & GTK_FILE_INFO_MIME_TYPE) != 0)
2559     entry->mime_type = get_mime_type_for_file (filename, wfad);
2560
2561   g_hash_table_insert (folder_win32->stat_info,
2562                        g_strdup (basename),
2563                        entry);
2564
2565   path = gtk_file_path_new_dup (filename);
2566   paths = g_slist_append (NULL, path);
2567   g_signal_emit_by_name (folder_win32, "files-added", paths);
2568   gtk_file_path_free (path);
2569   g_slist_free (paths);
2570
2571   return entry;
2572 }
2573
2574 static GtkFileInfo *
2575 gtk_file_folder_win32_get_info (GtkFileFolder      *folder,
2576                                 const GtkFilePath  *path,
2577                                 GError            **error)
2578 {
2579   GtkFileFolderWin32 *folder_win32 = GTK_FILE_FOLDER_WIN32 (folder);
2580   GtkFileInfo *info;
2581   const char *filename;
2582   GtkFileInfoType types;
2583   WIN32_FILE_ATTRIBUTE_DATA wfad;
2584   const char *mime_type;
2585
2586   /* Get_info for "/" */
2587   if (!path)
2588     {
2589       g_return_val_if_fail (filename_is_root (folder_win32->filename), NULL);
2590       return file_info_for_root_with_error (folder_win32->filename, error);
2591     }
2592
2593   /* Get_info for normal files */
2594
2595   filename = gtk_file_path_get_string (path);
2596   g_return_val_if_fail (filename != NULL, NULL);
2597   g_return_val_if_fail (g_path_is_absolute (filename), NULL);
2598
2599 #if 0
2600   /* Skip this sanity check, as it fails for server share roots, where
2601    * dirname gets set to \\server\share\ and folder_win32->filename is
2602    * \\server\share. Also, should we do a casefolded comparison here,
2603    * too, anyway?
2604    */
2605   {
2606     gchar *dirname = get_parent_dir (filename);
2607     g_return_val_if_fail (strcmp (dirname, folder_win32->filename) == 0, NULL);
2608     g_free (dirname);
2609   }
2610 #endif
2611
2612   types = folder_win32->types;
2613
2614   if (folder_win32->have_stat)
2615     {
2616       struct stat_info_entry *entry;
2617       gchar *basename;
2618
2619       g_assert (folder_win32->stat_info != NULL);
2620
2621       basename = g_path_get_basename (filename);
2622       entry = g_hash_table_lookup (folder_win32->stat_info, basename);
2623
2624       if (!entry)
2625         {
2626           if (!stat_with_error (filename, &wfad, error))
2627             {
2628               g_free (basename);
2629               return NULL;
2630             }
2631
2632           entry = create_stat_info_entry_and_emit_add (folder_win32, filename, basename, &wfad);
2633         }
2634       g_free (basename);
2635
2636       info = create_file_info (folder_win32, filename, types, &entry->wfad, entry->mime_type);
2637       return info;
2638     }
2639   else
2640     {
2641       if (!stat_with_error (filename, &wfad, error))
2642         return NULL;
2643
2644       if ((types & GTK_FILE_INFO_MIME_TYPE) != 0)
2645         mime_type = get_mime_type_for_file (filename, &wfad);
2646       else
2647         mime_type = NULL;
2648
2649       info = create_file_info (folder_win32, filename, types, &wfad, mime_type);
2650
2651       return info;
2652     }
2653 }
2654
2655 static void
2656 cb_list_children (gpointer key, gpointer value, gpointer user_data)
2657 {
2658   GSList **children = user_data;
2659   *children = g_slist_prepend (*children, key);
2660 }
2661
2662 static gboolean
2663 gtk_file_folder_win32_list_children (GtkFileFolder  *folder,
2664                                      GSList        **children,
2665                                      GError        **error)
2666 {
2667   GtkFileFolderWin32 *folder_win32 = GTK_FILE_FOLDER_WIN32 (folder);
2668   GSList *l;
2669
2670   *children = NULL;
2671
2672   /* Get the list of basenames.  */
2673   if (folder_win32->stat_info)
2674     g_hash_table_foreach (folder_win32->stat_info, cb_list_children, children);
2675
2676   /* Turn basenames into GFilePaths.  */
2677   for (l = *children; l; l = l->next)
2678     {
2679       const char *basename = l->data;
2680       char *fullname = g_build_filename (folder_win32->filename, basename, NULL);
2681       l->data = filename_to_path (fullname);
2682       g_free (fullname);
2683     }
2684
2685   return TRUE;
2686 }
2687
2688 static gboolean
2689 gtk_file_folder_win32_is_finished_loading (GtkFileFolder *folder)
2690 {
2691   return GTK_FILE_FOLDER_WIN32 (folder)->is_finished_loading;
2692 }
2693
2694 static void
2695 free_stat_info_entry (struct stat_info_entry *entry)
2696 {
2697   g_free (entry->mime_type);
2698   g_free (entry);
2699 }
2700
2701 static gboolean
2702 fill_in_names (GtkFileFolderWin32 *folder_win32, GError **error)
2703 {
2704   GDir *dir;
2705
2706   if (folder_win32->stat_info)
2707     return TRUE;
2708
2709   dir = g_dir_open (folder_win32->filename, 0, error);
2710   if (!dir)
2711     return FALSE;
2712
2713   folder_win32->stat_info = g_hash_table_new_full (casefolded_hash, casefolded_equal,
2714                                                   (GDestroyNotify) g_free,
2715                                                   (GDestroyNotify) free_stat_info_entry);
2716
2717   while (TRUE)
2718     {
2719       struct stat_info_entry *entry;
2720       const gchar *basename;
2721
2722       basename = g_dir_read_name (dir);
2723       if (!basename)
2724         break;
2725
2726       entry = g_new0 (struct stat_info_entry, 1);
2727       if (folder_win32->is_network_dir)
2728         {
2729           g_assert_not_reached ();
2730           entry->wfad.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
2731           entry->mime_type = g_strdup ("x-directory/normal");
2732         }
2733
2734       g_hash_table_insert (folder_win32->stat_info,
2735                            g_strdup (basename),
2736                            entry);
2737     }
2738
2739   g_dir_close (dir);
2740
2741   folder_win32->asof = time (NULL);
2742   return TRUE;
2743 }
2744
2745 static gboolean
2746 cb_fill_in_stats (gpointer key, gpointer value, gpointer user_data)
2747 {
2748   const char *basename = key;
2749   struct stat_info_entry *entry = value;
2750   GtkFileFolderWin32 *folder_win32 = user_data;
2751   char *fullname = g_build_filename (folder_win32->filename, basename, NULL);
2752   gboolean result;
2753
2754   if (!stat_with_error (fullname, &entry->wfad, NULL))
2755     result = TRUE;  /* Couldn't stat -- remove from hash.  */
2756   else
2757     result = FALSE;
2758
2759   g_free (fullname);
2760   return result;
2761 }
2762
2763
2764 static void
2765 fill_in_stats (GtkFileFolderWin32 *folder_win32)
2766 {
2767   if (folder_win32->have_stat)
2768     return;
2769
2770   if (!fill_in_names (folder_win32, NULL))
2771     return;
2772
2773   if (!folder_win32->is_network_dir)
2774     g_hash_table_foreach_remove (folder_win32->stat_info,
2775                                  cb_fill_in_stats,
2776                                  folder_win32);
2777
2778   folder_win32->have_stat = TRUE;
2779 }
2780
2781 static gboolean
2782 cb_fill_in_mime_type (gpointer key, gpointer value, gpointer user_data)
2783 {
2784   const char *basename = key;
2785   struct stat_info_entry *entry = value;
2786   GtkFileFolderWin32 *folder_win32 = user_data;
2787   char *fullname = g_build_filename (folder_win32->filename, basename, NULL);
2788
2789   entry->mime_type = get_mime_type_for_file (fullname, &entry->wfad);
2790
2791   g_free (fullname);
2792
2793   return FALSE;
2794 }
2795
2796 static void
2797 fill_in_mime_type (GtkFileFolderWin32 *folder_win32)
2798 {
2799   if (folder_win32->have_mime_type)
2800     return;
2801
2802   if (!folder_win32->have_stat)
2803     return;
2804
2805   g_assert (folder_win32->stat_info != NULL);
2806
2807   if (!folder_win32->is_network_dir)
2808     g_hash_table_foreach_remove (folder_win32->stat_info,
2809                                  cb_fill_in_mime_type,
2810                                  folder_win32);
2811
2812   folder_win32->have_mime_type = TRUE;
2813 }
2814
2815 static GtkFilePath *
2816 filename_to_path (const char *filename)
2817 {
2818   char *tmp;
2819
2820   tmp = remove_trailing_slash (filename);
2821   return gtk_file_path_new_steal (tmp);
2822 }
2823
2824 static gboolean
2825 filename_is_root (const char *filename)
2826 {
2827   const gchar *after_root;
2828
2829   after_root = g_path_skip_root (filename);
2830
2831   return (after_root != NULL && *after_root == '\0');
2832 }
2833
2834 static gboolean
2835 filename_is_drive_root (const char *filename)
2836 {
2837   guint len = strlen (filename);
2838
2839   return (len == 3 &&
2840           g_ascii_isalpha (filename[0]) &&
2841           filename[1] == ':' &&
2842           G_IS_DIR_SEPARATOR (filename[2]));
2843 }
2844
2845 static gboolean
2846 filename_is_some_root (const char *filename)
2847 {
2848   return (g_path_is_absolute (filename) &&
2849           *(g_path_skip_root (filename)) == '\0');
2850 }    
2851
2852 int
2853 _gtk_file_system_win32_path_compare (const gchar *path1,
2854                                      const gchar *path2)
2855 {
2856   while (*path1 && *path2)
2857     {
2858       gunichar c1 = g_utf8_get_char (path1);
2859       gunichar c2 = g_utf8_get_char (path2);
2860       if (c1 == c2 ||
2861           (G_IS_DIR_SEPARATOR (c1) && G_IS_DIR_SEPARATOR (c1)) ||
2862           g_unichar_toupper (c1) == g_unichar_toupper (c2))
2863         {
2864           path1 = g_utf8_next_char (path1);
2865           path2 = g_utf8_next_char (path2);
2866         }
2867       else
2868         break;
2869     }
2870   if (!*path1 && !*path2)
2871     return 0;
2872   else if (!*path1)
2873     return -1;
2874   else if (!*path2)
2875     return 1;
2876   else
2877     return g_unichar_toupper (g_utf8_get_char (path1)) - g_unichar_toupper (g_utf8_get_char (path2));
2878 }
2879
2880 #define __GTK_FILE_SYSTEM_WIN32_C__
2881 #include "gtkaliasdef.c"