From 215cabd938150ecfa32d50ac48ac43d00819e596 Mon Sep 17 00:00:00 2001 From: Tor Lillqvist Date: Thu, 22 Apr 2004 05:08:19 +0000 Subject: [PATCH] Fix the file chooser on Windows. I can't make it misbehave or crash any 2004-04-22 Tor Lillqvist Fix the file chooser on Windows. I can't make it misbehave or crash any more now. But presumably there are still corner cases not handled. I haven't really checked behaviour of UNC paths, for instance. * gtk/gtkfilesystemwin32.c: Accept both backslash and slash in several places. Use G_IS_DIR_SEPARATOR macro (which could be added to GLib in 2.6). (gtk_file_system_win32_get_parent): Like the Unix version, assert filename is absolute, and avoid one unnecessary string allocation and freeing. (canonicalize_filename,gtk_file_system_win32_parse): Handle drive letters more correctly. (gtk_file_system_win32_render_icon): Assure correct syntax is used for root folder of a drive. (#137962, Morten Welinder) (filename_is_some_root): New function that accepts also root without any drive specified. (filename_is_drive_root): Rename from filename_is_root. * gtk/gtkfilechooserentry.c (completion_match_func): Casefold on Windows. --- ChangeLog | 29 ++++++++++ ChangeLog.pre-2-10 | 29 ++++++++++ ChangeLog.pre-2-4 | 29 ++++++++++ ChangeLog.pre-2-6 | 29 ++++++++++ ChangeLog.pre-2-8 | 29 ++++++++++ gtk/gtkfilechooserentry.c | 26 ++++++--- gtk/gtkfilesystemwin32.c | 109 ++++++++++++++++++++++++++++---------- 7 files changed, 247 insertions(+), 33 deletions(-) diff --git a/ChangeLog b/ChangeLog index f72369a377..65e1c69bdb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,32 @@ +2004-04-22 Tor Lillqvist + + Fix the file chooser on Windows. I can't make it misbehave or + crash any more now. But presumably there are still corner cases + not handled. I haven't really checked behaviour of UNC paths, for + instance. + + * gtk/gtkfilesystemwin32.c: Accept both backslash and slash in + several places. Use G_IS_DIR_SEPARATOR macro (which could be added + to GLib in 2.6). + + (gtk_file_system_win32_get_parent): Like the Unix version, assert + filename is absolute, and avoid one unnecessary string allocation + and freeing. + + (canonicalize_filename,gtk_file_system_win32_parse): Handle drive + letters more correctly. + + (gtk_file_system_win32_render_icon): Assure correct syntax is used + for root folder of a drive. (#137962, Morten Welinder) + + (filename_is_some_root): New function that accepts also root + without any drive specified. + + (filename_is_drive_root): Rename from filename_is_root. + + * gtk/gtkfilechooserentry.c (completion_match_func): Casefold on + Windows. + 2004-04-21 Matthias Clasen * gtk/gtkentry.c (gtk_entry_completion_timeout): Pop down the diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index f72369a377..65e1c69bdb 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,3 +1,32 @@ +2004-04-22 Tor Lillqvist + + Fix the file chooser on Windows. I can't make it misbehave or + crash any more now. But presumably there are still corner cases + not handled. I haven't really checked behaviour of UNC paths, for + instance. + + * gtk/gtkfilesystemwin32.c: Accept both backslash and slash in + several places. Use G_IS_DIR_SEPARATOR macro (which could be added + to GLib in 2.6). + + (gtk_file_system_win32_get_parent): Like the Unix version, assert + filename is absolute, and avoid one unnecessary string allocation + and freeing. + + (canonicalize_filename,gtk_file_system_win32_parse): Handle drive + letters more correctly. + + (gtk_file_system_win32_render_icon): Assure correct syntax is used + for root folder of a drive. (#137962, Morten Welinder) + + (filename_is_some_root): New function that accepts also root + without any drive specified. + + (filename_is_drive_root): Rename from filename_is_root. + + * gtk/gtkfilechooserentry.c (completion_match_func): Casefold on + Windows. + 2004-04-21 Matthias Clasen * gtk/gtkentry.c (gtk_entry_completion_timeout): Pop down the diff --git a/ChangeLog.pre-2-4 b/ChangeLog.pre-2-4 index f72369a377..65e1c69bdb 100644 --- a/ChangeLog.pre-2-4 +++ b/ChangeLog.pre-2-4 @@ -1,3 +1,32 @@ +2004-04-22 Tor Lillqvist + + Fix the file chooser on Windows. I can't make it misbehave or + crash any more now. But presumably there are still corner cases + not handled. I haven't really checked behaviour of UNC paths, for + instance. + + * gtk/gtkfilesystemwin32.c: Accept both backslash and slash in + several places. Use G_IS_DIR_SEPARATOR macro (which could be added + to GLib in 2.6). + + (gtk_file_system_win32_get_parent): Like the Unix version, assert + filename is absolute, and avoid one unnecessary string allocation + and freeing. + + (canonicalize_filename,gtk_file_system_win32_parse): Handle drive + letters more correctly. + + (gtk_file_system_win32_render_icon): Assure correct syntax is used + for root folder of a drive. (#137962, Morten Welinder) + + (filename_is_some_root): New function that accepts also root + without any drive specified. + + (filename_is_drive_root): Rename from filename_is_root. + + * gtk/gtkfilechooserentry.c (completion_match_func): Casefold on + Windows. + 2004-04-21 Matthias Clasen * gtk/gtkentry.c (gtk_entry_completion_timeout): Pop down the diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6 index f72369a377..65e1c69bdb 100644 --- a/ChangeLog.pre-2-6 +++ b/ChangeLog.pre-2-6 @@ -1,3 +1,32 @@ +2004-04-22 Tor Lillqvist + + Fix the file chooser on Windows. I can't make it misbehave or + crash any more now. But presumably there are still corner cases + not handled. I haven't really checked behaviour of UNC paths, for + instance. + + * gtk/gtkfilesystemwin32.c: Accept both backslash and slash in + several places. Use G_IS_DIR_SEPARATOR macro (which could be added + to GLib in 2.6). + + (gtk_file_system_win32_get_parent): Like the Unix version, assert + filename is absolute, and avoid one unnecessary string allocation + and freeing. + + (canonicalize_filename,gtk_file_system_win32_parse): Handle drive + letters more correctly. + + (gtk_file_system_win32_render_icon): Assure correct syntax is used + for root folder of a drive. (#137962, Morten Welinder) + + (filename_is_some_root): New function that accepts also root + without any drive specified. + + (filename_is_drive_root): Rename from filename_is_root. + + * gtk/gtkfilechooserentry.c (completion_match_func): Casefold on + Windows. + 2004-04-21 Matthias Clasen * gtk/gtkentry.c (gtk_entry_completion_timeout): Pop down the diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index f72369a377..65e1c69bdb 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,3 +1,32 @@ +2004-04-22 Tor Lillqvist + + Fix the file chooser on Windows. I can't make it misbehave or + crash any more now. But presumably there are still corner cases + not handled. I haven't really checked behaviour of UNC paths, for + instance. + + * gtk/gtkfilesystemwin32.c: Accept both backslash and slash in + several places. Use G_IS_DIR_SEPARATOR macro (which could be added + to GLib in 2.6). + + (gtk_file_system_win32_get_parent): Like the Unix version, assert + filename is absolute, and avoid one unnecessary string allocation + and freeing. + + (canonicalize_filename,gtk_file_system_win32_parse): Handle drive + letters more correctly. + + (gtk_file_system_win32_render_icon): Assure correct syntax is used + for root folder of a drive. (#137962, Morten Welinder) + + (filename_is_some_root): New function that accepts also root + without any drive specified. + + (filename_is_drive_root): Rename from filename_is_root. + + * gtk/gtkfilechooserentry.c (completion_match_func): Casefold on + Windows. + 2004-04-21 Matthias Clasen * gtk/gtkentry.c (gtk_entry_completion_timeout): Pop down the diff --git a/gtk/gtkfilechooserentry.c b/gtk/gtkfilechooserentry.c index 9c7380d5e2..6bc9d8a9a3 100644 --- a/gtk/gtkfilechooserentry.c +++ b/gtk/gtkfilechooserentry.c @@ -99,7 +99,6 @@ static char *maybe_append_separator_to_path (GtkFileChooserEntry *chooser_ent GtkFilePath *path, gchar *display_name); - static GObjectClass *parent_class; static GtkEditableClass *parent_editable_iface; @@ -317,6 +316,20 @@ completion_match_func (GtkEntryCompletion *comp, norm_file_part = g_utf8_normalize (chooser_entry->file_part, -1, G_NORMALIZE_ALL); norm_name = g_utf8_normalize (name, -1, G_NORMALIZE_ALL); +#ifdef G_PLATFORM_WIN32 + { + gchar *temp; + + temp = norm_file_part; + norm_file_part = g_utf8_casefold (norm_file_part, -1); + g_free (temp); + + temp = norm_name; + norm_name = g_utf8_casefold (norm_name, -1); + g_free (temp); + } +#endif + result = (strncmp (norm_file_part, norm_name, strlen (norm_file_part)) == 0); g_free (norm_file_part); @@ -326,10 +339,11 @@ completion_match_func (GtkEntryCompletion *comp, return result; } -/* This function will append a '/' character to paths to display_name iff the - * path associated with it is a directory. maybe_append_separator_to_path will - * g_free the display_name and return a new one if needed. Otherwise, it will - * return the old one. You should be safe calling +/* This function will append a directory separator to paths to + * display_name iff the path associated with it is a directory. + * maybe_append_separator_to_path will g_free the display_name and + * return a new one if needed. Otherwise, it will return the old one. + * You should be safe calling * * display_name = maybe_append_separator_to_path (entry, path, display_name); * ... @@ -352,7 +366,7 @@ maybe_append_separator_to_path (GtkFileChooserEntry *chooser_entry, if (gtk_file_info_get_is_folder (info)) { gchar *tmp = display_name; - display_name = g_strconcat (tmp, "/", NULL); + display_name = g_strconcat (tmp, G_DIR_SEPARATOR_S, NULL); g_free (tmp); } diff --git a/gtk/gtkfilesystemwin32.c b/gtk/gtkfilesystemwin32.c index cc70b23227..3d2c5aec16 100644 --- a/gtk/gtkfilesystemwin32.c +++ b/gtk/gtkfilesystemwin32.c @@ -43,6 +43,10 @@ #error "The implementation is win32 only." #endif /* G_OS_WIN32 */ +#ifndef G_IS_DIR_SEPARATOR +#define G_IS_DIR_SEPARATOR(c) ((c) == G_DIR_SEPARATOR || (c) == '/') +#endif + typedef struct _GtkFileSystemWin32Class GtkFileSystemWin32Class; #define GTK_FILE_SYSTEM_WIN32_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_FILE_SYSTEM_WIN32, GtkFileSystemWin32Class)) @@ -173,7 +177,8 @@ static gboolean gtk_file_folder_win32_list_children (GtkFileFolder static gchar * filename_from_path (const GtkFilePath *path); static GtkFilePath * filename_to_path (const gchar *filename); -static gboolean filename_is_root (const char *filename); +static gboolean filename_is_drive_root (const char *filename); +static gboolean filename_is_some_root (const char *filename); static GtkFileInfo * filename_get_info (const gchar *filename, GtkFileInfoType types, GError **error); @@ -430,7 +435,7 @@ gtk_file_system_win32_create_folder (GtkFileSystem *file_system, g_strerror (errno)); g_free (filename_utf8); } - else if (!filename_is_root (filename)) + else if (!filename_is_drive_root (filename)) { parent = g_path_get_dirname (filename); if (parent) @@ -565,10 +570,13 @@ gtk_file_system_win32_get_parent (GtkFileSystem *file_system, GtkFilePath **parent, GError **error) { - gchar *filename = filename_from_path (path); - g_return_val_if_fail (filename != NULL, FALSE); + const char *filename; - if (filename_is_root (filename)) + filename = gtk_file_path_get_string (path); + g_return_val_if_fail (filename != NULL, FALSE); + g_return_val_if_fail (g_path_is_absolute (filename), FALSE); + + if (filename_is_some_root (filename)) { *parent = NULL; } @@ -579,8 +587,6 @@ gtk_file_system_win32_get_parent (GtkFileSystem *file_system, g_free (parent_filename); } - g_free (filename); - return TRUE; } @@ -630,14 +636,27 @@ static void canonicalize_filename (gchar *filename) { gchar *p, *q; + gchar *past_root; gboolean last_was_slash = FALSE; +#if 0 + printf("canonicalize_filename: %s ", filename); +#endif + p = filename; q = filename; + if (g_ascii_isalpha (*filename) && + filename[1] == ':' && + G_IS_DIR_SEPARATOR (filename[2])) + past_root = filename + 3; + else + past_root = filename + 1; + + while (*p) { - if (*p == G_DIR_SEPARATOR) + if (G_IS_DIR_SEPARATOR (*p)) { if (!last_was_slash) *q++ = G_DIR_SEPARATOR; @@ -648,7 +667,7 @@ canonicalize_filename (gchar *filename) { if (last_was_slash && *p == '.') { - if (*(p + 1) == G_DIR_SEPARATOR || + if (G_IS_DIR_SEPARATOR (*(p + 1)) || *(p + 1) == '\0') { if (*(p + 1) == '\0') @@ -657,14 +676,14 @@ canonicalize_filename (gchar *filename) p += 1; } else if (*(p + 1) == '.' && - (*(p + 2) == G_DIR_SEPARATOR || + (G_IS_DIR_SEPARATOR (*(p + 2)) || *(p + 2) == '\0')) { - if (q > filename + 1) + if (q > past_root) { q--; - while (q > filename + 1 && - *(q - 1) != G_DIR_SEPARATOR) + while (q > past_root && + !G_IS_DIR_SEPARATOR (*(q - 1))) q--; } @@ -689,10 +708,13 @@ canonicalize_filename (gchar *filename) p++; } - if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR) + if (q > past_root && G_IS_DIR_SEPARATOR (*(q - 1))) q--; *q = '\0'; +#if 0 + printf(" => %s\n", filename); +#endif } static gboolean @@ -704,14 +726,23 @@ gtk_file_system_win32_parse (GtkFileSystem *file_system, GError **error) { const char *base_filename; - gchar *last_slash; + gchar *last_backslash, *last_slash; gboolean result = FALSE; +#if 0 + printf("gtk_file_system_win32_parse: base_path=%s str=%s\n",(char*)base_path,str); +#endif + base_filename = gtk_file_path_get_string (base_path); g_return_val_if_fail (base_filename != NULL, FALSE); g_return_val_if_fail (g_path_is_absolute (base_filename), FALSE); - last_slash = strrchr (str, G_DIR_SEPARATOR); + last_backslash = strrchr (str, G_DIR_SEPARATOR); + last_slash = strrchr (str, '/'); + if (last_slash == NULL || + (last_backslash != NULL && last_backslash > last_slash)) + last_slash = last_backslash; + if (!last_slash) { *folder = gtk_file_path_copy (base_path); @@ -725,7 +756,19 @@ gtk_file_system_win32_parse (GtkFileSystem *file_system, GError *tmp_error = NULL; if (last_slash == str) - folder_part = g_strdup ("/"); + { + if (g_ascii_isalpha (base_filename[0]) && + base_filename[1] == ':') + folder_part = g_strdup_printf ("%c:%c", base_filename[0], + G_DIR_SEPARATOR); + else + folder_part = g_strdup (G_DIR_SEPARATOR_S); + } + else if (g_ascii_isalpha (str[0]) && + str[1] == ':' && + G_IS_DIR_SEPARATOR (str[2])) + folder_part = g_filename_from_utf8 (str, last_slash - str + 1, + NULL, NULL, &tmp_error); else folder_part = g_filename_from_utf8 (str, last_slash - str, NULL, NULL, &tmp_error); @@ -741,7 +784,7 @@ gtk_file_system_win32_parse (GtkFileSystem *file_system, } else { - if (folder_part[1] == ':') + if (g_path_is_absolute (folder_part)) folder_path = folder_part; else { @@ -760,6 +803,10 @@ gtk_file_system_win32_parse (GtkFileSystem *file_system, } } +#if 0 + printf("gtk_file_system_win32_parse:returning folder=%s file_part=%s\n",(*folder?(char*)*folder:"NULL"),*file_part); +#endif + return result; } @@ -985,10 +1032,13 @@ gtk_file_system_win32_render_icon (GtkFileSystem *file_system, const char* filename = gtk_file_path_get_string (path); /* handle drives with stock icons */ - if (filename_is_root (filename)) + if (filename_is_drive_root (filename)) { - gchar *filename2 = g_strconcat(filename, "\\", NULL); - DWORD dt = GetDriveType (filename2); + gchar filename2[] = "?:\\"; + DWORD dt; + + filename2[0] = filename[0]; + dt = GetDriveType (filename2); switch (dt) { @@ -1004,7 +1054,6 @@ gtk_file_system_win32_render_icon (GtkFileSystem *file_system, default : break; } - g_free (filename2); } else if (g_file_test (filename, G_FILE_TEST_IS_DIR)) { @@ -1180,7 +1229,7 @@ gtk_file_folder_win32_get_info (GtkFileFolder *folder, if (!path) { - g_return_val_if_fail (filename_is_root (folder_win32->filename), NULL); + g_return_val_if_fail (filename_is_some_root (folder_win32->filename), NULL); /* ??? */ info = filename_get_info (folder_win32->filename, folder_win32->types, error); @@ -1276,7 +1325,7 @@ filename_get_info (const gchar *filename, info = gtk_file_info_new (); - if (filename_is_root (filename)) + if (filename_is_some_root (filename)) { if (types & GTK_FILE_INFO_DISPLAY_NAME) gtk_file_info_set_display_name (info, filename); @@ -1377,12 +1426,18 @@ filename_to_path (const char *filename) } static gboolean -filename_is_root (const char *filename) +filename_is_drive_root (const char *filename) { - guint len = strlen(filename); + guint len = strlen (filename); /* accept both forms */ - return (len == 3 && filename[1] == ':' && (filename[2] == '\\' || filename[2] == '/')); + return (len == 3 && filename[1] == ':' && G_IS_DIR_SEPARATOR (filename[2])); } +static gboolean +filename_is_some_root (const char *filename) +{ + return (G_IS_DIR_SEPARATOR (filename[0]) && filename[1] == '\0') || + filename_is_drive_root (filename); +}