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
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
22 /* #define this if you want the program to crash when a file system gets
23 * finalized while async handles are still outstanding.
25 #undef HANDLE_ME_HARDER
29 #include "gtkfilesystem.h"
30 #include "gtkfilesystemwin32.h"
31 #include "gtkicontheme.h"
34 #include "gtkiconfactory.h"
37 #include <glib/gstdio.h>
43 #include <sys/types.h>
45 #define WIN32_LEAN_AND_MEAN
47 #include "gdk/win32/gdkwin32.h"
52 #define BOOKMARKS_FILENAME ".gtk-bookmarks"
54 #define FOLDER_CACHE_LIFETIME 2 /* seconds */
56 typedef struct _GtkFileSystemWin32Class GtkFileSystemWin32Class;
58 #define GTK_FILE_SYSTEM_WIN32_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_SYSTEM_WIN32, GtkFileSystemWin32Class))
59 #define GTK_IS_FILE_SYSTEM_WIN32_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_SYSTEM_WIN32))
60 #define GTK_FILE_SYSTEM_WIN32_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_SYSTEM_WIN32, GtkFileSystemWin32Class))
62 struct _GtkFileSystemWin32Class
64 GObjectClass parent_class;
67 struct _GtkFileSystemWin32
69 GObject parent_instance;
71 guint32 drives; /* bitmask as returned by GetLogicalDrives() */
72 GHashTable *folder_hash;
77 guint execute_callbacks_idle_id;
81 /* Icon type, supplemented by MIME type
84 ICON_UNDECIDED, /* Only used while we have not yet computed the icon in a struct stat_info_entry */
85 ICON_NONE, /* "Could not compute the icon type" */
86 ICON_REGULAR, /* Use mime type for icon */
92 #define GTK_TYPE_FILE_FOLDER_WIN32 (_gtk_file_folder_win32_get_type ())
93 #define GTK_FILE_FOLDER_WIN32(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_FILE_FOLDER_WIN32, GtkFileFolderWin32))
94 #define GTK_IS_FILE_FOLDER_WIN32(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_FILE_FOLDER_WIN32))
95 #define GTK_FILE_FOLDER_WIN32_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_FOLDER_WIN32, GtkFileFolderWin32Class))
96 #define GTK_IS_FILE_FOLDER_WIN32_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_FILE_FOLDER_WIN32))
97 #define GTK_FILE_FOLDER_WIN32_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_FILE_FOLDER_WIN32, GtkFileFolderWin32Class))
99 typedef struct _GtkFileFolderWin32 GtkFileFolderWin32;
100 typedef struct _GtkFileFolderWin32Class GtkFileFolderWin32Class;
102 struct _GtkFileFolderWin32Class
104 GObjectClass parent_class;
107 struct _GtkFileFolderWin32
109 GObject parent_instance;
111 GtkFileSystemWin32 *system_win32;
112 GtkFileInfoType types;
114 GHashTable *stat_info;
115 guint load_folder_id;
117 guint have_mime_type : 1;
118 guint is_network_dir : 1;
119 guint is_finished_loading : 1;
123 struct stat_info_entry {
124 WIN32_FILE_ATTRIBUTE_DATA wfad;
130 static const GtkFileInfoType STAT_NEEDED_MASK = (GTK_FILE_INFO_IS_FOLDER |
131 GTK_FILE_INFO_MODIFICATION_TIME |
135 static void gtk_file_system_win32_iface_init (GtkFileSystemIface *iface);
136 static void gtk_file_system_win32_dispose (GObject *object);
137 static void gtk_file_system_win32_finalize (GObject *object);
139 static GSList * gtk_file_system_win32_list_volumes (GtkFileSystem *file_system);
140 static GtkFileSystemVolume *gtk_file_system_win32_get_volume_for_path (GtkFileSystem *file_system,
141 const GtkFilePath *path);
143 static GtkFileSystemHandle *gtk_file_system_win32_get_folder (GtkFileSystem *file_system,
144 const GtkFilePath *path,
145 GtkFileInfoType types,
146 GtkFileSystemGetFolderCallback callback,
148 static GtkFileSystemHandle *gtk_file_system_win32_get_info (GtkFileSystem *file_system,
149 const GtkFilePath *path,
150 GtkFileInfoType types,
151 GtkFileSystemGetInfoCallback callback,
153 static GtkFileSystemHandle *gtk_file_system_win32_create_folder (GtkFileSystem *file_system,
154 const GtkFilePath *path,
155 GtkFileSystemCreateFolderCallback callback,
157 static void gtk_file_system_win32_cancel_operation (GtkFileSystemHandle *handle);
159 static void gtk_file_system_win32_volume_free (GtkFileSystem *file_system,
160 GtkFileSystemVolume *volume);
161 static GtkFilePath *gtk_file_system_win32_volume_get_base_path (GtkFileSystem *file_system,
162 GtkFileSystemVolume *volume);
163 static gboolean gtk_file_system_win32_volume_get_is_mounted (GtkFileSystem *file_system,
164 GtkFileSystemVolume *volume);
165 static GtkFileSystemHandle *gtk_file_system_win32_volume_mount (GtkFileSystem *file_system,
166 GtkFileSystemVolume *volume,
167 GtkFileSystemVolumeMountCallback callback,
169 static gchar * gtk_file_system_win32_volume_get_display_name (GtkFileSystem *file_system,
170 GtkFileSystemVolume *volume);
171 static gchar * gtk_file_system_win32_volume_get_icon_name (GtkFileSystem *file_system,
172 GtkFileSystemVolume *volume,
175 static gboolean gtk_file_system_win32_get_parent (GtkFileSystem *file_system,
176 const GtkFilePath *path,
177 GtkFilePath **parent,
179 static GtkFilePath * gtk_file_system_win32_make_path (GtkFileSystem *file_system,
180 const GtkFilePath *base_path,
181 const gchar *display_name,
183 static gboolean gtk_file_system_win32_parse (GtkFileSystem *file_system,
184 const GtkFilePath *base_path,
186 GtkFilePath **folder,
190 static gchar * gtk_file_system_win32_path_to_uri (GtkFileSystem *file_system,
191 const GtkFilePath *path);
192 static gchar * gtk_file_system_win32_path_to_filename (GtkFileSystem *file_system,
193 const GtkFilePath *path);
194 static GtkFilePath *gtk_file_system_win32_uri_to_path (GtkFileSystem *file_system,
196 static GtkFilePath *gtk_file_system_win32_filename_to_path (GtkFileSystem *file_system,
197 const gchar *filename);
200 static gboolean gtk_file_system_win32_insert_bookmark (GtkFileSystem *file_system,
201 const GtkFilePath *path,
204 static gboolean gtk_file_system_win32_remove_bookmark (GtkFileSystem *file_system,
205 const GtkFilePath *path,
207 static GSList * gtk_file_system_win32_list_bookmarks (GtkFileSystem *file_system);
208 static gchar * gtk_file_system_win32_get_bookmark_label (GtkFileSystem *file_system,
209 const GtkFilePath *path);
210 static void gtk_file_system_win32_set_bookmark_label (GtkFileSystem *file_system,
211 const GtkFilePath *path,
214 static void gtk_file_folder_win32_iface_init (GtkFileFolderIface *iface);
215 static void gtk_file_folder_win32_finalize (GObject *object);
217 static GtkFileInfo *gtk_file_folder_win32_get_info (GtkFileFolder *folder,
218 const GtkFilePath *path,
220 static gboolean gtk_file_folder_win32_list_children (GtkFileFolder *folder,
224 static gboolean gtk_file_folder_win32_is_finished_loading (GtkFileFolder *folder);
226 static GtkFilePath *filename_to_path (const gchar *filename);
228 static gboolean filename_is_root (const char *filename);
230 static gboolean filename_is_drive_root (const char *filename);
231 static gboolean filename_is_some_root (const char *filename);
233 static gboolean stat_with_error (const char *filename,
234 WIN32_FILE_ATTRIBUTE_DATA *wfad,
236 static GtkFileInfo *create_file_info (GtkFileFolderWin32 *folder_win32,
237 const char *filename,
238 GtkFileInfoType types,
239 WIN32_FILE_ATTRIBUTE_DATA *wfad,
240 const char *mime_type);
242 static gboolean execute_callbacks_idle (gpointer data);
244 static gboolean fill_in_names (GtkFileFolderWin32 *folder_win32,
246 static void fill_in_stats (GtkFileFolderWin32 *folder_win32);
247 static void fill_in_mime_type (GtkFileFolderWin32 *folder_win32);
249 static gboolean cb_fill_in_stats (gpointer key,
252 static gboolean cb_fill_in_mime_type (gpointer key,
256 /* some info kept together for volumes */
257 struct _GtkFileSystemVolume
266 G_DEFINE_TYPE_WITH_CODE (GtkFileSystemWin32, gtk_file_system_win32, G_TYPE_OBJECT,
267 G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_SYSTEM,
268 gtk_file_system_win32_iface_init))
273 G_DEFINE_TYPE_WITH_CODE (GtkFileFolderWin32, _gtk_file_folder_win32, G_TYPE_OBJECT,
274 G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_FOLDER,
275 gtk_file_folder_win32_iface_init))
278 * gtk_file_system_win32_new:
280 * Creates a new #GtkFileSystemWin32 object. #GtkFileSystemWin32
281 * implements the #GtkFileSystem interface with direct access to
282 * the filesystem using Windows API calls
284 * Return value: the new #GtkFileSystemWin32 object
287 gtk_file_system_win32_new (void)
289 return g_object_new (GTK_TYPE_FILE_SYSTEM_WIN32, NULL);
293 gtk_file_system_win32_class_init (GtkFileSystemWin32Class *class)
295 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
297 gobject_class->dispose = gtk_file_system_win32_dispose;
298 gobject_class->finalize = gtk_file_system_win32_finalize;
302 gtk_file_system_win32_iface_init (GtkFileSystemIface *iface)
304 iface->list_volumes = gtk_file_system_win32_list_volumes;
305 iface->get_volume_for_path = gtk_file_system_win32_get_volume_for_path;
306 iface->get_folder = gtk_file_system_win32_get_folder;
307 iface->get_info = gtk_file_system_win32_get_info;
308 iface->create_folder = gtk_file_system_win32_create_folder;
309 iface->cancel_operation = gtk_file_system_win32_cancel_operation;
310 iface->volume_free = gtk_file_system_win32_volume_free;
311 iface->volume_get_base_path = gtk_file_system_win32_volume_get_base_path;
312 iface->volume_get_is_mounted = gtk_file_system_win32_volume_get_is_mounted;
313 iface->volume_mount = gtk_file_system_win32_volume_mount;
314 iface->volume_get_display_name = gtk_file_system_win32_volume_get_display_name;
315 iface->volume_get_icon_name = gtk_file_system_win32_volume_get_icon_name;
316 iface->get_parent = gtk_file_system_win32_get_parent;
317 iface->make_path = gtk_file_system_win32_make_path;
318 iface->parse = gtk_file_system_win32_parse;
319 iface->path_to_uri = gtk_file_system_win32_path_to_uri;
320 iface->path_to_filename = gtk_file_system_win32_path_to_filename;
321 iface->uri_to_path = gtk_file_system_win32_uri_to_path;
322 iface->filename_to_path = gtk_file_system_win32_filename_to_path;
323 iface->insert_bookmark = gtk_file_system_win32_insert_bookmark;
324 iface->remove_bookmark = gtk_file_system_win32_remove_bookmark;
325 iface->list_bookmarks = gtk_file_system_win32_list_bookmarks;
326 iface->get_bookmark_label = gtk_file_system_win32_get_bookmark_label;
327 iface->set_bookmark_label = gtk_file_system_win32_set_bookmark_label;
331 check_volumes (gpointer data)
333 GtkFileSystemWin32 *system_win32 = GTK_FILE_SYSTEM_WIN32 (data);
335 g_return_val_if_fail (system_win32, FALSE);
337 if (system_win32->drives != GetLogicalDrives())
338 g_signal_emit_by_name (system_win32, "volumes-changed", 0);
344 casefolded_hash (gconstpointer v)
346 const gchar *p = (const gchar *) v;
351 h = (h << 5) - h + g_unichar_toupper (g_utf8_get_char (p));
352 p = g_utf8_next_char (p);
358 static gboolean casefolded_equal (gconstpointer v1,
361 return (_gtk_file_system_win32_path_compare ((const gchar *) v1, (const gchar *) v2) == 0);
365 gtk_file_system_win32_init (GtkFileSystemWin32 *system_win32)
367 system_win32->folder_hash = g_hash_table_new (casefolded_hash, casefolded_equal);
369 /* Set up an idle handler for volume changes. Once a second should
372 system_win32->timeout = g_timeout_add_full (0, 1000, check_volumes, system_win32, NULL);
374 system_win32->handles = g_hash_table_new (g_direct_hash, g_direct_equal);
376 system_win32->execute_callbacks_idle_id = 0;
377 system_win32->callbacks = NULL;
381 check_handle_fn (gpointer key, gpointer value, gpointer data)
383 GtkFileSystemHandle *handle;
384 int *num_live_handles;
387 num_live_handles = data;
389 (*num_live_handles)++;
391 g_warning ("file_system_win32=%p still has handle=%p at finalization which is %s!",
394 handle->cancelled ? "CANCELLED" : "NOT CANCELLED");
398 check_handles_at_finalization (GtkFileSystemWin32 *system_win32)
400 int num_live_handles;
402 num_live_handles = 0;
404 g_hash_table_foreach (system_win32->handles, check_handle_fn, &num_live_handles);
405 #ifdef HANDLE_ME_HARDER
406 g_assert (num_live_handles == 0);
409 g_hash_table_destroy (system_win32->handles);
410 system_win32->handles = NULL;
413 #define GTK_TYPE_FILE_SYSTEM_HANDLE_WIN32 (_gtk_file_system_handle_win32_get_type ())
415 typedef struct _GtkFileSystemHandle GtkFileSystemHandleWin32;
416 typedef struct _GtkFileSystemHandleClass GtkFileSystemHandleWin32Class;
418 G_DEFINE_TYPE (GtkFileSystemHandleWin32, _gtk_file_system_handle_win32, GTK_TYPE_FILE_SYSTEM_HANDLE)
421 _gtk_file_system_handle_win32_init (GtkFileSystemHandleWin32 *handle)
426 _gtk_file_system_handle_win32_finalize (GObject *object)
428 GtkFileSystemHandleWin32 *handle;
429 GtkFileSystemWin32 *system_win32;
431 handle = (GtkFileSystemHandleWin32 *)object;
433 system_win32 = GTK_FILE_SYSTEM_WIN32 (GTK_FILE_SYSTEM_HANDLE (handle)->file_system);
435 g_assert (g_hash_table_lookup (system_win32->handles, handle) != NULL);
436 g_hash_table_remove (system_win32->handles, handle);
438 if (G_OBJECT_CLASS (_gtk_file_system_handle_win32_parent_class)->finalize)
439 G_OBJECT_CLASS (_gtk_file_system_handle_win32_parent_class)->finalize (object);
443 _gtk_file_system_handle_win32_class_init (GtkFileSystemHandleWin32Class *class)
445 GObjectClass *gobject_class = (GObjectClass *) class;
447 gobject_class->finalize = _gtk_file_system_handle_win32_finalize;
451 gtk_file_system_win32_dispose (GObject *object)
453 GtkFileSystemWin32 *system_win32;
455 system_win32 = GTK_FILE_SYSTEM_WIN32 (object);
457 if (system_win32->execute_callbacks_idle_id)
459 g_source_remove (system_win32->execute_callbacks_idle_id);
460 system_win32->execute_callbacks_idle_id = 0;
462 /* call pending callbacks */
463 execute_callbacks_idle (system_win32);
466 G_OBJECT_CLASS (gtk_file_system_win32_parent_class)->dispose (object);
470 gtk_file_system_win32_finalize (GObject *object)
472 GtkFileSystemWin32 *system_win32;
474 system_win32 = GTK_FILE_SYSTEM_WIN32 (object);
476 g_source_remove (system_win32->timeout);
478 check_handles_at_finalization (system_win32);
480 /* FIXME: assert that the hash is empty? */
481 g_hash_table_destroy (system_win32->folder_hash);
483 G_OBJECT_CLASS (gtk_file_system_win32_parent_class)->finalize (object);
486 /* Lifted from GLib */
489 get_special_folder (int csidl)
493 wchar_t wc[MAX_PATH+1];
496 LPITEMIDLIST pidl = NULL;
498 gchar *retval = NULL;
500 hr = SHGetSpecialFolderLocation (NULL, csidl, &pidl);
503 b = SHGetPathFromIDListW (pidl, path.wc);
505 retval = g_utf16_to_utf8 (path.wc, -1, NULL, NULL, NULL);
506 CoTaskMemFree (pidl);
512 _gtk_file_system_win32_get_desktop (void)
514 return get_special_folder (CSIDL_DESKTOPDIRECTORY);
518 gtk_file_system_win32_list_volumes (GtkFileSystem *file_system)
521 gchar drive[4] = "A:\\";
523 GtkFileSystemWin32 *system_win32 = (GtkFileSystemWin32 *)file_system;
525 drives = GetLogicalDrives();
527 system_win32->drives = drives;
529 g_warning ("GetLogicalDrives failed.");
531 while (drives && drive[0] <= 'Z')
535 GtkFileSystemVolume *vol = g_new0 (GtkFileSystemVolume, 1);
536 vol->drive = g_strdup (drive);
537 vol->drive_type = GetDriveType (drive);
538 list = g_slist_append (list, vol);
546 static GtkFileSystemVolume *
547 gtk_file_system_win32_get_volume_for_path (GtkFileSystem *file_system,
548 const GtkFilePath *path)
550 GtkFileSystemVolume *vol = g_new0 (GtkFileSystemVolume, 1);
553 g_return_val_if_fail (path != NULL, NULL);
555 p = gtk_file_path_get_string (path);
557 if (!g_path_is_absolute (p))
559 if (g_ascii_isalpha (p[0]) && p[1] == ':')
560 vol->drive = g_strdup_printf ("%c:\\", p[0]);
562 vol->drive = g_strdup ("\\");
563 vol->drive_type = GetDriveType (vol->drive);
567 const gchar *q = g_path_skip_root (p);
568 vol->drive = g_strndup (p, q - p);
569 if (!G_IS_DIR_SEPARATOR (q[-1]))
571 /* Make sure "drive" always ends in a slash */
572 gchar *tem = vol->drive;
573 vol->drive = g_strconcat (vol->drive, "\\", NULL);
577 if (filename_is_drive_root (vol->drive))
579 vol->drive[0] = g_ascii_toupper (vol->drive[0]);
580 vol->drive_type = GetDriveType (vol->drive);
584 wchar_t *wdrive = g_utf8_to_utf16 (vol->drive, -1, NULL, NULL, NULL);
585 vol->drive_type = GetDriveTypeW (wdrive);
593 remove_trailing_slash (const char *filename)
597 len = strlen (filename);
599 if (g_path_is_absolute (filename))
600 root_len = g_path_skip_root (filename) - filename;
603 if (len > root_len && G_IS_DIR_SEPARATOR (filename[len - 1]))
604 return g_strndup (filename, len - 1);
606 return g_memdup (filename, len + 1);
609 /* Delay callback dispatching
616 CALLBACK_CREATE_FOLDER,
617 CALLBACK_VOLUME_MOUNT
620 static void queue_callback (GtkFileSystemWin32 *system_win32, enum callback_types type, gpointer data);
622 struct get_info_callback
624 GtkFileSystemGetInfoCallback callback;
625 GtkFileSystemHandle *handle;
626 GtkFileInfo *file_info;
632 dispatch_get_info_callback (struct get_info_callback *info)
634 (* info->callback) (info->handle, info->file_info, info->error, info->data);
637 gtk_file_info_free (info->file_info);
640 g_error_free (info->error);
642 g_object_unref (info->handle);
648 queue_get_info_callback (GtkFileSystemGetInfoCallback callback,
649 GtkFileSystemHandle *handle,
650 GtkFileInfo *file_info,
654 struct get_info_callback *info;
656 info = g_new (struct get_info_callback, 1);
657 info->callback = callback;
658 info->handle = handle;
659 info->file_info = file_info;
663 queue_callback (GTK_FILE_SYSTEM_WIN32 (handle->file_system), CALLBACK_GET_INFO, info);
667 struct get_folder_callback
669 GtkFileSystemGetFolderCallback callback;
670 GtkFileSystemHandle *handle;
671 GtkFileFolder *folder;
677 dispatch_get_folder_callback (struct get_folder_callback *info)
679 (* info->callback) (info->handle, info->folder, info->error, info->data);
682 g_error_free (info->error);
684 g_object_unref (info->handle);
690 queue_get_folder_callback (GtkFileSystemGetFolderCallback callback,
691 GtkFileSystemHandle *handle,
692 GtkFileFolder *folder,
696 struct get_folder_callback *info;
698 info = g_new (struct get_folder_callback, 1);
699 info->callback = callback;
700 info->handle = handle;
701 info->folder = folder;
705 queue_callback (GTK_FILE_SYSTEM_WIN32 (handle->file_system), CALLBACK_GET_FOLDER, info);
709 struct create_folder_callback
711 GtkFileSystemCreateFolderCallback callback;
712 GtkFileSystemHandle *handle;
719 dispatch_create_folder_callback (struct create_folder_callback *info)
721 (* info->callback) (info->handle, info->path, info->error, info->data);
724 g_error_free (info->error);
727 gtk_file_path_free (info->path);
729 g_object_unref (info->handle);
735 queue_create_folder_callback (GtkFileSystemCreateFolderCallback callback,
736 GtkFileSystemHandle *handle,
737 const GtkFilePath *path,
741 struct create_folder_callback *info;
743 info = g_new (struct create_folder_callback, 1);
744 info->callback = callback;
745 info->handle = handle;
746 info->path = gtk_file_path_copy (path);
750 queue_callback (GTK_FILE_SYSTEM_WIN32 (handle->file_system), CALLBACK_CREATE_FOLDER, info);
754 struct volume_mount_callback
756 GtkFileSystemVolumeMountCallback callback;
757 GtkFileSystemHandle *handle;
758 GtkFileSystemVolume *volume;
764 dispatch_volume_mount_callback (struct volume_mount_callback *info)
766 (* info->callback) (info->handle, info->volume, info->error, info->data);
769 g_error_free (info->error);
771 g_object_unref (info->handle);
777 queue_volume_mount_callback (GtkFileSystemVolumeMountCallback callback,
778 GtkFileSystemHandle *handle,
779 GtkFileSystemVolume *volume,
783 struct volume_mount_callback *info;
785 info = g_new (struct volume_mount_callback, 1);
786 info->callback = callback;
787 info->handle = handle;
788 info->volume = volume;
792 queue_callback (GTK_FILE_SYSTEM_WIN32 (handle->file_system), CALLBACK_VOLUME_MOUNT, info);
798 enum callback_types type;
802 struct get_info_callback *get_info;
803 struct get_folder_callback *get_folder;
804 struct create_folder_callback *create_folder;
805 struct volume_mount_callback *volume_mount;
812 execute_callbacks_idle (gpointer data)
815 gboolean unref_file_system = TRUE;
816 GtkFileSystemWin32 *system_win32 = GTK_FILE_SYSTEM_WIN32 (data);
818 GDK_THREADS_ENTER ();
820 if (!system_win32->execute_callbacks_idle_id)
821 unref_file_system = FALSE;
823 g_object_ref (system_win32);
825 for (l = system_win32->callbacks; l; l = l->next)
827 struct callback_info *info = l->data;
831 case CALLBACK_GET_INFO:
832 dispatch_get_info_callback (info->info.get_info);
835 case CALLBACK_GET_FOLDER:
836 dispatch_get_folder_callback (info->info.get_folder);
839 case CALLBACK_CREATE_FOLDER:
840 dispatch_create_folder_callback (info->info.create_folder);
843 case CALLBACK_VOLUME_MOUNT:
844 dispatch_volume_mount_callback (info->info.volume_mount);
851 g_slist_free (system_win32->callbacks);
852 system_win32->callbacks = NULL;
854 if (unref_file_system)
855 g_object_unref (system_win32);
857 system_win32->execute_callbacks_idle_id = 0;
859 GDK_THREADS_LEAVE ();
865 queue_callback (GtkFileSystemWin32 *system_win32,
866 enum callback_types type,
869 struct callback_info *info;
871 info = g_new (struct callback_info, 1);
876 case CALLBACK_GET_INFO:
877 info->info.get_info = data;
880 case CALLBACK_GET_FOLDER:
881 info->info.get_folder = data;
884 case CALLBACK_CREATE_FOLDER:
885 info->info.create_folder = data;
888 case CALLBACK_VOLUME_MOUNT:
889 info->info.volume_mount = data;
893 system_win32->callbacks = g_slist_append (system_win32->callbacks, info);
895 if (!system_win32->execute_callbacks_idle_id)
896 system_win32->execute_callbacks_idle_id = g_idle_add (execute_callbacks_idle, system_win32);
899 static GtkFileSystemHandle *
900 create_handle (GtkFileSystem *file_system)
902 GtkFileSystemWin32 *system_win32;
903 GtkFileSystemHandle *handle;
905 system_win32 = GTK_FILE_SYSTEM_WIN32 (file_system);
907 handle = g_object_new (GTK_TYPE_FILE_SYSTEM_HANDLE_WIN32, NULL);
908 handle->file_system = file_system;
910 g_assert (g_hash_table_lookup (system_win32->handles, handle) == NULL);
911 g_hash_table_insert (system_win32->handles, handle, handle);
917 get_mime_type_for_file (const char *filename,
918 const WIN32_FILE_ATTRIBUTE_DATA *wfad)
920 const char *extension;
922 DWORD type, nbytes = 0;
925 if (wfad->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
926 return g_strdup ("x-directory/normal");
928 extension = strrchr (filename, '.');
930 if (extension != NULL &&
931 (stricmp (extension, ".exe") == 0 ||
932 stricmp (extension, ".com") == 0))
933 return g_strdup ("application/x-executable");
935 if (extension != NULL &&
936 extension[1] != '\0' &&
937 RegOpenKeyEx (HKEY_CLASSES_ROOT, extension, 0,
938 KEY_QUERY_VALUE, &key) == ERROR_SUCCESS &&
939 RegQueryValueEx (key, "Content Type", 0,
940 &type, NULL, &nbytes) == ERROR_SUCCESS &&
942 (value = g_try_malloc (nbytes + 1)) &&
943 RegQueryValueEx (key, "Content Type", 0,
944 &type, value, &nbytes) == ERROR_SUCCESS)
946 value[nbytes] = '\0';
949 value = g_strdup ("application/octet-stream");
956 static GtkFileSystemHandle *
957 gtk_file_system_win32_get_info (GtkFileSystem *file_system,
958 const GtkFilePath *path,
959 GtkFileInfoType types,
960 GtkFileSystemGetInfoCallback callback,
963 GError *error = NULL;
964 GtkFileSystemWin32 *system_win32;
965 GtkFileSystemHandle *handle;
966 const char *filename;
968 WIN32_FILE_ATTRIBUTE_DATA wfad;
969 const char *mime_type;
971 system_win32 = GTK_FILE_SYSTEM_WIN32 (file_system);
972 handle = create_handle (file_system);
974 filename = gtk_file_path_get_string (path);
975 g_return_val_if_fail (filename != NULL, FALSE);
976 g_return_val_if_fail (g_path_is_absolute (filename), FALSE);
978 if (!stat_with_error (filename, &wfad, &error))
980 g_object_ref (handle);
981 queue_get_info_callback (callback, handle, NULL, error, data);
985 if ((types & GTK_FILE_INFO_MIME_TYPE) != 0)
986 mime_type = get_mime_type_for_file (filename, &wfad);
990 info = create_file_info (NULL, filename, types, &wfad, mime_type);
991 g_object_ref (handle);
992 queue_get_info_callback (callback, handle, info, NULL, data);
998 load_folder (gpointer data)
1000 GtkFileFolderWin32 *folder_win32 = data;
1003 GDK_THREADS_ENTER ();
1005 if ((folder_win32->types & STAT_NEEDED_MASK) != 0)
1006 fill_in_stats (folder_win32);
1008 if ((folder_win32->types & GTK_FILE_INFO_MIME_TYPE) != 0)
1009 fill_in_mime_type (folder_win32);
1011 if (gtk_file_folder_win32_list_children (GTK_FILE_FOLDER (folder_win32), &children, NULL))
1013 folder_win32->is_finished_loading = TRUE;
1014 g_signal_emit_by_name (folder_win32, "files-added", children);
1015 gtk_file_paths_free (children);
1018 folder_win32->load_folder_id = 0;
1020 g_signal_emit_by_name (folder_win32, "finished-loading", 0);
1022 GDK_THREADS_LEAVE ();
1027 static GtkFileSystemHandle *
1028 gtk_file_system_win32_get_folder (GtkFileSystem *file_system,
1029 const GtkFilePath *path,
1030 GtkFileInfoType types,
1031 GtkFileSystemGetFolderCallback callback,
1034 GError *error = NULL;
1035 GtkFileSystemWin32 *system_win32;
1036 GtkFileFolderWin32 *folder_win32;
1037 GtkFileSystemHandle *handle;
1038 const gchar *filename;
1039 char *filename_copy;
1040 gboolean set_asof = FALSE;
1042 system_win32 = GTK_FILE_SYSTEM_WIN32 (file_system);
1044 filename = gtk_file_path_get_string (path);
1045 g_return_val_if_fail (filename != NULL, NULL);
1046 g_return_val_if_fail (g_path_is_absolute (filename), NULL);
1048 handle = create_handle (file_system);
1050 filename_copy = remove_trailing_slash (filename);
1051 folder_win32 = g_hash_table_lookup (system_win32->folder_hash, filename_copy);
1055 g_free (filename_copy);
1056 if (folder_win32->stat_info &&
1057 time (NULL) - folder_win32->asof >= FOLDER_CACHE_LIFETIME)
1060 g_print ("Cleaning out cached directory %s\n", filename);
1062 g_hash_table_destroy (folder_win32->stat_info);
1063 folder_win32->stat_info = NULL;
1064 folder_win32->have_mime_type = FALSE;
1065 folder_win32->have_stat = FALSE;
1069 g_object_ref (folder_win32);
1070 folder_win32->types |= types;
1071 types = folder_win32->types;
1075 WIN32_FILE_ATTRIBUTE_DATA wfad;
1077 if (!stat_with_error (filename, &wfad, &error))
1079 g_object_ref (handle);
1080 queue_get_folder_callback (callback, handle, NULL, error, data);
1082 g_free (filename_copy);
1086 if (!wfad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1088 gchar *display_name = g_filename_display_name (filename);
1090 g_set_error (&error,
1091 GTK_FILE_SYSTEM_ERROR,
1092 GTK_FILE_SYSTEM_ERROR_NOT_FOLDER,
1093 _("Path is not a folder: '%s'"),
1096 g_object_ref (handle);
1097 queue_get_folder_callback (callback, handle, NULL, error, data);
1099 g_free (display_name);
1100 g_free (filename_copy);
1104 folder_win32 = g_object_new (GTK_TYPE_FILE_FOLDER_WIN32, NULL);
1105 folder_win32->system_win32 = system_win32;
1106 folder_win32->filename = filename_copy;
1107 folder_win32->types = types;
1108 folder_win32->stat_info = NULL;
1109 folder_win32->load_folder_id = 0;
1110 folder_win32->have_mime_type = FALSE;
1111 folder_win32->have_stat = FALSE;
1112 folder_win32->is_finished_loading = FALSE;
1115 /* Browsing for shares not yet implemented */
1116 folder_win32->is_network_dir = FALSE;
1118 g_hash_table_insert (system_win32->folder_hash,
1119 folder_win32->filename,
1124 folder_win32->asof = time (NULL);
1126 g_object_ref (handle);
1127 queue_get_folder_callback (callback, handle, GTK_FILE_FOLDER (folder_win32), NULL, data);
1129 /* Start loading the folder contents in an idle */
1130 if (!folder_win32->load_folder_id)
1131 folder_win32->load_folder_id =
1132 g_idle_add ((GSourceFunc) load_folder, folder_win32);
1137 static GtkFileSystemHandle *
1138 gtk_file_system_win32_create_folder (GtkFileSystem *file_system,
1139 const GtkFilePath *path,
1140 GtkFileSystemCreateFolderCallback callback,
1143 GError *error = NULL;
1144 GtkFileSystemWin32 *system_win32;
1145 GtkFileSystemHandle *handle;
1146 const char *filename;
1151 system_win32 = GTK_FILE_SYSTEM_WIN32 (file_system);
1153 filename = gtk_file_path_get_string (path);
1154 g_return_val_if_fail (filename != NULL, FALSE);
1155 g_return_val_if_fail (g_path_is_absolute (filename), NULL);
1157 handle = create_handle (file_system);
1159 tmp = remove_trailing_slash (filename);
1160 result = g_mkdir (tmp, 0777) == 0;
1166 gchar *display_filename = g_filename_display_name (filename);
1167 g_set_error (&error,
1168 GTK_FILE_SYSTEM_ERROR,
1169 GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
1170 _("Error creating directory '%s': %s"),
1172 g_strerror (save_errno));
1174 g_object_ref (handle);
1175 queue_create_folder_callback (callback, handle, path, error, data);
1177 g_free (display_filename);
1181 if (!filename_is_some_root (filename))
1183 parent = g_path_get_dirname (filename);
1186 GtkFileFolderWin32 *folder_win32;
1188 folder_win32 = g_hash_table_lookup (system_win32->folder_hash, parent);
1193 struct stat_info_entry *entry;
1195 /* Make sure the new folder exists in the parent's folder */
1196 entry = g_new0 (struct stat_info_entry, 1);
1197 if (folder_win32->is_network_dir)
1199 entry->wfad.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
1200 entry->mime_type = g_strdup ("x-directory/normal");
1203 basename = g_path_get_basename (filename);
1204 g_hash_table_insert (folder_win32->stat_info,
1208 if (folder_win32->have_stat)
1211 if ((folder_win32->types & STAT_NEEDED_MASK) != 0)
1212 cb_fill_in_stats (basename, entry, folder_win32);
1214 if ((folder_win32->types & GTK_FILE_INFO_MIME_TYPE) != 0)
1215 cb_fill_in_mime_type (basename, entry, folder_win32);
1218 paths = g_slist_append (NULL, (GtkFilePath *) path);
1219 g_signal_emit_by_name (folder_win32, "files-added", paths);
1220 g_slist_free (paths);
1231 gtk_file_system_win32_cancel_operation (GtkFileSystemHandle *handle)
1233 /* We don't set "cancelled" to TRUE here, since the actual operation
1234 * is executed in the function itself and not in a callback. So
1235 * the operations can never be cancelled (since they will be already
1236 * completed at this point.
1241 gtk_file_system_win32_volume_free (GtkFileSystem *file_system,
1242 GtkFileSystemVolume *volume)
1246 g_free (volume->drive);
1247 path = (GtkFilePath *) volume;
1248 gtk_file_path_free (path);
1251 static GtkFilePath *
1252 gtk_file_system_win32_volume_get_base_path (GtkFileSystem *file_system,
1253 GtkFileSystemVolume *volume)
1255 return (GtkFilePath *) g_strdup (volume->drive);
1259 gtk_file_system_win32_volume_get_is_mounted (GtkFileSystem *file_system,
1260 GtkFileSystemVolume *volume)
1265 static GtkFileSystemHandle *
1266 gtk_file_system_win32_volume_mount (GtkFileSystem *file_system,
1267 GtkFileSystemVolume *volume,
1268 GtkFileSystemVolumeMountCallback callback,
1271 GError *error = NULL;
1272 GtkFileSystemHandle *handle = create_handle (file_system);
1274 g_set_error (&error,
1275 GTK_FILE_SYSTEM_ERROR,
1276 GTK_FILE_SYSTEM_ERROR_FAILED,
1277 _("This file system does not support mounting"));
1279 g_object_ref (handle);
1280 queue_volume_mount_callback (callback, handle, volume, error, data);
1286 gtk_file_system_win32_volume_get_display_name (GtkFileSystem *file_system,
1287 GtkFileSystemVolume *volume)
1289 gchar *real_display_name;
1291 g_return_val_if_fail (volume->drive != NULL, NULL);
1293 if (filename_is_drive_root (volume->drive) &&
1294 volume->drive_type == DRIVE_REMOTE)
1295 real_display_name = g_strdup_printf (_("Network Drive (%s)"), volume->drive);
1296 else if ((filename_is_drive_root (volume->drive) && volume->drive[0] >= 'C') ||
1297 volume->drive_type != DRIVE_REMOVABLE)
1300 gunichar2 *wdrive = g_utf8_to_utf16 (volume->drive, -1, NULL, NULL, NULL);
1301 gunichar2 wname[80];
1303 if (GetVolumeInformationW (wdrive,
1304 wname, G_N_ELEMENTS(wname),
1305 NULL, /* serial number */
1306 NULL, /* max. component length */
1307 NULL, /* fs flags */
1308 NULL, 0) /* fs type like FAT, NTFS */ &&
1311 name = g_utf16_to_utf8 (wname, -1, NULL, NULL, NULL);
1317 real_display_name = g_strdup_printf (_("%s (%s)"), name, volume->drive);
1322 real_display_name = g_strdup (volume->drive);
1326 real_display_name = g_strdup (volume->drive);
1328 return real_display_name;
1332 get_icon_type_from_stat (WIN32_FILE_ATTRIBUTE_DATA *wfad)
1334 if (wfad->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1335 return ICON_DIRECTORY;
1337 return ICON_REGULAR;
1340 /* Renders a fallback icon from the stock system */
1341 static const gchar *
1342 get_fallback_icon_name (IconType icon_type)
1344 const char *stock_name;
1348 case ICON_DIRECTORY:
1349 stock_name = GTK_STOCK_DIRECTORY;
1352 case ICON_EXECUTABLE:
1353 stock_name = GTK_STOCK_EXECUTE;
1357 stock_name = GTK_STOCK_FILE;
1365 gtk_file_system_win32_volume_get_icon_name (GtkFileSystem *file_system,
1366 GtkFileSystemVolume *volume,
1369 /* FIXME: maybe we just always want to return GTK_STOCK_HARDDISK here?
1370 * or the new tango icon name?
1372 return g_strdup ("gnome-dev-harddisk");
1375 #if 0 /* Unused, see below */
1378 get_parent_dir (const char *filename)
1382 len = strlen (filename);
1384 /* Ignore trailing slashes */
1385 if (len > 1 && G_IS_DIR_SEPARATOR (filename[len - 1]))
1389 tmp = g_strndup (filename, len - 1);
1391 parent = g_path_get_dirname (tmp);
1397 return g_path_get_dirname (filename);
1403 gtk_file_system_win32_get_parent (GtkFileSystem *file_system,
1404 const GtkFilePath *path,
1405 GtkFilePath **parent,
1408 const char *filename;
1410 filename = gtk_file_path_get_string (path);
1411 g_return_val_if_fail (filename != NULL, FALSE);
1412 g_return_val_if_fail (g_path_is_absolute (filename), FALSE);
1414 if (filename_is_some_root (filename))
1420 gchar *parent_filename = g_path_get_dirname (filename);
1421 *parent = filename_to_path (parent_filename);
1422 g_free (parent_filename);
1424 #if DEBUGGING_OUTPUT
1425 printf ("%s: %s => %s\n", __FUNCTION__, (char*)path, (*parent)?(char*)*parent:"NULL"), fflush(stdout);
1430 static GtkFilePath *
1431 gtk_file_system_win32_make_path (GtkFileSystem *file_system,
1432 const GtkFilePath *base_path,
1433 const gchar *display_name,
1436 const char *base_filename;
1437 gchar *full_filename;
1438 GtkFilePath *result;
1441 base_filename = gtk_file_path_get_string (base_path);
1442 g_return_val_if_fail (base_filename != NULL, NULL);
1443 g_return_val_if_fail (g_path_is_absolute (base_filename), NULL);
1445 if ((p = strpbrk (display_name, "<>\"/\\|")))
1449 badchar[0] = *p; /* We know it is a single-byte char */
1452 GTK_FILE_SYSTEM_ERROR,
1453 GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
1454 _("The name \"%s\" is not valid because it contains the character \"%s\". "
1455 "Please use a different name."),
1461 full_filename = g_build_filename (base_filename, display_name, NULL);
1462 result = filename_to_path (full_filename);
1463 g_free (full_filename);
1468 /* If this was a publically exported function, it should return
1469 * a dup'ed result, but we make it modify-in-place for efficiency
1470 * here, and because it works for us.
1473 canonicalize_filename (gchar *filename)
1477 gboolean last_was_slash = FALSE;
1479 #if DEBUGGING_OUTPUT
1480 printf("%s: %s ", __FUNCTION__, filename), fflush (stdout);
1483 past_root = (gchar *) g_path_skip_root (filename);
1489 if (G_IS_DIR_SEPARATOR (*p))
1491 if (!last_was_slash)
1494 last_was_slash = TRUE;
1498 if (last_was_slash && *p == '.')
1500 if (G_IS_DIR_SEPARATOR (*(p + 1)) ||
1503 if (*(p + 1) == '\0')
1508 else if (*(p + 1) == '.' &&
1509 (G_IS_DIR_SEPARATOR (*(p + 2)) ||
1515 while (q > past_root &&
1516 !G_IS_DIR_SEPARATOR (*(q - 1)))
1520 if (*(p + 2) == '\0')
1528 last_was_slash = FALSE;
1534 last_was_slash = FALSE;
1541 if (q > past_root && G_IS_DIR_SEPARATOR (*(q - 1)))
1546 #if DEBUGGING_OUTPUT
1547 printf(" => %s\n", filename), fflush (stdout);
1552 gtk_file_system_win32_parse (GtkFileSystem *file_system,
1553 const GtkFilePath *base_path,
1555 GtkFilePath **folder,
1559 const char *base_filename;
1560 gchar *last_backslash, *last_slash;
1561 gboolean result = FALSE;
1563 #if DEBUGGING_OUTPUT
1564 printf("%s: base_path=%s str=%s\n",__FUNCTION__,(char*)base_path,str),fflush(stdout);
1567 base_filename = gtk_file_path_get_string (base_path);
1568 g_return_val_if_fail (base_filename != NULL, FALSE);
1569 g_return_val_if_fail (g_path_is_absolute (base_filename), FALSE);
1571 last_backslash = strrchr (str, '\\');
1572 last_slash = strrchr (str, '/');
1573 if (last_slash == NULL ||
1574 (last_backslash != NULL && last_backslash > last_slash))
1575 last_slash = last_backslash;
1579 *folder = gtk_file_path_copy (base_path);
1580 *file_part = g_strdup (str);
1588 if (last_slash == str)
1590 if (g_ascii_isalpha (base_filename[0]) &&
1591 base_filename[1] == ':')
1592 folder_part = g_strdup_printf ("%c:\\", base_filename[0]);
1594 folder_part = g_strdup ("\\");
1596 else if (g_ascii_isalpha (str[0]) &&
1598 last_slash == str + 2)
1599 folder_part = g_strndup (str, last_slash - str + 1);
1601 /* Hmm, what the heck was this case supposed to do? It splits up
1602 * \\server\share\foo\bar into folder_part
1603 * \\server\share\foo\bar and file_path bar. Not good. As far as
1604 * I can see, this isn't needed.
1606 else if (G_IS_DIR_SEPARATOR (str[0]) &&
1607 G_IS_DIR_SEPARATOR (str[1]) &&
1608 (!str[2] || !G_IS_DIR_SEPARATOR (str[2])))
1609 folder_part = g_strdup (str);
1612 folder_part = g_strndup (str, last_slash - str);
1614 g_assert (folder_part);
1616 if (g_path_is_absolute (folder_part))
1617 folder_path = folder_part;
1620 folder_path = g_build_filename (base_filename, folder_part, NULL);
1621 g_free (folder_part);
1624 canonicalize_filename (folder_path);
1626 *folder = filename_to_path (folder_path);
1627 *file_part = g_strdup (last_slash + 1);
1629 g_free (folder_path);
1634 #if DEBUGGING_OUTPUT
1635 printf ("%s:returning folder=%s file_part=%s\n", __FUNCTION__, (*folder?(char*)*folder:"NULL"), *file_part), fflush(stdout);
1642 gtk_file_system_win32_path_to_uri (GtkFileSystem *file_system,
1643 const GtkFilePath *path)
1645 return g_filename_to_uri (gtk_file_path_get_string (path), NULL, NULL);
1649 gtk_file_system_win32_path_to_filename (GtkFileSystem *file_system,
1650 const GtkFilePath *path)
1652 return g_strdup (gtk_file_path_get_string (path));
1655 static GtkFilePath *
1656 gtk_file_system_win32_uri_to_path (GtkFileSystem *file_system,
1660 gchar *filename = g_filename_from_uri (uri, NULL, NULL);
1662 #if DEBUGGING_OUTPUT
1663 printf ("%s: %s -> %s\n", __FUNCTION__, uri, filename?filename:"NULL"), fflush (stdout);
1668 path = filename_to_path (filename);
1677 static GtkFilePath *
1678 gtk_file_system_win32_filename_to_path (GtkFileSystem *file_system,
1679 const gchar *filename)
1681 return filename_to_path (filename);
1686 /* These are unused currently, hmm */
1689 extract_icon (const char* filename)
1694 wchar_t filename_copy[MAX_PATH];
1697 wchar_t *wfn = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
1701 GdkPixbuf *pixbuf = NULL;
1704 if (!filename || !filename[0])
1708 /* ExtractAssociatedIconW() is about twice as slow as SHGetFileInfoW() */
1710 /* The ugly ExtractAssociatedIcon modifies filename in place. It
1711 * doesn't even take any argument saying how large the buffer is?
1712 * Let's hope MAX_PATH will be large enough. What dork designed that
1716 wfn = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
1717 if (wcslen (wfn) >= MAX_PATH)
1722 wcscpy (filename_copy, wfn);
1724 hicon = ExtractAssociatedIconW (GetModuleHandle (NULL), filename_copy, &iicon);
1728 g_warning (G_STRLOC ":ExtractAssociatedIcon(%s) failed: %s", filename, g_win32_error_message (GetLastError ()));
1732 rc = (int) SHGetFileInfoW (wfn, 0, &shfi, sizeof (shfi),
1733 SHGFI_ICON|SHGFI_LARGEICON);
1740 pixbuf = gdk_win32_icon_to_pixbuf_libgtk_only (hicon);
1742 if (!DestroyIcon (hicon))
1743 g_warning (G_STRLOC ": DestroyIcon failed: %s\n", g_win32_error_message (GetLastError ()));
1749 win32_pseudo_mime_lookup (const char* name)
1751 gboolean use_cache = TRUE;
1752 static GHashTable *mime_hash = NULL;
1753 GtkIconSet *is = NULL;
1754 char *p = strrchr(name, '.');
1755 char *extension = p ? g_utf8_casefold (p, -1) : g_strdup ("");
1758 /* Don't cache icons for files that might have embedded icons */
1759 if (strcmp (extension, ".lnk") == 0 ||
1760 strcmp (extension, ".exe") == 0 ||
1761 strcmp (extension, ".dll") == 0)
1769 mime_hash = g_hash_table_new (g_str_hash, g_str_equal);
1771 /* do we already have it ? */
1772 is = g_hash_table_lookup (mime_hash, extension);
1780 /* create icon and set */
1781 pixbuf = extract_icon (name);
1784 GtkIconSource* source = gtk_icon_source_new ();
1786 is = gtk_icon_set_new_from_pixbuf (pixbuf);
1787 gtk_icon_source_set_pixbuf (source, pixbuf);
1788 gtk_icon_set_add_source (is, source);
1790 gtk_icon_source_free (source);
1794 g_hash_table_insert (mime_hash, extension, is);
1801 /* Returns the name of the icon to be used for a path which is known to be a
1802 * directory. This can vary for Home, Desktop, etc.
1805 get_icon_name_for_directory (const char *path)
1807 static char *desktop_path = NULL;
1809 if (!g_get_home_dir ())
1810 return "gnome-fs-directory";
1813 desktop_path = g_build_filename (g_get_home_dir (), "Desktop", NULL);
1815 if (strcmp (g_get_home_dir (), path) == 0)
1816 return "gnome-fs-home";
1817 else if (strcmp (desktop_path, path) == 0)
1818 return "gnome-fs-desktop";
1820 return "gnome-fs-directory";
1825 /* Computes our internal icon type based on a path name; also returns the MIME
1826 * type in case we come up with ICON_REGULAR.
1829 get_icon_type_from_path (GtkFileFolderWin32 *folder_win32,
1830 WIN32_FILE_ATTRIBUTE_DATA *wfad,
1831 const char *filename,
1832 const char **mime_type)
1838 if (folder_win32 && folder_win32->have_stat)
1841 struct stat_info_entry *entry;
1843 g_assert (folder_win32->stat_info != NULL);
1845 basename = g_path_get_basename (filename);
1846 entry = g_hash_table_lookup (folder_win32->stat_info, basename);
1850 if (entry->icon_type == ICON_UNDECIDED)
1852 entry->icon_type = get_icon_type_from_stat (&entry->wfad);
1853 g_assert (entry->icon_type != ICON_UNDECIDED);
1855 icon_type = entry->icon_type;
1856 if (icon_type == ICON_REGULAR)
1858 fill_in_mime_type (folder_win32);
1859 *mime_type = entry->mime_type;
1866 icon_type = get_icon_type_from_stat (wfad);
1868 if (icon_type == ICON_REGULAR)
1869 *mime_type = get_mime_type_for_file (filename, wfad);
1874 /* Renders an icon for a non-ICON_REGULAR file */
1875 static const gchar *
1876 get_special_icon_name (IconType icon_type,
1877 const gchar *filename)
1881 g_assert (icon_type != ICON_REGULAR);
1885 case ICON_DIRECTORY:
1886 /* get_icon_name_for_directory() returns a dupped string */
1887 return get_icon_name_for_directory (filename);
1888 case ICON_EXECUTABLE:
1889 name ="gnome-fs-executable";
1892 g_assert_not_reached ();
1900 get_icon_name_for_mime_type (const char *mime_type)
1903 const char *separator;
1909 separator = strchr (mime_type, '/');
1911 return NULL; /* maybe we should return a GError with "invalid MIME-type" */
1913 /* FIXME: we default to the gnome icon naming for now. Some question
1914 * as below, how are we going to handle a second attempt?
1917 icon_name = g_string_new ("");
1918 g_string_append_len (icon_name, mime_type, separator - mime_type);
1919 g_string_append_c (icon_name, '-');
1920 g_string_append (icon_name, separator + 1);
1921 pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
1922 g_string_free (icon_name, TRUE);
1926 icon_name = g_string_new ("");
1927 g_string_append_len (icon_name, mime_type, separator - mime_type);
1928 g_string_append (icon_name, "-x-generic");
1929 pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
1930 g_string_free (icon_name, TRUE);
1935 icon_name = g_string_new ("gnome-mime-");
1936 g_string_append_len (icon_name, mime_type, separator - mime_type);
1937 g_string_append_c (icon_name, '-');
1938 g_string_append (icon_name, separator + 1);
1939 name = icon_name->str;
1940 g_string_free (icon_name, FALSE);
1944 /* FIXME: how are we going to implement a second attempt? */
1949 icon_name = g_string_new ("gnome-mime-");
1950 g_string_append_len (icon_name, mime_type, separator - mime_type);
1951 pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
1952 g_string_free (icon_name, TRUE);
1959 bookmark_list_free (GSList *list)
1963 for (l = list; l; l = l->next)
1966 g_slist_free (list);
1969 /* Returns whether a URI is a local file:// */
1971 is_local_uri (const char *uri)
1977 /* This is rather crude, but hey */
1978 filename = g_filename_from_uri (uri, &hostname, NULL);
1980 result = (filename && !hostname);
1989 bookmark_get_filename (void)
1993 filename = g_build_filename (g_get_home_dir (),
1994 BOOKMARKS_FILENAME, NULL);
1995 g_assert (filename != NULL);
2000 bookmark_list_read (GSList **bookmarks, GError **error)
2004 gboolean result = FALSE;
2006 filename = bookmark_get_filename ();
2009 if (g_file_get_contents (filename, &contents, NULL, error))
2011 gchar **lines = g_strsplit (contents, "\n", -1);
2015 table = g_hash_table_new (g_str_hash, g_str_equal);
2017 for (i = 0; lines[i]; i++)
2019 if (lines[i][0] && !g_hash_table_lookup (table, lines[i]))
2021 *bookmarks = g_slist_prepend (*bookmarks, g_strdup (lines[i]));
2022 g_hash_table_insert (table, lines[i], lines[i]);
2027 g_hash_table_destroy (table);
2030 *bookmarks = g_slist_reverse (*bookmarks);
2040 bookmark_list_write (GSList *bookmarks,
2046 GError *tmp_error = NULL;
2049 string = g_string_new ("");
2051 for (l = bookmarks; l; l = l->next)
2053 g_string_append (string, l->data);
2054 g_string_append_c (string, '\n');
2057 filename = bookmark_get_filename ();
2059 result = g_file_set_contents (filename, string->str, -1, &tmp_error);
2062 g_string_free (string, TRUE);
2067 GTK_FILE_SYSTEM_ERROR,
2068 GTK_FILE_SYSTEM_ERROR_FAILED,
2069 _("Bookmark saving failed: %s"),
2070 tmp_error->message);
2072 g_error_free (tmp_error);
2079 gtk_file_system_win32_insert_bookmark (GtkFileSystem *file_system,
2080 const GtkFilePath *path,
2092 if (!bookmark_list_read (&bookmarks, &err) && err->code != G_FILE_ERROR_NOENT)
2094 g_propagate_error (error, err);
2098 num_bookmarks = g_slist_length (bookmarks);
2099 g_return_val_if_fail (position >= -1 && position <= num_bookmarks, FALSE);
2103 uri = gtk_file_system_win32_path_to_uri (file_system, path);
2105 for (l = bookmarks; l; l = l->next)
2107 char *bookmark, *space;
2111 space = strchr (bookmark, ' ');
2114 if (strcmp (bookmark, uri) != 0)
2122 GTK_FILE_SYSTEM_ERROR,
2123 GTK_FILE_SYSTEM_ERROR_ALREADY_EXISTS,
2124 _("'%s' already exists in the bookmarks list"),
2130 bookmarks = g_slist_insert (bookmarks, g_strdup (uri), position);
2131 if (bookmark_list_write (bookmarks, error))
2134 g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
2140 bookmark_list_free (bookmarks);
2146 gtk_file_system_win32_remove_bookmark (GtkFileSystem *file_system,
2147 const GtkFilePath *path,
2155 if (!bookmark_list_read (&bookmarks, error))
2160 uri = gtk_file_system_path_to_uri (file_system, path);
2162 for (l = bookmarks; l; l = l->next)
2164 char *bookmark, *space;
2166 bookmark = (char *)l->data;
2167 space = strchr (bookmark, ' ');
2171 if (strcmp (bookmark, uri) != 0)
2179 bookmarks = g_slist_remove_link (bookmarks, l);
2182 if (bookmark_list_write (bookmarks, error))
2186 g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
2194 GTK_FILE_SYSTEM_ERROR,
2195 GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
2196 _("'%s' does not exist in the bookmarks list"),
2202 bookmark_list_free (bookmarks);
2208 gtk_file_system_win32_list_bookmarks (GtkFileSystem *file_system)
2214 if (!bookmark_list_read (&bookmarks, NULL))
2219 for (l = bookmarks; l; l = l->next)
2221 char *bookmark, *space;
2223 bookmark = (char *)l->data;
2224 space = strchr (bookmark, ' ');
2228 if (is_local_uri (bookmark))
2229 result = g_slist_prepend (result, gtk_file_system_win32_uri_to_path (file_system, bookmark));
2232 bookmark_list_free (bookmarks);
2234 result = g_slist_reverse (result);
2239 gtk_file_system_win32_get_bookmark_label (GtkFileSystem *file_system,
2240 const GtkFilePath *path)
2245 char *bookmark, *space, *uri;
2250 uri = gtk_file_system_path_to_uri (file_system, path);
2251 bookmark_list_read (&labels, NULL);
2253 for (l = labels; l && !label; l = l->next)
2255 bookmark = (char *)l->data;
2256 space = strchr (bookmark, ' ');
2262 if (strcmp (uri, bookmark) == 0)
2263 label = g_strdup (space + 1);
2266 bookmark_list_free (labels);
2273 gtk_file_system_win32_set_bookmark_label (GtkFileSystem *file_system,
2274 const GtkFilePath *path,
2279 gchar *bookmark, *space, *uri;
2284 uri = gtk_file_system_path_to_uri (file_system, path);
2285 bookmark_list_read (&labels, NULL);
2288 for (l = labels; l && !found; l = l->next)
2290 bookmark = (gchar *)l->data;
2291 space = strchr (bookmark, ' ');
2295 if (strcmp (bookmark, uri) != 0)
2304 if (label && *label)
2305 l->data = g_strdup_printf ("%s %s", uri, label);
2307 l->data = g_strdup (uri);
2316 if (bookmark_list_write (labels, NULL))
2317 g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
2320 bookmark_list_free (labels);
2325 _gtk_file_folder_win32_class_init (GtkFileFolderWin32Class *class)
2327 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
2329 gobject_class->finalize = gtk_file_folder_win32_finalize;
2333 gtk_file_folder_win32_iface_init (GtkFileFolderIface *iface)
2335 iface->get_info = gtk_file_folder_win32_get_info;
2336 iface->list_children = gtk_file_folder_win32_list_children;
2337 iface->is_finished_loading = gtk_file_folder_win32_is_finished_loading;
2341 _gtk_file_folder_win32_init (GtkFileFolderWin32 *impl)
2346 gtk_file_folder_win32_finalize (GObject *object)
2348 GtkFileFolderWin32 *folder_win32 = GTK_FILE_FOLDER_WIN32 (object);
2350 if (folder_win32->load_folder_id)
2352 g_source_remove (folder_win32->load_folder_id);
2353 folder_win32->load_folder_id = 0;
2356 g_hash_table_remove (folder_win32->system_win32->folder_hash, folder_win32->filename);
2358 if (folder_win32->stat_info)
2361 g_print ("Releasing information for directory %s\n", folder_win32->filename);
2363 g_hash_table_destroy (folder_win32->stat_info);
2366 g_free (folder_win32->filename);
2368 G_OBJECT_CLASS (_gtk_file_folder_win32_parent_class)->finalize (object);
2371 /* Creates a GtkFileInfo for a volume root by stat()ing it */
2372 static GtkFileInfo *
2373 file_info_for_root_with_error (const char *root_name,
2376 struct stat statbuf;
2379 if (g_stat (root_name, &statbuf) != 0)
2384 saved_errno = errno;
2385 display_name = g_filename_display_name (root_name);
2387 GTK_FILE_SYSTEM_ERROR,
2388 GTK_FILE_SYSTEM_ERROR_FAILED,
2389 _("Error getting information for '%s': %s"),
2391 g_strerror (saved_errno));
2393 g_free (display_name);
2397 info = gtk_file_info_new ();
2398 gtk_file_info_set_display_name (info, root_name);
2399 gtk_file_info_set_is_folder (info, TRUE);
2400 gtk_file_info_set_is_hidden (info, FALSE);
2401 gtk_file_info_set_mime_type (info, "x-directory/normal");
2402 gtk_file_info_set_modification_time (info, statbuf.st_mtime);
2403 gtk_file_info_set_size (info, statbuf.st_size);
2409 stat_with_error (const char *filename,
2410 WIN32_FILE_ATTRIBUTE_DATA *wfad,
2413 wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, error);
2416 if (wfilename == NULL)
2419 rc = GetFileAttributesExW (wfilename, GetFileExInfoStandard, wfad);
2425 int error_number = GetLastError ();
2426 char *emsg = g_win32_error_message (error_number);
2427 gchar *display_name = g_filename_display_name (filename);
2430 if (error_number == ERROR_FILE_NOT_FOUND ||
2431 error_number == ERROR_PATH_NOT_FOUND)
2432 code = GTK_FILE_SYSTEM_ERROR_NONEXISTENT;
2434 code = GTK_FILE_SYSTEM_ERROR_FAILED;
2437 GTK_FILE_SYSTEM_ERROR,
2439 _("Error getting information for '%s': %s"),
2442 g_free (display_name);
2453 /* Creates a new GtkFileInfo from the specified data */
2454 static GtkFileInfo *
2455 create_file_info (GtkFileFolderWin32 *folder_win32,
2456 const char *filename,
2457 GtkFileInfoType types,
2458 WIN32_FILE_ATTRIBUTE_DATA *wfad,
2459 const char *mime_type)
2463 info = gtk_file_info_new ();
2465 if (types & GTK_FILE_INFO_DISPLAY_NAME)
2467 gchar *display_name;
2469 if (filename_is_root (filename))
2470 display_name = g_filename_display_name (filename);
2472 display_name = g_filename_display_basename (filename);
2474 gtk_file_info_set_display_name (info, display_name);
2475 g_free (display_name);
2478 if (types & GTK_FILE_INFO_IS_HIDDEN)
2480 gboolean is_hidden = !!(wfad->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN);
2481 gtk_file_info_set_is_hidden (info, is_hidden);
2484 if (types & GTK_FILE_INFO_IS_FOLDER)
2485 gtk_file_info_set_is_folder (info, !!(wfad->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY));
2487 if (types & GTK_FILE_INFO_MIME_TYPE)
2488 gtk_file_info_set_mime_type (info, mime_type);
2490 if (types & GTK_FILE_INFO_MODIFICATION_TIME)
2492 GtkFileTime time = (wfad->ftLastWriteTime.dwLowDateTime
2493 | ((guint64)wfad->ftLastWriteTime.dwHighDateTime) << 32);
2494 /* 100-nanosecond intervals since January 1, 1601, urgh! */
2495 time /= G_GINT64_CONSTANT (10000000); /* now seconds */
2496 time -= G_GINT64_CONSTANT (134774) * 24 * 3600; /* good old Unix time */
2497 gtk_file_info_set_modification_time (info, time);
2500 if (types & GTK_FILE_INFO_SIZE)
2502 gint64 size = wfad->nFileSizeLow | ((guint64)wfad->nFileSizeHigh) << 32;
2503 gtk_file_info_set_size (info, size);
2506 if (types & GTK_FILE_INFO_ICON)
2509 gboolean free_icon_name = FALSE;
2510 const char *icon_name;
2511 const char *icon_mime_type;
2513 icon_type = get_icon_type_from_path (folder_win32, wfad, filename, &icon_mime_type);
2518 icon_name = get_fallback_icon_name (icon_type);
2522 free_icon_name = TRUE;
2524 icon_name = get_icon_name_for_mime_type (icon_mime_type);
2526 icon_name = get_icon_name_for_mime_type (mime_type);
2530 icon_name = get_special_icon_name (icon_type, filename);
2534 gtk_file_info_set_icon_name (info, icon_name);
2537 g_free ((char *) icon_name);
2543 static struct stat_info_entry *
2544 create_stat_info_entry_and_emit_add (GtkFileFolderWin32 *folder_win32,
2545 const char *filename,
2546 const char *basename,
2547 WIN32_FILE_ATTRIBUTE_DATA *wfad)
2551 struct stat_info_entry *entry;
2553 entry = g_new0 (struct stat_info_entry, 1);
2555 if ((folder_win32->types & STAT_NEEDED_MASK) != 0)
2556 entry->wfad = *wfad;
2558 if ((folder_win32->types & GTK_FILE_INFO_MIME_TYPE) != 0)
2559 entry->mime_type = get_mime_type_for_file (filename, wfad);
2561 g_hash_table_insert (folder_win32->stat_info,
2562 g_strdup (basename),
2565 path = gtk_file_path_new_dup (filename);
2566 paths = g_slist_append (NULL, path);
2567 g_signal_emit_by_name (folder_win32, "files-added", paths);
2568 gtk_file_path_free (path);
2569 g_slist_free (paths);
2574 static GtkFileInfo *
2575 gtk_file_folder_win32_get_info (GtkFileFolder *folder,
2576 const GtkFilePath *path,
2579 GtkFileFolderWin32 *folder_win32 = GTK_FILE_FOLDER_WIN32 (folder);
2581 const char *filename;
2582 GtkFileInfoType types;
2583 WIN32_FILE_ATTRIBUTE_DATA wfad;
2584 const char *mime_type;
2586 /* Get_info for "/" */
2589 g_return_val_if_fail (filename_is_root (folder_win32->filename), NULL);
2590 return file_info_for_root_with_error (folder_win32->filename, error);
2593 /* Get_info for normal files */
2595 filename = gtk_file_path_get_string (path);
2596 g_return_val_if_fail (filename != NULL, NULL);
2597 g_return_val_if_fail (g_path_is_absolute (filename), NULL);
2600 /* Skip this sanity check, as it fails for server share roots, where
2601 * dirname gets set to \\server\share\ and folder_win32->filename is
2602 * \\server\share. Also, should we do a casefolded comparison here,
2606 gchar *dirname = get_parent_dir (filename);
2607 g_return_val_if_fail (strcmp (dirname, folder_win32->filename) == 0, NULL);
2612 types = folder_win32->types;
2614 if (folder_win32->have_stat)
2616 struct stat_info_entry *entry;
2619 g_assert (folder_win32->stat_info != NULL);
2621 basename = g_path_get_basename (filename);
2622 entry = g_hash_table_lookup (folder_win32->stat_info, basename);
2626 if (!stat_with_error (filename, &wfad, error))
2632 entry = create_stat_info_entry_and_emit_add (folder_win32, filename, basename, &wfad);
2636 info = create_file_info (folder_win32, filename, types, &entry->wfad, entry->mime_type);
2641 if (!stat_with_error (filename, &wfad, error))
2644 if ((types & GTK_FILE_INFO_MIME_TYPE) != 0)
2645 mime_type = get_mime_type_for_file (filename, &wfad);
2649 info = create_file_info (folder_win32, filename, types, &wfad, mime_type);
2656 cb_list_children (gpointer key, gpointer value, gpointer user_data)
2658 GSList **children = user_data;
2659 *children = g_slist_prepend (*children, key);
2663 gtk_file_folder_win32_list_children (GtkFileFolder *folder,
2667 GtkFileFolderWin32 *folder_win32 = GTK_FILE_FOLDER_WIN32 (folder);
2672 /* Get the list of basenames. */
2673 if (folder_win32->stat_info)
2674 g_hash_table_foreach (folder_win32->stat_info, cb_list_children, children);
2676 /* Turn basenames into GFilePaths. */
2677 for (l = *children; l; l = l->next)
2679 const char *basename = l->data;
2680 char *fullname = g_build_filename (folder_win32->filename, basename, NULL);
2681 l->data = filename_to_path (fullname);
2689 gtk_file_folder_win32_is_finished_loading (GtkFileFolder *folder)
2691 return GTK_FILE_FOLDER_WIN32 (folder)->is_finished_loading;
2695 free_stat_info_entry (struct stat_info_entry *entry)
2697 g_free (entry->mime_type);
2702 fill_in_names (GtkFileFolderWin32 *folder_win32, GError **error)
2706 if (folder_win32->stat_info)
2709 dir = g_dir_open (folder_win32->filename, 0, error);
2713 folder_win32->stat_info = g_hash_table_new_full (casefolded_hash, casefolded_equal,
2714 (GDestroyNotify) g_free,
2715 (GDestroyNotify) free_stat_info_entry);
2719 struct stat_info_entry *entry;
2720 const gchar *basename;
2722 basename = g_dir_read_name (dir);
2726 entry = g_new0 (struct stat_info_entry, 1);
2727 if (folder_win32->is_network_dir)
2729 g_assert_not_reached ();
2730 entry->wfad.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
2731 entry->mime_type = g_strdup ("x-directory/normal");
2734 g_hash_table_insert (folder_win32->stat_info,
2735 g_strdup (basename),
2741 folder_win32->asof = time (NULL);
2746 cb_fill_in_stats (gpointer key, gpointer value, gpointer user_data)
2748 const char *basename = key;
2749 struct stat_info_entry *entry = value;
2750 GtkFileFolderWin32 *folder_win32 = user_data;
2751 char *fullname = g_build_filename (folder_win32->filename, basename, NULL);
2754 if (!stat_with_error (fullname, &entry->wfad, NULL))
2755 result = TRUE; /* Couldn't stat -- remove from hash. */
2765 fill_in_stats (GtkFileFolderWin32 *folder_win32)
2767 if (folder_win32->have_stat)
2770 if (!fill_in_names (folder_win32, NULL))
2773 if (!folder_win32->is_network_dir)
2774 g_hash_table_foreach_remove (folder_win32->stat_info,
2778 folder_win32->have_stat = TRUE;
2782 cb_fill_in_mime_type (gpointer key, gpointer value, gpointer user_data)
2784 const char *basename = key;
2785 struct stat_info_entry *entry = value;
2786 GtkFileFolderWin32 *folder_win32 = user_data;
2787 char *fullname = g_build_filename (folder_win32->filename, basename, NULL);
2789 entry->mime_type = get_mime_type_for_file (fullname, &entry->wfad);
2797 fill_in_mime_type (GtkFileFolderWin32 *folder_win32)
2799 if (folder_win32->have_mime_type)
2802 if (!folder_win32->have_stat)
2805 g_assert (folder_win32->stat_info != NULL);
2807 if (!folder_win32->is_network_dir)
2808 g_hash_table_foreach_remove (folder_win32->stat_info,
2809 cb_fill_in_mime_type,
2812 folder_win32->have_mime_type = TRUE;
2815 static GtkFilePath *
2816 filename_to_path (const char *filename)
2820 tmp = remove_trailing_slash (filename);
2821 return gtk_file_path_new_steal (tmp);
2825 filename_is_root (const char *filename)
2827 const gchar *after_root;
2829 after_root = g_path_skip_root (filename);
2831 return (after_root != NULL && *after_root == '\0');
2835 filename_is_drive_root (const char *filename)
2837 guint len = strlen (filename);
2840 g_ascii_isalpha (filename[0]) &&
2841 filename[1] == ':' &&
2842 G_IS_DIR_SEPARATOR (filename[2]));
2846 filename_is_some_root (const char *filename)
2848 return (g_path_is_absolute (filename) &&
2849 *(g_path_skip_root (filename)) == '\0');
2853 _gtk_file_system_win32_path_compare (const gchar *path1,
2856 while (*path1 && *path2)
2858 gunichar c1 = g_utf8_get_char (path1);
2859 gunichar c2 = g_utf8_get_char (path2);
2861 (G_IS_DIR_SEPARATOR (c1) && G_IS_DIR_SEPARATOR (c1)) ||
2862 g_unichar_toupper (c1) == g_unichar_toupper (c2))
2864 path1 = g_utf8_next_char (path1);
2865 path2 = g_utf8_next_char (path2);
2870 if (!*path1 && !*path2)
2877 return g_unichar_toupper (g_utf8_get_char (path1)) - g_unichar_toupper (g_utf8_get_char (path2));
2880 #define __GTK_FILE_SYSTEM_WIN32_C__
2881 #include "gtkaliasdef.c"