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