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);
243 static void execute_callbacks (gpointer data);
245 static gboolean fill_in_names (GtkFileFolderWin32 *folder_win32,
247 static void fill_in_stats (GtkFileFolderWin32 *folder_win32);
248 static void fill_in_mime_type (GtkFileFolderWin32 *folder_win32);
250 static gboolean cb_fill_in_stats (gpointer key,
253 static gboolean cb_fill_in_mime_type (gpointer key,
257 /* some info kept together for volumes */
258 struct _GtkFileSystemVolume
267 G_DEFINE_TYPE_WITH_CODE (GtkFileSystemWin32, gtk_file_system_win32, G_TYPE_OBJECT,
268 G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_SYSTEM,
269 gtk_file_system_win32_iface_init))
274 G_DEFINE_TYPE_WITH_CODE (GtkFileFolderWin32, _gtk_file_folder_win32, G_TYPE_OBJECT,
275 G_IMPLEMENT_INTERFACE (GTK_TYPE_FILE_FOLDER,
276 gtk_file_folder_win32_iface_init))
279 * gtk_file_system_win32_new:
281 * Creates a new #GtkFileSystemWin32 object. #GtkFileSystemWin32
282 * implements the #GtkFileSystem interface with direct access to
283 * the filesystem using Windows API calls
285 * Return value: the new #GtkFileSystemWin32 object
288 gtk_file_system_win32_new (void)
290 return g_object_new (GTK_TYPE_FILE_SYSTEM_WIN32, NULL);
294 gtk_file_system_win32_class_init (GtkFileSystemWin32Class *class)
296 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
298 gobject_class->dispose = gtk_file_system_win32_dispose;
299 gobject_class->finalize = gtk_file_system_win32_finalize;
303 gtk_file_system_win32_iface_init (GtkFileSystemIface *iface)
305 iface->list_volumes = gtk_file_system_win32_list_volumes;
306 iface->get_volume_for_path = gtk_file_system_win32_get_volume_for_path;
307 iface->get_folder = gtk_file_system_win32_get_folder;
308 iface->get_info = gtk_file_system_win32_get_info;
309 iface->create_folder = gtk_file_system_win32_create_folder;
310 iface->cancel_operation = gtk_file_system_win32_cancel_operation;
311 iface->volume_free = gtk_file_system_win32_volume_free;
312 iface->volume_get_base_path = gtk_file_system_win32_volume_get_base_path;
313 iface->volume_get_is_mounted = gtk_file_system_win32_volume_get_is_mounted;
314 iface->volume_mount = gtk_file_system_win32_volume_mount;
315 iface->volume_get_display_name = gtk_file_system_win32_volume_get_display_name;
316 iface->volume_get_icon_name = gtk_file_system_win32_volume_get_icon_name;
317 iface->get_parent = gtk_file_system_win32_get_parent;
318 iface->make_path = gtk_file_system_win32_make_path;
319 iface->parse = gtk_file_system_win32_parse;
320 iface->path_to_uri = gtk_file_system_win32_path_to_uri;
321 iface->path_to_filename = gtk_file_system_win32_path_to_filename;
322 iface->uri_to_path = gtk_file_system_win32_uri_to_path;
323 iface->filename_to_path = gtk_file_system_win32_filename_to_path;
324 iface->insert_bookmark = gtk_file_system_win32_insert_bookmark;
325 iface->remove_bookmark = gtk_file_system_win32_remove_bookmark;
326 iface->list_bookmarks = gtk_file_system_win32_list_bookmarks;
327 iface->get_bookmark_label = gtk_file_system_win32_get_bookmark_label;
328 iface->set_bookmark_label = gtk_file_system_win32_set_bookmark_label;
332 check_volumes (gpointer data)
334 GtkFileSystemWin32 *system_win32 = GTK_FILE_SYSTEM_WIN32 (data);
336 g_return_val_if_fail (system_win32, FALSE);
338 if (system_win32->drives != GetLogicalDrives())
339 g_signal_emit_by_name (system_win32, "volumes-changed", 0);
345 casefolded_hash (gconstpointer v)
347 const gchar *p = (const gchar *) v;
352 h = (h << 5) - h + g_unichar_toupper (g_utf8_get_char (p));
353 p = g_utf8_next_char (p);
359 static gboolean casefolded_equal (gconstpointer v1,
362 return (_gtk_file_system_win32_path_compare ((const gchar *) v1, (const gchar *) v2) == 0);
366 gtk_file_system_win32_init (GtkFileSystemWin32 *system_win32)
368 system_win32->folder_hash = g_hash_table_new (casefolded_hash, casefolded_equal);
370 /* Set up an idle handler for volume changes. Once a second should
373 system_win32->timeout = g_timeout_add_full (0, 1000, check_volumes, system_win32, NULL);
375 system_win32->handles = g_hash_table_new (g_direct_hash, g_direct_equal);
377 system_win32->execute_callbacks_idle_id = 0;
378 system_win32->callbacks = NULL;
382 check_handle_fn (gpointer key, gpointer value, gpointer data)
384 GtkFileSystemHandle *handle;
385 int *num_live_handles;
388 num_live_handles = data;
390 (*num_live_handles)++;
392 g_warning ("file_system_win32=%p still has handle=%p at finalization which is %s!",
395 handle->cancelled ? "CANCELLED" : "NOT CANCELLED");
399 check_handles_at_finalization (GtkFileSystemWin32 *system_win32)
401 int num_live_handles;
403 num_live_handles = 0;
405 g_hash_table_foreach (system_win32->handles, check_handle_fn, &num_live_handles);
406 #ifdef HANDLE_ME_HARDER
407 g_assert (num_live_handles == 0);
410 g_hash_table_destroy (system_win32->handles);
411 system_win32->handles = NULL;
414 #define GTK_TYPE_FILE_SYSTEM_HANDLE_WIN32 (_gtk_file_system_handle_win32_get_type ())
416 typedef struct _GtkFileSystemHandle GtkFileSystemHandleWin32;
417 typedef struct _GtkFileSystemHandleClass GtkFileSystemHandleWin32Class;
419 G_DEFINE_TYPE (GtkFileSystemHandleWin32, _gtk_file_system_handle_win32, GTK_TYPE_FILE_SYSTEM_HANDLE)
422 _gtk_file_system_handle_win32_init (GtkFileSystemHandleWin32 *handle)
427 _gtk_file_system_handle_win32_finalize (GObject *object)
429 GtkFileSystemHandleWin32 *handle;
430 GtkFileSystemWin32 *system_win32;
432 handle = (GtkFileSystemHandleWin32 *)object;
434 system_win32 = GTK_FILE_SYSTEM_WIN32 (GTK_FILE_SYSTEM_HANDLE (handle)->file_system);
436 g_assert (g_hash_table_lookup (system_win32->handles, handle) != NULL);
437 g_hash_table_remove (system_win32->handles, handle);
439 if (G_OBJECT_CLASS (_gtk_file_system_handle_win32_parent_class)->finalize)
440 G_OBJECT_CLASS (_gtk_file_system_handle_win32_parent_class)->finalize (object);
444 _gtk_file_system_handle_win32_class_init (GtkFileSystemHandleWin32Class *class)
446 GObjectClass *gobject_class = (GObjectClass *) class;
448 gobject_class->finalize = _gtk_file_system_handle_win32_finalize;
452 gtk_file_system_win32_dispose (GObject *object)
454 GtkFileSystemWin32 *system_win32;
456 system_win32 = GTK_FILE_SYSTEM_WIN32 (object);
458 if (system_win32->execute_callbacks_idle_id)
460 g_source_remove (system_win32->execute_callbacks_idle_id);
461 system_win32->execute_callbacks_idle_id = 0;
463 /* call pending callbacks */
464 execute_callbacks (system_win32);
467 G_OBJECT_CLASS (gtk_file_system_win32_parent_class)->dispose (object);
471 gtk_file_system_win32_finalize (GObject *object)
473 GtkFileSystemWin32 *system_win32;
475 system_win32 = GTK_FILE_SYSTEM_WIN32 (object);
477 g_source_remove (system_win32->timeout);
479 check_handles_at_finalization (system_win32);
481 /* FIXME: assert that the hash is empty? */
482 g_hash_table_destroy (system_win32->folder_hash);
484 G_OBJECT_CLASS (gtk_file_system_win32_parent_class)->finalize (object);
487 /* Lifted from GLib */
490 get_special_folder (int csidl)
494 wchar_t wc[MAX_PATH+1];
497 LPITEMIDLIST pidl = NULL;
499 gchar *retval = NULL;
501 hr = SHGetSpecialFolderLocation (NULL, csidl, &pidl);
504 b = SHGetPathFromIDListW (pidl, path.wc);
506 retval = g_utf16_to_utf8 (path.wc, -1, NULL, NULL, NULL);
507 CoTaskMemFree (pidl);
513 _gtk_file_system_win32_get_desktop (void)
515 return get_special_folder (CSIDL_DESKTOPDIRECTORY);
519 gtk_file_system_win32_list_volumes (GtkFileSystem *file_system)
522 gchar drive[4] = "A:\\";
524 GtkFileSystemWin32 *system_win32 = (GtkFileSystemWin32 *)file_system;
526 drives = GetLogicalDrives();
528 system_win32->drives = drives;
530 g_warning ("GetLogicalDrives failed.");
532 while (drives && drive[0] <= 'Z')
536 GtkFileSystemVolume *vol = g_new0 (GtkFileSystemVolume, 1);
537 vol->drive = g_strdup (drive);
538 vol->drive_type = GetDriveType (drive);
539 list = g_slist_append (list, vol);
547 static GtkFileSystemVolume *
548 gtk_file_system_win32_get_volume_for_path (GtkFileSystem *file_system,
549 const GtkFilePath *path)
551 GtkFileSystemVolume *vol = g_new0 (GtkFileSystemVolume, 1);
554 g_return_val_if_fail (path != NULL, NULL);
556 p = gtk_file_path_get_string (path);
558 if (!g_path_is_absolute (p))
560 if (g_ascii_isalpha (p[0]) && p[1] == ':')
561 vol->drive = g_strdup_printf ("%c:\\", p[0]);
563 vol->drive = g_strdup ("\\");
564 vol->drive_type = GetDriveType (vol->drive);
568 const gchar *q = g_path_skip_root (p);
569 vol->drive = g_strndup (p, q - p);
570 if (!G_IS_DIR_SEPARATOR (q[-1]))
572 /* Make sure "drive" always ends in a slash */
573 gchar *tem = vol->drive;
574 vol->drive = g_strconcat (vol->drive, "\\", NULL);
578 if (filename_is_drive_root (vol->drive))
580 vol->drive[0] = g_ascii_toupper (vol->drive[0]);
581 vol->drive_type = GetDriveType (vol->drive);
585 wchar_t *wdrive = g_utf8_to_utf16 (vol->drive, -1, NULL, NULL, NULL);
586 vol->drive_type = GetDriveTypeW (wdrive);
594 remove_trailing_slash (const char *filename)
598 len = strlen (filename);
600 if (g_path_is_absolute (filename))
601 root_len = g_path_skip_root (filename) - filename;
604 if (len > root_len && G_IS_DIR_SEPARATOR (filename[len - 1]))
605 return g_strndup (filename, len - 1);
607 return g_memdup (filename, len + 1);
610 /* Delay callback dispatching
617 CALLBACK_CREATE_FOLDER,
618 CALLBACK_VOLUME_MOUNT
621 static void queue_callback (GtkFileSystemWin32 *system_win32, enum callback_types type, gpointer data);
623 struct get_info_callback
625 GtkFileSystemGetInfoCallback callback;
626 GtkFileSystemHandle *handle;
627 GtkFileInfo *file_info;
633 dispatch_get_info_callback (struct get_info_callback *info)
635 (* info->callback) (info->handle, info->file_info, info->error, info->data);
638 gtk_file_info_free (info->file_info);
641 g_error_free (info->error);
643 g_object_unref (info->handle);
649 queue_get_info_callback (GtkFileSystemGetInfoCallback callback,
650 GtkFileSystemHandle *handle,
651 GtkFileInfo *file_info,
655 struct get_info_callback *info;
657 info = g_new (struct get_info_callback, 1);
658 info->callback = callback;
659 info->handle = handle;
660 info->file_info = file_info;
664 queue_callback (GTK_FILE_SYSTEM_WIN32 (handle->file_system), CALLBACK_GET_INFO, info);
668 struct get_folder_callback
670 GtkFileSystemGetFolderCallback callback;
671 GtkFileSystemHandle *handle;
672 GtkFileFolder *folder;
678 dispatch_get_folder_callback (struct get_folder_callback *info)
680 (* info->callback) (info->handle, info->folder, info->error, info->data);
683 g_error_free (info->error);
685 g_object_unref (info->handle);
691 queue_get_folder_callback (GtkFileSystemGetFolderCallback callback,
692 GtkFileSystemHandle *handle,
693 GtkFileFolder *folder,
697 struct get_folder_callback *info;
699 info = g_new (struct get_folder_callback, 1);
700 info->callback = callback;
701 info->handle = handle;
702 info->folder = folder;
706 queue_callback (GTK_FILE_SYSTEM_WIN32 (handle->file_system), CALLBACK_GET_FOLDER, info);
710 struct create_folder_callback
712 GtkFileSystemCreateFolderCallback callback;
713 GtkFileSystemHandle *handle;
720 dispatch_create_folder_callback (struct create_folder_callback *info)
722 (* info->callback) (info->handle, info->path, info->error, info->data);
725 g_error_free (info->error);
728 gtk_file_path_free (info->path);
730 g_object_unref (info->handle);
736 queue_create_folder_callback (GtkFileSystemCreateFolderCallback callback,
737 GtkFileSystemHandle *handle,
738 const GtkFilePath *path,
742 struct create_folder_callback *info;
744 info = g_new (struct create_folder_callback, 1);
745 info->callback = callback;
746 info->handle = handle;
747 info->path = gtk_file_path_copy (path);
751 queue_callback (GTK_FILE_SYSTEM_WIN32 (handle->file_system), CALLBACK_CREATE_FOLDER, info);
755 struct volume_mount_callback
757 GtkFileSystemVolumeMountCallback callback;
758 GtkFileSystemHandle *handle;
759 GtkFileSystemVolume *volume;
765 dispatch_volume_mount_callback (struct volume_mount_callback *info)
767 (* info->callback) (info->handle, info->volume, info->error, info->data);
770 g_error_free (info->error);
772 g_object_unref (info->handle);
778 queue_volume_mount_callback (GtkFileSystemVolumeMountCallback callback,
779 GtkFileSystemHandle *handle,
780 GtkFileSystemVolume *volume,
784 struct volume_mount_callback *info;
786 info = g_new (struct volume_mount_callback, 1);
787 info->callback = callback;
788 info->handle = handle;
789 info->volume = volume;
793 queue_callback (GTK_FILE_SYSTEM_WIN32 (handle->file_system), CALLBACK_VOLUME_MOUNT, info);
799 enum callback_types type;
803 struct get_info_callback *get_info;
804 struct get_folder_callback *get_folder;
805 struct create_folder_callback *create_folder;
806 struct volume_mount_callback *volume_mount;
813 execute_callbacks (gpointer data)
816 gboolean unref_file_system = TRUE;
817 GtkFileSystemWin32 *system_win32 = GTK_FILE_SYSTEM_WIN32 (data);
819 if (!system_win32->execute_callbacks_idle_id)
820 unref_file_system = FALSE;
822 g_object_ref (system_win32);
824 for (l = system_win32->callbacks; l; l = l->next)
826 struct callback_info *info = l->data;
830 case CALLBACK_GET_INFO:
831 dispatch_get_info_callback (info->info.get_info);
834 case CALLBACK_GET_FOLDER:
835 dispatch_get_folder_callback (info->info.get_folder);
838 case CALLBACK_CREATE_FOLDER:
839 dispatch_create_folder_callback (info->info.create_folder);
842 case CALLBACK_VOLUME_MOUNT:
843 dispatch_volume_mount_callback (info->info.volume_mount);
850 g_slist_free (system_win32->callbacks);
851 system_win32->callbacks = NULL;
853 if (unref_file_system)
854 g_object_unref (system_win32);
856 system_win32->execute_callbacks_idle_id = 0;
860 execute_callbacks_idle (gpointer data)
862 GDK_THREADS_ENTER ();
864 execute_callbacks(data);
866 GDK_THREADS_LEAVE ();
872 queue_callback (GtkFileSystemWin32 *system_win32,
873 enum callback_types type,
876 struct callback_info *info;
878 info = g_new (struct callback_info, 1);
883 case CALLBACK_GET_INFO:
884 info->info.get_info = data;
887 case CALLBACK_GET_FOLDER:
888 info->info.get_folder = data;
891 case CALLBACK_CREATE_FOLDER:
892 info->info.create_folder = data;
895 case CALLBACK_VOLUME_MOUNT:
896 info->info.volume_mount = data;
900 system_win32->callbacks = g_slist_append (system_win32->callbacks, info);
902 if (!system_win32->execute_callbacks_idle_id)
903 system_win32->execute_callbacks_idle_id = g_idle_add (execute_callbacks_idle, system_win32);
906 static GtkFileSystemHandle *
907 create_handle (GtkFileSystem *file_system)
909 GtkFileSystemWin32 *system_win32;
910 GtkFileSystemHandle *handle;
912 system_win32 = GTK_FILE_SYSTEM_WIN32 (file_system);
914 handle = g_object_new (GTK_TYPE_FILE_SYSTEM_HANDLE_WIN32, NULL);
915 handle->file_system = file_system;
917 g_assert (g_hash_table_lookup (system_win32->handles, handle) == NULL);
918 g_hash_table_insert (system_win32->handles, handle, handle);
924 get_mime_type_for_file (const char *filename,
925 const WIN32_FILE_ATTRIBUTE_DATA *wfad)
927 const char *extension;
929 DWORD type, nbytes = 0;
932 if (wfad->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
933 return g_strdup ("x-directory/normal");
935 extension = strrchr (filename, '.');
937 if (extension != NULL &&
938 (stricmp (extension, ".exe") == 0 ||
939 stricmp (extension, ".com") == 0))
940 return g_strdup ("application/x-executable");
942 if (extension != NULL &&
943 extension[1] != '\0' &&
944 RegOpenKeyEx (HKEY_CLASSES_ROOT, extension, 0,
945 KEY_QUERY_VALUE, &key) == ERROR_SUCCESS &&
946 RegQueryValueEx (key, "Content Type", 0,
947 &type, NULL, &nbytes) == ERROR_SUCCESS &&
949 (value = g_try_malloc (nbytes + 1)) &&
950 RegQueryValueEx (key, "Content Type", 0,
951 &type, value, &nbytes) == ERROR_SUCCESS)
953 value[nbytes] = '\0';
956 value = g_strdup ("application/octet-stream");
963 static GtkFileSystemHandle *
964 gtk_file_system_win32_get_info (GtkFileSystem *file_system,
965 const GtkFilePath *path,
966 GtkFileInfoType types,
967 GtkFileSystemGetInfoCallback callback,
970 GError *error = NULL;
971 GtkFileSystemWin32 *system_win32;
972 GtkFileSystemHandle *handle;
973 const char *filename;
975 WIN32_FILE_ATTRIBUTE_DATA wfad;
976 const char *mime_type;
978 system_win32 = GTK_FILE_SYSTEM_WIN32 (file_system);
979 handle = create_handle (file_system);
981 filename = gtk_file_path_get_string (path);
982 g_return_val_if_fail (filename != NULL, FALSE);
983 g_return_val_if_fail (g_path_is_absolute (filename), FALSE);
985 if (!stat_with_error (filename, &wfad, &error))
987 g_object_ref (handle);
988 queue_get_info_callback (callback, handle, NULL, error, data);
992 if ((types & GTK_FILE_INFO_MIME_TYPE) != 0)
993 mime_type = get_mime_type_for_file (filename, &wfad);
997 info = create_file_info (NULL, filename, types, &wfad, mime_type);
998 g_object_ref (handle);
999 queue_get_info_callback (callback, handle, info, NULL, data);
1005 load_folder (gpointer data)
1007 GtkFileFolderWin32 *folder_win32 = data;
1010 GDK_THREADS_ENTER ();
1012 if ((folder_win32->types & STAT_NEEDED_MASK) != 0)
1013 fill_in_stats (folder_win32);
1015 if ((folder_win32->types & GTK_FILE_INFO_MIME_TYPE) != 0)
1016 fill_in_mime_type (folder_win32);
1018 if (gtk_file_folder_win32_list_children (GTK_FILE_FOLDER (folder_win32), &children, NULL))
1020 folder_win32->is_finished_loading = TRUE;
1021 g_signal_emit_by_name (folder_win32, "files-added", children);
1022 gtk_file_paths_free (children);
1025 folder_win32->load_folder_id = 0;
1027 g_signal_emit_by_name (folder_win32, "finished-loading", 0);
1029 GDK_THREADS_LEAVE ();
1034 static GtkFileSystemHandle *
1035 gtk_file_system_win32_get_folder (GtkFileSystem *file_system,
1036 const GtkFilePath *path,
1037 GtkFileInfoType types,
1038 GtkFileSystemGetFolderCallback callback,
1041 GError *error = NULL;
1042 GtkFileSystemWin32 *system_win32;
1043 GtkFileFolderWin32 *folder_win32;
1044 GtkFileSystemHandle *handle;
1045 const gchar *filename;
1046 char *filename_copy;
1047 gboolean set_asof = FALSE;
1049 system_win32 = GTK_FILE_SYSTEM_WIN32 (file_system);
1051 filename = gtk_file_path_get_string (path);
1052 g_return_val_if_fail (filename != NULL, NULL);
1053 g_return_val_if_fail (g_path_is_absolute (filename), NULL);
1055 handle = create_handle (file_system);
1057 filename_copy = remove_trailing_slash (filename);
1058 folder_win32 = g_hash_table_lookup (system_win32->folder_hash, filename_copy);
1062 g_free (filename_copy);
1063 if (folder_win32->stat_info &&
1064 time (NULL) - folder_win32->asof >= FOLDER_CACHE_LIFETIME)
1067 g_print ("Cleaning out cached directory %s\n", filename);
1069 g_hash_table_destroy (folder_win32->stat_info);
1070 folder_win32->stat_info = NULL;
1071 folder_win32->have_mime_type = FALSE;
1072 folder_win32->have_stat = FALSE;
1076 g_object_ref (folder_win32);
1077 folder_win32->types |= types;
1078 types = folder_win32->types;
1082 WIN32_FILE_ATTRIBUTE_DATA wfad;
1084 if (!stat_with_error (filename, &wfad, &error))
1086 g_object_ref (handle);
1087 queue_get_folder_callback (callback, handle, NULL, error, data);
1089 g_free (filename_copy);
1093 if (!wfad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1095 gchar *display_name = g_filename_display_name (filename);
1097 g_set_error (&error,
1098 GTK_FILE_SYSTEM_ERROR,
1099 GTK_FILE_SYSTEM_ERROR_NOT_FOLDER,
1100 _("Path is not a folder: '%s'"),
1103 g_object_ref (handle);
1104 queue_get_folder_callback (callback, handle, NULL, error, data);
1106 g_free (display_name);
1107 g_free (filename_copy);
1111 folder_win32 = g_object_new (GTK_TYPE_FILE_FOLDER_WIN32, NULL);
1112 folder_win32->system_win32 = system_win32;
1113 folder_win32->filename = filename_copy;
1114 folder_win32->types = types;
1115 folder_win32->stat_info = NULL;
1116 folder_win32->load_folder_id = 0;
1117 folder_win32->have_mime_type = FALSE;
1118 folder_win32->have_stat = FALSE;
1119 folder_win32->is_finished_loading = FALSE;
1122 /* Browsing for shares not yet implemented */
1123 folder_win32->is_network_dir = FALSE;
1125 g_hash_table_insert (system_win32->folder_hash,
1126 folder_win32->filename,
1131 folder_win32->asof = time (NULL);
1133 g_object_ref (handle);
1134 queue_get_folder_callback (callback, handle, GTK_FILE_FOLDER (folder_win32), NULL, data);
1136 /* Start loading the folder contents in an idle */
1137 if (!folder_win32->load_folder_id)
1138 folder_win32->load_folder_id =
1139 g_idle_add ((GSourceFunc) load_folder, folder_win32);
1144 static GtkFileSystemHandle *
1145 gtk_file_system_win32_create_folder (GtkFileSystem *file_system,
1146 const GtkFilePath *path,
1147 GtkFileSystemCreateFolderCallback callback,
1150 GError *error = NULL;
1151 GtkFileSystemWin32 *system_win32;
1152 GtkFileSystemHandle *handle;
1153 const char *filename;
1158 system_win32 = GTK_FILE_SYSTEM_WIN32 (file_system);
1160 filename = gtk_file_path_get_string (path);
1161 g_return_val_if_fail (filename != NULL, FALSE);
1162 g_return_val_if_fail (g_path_is_absolute (filename), NULL);
1164 handle = create_handle (file_system);
1166 tmp = remove_trailing_slash (filename);
1167 result = g_mkdir (tmp, 0777) == 0;
1173 gchar *display_filename = g_filename_display_name (filename);
1174 g_set_error (&error,
1175 GTK_FILE_SYSTEM_ERROR,
1176 GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
1177 _("Error creating directory '%s': %s"),
1179 g_strerror (save_errno));
1181 g_object_ref (handle);
1182 queue_create_folder_callback (callback, handle, path, error, data);
1184 g_free (display_filename);
1188 if (!filename_is_some_root (filename))
1190 parent = g_path_get_dirname (filename);
1193 GtkFileFolderWin32 *folder_win32;
1195 folder_win32 = g_hash_table_lookup (system_win32->folder_hash, parent);
1200 struct stat_info_entry *entry;
1202 /* Make sure the new folder exists in the parent's folder */
1203 entry = g_new0 (struct stat_info_entry, 1);
1204 if (folder_win32->is_network_dir)
1206 entry->wfad.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
1207 entry->mime_type = g_strdup ("x-directory/normal");
1210 basename = g_path_get_basename (filename);
1211 g_hash_table_insert (folder_win32->stat_info,
1215 if (folder_win32->have_stat)
1218 if ((folder_win32->types & STAT_NEEDED_MASK) != 0)
1219 cb_fill_in_stats (basename, entry, folder_win32);
1221 if ((folder_win32->types & GTK_FILE_INFO_MIME_TYPE) != 0)
1222 cb_fill_in_mime_type (basename, entry, folder_win32);
1225 paths = g_slist_append (NULL, (GtkFilePath *) path);
1226 g_signal_emit_by_name (folder_win32, "files-added", paths);
1227 g_slist_free (paths);
1238 gtk_file_system_win32_cancel_operation (GtkFileSystemHandle *handle)
1240 /* We don't set "cancelled" to TRUE here, since the actual operation
1241 * is executed in the function itself and not in a callback. So
1242 * the operations can never be cancelled (since they will be already
1243 * completed at this point.
1248 gtk_file_system_win32_volume_free (GtkFileSystem *file_system,
1249 GtkFileSystemVolume *volume)
1253 g_free (volume->drive);
1254 path = (GtkFilePath *) volume;
1255 gtk_file_path_free (path);
1258 static GtkFilePath *
1259 gtk_file_system_win32_volume_get_base_path (GtkFileSystem *file_system,
1260 GtkFileSystemVolume *volume)
1262 return (GtkFilePath *) g_strdup (volume->drive);
1266 gtk_file_system_win32_volume_get_is_mounted (GtkFileSystem *file_system,
1267 GtkFileSystemVolume *volume)
1272 static GtkFileSystemHandle *
1273 gtk_file_system_win32_volume_mount (GtkFileSystem *file_system,
1274 GtkFileSystemVolume *volume,
1275 GtkFileSystemVolumeMountCallback callback,
1278 GError *error = NULL;
1279 GtkFileSystemHandle *handle = create_handle (file_system);
1281 g_set_error (&error,
1282 GTK_FILE_SYSTEM_ERROR,
1283 GTK_FILE_SYSTEM_ERROR_FAILED,
1284 _("This file system does not support mounting"));
1286 g_object_ref (handle);
1287 queue_volume_mount_callback (callback, handle, volume, error, data);
1293 gtk_file_system_win32_volume_get_display_name (GtkFileSystem *file_system,
1294 GtkFileSystemVolume *volume)
1296 gchar *real_display_name;
1298 g_return_val_if_fail (volume->drive != NULL, NULL);
1300 if (filename_is_drive_root (volume->drive) &&
1301 volume->drive_type == DRIVE_REMOTE)
1302 real_display_name = g_strdup_printf (_("Network Drive (%s)"), volume->drive);
1303 else if ((filename_is_drive_root (volume->drive) && volume->drive[0] >= 'C') ||
1304 volume->drive_type != DRIVE_REMOVABLE)
1307 gunichar2 *wdrive = g_utf8_to_utf16 (volume->drive, -1, NULL, NULL, NULL);
1308 gunichar2 wname[80];
1310 if (GetVolumeInformationW (wdrive,
1311 wname, G_N_ELEMENTS(wname),
1312 NULL, /* serial number */
1313 NULL, /* max. component length */
1314 NULL, /* fs flags */
1315 NULL, 0) /* fs type like FAT, NTFS */ &&
1318 name = g_utf16_to_utf8 (wname, -1, NULL, NULL, NULL);
1324 real_display_name = g_strdup_printf (_("%s (%s)"), name, volume->drive);
1329 real_display_name = g_strdup (volume->drive);
1333 real_display_name = g_strdup (volume->drive);
1335 return real_display_name;
1339 get_icon_type_from_stat (WIN32_FILE_ATTRIBUTE_DATA *wfad)
1341 if (wfad->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1342 return ICON_DIRECTORY;
1344 return ICON_REGULAR;
1347 /* Renders a fallback icon from the stock system */
1348 static const gchar *
1349 get_fallback_icon_name (IconType icon_type)
1351 const char *stock_name;
1355 case ICON_DIRECTORY:
1356 stock_name = GTK_STOCK_DIRECTORY;
1359 case ICON_EXECUTABLE:
1360 stock_name = GTK_STOCK_EXECUTE;
1364 stock_name = GTK_STOCK_FILE;
1372 gtk_file_system_win32_volume_get_icon_name (GtkFileSystem *file_system,
1373 GtkFileSystemVolume *volume,
1376 /* FIXME: maybe we just always want to return GTK_STOCK_HARDDISK here?
1377 * or the new tango icon name?
1379 return g_strdup ("gnome-dev-harddisk");
1382 #if 0 /* Unused, see below */
1385 get_parent_dir (const char *filename)
1389 len = strlen (filename);
1391 /* Ignore trailing slashes */
1392 if (len > 1 && G_IS_DIR_SEPARATOR (filename[len - 1]))
1396 tmp = g_strndup (filename, len - 1);
1398 parent = g_path_get_dirname (tmp);
1404 return g_path_get_dirname (filename);
1410 gtk_file_system_win32_get_parent (GtkFileSystem *file_system,
1411 const GtkFilePath *path,
1412 GtkFilePath **parent,
1415 const char *filename;
1417 filename = gtk_file_path_get_string (path);
1418 g_return_val_if_fail (filename != NULL, FALSE);
1419 g_return_val_if_fail (g_path_is_absolute (filename), FALSE);
1421 if (filename_is_some_root (filename))
1427 gchar *parent_filename = g_path_get_dirname (filename);
1428 *parent = filename_to_path (parent_filename);
1429 g_free (parent_filename);
1431 #if DEBUGGING_OUTPUT
1432 printf ("%s: %s => %s\n", __FUNCTION__, (char*)path, (*parent)?(char*)*parent:"NULL"), fflush(stdout);
1437 static GtkFilePath *
1438 gtk_file_system_win32_make_path (GtkFileSystem *file_system,
1439 const GtkFilePath *base_path,
1440 const gchar *display_name,
1443 const char *base_filename;
1444 gchar *full_filename;
1445 GtkFilePath *result;
1448 base_filename = gtk_file_path_get_string (base_path);
1449 g_return_val_if_fail (base_filename != NULL, NULL);
1450 g_return_val_if_fail (g_path_is_absolute (base_filename), NULL);
1452 if ((p = strpbrk (display_name, "<>\"/\\|")))
1456 badchar[0] = *p; /* We know it is a single-byte char */
1459 GTK_FILE_SYSTEM_ERROR,
1460 GTK_FILE_SYSTEM_ERROR_BAD_FILENAME,
1461 _("The name \"%s\" is not valid because it contains the character \"%s\". "
1462 "Please use a different name."),
1468 full_filename = g_build_filename (base_filename, display_name, NULL);
1469 result = filename_to_path (full_filename);
1470 g_free (full_filename);
1475 /* If this was a publically exported function, it should return
1476 * a dup'ed result, but we make it modify-in-place for efficiency
1477 * here, and because it works for us.
1480 canonicalize_filename (gchar *filename)
1484 gboolean last_was_slash = FALSE;
1486 #if DEBUGGING_OUTPUT
1487 printf("%s: %s ", __FUNCTION__, filename), fflush (stdout);
1490 past_root = (gchar *) g_path_skip_root (filename);
1496 if (G_IS_DIR_SEPARATOR (*p))
1498 if (!last_was_slash)
1501 last_was_slash = TRUE;
1505 if (last_was_slash && *p == '.')
1507 if (G_IS_DIR_SEPARATOR (*(p + 1)) ||
1510 if (*(p + 1) == '\0')
1515 else if (*(p + 1) == '.' &&
1516 (G_IS_DIR_SEPARATOR (*(p + 2)) ||
1522 while (q > past_root &&
1523 !G_IS_DIR_SEPARATOR (*(q - 1)))
1527 if (*(p + 2) == '\0')
1535 last_was_slash = FALSE;
1541 last_was_slash = FALSE;
1548 if (q > past_root && G_IS_DIR_SEPARATOR (*(q - 1)))
1553 #if DEBUGGING_OUTPUT
1554 printf(" => %s\n", filename), fflush (stdout);
1559 gtk_file_system_win32_parse (GtkFileSystem *file_system,
1560 const GtkFilePath *base_path,
1562 GtkFilePath **folder,
1566 const char *base_filename;
1567 gchar *last_backslash, *last_slash;
1568 gboolean result = FALSE;
1570 #if DEBUGGING_OUTPUT
1571 printf("%s: base_path=%s str=%s\n",__FUNCTION__,(char*)base_path,str),fflush(stdout);
1574 base_filename = gtk_file_path_get_string (base_path);
1575 g_return_val_if_fail (base_filename != NULL, FALSE);
1576 g_return_val_if_fail (g_path_is_absolute (base_filename), FALSE);
1578 last_backslash = strrchr (str, '\\');
1579 last_slash = strrchr (str, '/');
1580 if (last_slash == NULL ||
1581 (last_backslash != NULL && last_backslash > last_slash))
1582 last_slash = last_backslash;
1586 *folder = gtk_file_path_copy (base_path);
1587 *file_part = g_strdup (str);
1595 if (last_slash == str)
1597 if (g_ascii_isalpha (base_filename[0]) &&
1598 base_filename[1] == ':')
1599 folder_part = g_strdup_printf ("%c:\\", base_filename[0]);
1601 folder_part = g_strdup ("\\");
1603 else if (g_ascii_isalpha (str[0]) &&
1605 last_slash == str + 2)
1606 folder_part = g_strndup (str, last_slash - str + 1);
1608 /* Hmm, what the heck was this case supposed to do? It splits up
1609 * \\server\share\foo\bar into folder_part
1610 * \\server\share\foo\bar and file_path bar. Not good. As far as
1611 * I can see, this isn't needed.
1613 else if (G_IS_DIR_SEPARATOR (str[0]) &&
1614 G_IS_DIR_SEPARATOR (str[1]) &&
1615 (!str[2] || !G_IS_DIR_SEPARATOR (str[2])))
1616 folder_part = g_strdup (str);
1619 folder_part = g_strndup (str, last_slash - str);
1621 g_assert (folder_part);
1623 if (g_path_is_absolute (folder_part))
1624 folder_path = folder_part;
1627 folder_path = g_build_filename (base_filename, folder_part, NULL);
1628 g_free (folder_part);
1631 canonicalize_filename (folder_path);
1633 *folder = filename_to_path (folder_path);
1634 *file_part = g_strdup (last_slash + 1);
1636 g_free (folder_path);
1641 #if DEBUGGING_OUTPUT
1642 printf ("%s:returning folder=%s file_part=%s\n", __FUNCTION__, (*folder?(char*)*folder:"NULL"), *file_part), fflush(stdout);
1649 gtk_file_system_win32_path_to_uri (GtkFileSystem *file_system,
1650 const GtkFilePath *path)
1652 return g_filename_to_uri (gtk_file_path_get_string (path), NULL, NULL);
1656 gtk_file_system_win32_path_to_filename (GtkFileSystem *file_system,
1657 const GtkFilePath *path)
1659 return g_strdup (gtk_file_path_get_string (path));
1662 static GtkFilePath *
1663 gtk_file_system_win32_uri_to_path (GtkFileSystem *file_system,
1667 gchar *filename = g_filename_from_uri (uri, NULL, NULL);
1669 #if DEBUGGING_OUTPUT
1670 printf ("%s: %s -> %s\n", __FUNCTION__, uri, filename?filename:"NULL"), fflush (stdout);
1675 path = filename_to_path (filename);
1684 static GtkFilePath *
1685 gtk_file_system_win32_filename_to_path (GtkFileSystem *file_system,
1686 const gchar *filename)
1688 return filename_to_path (filename);
1693 /* These are unused currently, hmm */
1696 extract_icon (const char* filename)
1701 wchar_t filename_copy[MAX_PATH];
1704 wchar_t *wfn = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
1708 GdkPixbuf *pixbuf = NULL;
1711 if (!filename || !filename[0])
1715 /* ExtractAssociatedIconW() is about twice as slow as SHGetFileInfoW() */
1717 /* The ugly ExtractAssociatedIcon modifies filename in place. It
1718 * doesn't even take any argument saying how large the buffer is?
1719 * Let's hope MAX_PATH will be large enough. What dork designed that
1723 wfn = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
1724 if (wcslen (wfn) >= MAX_PATH)
1729 wcscpy (filename_copy, wfn);
1731 hicon = ExtractAssociatedIconW (GetModuleHandle (NULL), filename_copy, &iicon);
1735 g_warning (G_STRLOC ":ExtractAssociatedIcon(%s) failed: %s", filename, g_win32_error_message (GetLastError ()));
1739 rc = (int) SHGetFileInfoW (wfn, 0, &shfi, sizeof (shfi),
1740 SHGFI_ICON|SHGFI_LARGEICON);
1747 pixbuf = gdk_win32_icon_to_pixbuf_libgtk_only (hicon);
1749 if (!DestroyIcon (hicon))
1750 g_warning (G_STRLOC ": DestroyIcon failed: %s\n", g_win32_error_message (GetLastError ()));
1756 win32_pseudo_mime_lookup (const char* name)
1758 gboolean use_cache = TRUE;
1759 static GHashTable *mime_hash = NULL;
1760 GtkIconSet *is = NULL;
1761 char *p = strrchr(name, '.');
1762 char *extension = p ? g_utf8_casefold (p, -1) : g_strdup ("");
1765 /* Don't cache icons for files that might have embedded icons */
1766 if (strcmp (extension, ".lnk") == 0 ||
1767 strcmp (extension, ".exe") == 0 ||
1768 strcmp (extension, ".dll") == 0)
1776 mime_hash = g_hash_table_new (g_str_hash, g_str_equal);
1778 /* do we already have it ? */
1779 is = g_hash_table_lookup (mime_hash, extension);
1787 /* create icon and set */
1788 pixbuf = extract_icon (name);
1791 GtkIconSource* source = gtk_icon_source_new ();
1793 is = gtk_icon_set_new_from_pixbuf (pixbuf);
1794 gtk_icon_source_set_pixbuf (source, pixbuf);
1795 gtk_icon_set_add_source (is, source);
1797 gtk_icon_source_free (source);
1801 g_hash_table_insert (mime_hash, extension, is);
1808 /* Returns the name of the icon to be used for a path which is known to be a
1809 * directory. This can vary for Home, Desktop, etc.
1812 get_icon_name_for_directory (const char *path)
1814 static char *desktop_path = NULL;
1816 if (!g_get_home_dir ())
1817 return "gnome-fs-directory";
1820 desktop_path = g_build_filename (g_get_home_dir (), "Desktop", NULL);
1822 if (strcmp (g_get_home_dir (), path) == 0)
1823 return "gnome-fs-home";
1824 else if (strcmp (desktop_path, path) == 0)
1825 return "gnome-fs-desktop";
1827 return "gnome-fs-directory";
1832 /* Computes our internal icon type based on a path name; also returns the MIME
1833 * type in case we come up with ICON_REGULAR.
1836 get_icon_type_from_path (GtkFileFolderWin32 *folder_win32,
1837 WIN32_FILE_ATTRIBUTE_DATA *wfad,
1838 const char *filename,
1839 const char **mime_type)
1845 if (folder_win32 && folder_win32->have_stat)
1848 struct stat_info_entry *entry;
1850 g_assert (folder_win32->stat_info != NULL);
1852 basename = g_path_get_basename (filename);
1853 entry = g_hash_table_lookup (folder_win32->stat_info, basename);
1857 if (entry->icon_type == ICON_UNDECIDED)
1859 entry->icon_type = get_icon_type_from_stat (&entry->wfad);
1860 g_assert (entry->icon_type != ICON_UNDECIDED);
1862 icon_type = entry->icon_type;
1863 if (icon_type == ICON_REGULAR)
1865 fill_in_mime_type (folder_win32);
1866 *mime_type = entry->mime_type;
1873 icon_type = get_icon_type_from_stat (wfad);
1875 if (icon_type == ICON_REGULAR)
1876 *mime_type = get_mime_type_for_file (filename, wfad);
1881 /* Renders an icon for a non-ICON_REGULAR file */
1882 static const gchar *
1883 get_special_icon_name (IconType icon_type,
1884 const gchar *filename)
1888 g_assert (icon_type != ICON_REGULAR);
1892 case ICON_DIRECTORY:
1893 /* get_icon_name_for_directory() returns a dupped string */
1894 return get_icon_name_for_directory (filename);
1895 case ICON_EXECUTABLE:
1896 name ="gnome-fs-executable";
1899 g_assert_not_reached ();
1907 get_icon_name_for_mime_type (const char *mime_type)
1910 const char *separator;
1916 separator = strchr (mime_type, '/');
1918 return NULL; /* maybe we should return a GError with "invalid MIME-type" */
1920 /* FIXME: we default to the gnome icon naming for now. Some question
1921 * as below, how are we going to handle a second attempt?
1924 icon_name = g_string_new ("");
1925 g_string_append_len (icon_name, mime_type, separator - mime_type);
1926 g_string_append_c (icon_name, '-');
1927 g_string_append (icon_name, separator + 1);
1928 pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
1929 g_string_free (icon_name, TRUE);
1933 icon_name = g_string_new ("");
1934 g_string_append_len (icon_name, mime_type, separator - mime_type);
1935 g_string_append (icon_name, "-x-generic");
1936 pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
1937 g_string_free (icon_name, TRUE);
1942 icon_name = g_string_new ("gnome-mime-");
1943 g_string_append_len (icon_name, mime_type, separator - mime_type);
1944 g_string_append_c (icon_name, '-');
1945 g_string_append (icon_name, separator + 1);
1946 name = icon_name->str;
1947 g_string_free (icon_name, FALSE);
1951 /* FIXME: how are we going to implement a second attempt? */
1956 icon_name = g_string_new ("gnome-mime-");
1957 g_string_append_len (icon_name, mime_type, separator - mime_type);
1958 pixbuf = get_cached_icon (widget, icon_name->str, pixel_size);
1959 g_string_free (icon_name, TRUE);
1966 bookmark_list_free (GSList *list)
1970 for (l = list; l; l = l->next)
1973 g_slist_free (list);
1976 /* Returns whether a URI is a local file:// */
1978 is_local_uri (const char *uri)
1984 /* This is rather crude, but hey */
1985 filename = g_filename_from_uri (uri, &hostname, NULL);
1987 result = (filename && !hostname);
1996 bookmark_get_filename (void)
2000 filename = g_build_filename (g_get_home_dir (),
2001 BOOKMARKS_FILENAME, NULL);
2002 g_assert (filename != NULL);
2007 bookmark_list_read (GSList **bookmarks, GError **error)
2011 gboolean result = FALSE;
2013 filename = bookmark_get_filename ();
2016 if (g_file_get_contents (filename, &contents, NULL, error))
2018 gchar **lines = g_strsplit (contents, "\n", -1);
2022 table = g_hash_table_new (g_str_hash, g_str_equal);
2024 for (i = 0; lines[i]; i++)
2026 if (lines[i][0] && !g_hash_table_lookup (table, lines[i]))
2028 *bookmarks = g_slist_prepend (*bookmarks, g_strdup (lines[i]));
2029 g_hash_table_insert (table, lines[i], lines[i]);
2034 g_hash_table_destroy (table);
2037 *bookmarks = g_slist_reverse (*bookmarks);
2047 bookmark_list_write (GSList *bookmarks,
2053 GError *tmp_error = NULL;
2056 string = g_string_new ("");
2058 for (l = bookmarks; l; l = l->next)
2060 g_string_append (string, l->data);
2061 g_string_append_c (string, '\n');
2064 filename = bookmark_get_filename ();
2066 result = g_file_set_contents (filename, string->str, -1, &tmp_error);
2069 g_string_free (string, TRUE);
2074 GTK_FILE_SYSTEM_ERROR,
2075 GTK_FILE_SYSTEM_ERROR_FAILED,
2076 _("Bookmark saving failed: %s"),
2077 tmp_error->message);
2079 g_error_free (tmp_error);
2086 gtk_file_system_win32_insert_bookmark (GtkFileSystem *file_system,
2087 const GtkFilePath *path,
2099 if (!bookmark_list_read (&bookmarks, &err) && err->code != G_FILE_ERROR_NOENT)
2101 g_propagate_error (error, err);
2105 num_bookmarks = g_slist_length (bookmarks);
2106 g_return_val_if_fail (position >= -1 && position <= num_bookmarks, FALSE);
2110 uri = gtk_file_system_win32_path_to_uri (file_system, path);
2112 for (l = bookmarks; l; l = l->next)
2114 char *bookmark, *space;
2118 space = strchr (bookmark, ' ');
2121 if (strcmp (bookmark, uri) != 0)
2129 GTK_FILE_SYSTEM_ERROR,
2130 GTK_FILE_SYSTEM_ERROR_ALREADY_EXISTS,
2131 _("'%s' already exists in the bookmarks list"),
2137 bookmarks = g_slist_insert (bookmarks, g_strdup (uri), position);
2138 if (bookmark_list_write (bookmarks, error))
2141 g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
2147 bookmark_list_free (bookmarks);
2153 gtk_file_system_win32_remove_bookmark (GtkFileSystem *file_system,
2154 const GtkFilePath *path,
2162 if (!bookmark_list_read (&bookmarks, error))
2167 uri = gtk_file_system_path_to_uri (file_system, path);
2169 for (l = bookmarks; l; l = l->next)
2171 char *bookmark, *space;
2173 bookmark = (char *)l->data;
2174 space = strchr (bookmark, ' ');
2178 if (strcmp (bookmark, uri) != 0)
2186 bookmarks = g_slist_remove_link (bookmarks, l);
2189 if (bookmark_list_write (bookmarks, error))
2193 g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
2201 GTK_FILE_SYSTEM_ERROR,
2202 GTK_FILE_SYSTEM_ERROR_NONEXISTENT,
2203 _("'%s' does not exist in the bookmarks list"),
2209 bookmark_list_free (bookmarks);
2215 gtk_file_system_win32_list_bookmarks (GtkFileSystem *file_system)
2221 if (!bookmark_list_read (&bookmarks, NULL))
2226 for (l = bookmarks; l; l = l->next)
2228 char *bookmark, *space;
2230 bookmark = (char *)l->data;
2231 space = strchr (bookmark, ' ');
2235 if (is_local_uri (bookmark))
2236 result = g_slist_prepend (result, gtk_file_system_win32_uri_to_path (file_system, bookmark));
2239 bookmark_list_free (bookmarks);
2241 result = g_slist_reverse (result);
2246 gtk_file_system_win32_get_bookmark_label (GtkFileSystem *file_system,
2247 const GtkFilePath *path)
2252 char *bookmark, *space, *uri;
2257 uri = gtk_file_system_path_to_uri (file_system, path);
2258 bookmark_list_read (&labels, NULL);
2260 for (l = labels; l && !label; l = l->next)
2262 bookmark = (char *)l->data;
2263 space = strchr (bookmark, ' ');
2269 if (strcmp (uri, bookmark) == 0)
2270 label = g_strdup (space + 1);
2273 bookmark_list_free (labels);
2280 gtk_file_system_win32_set_bookmark_label (GtkFileSystem *file_system,
2281 const GtkFilePath *path,
2286 gchar *bookmark, *space, *uri;
2291 uri = gtk_file_system_path_to_uri (file_system, path);
2292 bookmark_list_read (&labels, NULL);
2295 for (l = labels; l && !found; l = l->next)
2297 bookmark = (gchar *)l->data;
2298 space = strchr (bookmark, ' ');
2302 if (strcmp (bookmark, uri) != 0)
2311 if (label && *label)
2312 l->data = g_strdup_printf ("%s %s", uri, label);
2314 l->data = g_strdup (uri);
2323 if (bookmark_list_write (labels, NULL))
2324 g_signal_emit_by_name (file_system, "bookmarks-changed", 0);
2327 bookmark_list_free (labels);
2332 _gtk_file_folder_win32_class_init (GtkFileFolderWin32Class *class)
2334 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
2336 gobject_class->finalize = gtk_file_folder_win32_finalize;
2340 gtk_file_folder_win32_iface_init (GtkFileFolderIface *iface)
2342 iface->get_info = gtk_file_folder_win32_get_info;
2343 iface->list_children = gtk_file_folder_win32_list_children;
2344 iface->is_finished_loading = gtk_file_folder_win32_is_finished_loading;
2348 _gtk_file_folder_win32_init (GtkFileFolderWin32 *impl)
2353 gtk_file_folder_win32_finalize (GObject *object)
2355 GtkFileFolderWin32 *folder_win32 = GTK_FILE_FOLDER_WIN32 (object);
2357 if (folder_win32->load_folder_id)
2359 g_source_remove (folder_win32->load_folder_id);
2360 folder_win32->load_folder_id = 0;
2363 g_hash_table_remove (folder_win32->system_win32->folder_hash, folder_win32->filename);
2365 if (folder_win32->stat_info)
2368 g_print ("Releasing information for directory %s\n", folder_win32->filename);
2370 g_hash_table_destroy (folder_win32->stat_info);
2373 g_free (folder_win32->filename);
2375 G_OBJECT_CLASS (_gtk_file_folder_win32_parent_class)->finalize (object);
2378 /* Creates a GtkFileInfo for a volume root by stat()ing it */
2379 static GtkFileInfo *
2380 file_info_for_root_with_error (const char *root_name,
2383 struct stat statbuf;
2386 if (g_stat (root_name, &statbuf) != 0)
2391 saved_errno = errno;
2392 display_name = g_filename_display_name (root_name);
2394 GTK_FILE_SYSTEM_ERROR,
2395 GTK_FILE_SYSTEM_ERROR_FAILED,
2396 _("Error getting information for '%s': %s"),
2398 g_strerror (saved_errno));
2400 g_free (display_name);
2404 info = gtk_file_info_new ();
2405 gtk_file_info_set_display_name (info, root_name);
2406 gtk_file_info_set_is_folder (info, TRUE);
2407 gtk_file_info_set_is_hidden (info, FALSE);
2408 gtk_file_info_set_mime_type (info, "x-directory/normal");
2409 gtk_file_info_set_modification_time (info, statbuf.st_mtime);
2410 gtk_file_info_set_size (info, statbuf.st_size);
2416 stat_with_error (const char *filename,
2417 WIN32_FILE_ATTRIBUTE_DATA *wfad,
2420 wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, error);
2423 if (wfilename == NULL)
2426 rc = GetFileAttributesExW (wfilename, GetFileExInfoStandard, wfad);
2432 int error_number = GetLastError ();
2433 char *emsg = g_win32_error_message (error_number);
2434 gchar *display_name = g_filename_display_name (filename);
2437 if (error_number == ERROR_FILE_NOT_FOUND ||
2438 error_number == ERROR_PATH_NOT_FOUND)
2439 code = GTK_FILE_SYSTEM_ERROR_NONEXISTENT;
2441 code = GTK_FILE_SYSTEM_ERROR_FAILED;
2444 GTK_FILE_SYSTEM_ERROR,
2446 _("Error getting information for '%s': %s"),
2449 g_free (display_name);
2460 /* Creates a new GtkFileInfo from the specified data */
2461 static GtkFileInfo *
2462 create_file_info (GtkFileFolderWin32 *folder_win32,
2463 const char *filename,
2464 GtkFileInfoType types,
2465 WIN32_FILE_ATTRIBUTE_DATA *wfad,
2466 const char *mime_type)
2470 info = gtk_file_info_new ();
2472 if (types & GTK_FILE_INFO_DISPLAY_NAME)
2474 gchar *display_name;
2476 if (filename_is_root (filename))
2477 display_name = g_filename_display_name (filename);
2479 display_name = g_filename_display_basename (filename);
2481 gtk_file_info_set_display_name (info, display_name);
2482 g_free (display_name);
2485 if (types & GTK_FILE_INFO_IS_HIDDEN)
2487 gboolean is_hidden = !!(wfad->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN);
2488 gtk_file_info_set_is_hidden (info, is_hidden);
2491 if (types & GTK_FILE_INFO_IS_FOLDER)
2492 gtk_file_info_set_is_folder (info, !!(wfad->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY));
2494 if (types & GTK_FILE_INFO_MIME_TYPE)
2495 gtk_file_info_set_mime_type (info, mime_type);
2497 if (types & GTK_FILE_INFO_MODIFICATION_TIME)
2499 GtkFileTime time = (wfad->ftLastWriteTime.dwLowDateTime
2500 | ((guint64)wfad->ftLastWriteTime.dwHighDateTime) << 32);
2501 /* 100-nanosecond intervals since January 1, 1601, urgh! */
2502 time /= G_GINT64_CONSTANT (10000000); /* now seconds */
2503 time -= G_GINT64_CONSTANT (134774) * 24 * 3600; /* good old Unix time */
2504 gtk_file_info_set_modification_time (info, time);
2507 if (types & GTK_FILE_INFO_SIZE)
2509 gint64 size = wfad->nFileSizeLow | ((guint64)wfad->nFileSizeHigh) << 32;
2510 gtk_file_info_set_size (info, size);
2513 if (types & GTK_FILE_INFO_ICON)
2516 gboolean free_icon_name = FALSE;
2517 const char *icon_name;
2518 const char *icon_mime_type;
2520 icon_type = get_icon_type_from_path (folder_win32, wfad, filename, &icon_mime_type);
2525 icon_name = get_fallback_icon_name (icon_type);
2529 free_icon_name = TRUE;
2531 icon_name = get_icon_name_for_mime_type (icon_mime_type);
2533 icon_name = get_icon_name_for_mime_type (mime_type);
2537 icon_name = get_special_icon_name (icon_type, filename);
2541 gtk_file_info_set_icon_name (info, icon_name);
2544 g_free ((char *) icon_name);
2550 static struct stat_info_entry *
2551 create_stat_info_entry_and_emit_add (GtkFileFolderWin32 *folder_win32,
2552 const char *filename,
2553 const char *basename,
2554 WIN32_FILE_ATTRIBUTE_DATA *wfad)
2558 struct stat_info_entry *entry;
2560 entry = g_new0 (struct stat_info_entry, 1);
2562 if ((folder_win32->types & STAT_NEEDED_MASK) != 0)
2563 entry->wfad = *wfad;
2565 if ((folder_win32->types & GTK_FILE_INFO_MIME_TYPE) != 0)
2566 entry->mime_type = get_mime_type_for_file (filename, wfad);
2568 g_hash_table_insert (folder_win32->stat_info,
2569 g_strdup (basename),
2572 path = gtk_file_path_new_dup (filename);
2573 paths = g_slist_append (NULL, path);
2574 g_signal_emit_by_name (folder_win32, "files-added", paths);
2575 gtk_file_path_free (path);
2576 g_slist_free (paths);
2581 static GtkFileInfo *
2582 gtk_file_folder_win32_get_info (GtkFileFolder *folder,
2583 const GtkFilePath *path,
2586 GtkFileFolderWin32 *folder_win32 = GTK_FILE_FOLDER_WIN32 (folder);
2588 const char *filename;
2589 GtkFileInfoType types;
2590 WIN32_FILE_ATTRIBUTE_DATA wfad;
2591 const char *mime_type;
2593 /* Get_info for "/" */
2596 g_return_val_if_fail (filename_is_root (folder_win32->filename), NULL);
2597 return file_info_for_root_with_error (folder_win32->filename, error);
2600 /* Get_info for normal files */
2602 filename = gtk_file_path_get_string (path);
2603 g_return_val_if_fail (filename != NULL, NULL);
2604 g_return_val_if_fail (g_path_is_absolute (filename), NULL);
2607 /* Skip this sanity check, as it fails for server share roots, where
2608 * dirname gets set to \\server\share\ and folder_win32->filename is
2609 * \\server\share. Also, should we do a casefolded comparison here,
2613 gchar *dirname = get_parent_dir (filename);
2614 g_return_val_if_fail (strcmp (dirname, folder_win32->filename) == 0, NULL);
2619 types = folder_win32->types;
2621 if (folder_win32->have_stat)
2623 struct stat_info_entry *entry;
2626 g_assert (folder_win32->stat_info != NULL);
2628 basename = g_path_get_basename (filename);
2629 entry = g_hash_table_lookup (folder_win32->stat_info, basename);
2633 if (!stat_with_error (filename, &wfad, error))
2639 entry = create_stat_info_entry_and_emit_add (folder_win32, filename, basename, &wfad);
2643 info = create_file_info (folder_win32, filename, types, &entry->wfad, entry->mime_type);
2648 if (!stat_with_error (filename, &wfad, error))
2651 if ((types & GTK_FILE_INFO_MIME_TYPE) != 0)
2652 mime_type = get_mime_type_for_file (filename, &wfad);
2656 info = create_file_info (folder_win32, filename, types, &wfad, mime_type);
2663 cb_list_children (gpointer key, gpointer value, gpointer user_data)
2665 GSList **children = user_data;
2666 *children = g_slist_prepend (*children, key);
2670 gtk_file_folder_win32_list_children (GtkFileFolder *folder,
2674 GtkFileFolderWin32 *folder_win32 = GTK_FILE_FOLDER_WIN32 (folder);
2679 /* Get the list of basenames. */
2680 if (folder_win32->stat_info)
2681 g_hash_table_foreach (folder_win32->stat_info, cb_list_children, children);
2683 /* Turn basenames into GFilePaths. */
2684 for (l = *children; l; l = l->next)
2686 const char *basename = l->data;
2687 char *fullname = g_build_filename (folder_win32->filename, basename, NULL);
2688 l->data = filename_to_path (fullname);
2696 gtk_file_folder_win32_is_finished_loading (GtkFileFolder *folder)
2698 return GTK_FILE_FOLDER_WIN32 (folder)->is_finished_loading;
2702 free_stat_info_entry (struct stat_info_entry *entry)
2704 g_free (entry->mime_type);
2709 fill_in_names (GtkFileFolderWin32 *folder_win32, GError **error)
2713 if (folder_win32->stat_info)
2716 dir = g_dir_open (folder_win32->filename, 0, error);
2720 folder_win32->stat_info = g_hash_table_new_full (casefolded_hash, casefolded_equal,
2721 (GDestroyNotify) g_free,
2722 (GDestroyNotify) free_stat_info_entry);
2726 struct stat_info_entry *entry;
2727 const gchar *basename;
2729 basename = g_dir_read_name (dir);
2733 entry = g_new0 (struct stat_info_entry, 1);
2734 if (folder_win32->is_network_dir)
2736 g_assert_not_reached ();
2737 entry->wfad.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
2738 entry->mime_type = g_strdup ("x-directory/normal");
2741 g_hash_table_insert (folder_win32->stat_info,
2742 g_strdup (basename),
2748 folder_win32->asof = time (NULL);
2753 cb_fill_in_stats (gpointer key, gpointer value, gpointer user_data)
2755 const char *basename = key;
2756 struct stat_info_entry *entry = value;
2757 GtkFileFolderWin32 *folder_win32 = user_data;
2758 char *fullname = g_build_filename (folder_win32->filename, basename, NULL);
2761 if (!stat_with_error (fullname, &entry->wfad, NULL))
2762 result = TRUE; /* Couldn't stat -- remove from hash. */
2772 fill_in_stats (GtkFileFolderWin32 *folder_win32)
2774 if (folder_win32->have_stat)
2777 if (!fill_in_names (folder_win32, NULL))
2780 if (!folder_win32->is_network_dir)
2781 g_hash_table_foreach_remove (folder_win32->stat_info,
2785 folder_win32->have_stat = TRUE;
2789 cb_fill_in_mime_type (gpointer key, gpointer value, gpointer user_data)
2791 const char *basename = key;
2792 struct stat_info_entry *entry = value;
2793 GtkFileFolderWin32 *folder_win32 = user_data;
2794 char *fullname = g_build_filename (folder_win32->filename, basename, NULL);
2796 entry->mime_type = get_mime_type_for_file (fullname, &entry->wfad);
2804 fill_in_mime_type (GtkFileFolderWin32 *folder_win32)
2806 if (folder_win32->have_mime_type)
2809 if (!folder_win32->have_stat)
2812 g_assert (folder_win32->stat_info != NULL);
2814 if (!folder_win32->is_network_dir)
2815 g_hash_table_foreach_remove (folder_win32->stat_info,
2816 cb_fill_in_mime_type,
2819 folder_win32->have_mime_type = TRUE;
2822 static GtkFilePath *
2823 filename_to_path (const char *filename)
2827 tmp = remove_trailing_slash (filename);
2828 return gtk_file_path_new_steal (tmp);
2832 filename_is_root (const char *filename)
2834 const gchar *after_root;
2836 after_root = g_path_skip_root (filename);
2838 return (after_root != NULL && *after_root == '\0');
2842 filename_is_drive_root (const char *filename)
2844 guint len = strlen (filename);
2847 g_ascii_isalpha (filename[0]) &&
2848 filename[1] == ':' &&
2849 G_IS_DIR_SEPARATOR (filename[2]));
2853 filename_is_some_root (const char *filename)
2855 return (g_path_is_absolute (filename) &&
2856 *(g_path_skip_root (filename)) == '\0');
2860 _gtk_file_system_win32_path_compare (const gchar *path1,
2863 while (*path1 && *path2)
2865 gunichar c1 = g_utf8_get_char (path1);
2866 gunichar c2 = g_utf8_get_char (path2);
2868 (G_IS_DIR_SEPARATOR (c1) && G_IS_DIR_SEPARATOR (c1)) ||
2869 g_unichar_toupper (c1) == g_unichar_toupper (c2))
2871 path1 = g_utf8_next_char (path1);
2872 path2 = g_utf8_next_char (path2);
2877 if (!*path1 && !*path2)
2884 return g_unichar_toupper (g_utf8_get_char (path1)) - g_unichar_toupper (g_utf8_get_char (path2));
2887 #define __GTK_FILE_SYSTEM_WIN32_C__
2888 #include "gtkaliasdef.c"