]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkfilesel.c
Fix obvious reallocation bug in rarely or never hit code path (#118071,
[~andy/gtk] / gtk / gtkfilesel.c
index c8c2df3707afd006a5aab0766c45174e1d19122d..647deb45387c3480101d33a47cfc044cbb2685d0 100644 (file)
 #include <pwd.h>
 #endif
 
-#include <glib.h>              /* Include early to get G_OS_WIN32 and
-                                * G_WITH_CYGWIN */
+#include <glib.h>              /* Include early to get G_OS_WIN32 etc */
 
-#if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
+#if defined(G_PLATFORM_WIN32)
 #include <ctype.h>
 #define STRICT
 #include <windows.h>
 #undef STRICT
-#endif /* G_OS_WIN32 || G_WITH_CYGWIN */
-#ifdef HAVE_WINSOCK_H
+#endif /* G_PLATFORM_WIN32 */
+#ifdef G_OS_WIN32
 #include <winsock.h>           /* For gethostname */
 #endif
 
-#include "fnmatch.h"
-
 #include "gdk/gdkkeysyms.h"
 #include "gtkbutton.h"
 #include "gtkcellrenderertext.h"
 #include "gtkfilesel.h"
 #include "gtkhbox.h"
 #include "gtkhbbox.h"
+#include "gtkintl.h"
 #include "gtklabel.h"
 #include "gtkliststore.h"
 #include "gtkmain.h"
+#include "gtkprivate.h"
 #include "gtkscrolledwindow.h"
 #include "gtkstock.h"
-#include "gtksignal.h"
 #include "gtktreeselection.h"
 #include "gtktreeview.h"
 #include "gtkvbox.h"
 #include "gtkoptionmenu.h"
 #include "gtkdialog.h"
 #include "gtkmessagedialog.h"
-#include "gtkintl.h"
 #include "gtkdnd.h"
 #include "gtkeventbox.h"
 
+#define WANT_HPANED 1
+#include "gtkhpaned.h"
 
 #ifdef G_OS_WIN32
 #include <direct.h>
 #endif
 #endif /* G_OS_WIN32 */
 
+#ifdef G_WITH_CYGWIN
+#include <sys/cygwin.h>                /* For cygwin_conv_to_posix_path */
+#endif
+
 #define DIR_LIST_WIDTH   180
 #define DIR_LIST_HEIGHT  180
 #define FILE_LIST_WIDTH  180
@@ -137,10 +140,6 @@ typedef struct _PossibleCompletion PossibleCompletion;
  * match by first_diff_index()
  */
 #define PATTERN_MATCH -1
-/* The arguments used by all fnmatch() calls below
- */
-#define FNMATCH_FLAGS (FNM_PATHNAME | FNM_PERIOD)
-
 #define CMPL_ERRNO_TOO_LONG ((1<<16)-1)
 #define CMPL_ERRNO_DID_NOT_CONVERT ((1<<16)-2)
 
@@ -150,9 +149,11 @@ typedef struct _PossibleCompletion PossibleCompletion;
  */
 struct _CompletionDirSent
 {
+#ifndef G_PLATFORM_WIN32
   ino_t inode;
   time_t mtime;
   dev_t device;
+#endif
 
   gint entry_count;
   struct _CompletionDirEntry *entries;
@@ -182,6 +183,7 @@ struct _CompletionDirEntry
 {
   gboolean is_dir;
   gchar *entry_name;
+  gchar *sort_key;
 };
 
 struct _CompletionUserDir
@@ -307,14 +309,14 @@ static gint                cmpl_last_valid_char    (CompletionState* cmpl_state)
 /* When the user selects a non-directory, call cmpl_completion_fullname
  * to get the full name of the selected file.
  */
-static gchar*              cmpl_completion_fullname (const gchar*, CompletionState* cmpl_state);
+static const gchar*        cmpl_completion_fullname (const gchar*, CompletionState* cmpl_state);
 
 
 /* Directory operations. */
 static CompletionDir* open_ref_dir         (gchar* text_to_complete,
                                            gchar** remaining_text,
                                            CompletionState* cmpl_state);
-#if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
+#ifndef G_PLATFORM_WIN32
 static gboolean       check_dir            (gchar *dir_name, 
                                            struct stat *result, 
                                            gboolean *stat_subdirs);
@@ -333,7 +335,7 @@ static CompletionDirSent* open_new_dir     (gchar* dir_name,
 static gint           correct_dir_fullname (CompletionDir* cmpl_dir);
 static gint           correct_parent       (CompletionDir* cmpl_dir,
                                            struct stat *sbuf);
-#ifndef G_OS_WIN32
+#ifndef G_PLATFORM_WIN32
 static gchar*         find_parent_dir_fullname    (gchar* dirname);
 #endif
 static CompletionDir* attach_dir           (CompletionDirSent* sent,
@@ -384,6 +386,7 @@ static gint gtk_file_selection_insert_text   (GtkWidget             *widget,
                                              gint                   new_text_length,
                                              gint                  *position,
                                              gpointer               user_data);
+static void gtk_file_selection_update_fileops (GtkFileSelection     *filesel);
 
 static void gtk_file_selection_file_activate (GtkTreeView       *tree_view,
                                              GtkTreePath       *path,
@@ -411,12 +414,50 @@ static void gtk_file_selection_rename_file (GtkWidget *widget, gpointer data);
 
 static void free_selected_names (GPtrArray *names);
 
-#if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
-#define compare_filenames(a, b) strcmp(a, b)
+#ifndef G_PLATFORM_WIN32
+
+#define compare_utf8_filenames(a, b) strcmp(a, b)
+#define compare_sys_filenames(a, b) strcmp(a, b)
+
 #else
-#define compare_filenames(a, b) g_ascii_strcasecmp(a, b)
-#endif
 
+static gint
+compare_utf8_filenames (const gchar *a,
+                       const gchar *b)
+{
+  gchar *a_folded, *b_folded;
+  gint retval;
+
+  a_folded = g_utf8_strdown (a, -1);
+  b_folded = g_utf8_strdown (b, -1);
+
+  retval = strcmp (a_folded, b_folded);
+
+  g_free (a_folded);
+  g_free (b_folded);
+
+  return retval;
+}
+
+static gint
+compare_sys_filenames (const gchar *a,
+                      const gchar *b)
+{
+  gchar *a_utf8, *b_utf8;
+  gint retval;
+
+  a_utf8 = g_filename_to_utf8 (a, -1, NULL, NULL, NULL);
+  b_utf8 = g_filename_to_utf8 (b, -1, NULL, NULL, NULL);
+
+  retval = compare_utf8_filenames (a_utf8, b_utf8);
+
+  g_free (a_utf8);
+  g_free (b_utf8);
+
+  return retval;
+}
+
+#endif
 
 static GtkWindowClass *parent_class = NULL;
 
@@ -427,11 +468,11 @@ static gint cmpl_errno;
 /*
  * Take the path currently in the file selection
  * entry field and translate as necessary from
- * a WIN32 style to CYGWIN32 style path.  For
+ * a Win32 style to Cygwin style path.  For
  * instance translate:
  * x:\somepath\file.jpg
  * to:
- * //x/somepath/file.jpg
+ * /cygdrive/x/somepath/file.jpg
  *
  * Replace the path in the selection text field.
  * Return a boolean value concerning whether a
@@ -441,71 +482,47 @@ static int
 translate_win32_path (GtkFileSelection *filesel)
 {
   int updated = 0;
-  gchar *path;
+  const gchar *path;
+  gchar newPath[MAX_PATH];
 
   /*
    * Retrieve the current path
    */
   path = gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry));
 
-  /*
-   * Translate only if this looks like a DOS-ish
-   * path... First handle any drive letters.
-   */
-  if (isalpha (path[0]) && (path[1] == ':')) {
-    /*
-     * This part kind of stinks... It isn't possible
-     * to know if there is enough space in the current
-     * string for the extra character required in this
-     * conversion.  Assume that there isn't enough space
-     * and use the set function on the text field to
-     * set the newly created string.
-     */
-    gchar *newPath = g_strdup_printf ("//%c/%s", path[0], (path + 3));
-    gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), newPath);
-
-    path = newPath;
-    updated = 1;
-  }
+  cygwin_conv_to_posix_path (path, newPath);
+  updated = (strcmp (path, newPath) != 0);
 
-  /*
-   * Now, replace backslashes with forward slashes 
-   * if necessary.
-   */
-  if (strchr (path, '\\'))
-    {
-      int index;
-      for (index = 0; path[index] != '\0'; index++)
-       if (path[index] == '\\')
-         path[index] = '/';
-      
-      updated = 1;
-    }
+  if (updated)
+    gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), newPath);
     
   return updated;
 }
 #endif
 
-GtkType
+GType
 gtk_file_selection_get_type (void)
 {
-  static GtkType file_selection_type = 0;
+  static GType file_selection_type = 0;
 
   if (!file_selection_type)
     {
-      static const GtkTypeInfo filesel_info =
+      static const GTypeInfo filesel_info =
       {
-       "GtkFileSelection",
-       sizeof (GtkFileSelection),
        sizeof (GtkFileSelectionClass),
-       (GtkClassInitFunc) gtk_file_selection_class_init,
-       (GtkObjectInitFunc) gtk_file_selection_init,
-       /* reserved_1 */ NULL,
-       /* reserved_2 */ NULL,
-        (GtkClassInitFunc) NULL,
+       NULL,           /* base_init */
+       NULL,           /* base_finalize */
+       (GClassInitFunc) gtk_file_selection_class_init,
+       NULL,           /* class_finalize */
+       NULL,           /* class_data */
+       sizeof (GtkFileSelection),
+       0,              /* n_preallocs */
+       (GInstanceInitFunc) gtk_file_selection_init,
       };
 
-      file_selection_type = gtk_type_unique (GTK_TYPE_DIALOG, &filesel_info);
+      file_selection_type =
+       g_type_register_static (GTK_TYPE_DIALOG, "GtkFileSelection",
+                               &filesel_info, 0);
     }
 
   return file_selection_type;
@@ -522,7 +539,7 @@ gtk_file_selection_class_init (GtkFileSelectionClass *class)
   object_class = (GtkObjectClass*) class;
   widget_class = (GtkWidgetClass*) class;
 
-  parent_class = gtk_type_class (GTK_TYPE_DIALOG);
+  parent_class = g_type_class_peek_parent (class);
 
   gobject_class->finalize = gtk_file_selection_finalize;
   gobject_class->set_property = gtk_file_selection_set_property;
@@ -532,14 +549,14 @@ gtk_file_selection_class_init (GtkFileSelectionClass *class)
                                    PROP_FILENAME,
                                    g_param_spec_string ("filename",
                                                         _("Filename"),
-                                                        _("The currently selected filename."),
+                                                        _("The currently selected filename"),
                                                         NULL,
                                                         G_PARAM_READABLE | G_PARAM_WRITABLE));
   g_object_class_install_property (gobject_class,
                                   PROP_SHOW_FILEOPS,
                                   g_param_spec_boolean ("show_fileops",
                                                         _("Show file operations"),
-                                                        _("Whether buttons for creating/manipulating files should be displayed."),
+                                                        _("Whether buttons for creating/manipulating files should be displayed"),
                                                         FALSE,
                                                         G_PARAM_READABLE |
                                                         G_PARAM_WRITABLE));
@@ -547,7 +564,7 @@ gtk_file_selection_class_init (GtkFileSelectionClass *class)
                                   PROP_SELECT_MULTIPLE,
                                   g_param_spec_boolean ("select_multiple",
                                                         _("Select multiple"),
-                                                        _("Whether to allow multiple files to be selected."),
+                                                        _("Whether to allow multiple files to be selected"),
                                                         FALSE,
                                                         G_PARAM_READABLE |
                                                         G_PARAM_WRITABLE));
@@ -630,11 +647,12 @@ gtk_file_selection_init (GtkFileSelection *filesel)
 {
   GtkWidget *entry_vbox;
   GtkWidget *label;
-  GtkWidget *list_hbox;
+  GtkWidget *list_hbox, *list_container;
   GtkWidget *confirm_area;
   GtkWidget *pulldown_hbox;
   GtkWidget *scrolled_win;
   GtkWidget *eventbox;
+  GtkWidget *spacer;
   GtkDialog *dialog;
 
   GtkListStore *model;
@@ -672,10 +690,29 @@ gtk_file_selection_init (GtkFileSelection *filesel)
                      FALSE, FALSE, 0);
     
   /*  The horizontal box containing the directory and file listboxes  */
+
+  spacer = gtk_hbox_new (FALSE, 0);
+  gtk_widget_set_size_request (spacer, -1, 5);
+  gtk_box_pack_start (GTK_BOX (filesel->main_vbox), spacer, FALSE, FALSE, 0);
+  gtk_widget_show (spacer);
+  
   list_hbox = gtk_hbox_new (FALSE, 5);
   gtk_box_pack_start (GTK_BOX (filesel->main_vbox), list_hbox, TRUE, TRUE, 0);
   gtk_widget_show (list_hbox);
+  if (WANT_HPANED)
+    list_container = g_object_new (GTK_TYPE_HPANED,
+                                  "visible", TRUE,
+                                  "parent", list_hbox,
+                                  "border_width", 0,
+                                  NULL);
+  else
+    list_container = list_hbox;
 
+  spacer = gtk_hbox_new (FALSE, 0);
+  gtk_widget_set_size_request (spacer, -1, 5);
+  gtk_box_pack_start (GTK_BOX (filesel->main_vbox), spacer, FALSE, FALSE, 0);  
+  gtk_widget_show (spacer);
+  
   /* The directories list */
 
   model = gtk_list_store_new (1, G_TYPE_STRING);
@@ -693,7 +730,8 @@ gtk_file_selection_init (GtkFileSelection *filesel)
   gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
   gtk_tree_view_append_column (GTK_TREE_VIEW (filesel->dir_list), column);
 
-  gtk_widget_set_usize (filesel->dir_list, DIR_LIST_WIDTH, DIR_LIST_HEIGHT);
+  gtk_widget_set_size_request (filesel->dir_list,
+                              DIR_LIST_WIDTH, DIR_LIST_HEIGHT);
   g_signal_connect (filesel->dir_list, "row_activated",
                    G_CALLBACK (gtk_file_selection_dir_activate), filesel);
 
@@ -704,8 +742,11 @@ gtk_file_selection_init (GtkFileSelection *filesel)
   gtk_container_add (GTK_CONTAINER (scrolled_win), filesel->dir_list);
   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
                                  GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
-  gtk_container_set_border_width (GTK_CONTAINER (scrolled_win), 5);
-  gtk_box_pack_start (GTK_BOX (list_hbox), scrolled_win, TRUE, TRUE, 0);
+  gtk_container_set_border_width (GTK_CONTAINER (scrolled_win), 0);
+  if (GTK_IS_PANED (list_container))
+    gtk_paned_pack1 (GTK_PANED (list_container), scrolled_win, TRUE, TRUE);
+  else
+    gtk_container_add (GTK_CONTAINER (list_container), scrolled_win);
   gtk_widget_show (filesel->dir_list);
   gtk_widget_show (scrolled_win);
 
@@ -725,7 +766,8 @@ gtk_file_selection_init (GtkFileSelection *filesel)
   gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
   gtk_tree_view_append_column (GTK_TREE_VIEW (filesel->file_list), column);
 
-  gtk_widget_set_usize (filesel->file_list, FILE_LIST_WIDTH, FILE_LIST_HEIGHT);
+  gtk_widget_set_size_request (filesel->file_list,
+                              FILE_LIST_WIDTH, FILE_LIST_HEIGHT);
   g_signal_connect (filesel->file_list, "row_activated",
                    G_CALLBACK (gtk_file_selection_file_activate), filesel);
   g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel->file_list)), "changed",
@@ -738,8 +780,8 @@ gtk_file_selection_init (GtkFileSelection *filesel)
   gtk_container_add (GTK_CONTAINER (scrolled_win), filesel->file_list);
   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
                                  GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
-  gtk_container_set_border_width (GTK_CONTAINER (scrolled_win), 5);
-  gtk_box_pack_start (GTK_BOX (list_hbox), scrolled_win, TRUE, TRUE, 0);
+  gtk_container_set_border_width (GTK_CONTAINER (scrolled_win), 0);
+  gtk_container_add (GTK_CONTAINER (list_container), scrolled_win);
   gtk_widget_show (filesel->file_list);
   gtk_widget_show (scrolled_win);
 
@@ -777,24 +819,30 @@ gtk_file_selection_init (GtkFileSelection *filesel)
   gtk_widget_show (eventbox);
 
   filesel->selection_entry = gtk_entry_new ();
-  gtk_signal_connect (GTK_OBJECT (filesel->selection_entry), "key_press_event",
-                     (GtkSignalFunc) gtk_file_selection_key_press, filesel);
-  gtk_signal_connect (GTK_OBJECT (filesel->selection_entry), "insert_text",
-                     (GtkSignalFunc) gtk_file_selection_insert_text, NULL);
-  gtk_signal_connect_object (GTK_OBJECT (filesel->selection_entry), "focus_in_event",
-                            (GtkSignalFunc) grab_default,
-                            GTK_OBJECT (filesel->ok_button));
-  gtk_signal_connect_object (GTK_OBJECT (filesel->selection_entry), "activate",
-                             (GtkSignalFunc) gtk_button_clicked,
-                             GTK_OBJECT (filesel->ok_button));
+  g_signal_connect (filesel->selection_entry, "key_press_event",
+                   G_CALLBACK (gtk_file_selection_key_press), filesel);
+  g_signal_connect (filesel->selection_entry, "insert_text",
+                   G_CALLBACK (gtk_file_selection_insert_text), NULL);
+  g_signal_connect_swapped (filesel->selection_entry, "changed",
+                           G_CALLBACK (gtk_file_selection_update_fileops), filesel);
+  g_signal_connect_swapped (filesel->selection_entry, "focus_in_event",
+                           G_CALLBACK (grab_default),
+                           filesel->ok_button);
+  g_signal_connect_swapped (filesel->selection_entry, "activate",
+                           G_CALLBACK (gtk_button_clicked),
+                           filesel->ok_button);
+  
   gtk_box_pack_start (GTK_BOX (entry_vbox), filesel->selection_entry, TRUE, TRUE, 0);
   gtk_widget_show (filesel->selection_entry);
 
+  gtk_label_set_mnemonic_widget (GTK_LABEL (filesel->selection_text),
+                                filesel->selection_entry);
+
   if (!cmpl_state_okay (filesel->cmpl_state))
     {
       gchar err_buf[256];
 
-      sprintf (err_buf, _("Folder unreadable: %s"), cmpl_strerror (cmpl_errno));
+      g_snprintf (err_buf, sizeof (err_buf), _("Folder unreadable: %s"), cmpl_strerror (cmpl_errno));
 
       gtk_label_set_text (GTK_LABEL (filesel->selection_text), err_buf);
     }
@@ -913,13 +961,21 @@ filenames_dropped (GtkWidget        *widget,
   else
     {
       GtkWidget *dialog;
+      gchar *filename_utf8;
+
+      /* Conversion back to UTF-8 should always succeed for the result
+       * of g_filename_from_uri()
+       */
+      filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
+      g_assert (filename_utf8);
       
       dialog = gtk_message_dialog_new (GTK_WINDOW (widget),
                                       GTK_DIALOG_DESTROY_WITH_PARENT,
                                       GTK_MESSAGE_QUESTION,
                                       GTK_BUTTONS_YES_NO,
-                                      _("The file \"%s\" resides on another machine (called %s) and may not be availible to this program.\n"
-                                        "Are you sure that you want to select it?"), filename, hostname);
+                                      _("The file \"%s\" resides on another machine (called %s) and may not be available to this program.\n"
+                                        "Are you sure that you want to select it?"), filename_utf8, hostname);
+      g_free (filename_utf8);
 
       g_object_set_data_full (G_OBJECT (dialog), "gtk-fs-dnd-filename", g_strdup (filename), g_free);
       
@@ -983,8 +1039,10 @@ filenames_drag_get (GtkWidget        *widget,
        }
       else
        {
-         g_print ("Setting text: '%s'\n", file);
-         gtk_selection_data_set_text (selection_data, file, -1);
+         gchar *filename_utf8 = g_filename_to_utf8 (file, -1, NULL, NULL, NULL);
+         g_assert (filename_utf8);
+         gtk_selection_data_set_text (selection_data, filename_utf8, -1);
+         g_free (filename_utf8);
        }
     }
 }
@@ -993,11 +1051,11 @@ static void
 file_selection_setup_dnd (GtkFileSelection *filesel)
 {
   GtkWidget *eventbox;
-  static GtkTargetEntry drop_types[] = {
+  static const GtkTargetEntry drop_types[] = {
     { "text/uri-list", 0, TARGET_URILIST}
   };
   static gint n_drop_types = sizeof(drop_types)/sizeof(drop_types[0]);
-  static GtkTargetEntry drag_types[] = {
+  static const GtkTargetEntry drag_types[] = {
     { "text/uri-list", 0, TARGET_URILIST},
     { "UTF8_STRING", 0, TARGET_UTF8_STRING },
     { "STRING", 0, 0 },
@@ -1011,8 +1069,8 @@ file_selection_setup_dnd (GtkFileSelection *filesel)
                     drop_types, n_drop_types,
                     GDK_ACTION_COPY);
 
-  gtk_signal_connect (GTK_OBJECT(filesel), "drag_data_received",
-                     GTK_SIGNAL_FUNC(filenames_dropped), NULL);
+  g_signal_connect (filesel, "drag_data_received",
+                   G_CALLBACK (filenames_dropped), NULL);
 
   eventbox = gtk_widget_get_parent (filesel->selection_text);
   gtk_drag_source_set (eventbox,
@@ -1020,10 +1078,8 @@ file_selection_setup_dnd (GtkFileSelection *filesel)
                       drag_types, n_drag_types,
                       GDK_ACTION_COPY);
 
-  gtk_signal_connect (GTK_OBJECT (eventbox),
-                     "drag_data_get",
-                     GTK_SIGNAL_FUNC (filenames_drag_get),
-                     filesel);
+  g_signal_connect (eventbox, "drag_data_get",
+                   G_CALLBACK (filenames_drag_get), filesel);
 }
 
 GtkWidget*
@@ -1031,7 +1087,7 @@ gtk_file_selection_new (const gchar *title)
 {
   GtkFileSelection *filesel;
 
-  filesel = gtk_type_new (GTK_TYPE_FILE_SELECTION);
+  filesel = g_object_new (GTK_TYPE_FILE_SELECTION, NULL);
   gtk_window_set_title (GTK_WINDOW (filesel), title);
   gtk_dialog_set_has_separator (GTK_DIALOG (filesel), FALSE);
 
@@ -1049,9 +1105,9 @@ gtk_file_selection_show_fileop_buttons (GtkFileSelection *filesel)
   if (!filesel->fileop_c_dir) 
     {
       filesel->fileop_c_dir = gtk_button_new_with_mnemonic (_("_New Folder"));
-      gtk_signal_connect (GTK_OBJECT (filesel->fileop_c_dir), "clicked",
-                         (GtkSignalFunc) gtk_file_selection_create_dir, 
-                         (gpointer) filesel);
+      g_signal_connect (filesel->fileop_c_dir, "clicked",
+                       G_CALLBACK (gtk_file_selection_create_dir),
+                       filesel);
       gtk_box_pack_start (GTK_BOX (filesel->button_area), 
                          filesel->fileop_c_dir, TRUE, TRUE, 0);
       gtk_widget_show (filesel->fileop_c_dir);
@@ -1060,9 +1116,9 @@ gtk_file_selection_show_fileop_buttons (GtkFileSelection *filesel)
   if (!filesel->fileop_del_file) 
     {
       filesel->fileop_del_file = gtk_button_new_with_mnemonic (_("De_lete File"));
-      gtk_signal_connect (GTK_OBJECT (filesel->fileop_del_file), "clicked",
-                         (GtkSignalFunc) gtk_file_selection_delete_file, 
-                         (gpointer) filesel);
+      g_signal_connect (filesel->fileop_del_file, "clicked",
+                       G_CALLBACK (gtk_file_selection_delete_file),
+                       filesel);
       gtk_box_pack_start (GTK_BOX (filesel->button_area), 
                          filesel->fileop_del_file, TRUE, TRUE, 0);
       gtk_widget_show (filesel->fileop_del_file);
@@ -1071,15 +1127,17 @@ gtk_file_selection_show_fileop_buttons (GtkFileSelection *filesel)
   if (!filesel->fileop_ren_file)
     {
       filesel->fileop_ren_file = gtk_button_new_with_mnemonic (_("_Rename File"));
-      gtk_signal_connect (GTK_OBJECT (filesel->fileop_ren_file), "clicked",
-                         (GtkSignalFunc) gtk_file_selection_rename_file, 
-                         (gpointer) filesel);
+      g_signal_connect (filesel->fileop_ren_file, "clicked",
+                       G_CALLBACK (gtk_file_selection_rename_file),
+                       filesel);
       gtk_box_pack_start (GTK_BOX (filesel->button_area), 
                          filesel->fileop_ren_file, TRUE, TRUE, 0);
       gtk_widget_show (filesel->fileop_ren_file);
     }
+  
+  gtk_file_selection_update_fileops (filesel);
+  
   g_object_notify (G_OBJECT (filesel), "show_fileops");
-  gtk_widget_queue_resize (GTK_WIDGET (filesel));
 }
 
 void       
@@ -1109,27 +1167,47 @@ gtk_file_selection_hide_fileop_buttons (GtkFileSelection *filesel)
 
 
 
+/**
+ * gtk_file_selection_set_filename:
+ * @filesel: a #GtkFileSelection.
+ * @filename:  a string to set as the default file name.
+ * 
+ * Sets a default path for the file requestor. If @filename includes a
+ * directory path, then the requestor will open with that path as its
+ * current working directory.
+ *
+ * This has the consequence that in order to open the requestor with a 
+ * working directory and an empty filename, @filename must have a trailing
+ * directory separator.
+ *
+ * The encoding of @filename is the on-disk encoding, which
+ * may not be UTF-8. See g_filename_from_utf8().
+ **/
 void
 gtk_file_selection_set_filename (GtkFileSelection *filesel,
                                 const gchar      *filename)
 {
   gchar *buf;
   const char *name, *last_slash;
+  char *filename_utf8;
 
   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
   g_return_if_fail (filename != NULL);
 
-  last_slash = strrchr (filename, G_DIR_SEPARATOR);
+  filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
+  g_return_if_fail (filename_utf8 != NULL);
+
+  last_slash = strrchr (filename_utf8, G_DIR_SEPARATOR);
 
   if (!last_slash)
     {
       buf = g_strdup ("");
-      name = filename;
+      name = filename_utf8;
     }
   else
     {
-      buf = g_strdup (filename);
-      buf[last_slash - filename + 1] = 0;
+      buf = g_strdup (filename_utf8);
+      buf[last_slash - filename_utf8 + 1] = 0;
       name = last_slash + 1;
     }
 
@@ -1139,24 +1217,28 @@ gtk_file_selection_set_filename (GtkFileSelection *filesel,
     gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), name);
   g_free (buf);
   g_object_notify (G_OBJECT (filesel), "filename");
+
+  g_free (filename_utf8);
 }
 
 /**
  * gtk_file_selection_get_filename:
  * @filesel: a #GtkFileSelection
  * 
- * This function returns the selected filename in encoding of
- * g_filename_from_utf8(), which may or may not be the same as that
+ * This function returns the selected filename in the on-disk encoding
+ * (see g_filename_from_utf8()), which may or may not be the same as that
  * used by GTK+ (UTF-8). To convert to UTF-8, call g_filename_to_utf8().
  * The returned string points to a statically allocated buffer and
  * should be copied if you plan to keep it around.
+ *
+ * If no file is selected then the selected directory path is returned.
  * 
- * Return value: currently-selected filename in locale's encoding
+ * Return value: currently-selected filename in the on-disk encoding.
  **/
 G_CONST_RETURN gchar*
 gtk_file_selection_get_filename (GtkFileSelection *filesel)
 {
-  static gchar nothing[2] = "";
+  static const gchar nothing[2] = "";
   static gchar something[MAXPATHLEN*2];
   char *sys_filename;
   const char *text;
@@ -1287,9 +1369,9 @@ gtk_file_selection_fileop_error (GtkFileSelection *fs,
 
   gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
  
-  gtk_signal_connect_object (GTK_OBJECT (dialog), "response",
-                            (GtkSignalFunc) gtk_widget_destroy, 
-                            (gpointer) dialog);
+  g_signal_connect_swapped (dialog, "response",
+                           G_CALLBACK (gtk_widget_destroy),
+                           dialog);
 
   gtk_widget_show (dialog);
 }
@@ -1305,6 +1387,20 @@ gtk_file_selection_fileop_destroy (GtkWidget *widget,
   fs->fileop_dialog = NULL;
 }
 
+static gboolean
+entry_is_empty (GtkEntry *entry)
+{
+  const gchar *text = gtk_entry_get_text (entry);
+  
+  return *text == '\0';
+}
+
+static void
+gtk_file_selection_fileop_entry_changed (GtkEntry   *entry,
+                                        GtkWidget  *button)
+{
+  gtk_widget_set_sensitive (button, !entry_is_empty (entry));
+}
 
 static void
 gtk_file_selection_create_dir_confirmed (GtkWidget *widget,
@@ -1372,9 +1468,9 @@ gtk_file_selection_create_dir (GtkWidget *widget,
   /* main dialog */
   dialog = gtk_dialog_new ();
   fs->fileop_dialog = dialog;
-  gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
-                     (GtkSignalFunc) gtk_file_selection_fileop_destroy, 
-                     (gpointer) fs);
+  g_signal_connect (dialog, "destroy",
+                   G_CALLBACK (gtk_file_selection_fileop_destroy),
+                   fs);
   gtk_window_set_title (GTK_WINDOW (dialog), _("New Folder"));
   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
   gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (fs));
@@ -1401,29 +1497,29 @@ gtk_file_selection_create_dir (GtkWidget *widget,
   gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry, 
                      TRUE, TRUE, 5);
   GTK_WIDGET_SET_FLAGS (fs->fileop_entry, GTK_CAN_DEFAULT);
+  gtk_entry_set_activates_default (GTK_ENTRY (fs->fileop_entry), TRUE); 
   gtk_widget_show (fs->fileop_entry);
   
   /* buttons */
-  button = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
-  gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
-                            (GtkSignalFunc) gtk_widget_destroy, 
-                            (gpointer) dialog);
-  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
-                    button, TRUE, TRUE, 0);
-  GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
-  gtk_widget_grab_default (button);
-  gtk_widget_show (button);
+  button = gtk_dialog_add_button (GTK_DIALOG (dialog), 
+                                 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+  g_signal_connect_swapped (button, "clicked",
+                           G_CALLBACK (gtk_widget_destroy),
+                           dialog);
 
   gtk_widget_grab_focus (fs->fileop_entry);
 
-  button = gtk_button_new_with_label (_("Create"));
-  gtk_signal_connect (GTK_OBJECT (button), "clicked",
-                     (GtkSignalFunc) gtk_file_selection_create_dir_confirmed, 
-                     (gpointer) fs);
-  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
-                    button, TRUE, TRUE, 0);
-  GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
-  gtk_widget_show (button);
+  button = gtk_dialog_add_button (GTK_DIALOG (dialog), 
+                                 _("C_reate"), GTK_RESPONSE_OK);
+  gtk_widget_set_sensitive (button, FALSE);
+  g_signal_connect (button, "clicked",
+                   G_CALLBACK (gtk_file_selection_create_dir_confirmed),
+                   fs);
+  g_signal_connect (fs->fileop_entry, "changed",
+                    G_CALLBACK (gtk_file_selection_fileop_entry_changed),
+                   button);
+
+  gtk_widget_grab_default (button);
   
   gtk_widget_show (dialog);
 }
@@ -1516,9 +1612,9 @@ gtk_file_selection_delete_file (GtkWidget *widget,
                             GTK_BUTTONS_NONE,
                             _("Really delete file \"%s\" ?"), filename);
 
-  gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
-                     (GtkSignalFunc) gtk_file_selection_fileop_destroy, 
-                     (gpointer) fs);
+  g_signal_connect (dialog, "destroy",
+                   G_CALLBACK (gtk_file_selection_fileop_destroy),
+                   fs);
   gtk_window_set_title (GTK_WINDOW (dialog), _("Delete File"));
   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
   
@@ -1530,7 +1626,7 @@ gtk_file_selection_delete_file (GtkWidget *widget,
 
   gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL);
 
-  g_signal_connect (G_OBJECT (dialog), "response",
+  g_signal_connect (dialog, "response",
                     G_CALLBACK (gtk_file_selection_delete_file_response),
                     fs);
   
@@ -1635,9 +1731,9 @@ gtk_file_selection_rename_file (GtkWidget *widget,
   
   /* main dialog */
   fs->fileop_dialog = dialog = gtk_dialog_new ();
-  gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
-                     (GtkSignalFunc) gtk_file_selection_fileop_destroy, 
-                     (gpointer) fs);
+  g_signal_connect (dialog, "destroy",
+                   G_CALLBACK (gtk_file_selection_fileop_destroy),
+                   fs);
   gtk_window_set_title (GTK_WINDOW (dialog), _("Rename File"));
   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
   gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (fs));
@@ -1665,6 +1761,7 @@ gtk_file_selection_rename_file (GtkWidget *widget,
   gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry, 
                      TRUE, TRUE, 5);
   GTK_WIDGET_SET_FLAGS (fs->fileop_entry, GTK_CAN_DEFAULT);
+  gtk_entry_set_activates_default (GTK_ENTRY (fs->fileop_entry), TRUE); 
   gtk_widget_show (fs->fileop_entry);
   
   gtk_entry_set_text (GTK_ENTRY (fs->fileop_entry), fs->fileop_file);
@@ -1672,26 +1769,24 @@ gtk_file_selection_rename_file (GtkWidget *widget,
                              0, strlen (fs->fileop_file));
 
   /* buttons */
-  button = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
-  gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
-                            (GtkSignalFunc) gtk_widget_destroy, 
-                            (gpointer) dialog);
-  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
-                     button, TRUE, TRUE, 0);
-  GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
-  gtk_widget_grab_default (button);
-  gtk_widget_show (button);
+  button = gtk_dialog_add_button (GTK_DIALOG (dialog), 
+                                 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
+  g_signal_connect_swapped (button, "clicked",
+                           G_CALLBACK (gtk_widget_destroy),
+                           dialog);
 
   gtk_widget_grab_focus (fs->fileop_entry);
 
-  button = gtk_button_new_with_label (_("Rename"));
-  gtk_signal_connect (GTK_OBJECT (button), "clicked",
-                     (GtkSignalFunc) gtk_file_selection_rename_file_confirmed, 
-                     (gpointer) fs);
-  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
-                     button, TRUE, TRUE, 0);
-  GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
-  gtk_widget_show (button);
+  button = gtk_dialog_add_button (GTK_DIALOG (dialog), 
+                                 _("_Rename"), GTK_RESPONSE_OK);
+  g_signal_connect (button, "clicked",
+                   G_CALLBACK (gtk_file_selection_rename_file_confirmed),
+                   fs);
+  g_signal_connect (fs->fileop_entry, "changed",
+                   G_CALLBACK (gtk_file_selection_fileop_entry_changed),
+                   button);
+
+  gtk_widget_grab_default (button);
   
   gtk_widget_show (dialog);
 }
@@ -1709,8 +1804,8 @@ gtk_file_selection_insert_text (GtkWidget   *widget,
 
   if (!filename)
     {
-      gdk_beep ();
-      gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "insert_text");
+      gdk_display_beep (gtk_widget_get_display (widget));
+      g_signal_stop_emission_by_name (widget, "insert_text");
       return FALSE;
     }
   
@@ -1719,6 +1814,23 @@ gtk_file_selection_insert_text (GtkWidget   *widget,
   return TRUE;
 }
 
+static void
+gtk_file_selection_update_fileops (GtkFileSelection *fs)
+{
+  gboolean sensitive;
+
+  if (!fs->selection_entry)
+    return;
+
+  sensitive = !entry_is_empty (GTK_ENTRY (fs->selection_entry));
+
+  if (fs->fileop_del_file)
+    gtk_widget_set_sensitive (fs->fileop_del_file, sensitive);
+  
+  if (fs->fileop_ren_file)
+    gtk_widget_set_sensitive (fs->fileop_ren_file, sensitive);
+}
+
 static gint
 gtk_file_selection_key_press (GtkWidget   *widget,
                              GdkEventKey *event,
@@ -1842,9 +1954,9 @@ gtk_file_selection_update_history_menu (GtkFileSelection *fs,
          
          fs->history_list = g_list_append (fs->history_list, callback_arg);
          
-         gtk_signal_connect (GTK_OBJECT (menu_item), "activate",
-                             (GtkSignalFunc) gtk_file_selection_history_callback,
-                             (gpointer) fs);
+         g_signal_connect (menu_item, "activate",
+                           G_CALLBACK (gtk_file_selection_history_callback),
+                           fs);
          gtk_menu_shell_append (GTK_MENU_SHELL (fs->history_menu), menu_item);
          gtk_widget_show (menu_item);
        }
@@ -1863,17 +1975,26 @@ get_real_filename (gchar    *filename,
   /* Check to see if the selection was a drive selector */
   if (isalpha (filename[0]) && (filename[1] == ':'))
     {
-      /* It is... map it to a CYGWIN32 drive */
-      gchar *temp_filename = g_strdup_printf ("//%c/", tolower (filename[0]));
+      gchar temp_filename[MAX_PATH];
+      int len;
 
+      cygwin_conv_to_posix_path (filename, temp_filename);
+
+      /* we need trailing '/'. */
+      len = strlen (temp_filename);
+      if (len > 0 && temp_filename[len-1] != '/')
+        {
+          temp_filename[len]   = '/';
+          temp_filename[len+1] = '\0';
+        }
+      
       if (free_old)
        g_free (filename);
 
-      return temp_filename;
+      return g_strdup (temp_filename);
     }
-#else
-  return filename;
 #endif /* G_WITH_CYGWIN */
+  return filename;
 }
 
 static void
@@ -1890,7 +2011,6 @@ gtk_file_selection_file_activate (GtkTreeView       *tree_view,
   gtk_tree_model_get_iter (model, &iter, path);
   gtk_tree_model_get (model, &iter, FILE_COLUMN, &filename, -1);
   filename = get_real_filename (filename, TRUE);
-
   gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
   gtk_button_clicked (GTK_BUTTON (fs->ok_button));
 
@@ -1910,11 +2030,12 @@ gtk_file_selection_dir_activate (GtkTreeView       *tree_view,
 
   gtk_tree_model_get_iter (model, &iter, path);
   gtk_tree_model_get (model, &iter, DIR_COLUMN, &filename, -1);
+  filename = get_real_filename (filename, TRUE);
   gtk_file_selection_populate (fs, filename, FALSE, FALSE);
   g_free (filename);
 }
 
-#if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
+#ifdef G_PLATFORM_WIN32
 
 static void
 win32_gtk_add_drives_to_dir_list (GtkListStore *model)
@@ -1932,10 +2053,10 @@ win32_gtk_add_drives_to_dir_list (GtkListStore *model)
   while (*textPtr != '\0')
     {
       /* Ignore floppies (?) */
-      if ((tolower (textPtr[0]) != 'a') && (tolower (textPtr[0]) != 'b'))
+      if (GetDriveType (textPtr) != DRIVE_REMOVABLE)
        {
          /* Build the actual displayable string */
-         sprintf (formatBuffer, "%c:\\", toupper (textPtr[0]));
+         g_snprintf (formatBuffer, sizeof (formatBuffer), "%c:\\", toupper (textPtr[0]));
 
          /* Add to the list */
          gtk_list_store_append (model, &iter);
@@ -1946,6 +2067,22 @@ win32_gtk_add_drives_to_dir_list (GtkListStore *model)
 }
 #endif
 
+static gchar *
+escape_underscores (const gchar *str)
+{
+  GString *result = g_string_new (NULL);
+  while (*str)
+    {
+      if (*str == '_')
+       g_string_append_c (result, '_');
+
+      g_string_append_c (result, *str);
+      str++;
+    }
+
+  return g_string_free (result, FALSE);
+}
+
 static void
 gtk_file_selection_populate (GtkFileSelection *fs,
                             gchar            *rel_path,
@@ -2017,7 +2154,7 @@ gtk_file_selection_populate (GtkFileSelection *fs,
       poss = cmpl_next_completion (cmpl_state);
     }
 
-#if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
+#ifdef G_PLATFORM_WIN32
   /* For Windows, add drives as potential selections */
   win32_gtk_add_drives_to_dir_list (dir_model);
 #endif
@@ -2071,15 +2208,16 @@ gtk_file_selection_populate (GtkFileSelection *fs,
   if (!did_recurse)
     {
       if (fs->selection_entry)
-       gtk_entry_set_position (GTK_ENTRY (fs->selection_entry), selection_index);
+       gtk_editable_set_position (GTK_EDITABLE (fs->selection_entry),
+                                  selection_index);
 
       if (fs->selection_entry)
        {
-         sel_text = g_strconcat (_("Selection: "),
-                                 cmpl_reference_position (cmpl_state),
-                                 NULL);
+         char *escaped = escape_underscores (cmpl_reference_position (cmpl_state));
+         sel_text = g_strconcat (_("_Selection: "), escaped, NULL);
+         g_free (escaped);
 
-         gtk_label_set_text (GTK_LABEL (fs->selection_text), sel_text);
+         gtk_label_set_text_with_mnemonic (GTK_LABEL (fs->selection_text), sel_text);
          g_free (sel_text);
        }
 
@@ -2096,7 +2234,7 @@ gtk_file_selection_abort (GtkFileSelection *fs)
 {
   gchar err_buf[256];
 
-  sprintf (err_buf, _("Folder unreadable: %s"), cmpl_strerror (cmpl_errno));
+  g_snprintf (err_buf, sizeof (err_buf), _("Folder unreadable: %s"), cmpl_strerror (cmpl_errno));
 
   /*  BEEP gdk_beep();  */
 
@@ -2219,8 +2357,8 @@ gtk_file_selection_file_changed (GtkTreeSelection *selection,
          /* A common case is selecting a range of files from top to bottom,
           * so quickly check for that to avoid looping over the entire list
           */
-         if (compare_filenames (g_ptr_array_index (old_names, old_names->len - 1),
-                                g_ptr_array_index (new_names, new_names->len - 1)) != 0)
+         if (compare_utf8_filenames (g_ptr_array_index (old_names, old_names->len - 1),
+                                     g_ptr_array_index (new_names, new_names->len - 1)) != 0)
            index = new_names->len - 1;
          else
            {
@@ -2231,8 +2369,8 @@ gtk_file_selection_file_changed (GtkTreeSelection *selection,
               */
              while (i < old_names->len && j < new_names->len)
                {
-                 cmp = compare_filenames (g_ptr_array_index (old_names, i),
-                                          g_ptr_array_index (new_names, j));
+                 cmp = compare_utf8_filenames (g_ptr_array_index (old_names, i),
+                                               g_ptr_array_index (new_names, j));
                  if (cmp < 0)
                    {
                      i++;
@@ -2260,8 +2398,9 @@ gtk_file_selection_file_changed (GtkTreeSelection *selection,
           * was selected, which is used for subsequent range selections.
           * So search up from there.
           */
-         if (compare_filenames (fs->last_selected,
-                                g_ptr_array_index (new_names, 0)) == 0)
+         if (fs->last_selected &&
+             compare_utf8_filenames (fs->last_selected,
+                                     g_ptr_array_index (new_names, 0)) == 0)
            index = new_names->len - 1;
          else
            index = 0;
@@ -2295,7 +2434,7 @@ maybe_clear_entry:
 
   entry = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
   if ((entry != NULL) && (fs->last_selected != NULL) &&
-      (compare_filenames (entry, fs->last_selected) == 0))
+      (compare_utf8_filenames (entry, fs->last_selected) == 0))
     gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), "");
 }
 
@@ -2308,8 +2447,8 @@ maybe_clear_entry:
  * in the file list. The first file in the list is equivalent to what
  * gtk_file_selection_get_filename() would return.
  *
- * The filenames are in the encoding of g_filename_from_utf8, which may or may
- * not be the same as that used by GTK+ (UTF-8). To convert to UTF-8, call
+ * The filenames are in the encoding of g_filename_from_utf8(), which may or 
+ * may not be the same as that used by GTK+ (UTF-8). To convert to UTF-8, call
  * g_filename_to_utf8() on each string.
  *
  * Return value: a newly-allocated %NULL-terminated array of strings. Use
@@ -2323,6 +2462,7 @@ gtk_file_selection_get_selections (GtkFileSelection *filesel)
   gchar *filename, *dirname;
   gchar *current, *buf;
   gint i, count;
+  gboolean unselected_entry;
 
   g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), NULL);
 
@@ -2342,7 +2482,7 @@ gtk_file_selection_get_selections (GtkFileSelection *filesel)
     selections = g_new (gchar *, 2);
 
   count = 0;
-  selections[count++] = filename;
+  unselected_entry = TRUE;
 
   if (names != NULL)
     {
@@ -2355,13 +2495,20 @@ gtk_file_selection_get_selections (GtkFileSelection *filesel)
          current = g_build_filename (dirname, buf, NULL);
          g_free (buf);
 
-         if (compare_filenames (current, filename) != 0)
-           selections[count++] = current;
-         else
-           g_free (current);
+         selections[count++] = current;
+
+         if (unselected_entry && compare_sys_filenames (current, filename) == 0)
+           unselected_entry = FALSE;
        }
+
+      g_free (dirname);
     }
 
+  if (unselected_entry)
+    selections[count++] = filename;
+  else
+    g_free (filename);
+
   selections[count] = NULL;
 
   return selections;
@@ -2397,11 +2544,11 @@ cmpl_last_valid_char (CompletionState *cmpl_state)
   return cmpl_state->last_valid_char;
 }
 
-static gchar*
+static const gchar*
 cmpl_completion_fullname (const gchar     *text,
                          CompletionState *cmpl_state)
 {
-  static char nothing[2] = "";
+  static const char nothing[2] = "";
 
   if (!cmpl_state_okay (cmpl_state))
     {
@@ -2575,7 +2722,10 @@ free_dir_sent (CompletionDirSent* sent)
 {
   gint i;
   for (i = 0; i < sent->entry_count; i++)
-    g_free (sent->entries[i].entry_name);
+    {
+      g_free (sent->entries[i].entry_name);
+      g_free (sent->entries[i].sort_key);
+    }
   g_free (sent->entries);
   g_free (sent);
 }
@@ -2619,7 +2769,9 @@ cmpl_completion_matches (gchar           *text_to_complete,
                         gchar          **remaining_text,
                         CompletionState *cmpl_state)
 {
+#ifdef HAVE_PWD_H
   gchar* first_slash;
+#endif
   PossibleCompletion *poss;
 
   prune_memory_usage (cmpl_state);
@@ -2718,7 +2870,7 @@ open_ref_dir (gchar           *text_to_complete,
   if (text_to_complete[0] == '/' && text_to_complete[1] == '/')
     {
       char root_dir[5];
-      sprintf (root_dir, "//%c", text_to_complete[2]);
+      g_snprintf (root_dir, sizeof (root_dir), "//%c", text_to_complete[2]);
 
       new_dir = open_dir (root_dir, cmpl_state);
 
@@ -2761,16 +2913,15 @@ open_ref_dir (gchar           *text_to_complete,
       p = strrchr (tmp, G_DIR_SEPARATOR);
       if (p)
        {
-         if (p == tmp)
+         if (p + 1 == g_path_skip_root (tmp))
            p++;
       
          *p = '\0';
-
          new_dir = open_dir (tmp, cmpl_state);
 
          if (new_dir)
            *remaining_text = text_to_complete + 
-             ((p == tmp + 1) ? (p - tmp) : (p + 1 - tmp));
+             ((p == g_path_skip_root (tmp)) ? (p - tmp) : (p + 1 - tmp));
        }
       else
        {
@@ -2897,7 +3048,7 @@ open_new_dir (gchar       *dir_name,
   CompletionDirSent *sent;
   GDir *directory;
   const char *dirent;
-  GError *error;
+  GError *error = NULL;
   gint entry_count = 0;
   gint n_entries = 0;
   gint i;
@@ -2906,10 +3057,11 @@ open_new_dir (gchar       *dir_name,
   gchar *sys_dir_name;
 
   sent = g_new (CompletionDirSent, 1);
+#ifndef G_PLATFORM_WIN32
   sent->mtime = sbuf->st_mtime;
   sent->inode = sbuf->st_ino;
   sent->device = sbuf->st_dev;
-
+#endif
   path = g_string_sized_new (2*MAXPATHLEN + 10);
 
   sys_dir_name = g_filename_from_utf8 (dir_name, -1, NULL, NULL, NULL);
@@ -2929,6 +3081,7 @@ open_new_dir (gchar       *dir_name,
 
   while ((dirent = g_dir_read_name (directory)) != NULL)
     entry_count++;
+  entry_count += 2;            /* For ".",".." */
 
   sent->entries = g_new (CompletionDirEntry, entry_count);
   sent->entry_count = entry_count;
@@ -2937,24 +3090,36 @@ open_new_dir (gchar       *dir_name,
 
   for (i = 0; i < entry_count; i += 1)
     {
-      dirent = g_dir_read_name (directory);
+      GError *error = NULL;
 
-      if (!dirent)
+      if (i == 0)
+       dirent = ".";
+      else if (i == 1)
+       dirent = "..";
+      else
        {
-         g_warning ("Failure reading folder '%s'", sys_dir_name);
-         g_dir_close (directory);
-         g_free (sys_dir_name);
-         return NULL;
+         dirent = g_dir_read_name (directory);
+         if (!dirent)          /* Directory changed */
+           break;
        }
 
-      sent->entries[n_entries].entry_name = g_filename_to_utf8 (dirent, -1, NULL, NULL, NULL);
+      sent->entries[n_entries].entry_name = g_filename_to_utf8 (dirent, -1, NULL, NULL, &error);
       if (sent->entries[n_entries].entry_name == NULL
          || !g_utf8_validate (sent->entries[n_entries].entry_name, -1, NULL))
        {
-         g_warning (_("The filename %s couldn't be converted to UTF-8. Try setting the environment variable G_BROKEN_FILENAMES."), dirent);
+         gchar *escaped_str = g_strescape (dirent, NULL);
+         g_message (_("The filename \"%s\" couldn't be converted to UTF-8 "
+                      "(try setting the environment variable G_BROKEN_FILENAMES): %s"),
+                    escaped_str,
+                    error->message ? error->message : _("Invalid UTF-8"));
+         g_free (escaped_str);
+         g_clear_error (&error);
          continue;
        }
-
+      g_clear_error (&error);
+      
+      sent->entries[n_entries].sort_key = g_utf8_collate_key (sent->entries[n_entries].entry_name, -1);
+      
       g_string_assign (path, sys_dir_name);
       if (path->str[path->len-1] != G_DIR_SEPARATOR)
        {
@@ -2988,7 +3153,7 @@ open_new_dir (gchar       *dir_name,
   return sent;
 }
 
-#if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
+#ifndef G_PLATFORM_WIN32
 
 static gboolean
 check_dir (gchar       *dir_name,
@@ -3061,12 +3226,14 @@ static CompletionDir*
 open_dir (gchar           *dir_name,
          CompletionState *cmpl_state)
 {
+#ifndef G_PLATFORM_WIN32
   struct stat sbuf;
   gboolean stat_subdirs;
-  CompletionDirSent *sent;
   GList* cdsl;
+#endif
+  CompletionDirSent *sent;
 
-#if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
+#ifndef G_PLATFORM_WIN32
   if (!check_dir (dir_name, &sbuf, &stat_subdirs))
     return NULL;
 
@@ -3083,11 +3250,11 @@ open_dir (gchar           *dir_name,
 
       cdsl = cdsl->next;
     }
-#else
-  stat_subdirs = TRUE;
-#endif
 
   sent = open_new_dir (dir_name, &sbuf, stat_subdirs);
+#else
+  sent = open_new_dir (dir_name, NULL, TRUE);
+#endif
 
   if (sent)
     {
@@ -3231,7 +3398,9 @@ correct_parent (CompletionDir *cmpl_dir,
   struct stat parbuf;
   gchar *last_slash;
   gchar *first_slash;
+#ifndef G_PLATFORM_WIN32
   gchar *new_name;
+#endif
   gchar *sys_filename;
   gchar c = 0;
 
@@ -3271,7 +3440,7 @@ correct_parent (CompletionDir *cmpl_dir,
     }
   g_free (sys_filename);
 
-#ifndef G_OS_WIN32             /* No inode numbers on Win32 */
+#ifndef G_PLATFORM_WIN32       /* No inode numbers on Win32 */
   if (parbuf.st_ino == sbuf->st_ino && parbuf.st_dev == sbuf->st_dev)
     /* it wasn't a link */
     return TRUE;
@@ -3296,7 +3465,7 @@ correct_parent (CompletionDir *cmpl_dir,
   return TRUE;
 }
 
-#ifndef G_OS_WIN32
+#ifndef G_PLATFORM_WIN32
 
 static gchar*
 find_parent_dir_fullname (gchar* dirname)
@@ -3317,9 +3486,10 @@ find_parent_dir_fullname (gchar* dirname)
   
   if (chdir (sys_dirname) != 0 || chdir ("..") != 0)
     {
+      cmpl_errno = errno;
+      chdir (sys_orig_dir);
       g_free (sys_dirname);
       g_free (sys_orig_dir);
-      cmpl_errno = errno;
       return NULL;
     }
   g_free (sys_dirname);
@@ -3407,7 +3577,12 @@ attempt_homedir_completion (gchar           *text_to_complete,
 
 #endif
 
-#if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
+#ifdef G_PLATFORM_WIN32
+/* FIXME: determine whether we should casefold all Unicode letters
+ * here, too (and in in first_diff_index() walk through the strings with
+ * g_utf8_next_char()), or if this folding isn't actually needed at
+ * all.
+ */
 #define FOLD(c) (tolower(c))
 #else
 #define FOLD(c) (c)
@@ -3491,8 +3666,7 @@ find_completion_dir (gchar          *text_to_complete,
       for (i = 0; i < dir->sent->entry_count; i += 1)
        {
          if (dir->sent->entries[i].is_dir &&
-            fnmatch (pat_buf, dir->sent->entries[i].entry_name,
-                     FNMATCH_FLAGS)!= FNM_NOMATCH)
+             _gtk_fnmatch (pat_buf, dir->sent->entries[i].entry_name))
            {
              if (found)
                {
@@ -3519,7 +3693,7 @@ find_completion_dir (gchar          *text_to_complete,
        {
          g_free (pat_buf);
          return NULL;
-       }
+}
       
       next->cmpl_parent = dir;
       
@@ -3553,10 +3727,10 @@ update_cmpl (PossibleCompletion *poss,
 
   if (cmpl_state->updated_text_alloc < cmpl_len + 1)
     {
+      cmpl_state->updated_text_alloc = 2*cmpl_len;
       cmpl_state->updated_text =
        (gchar*)g_realloc (cmpl_state->updated_text,
                           cmpl_state->updated_text_alloc);
-      cmpl_state->updated_text_alloc = 2*cmpl_len;
     }
 
   if (cmpl_state->updated_text_len < 0)
@@ -3642,8 +3816,7 @@ attempt_file_completion (CompletionState *cmpl_state)
     {
       if (dir->sent->entries[dir->cmpl_index].is_dir)
        {
-         if (fnmatch (pat_buf, dir->sent->entries[dir->cmpl_index].entry_name,
-                      FNMATCH_FLAGS) != FNM_NOMATCH)
+         if (_gtk_fnmatch (pat_buf, dir->sent->entries[dir->cmpl_index].entry_name))
            {
              CompletionDir* new_dir;
 
@@ -3691,8 +3864,7 @@ attempt_file_completion (CompletionState *cmpl_state)
       append_completion_text (dir->sent->entries[dir->cmpl_index].entry_name, cmpl_state);
 
       cmpl_state->the_completion.is_a_completion =
-       fnmatch (pat_buf, dir->sent->entries[dir->cmpl_index].entry_name,
-                FNMATCH_FLAGS) != FNM_NOMATCH;
+       _gtk_fnmatch (pat_buf, dir->sent->entries[dir->cmpl_index].entry_name);
 
       cmpl_state->the_completion.is_directory = dir->sent->entries[dir->cmpl_index].is_dir;
       if (dir->sent->entries[dir->cmpl_index].is_dir)
@@ -3801,8 +3973,9 @@ static gint
 compare_cmpl_dir (const void *a,
                  const void *b)
 {
-  return compare_filenames ((((CompletionDirEntry*)a))->entry_name,
-                           (((CompletionDirEntry*)b))->entry_name);
+  
+  return strcmp (((CompletionDirEntry*)a)->sort_key,
+                (((CompletionDirEntry*)b))->sort_key);
 }
 
 static gint