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