? kk2
Index: gtkfilechooserdefault.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkfilechooserdefault.c,v
retrieving revision 1.240.2.20
diff -u -r1.240.2.20 gtkfilechooserdefault.c
--- gtkfilechooserdefault.c	23 May 2005 17:02:04 -0000	1.240.2.20
+++ gtkfilechooserdefault.c	10 Jun 2005 05:34:30 -0000
@@ -949,6 +949,21 @@
 		path, error);
 }
 
+/* Shows an error about not being able to create a folder because a file with
+ * the same name is already there.
+ */
+static void
+error_creating_folder_over_existing_file_dialog (GtkFileChooserDefault *impl,
+						 const GtkFilePath     *path,
+						 GError                *error)
+{
+  error_dialog (impl,
+		_("The folder could not be created, as a file with the same name "
+		  "already exists.  Try using a different name for the folder, "
+		  "or rename the file first."),
+		path, error);
+}
+
 /* Shows an error dialog about not being able to create a filename */
 static void
 error_building_filename_dialog (GtkFileChooserDefault *impl,
@@ -3011,11 +3026,14 @@
 		  gpointer     data)
 {
   GtkFileChooserDefault *impl;
+  int modifiers;
 
   impl = (GtkFileChooserDefault *) data;
+
+  modifiers = gtk_accelerator_get_default_mod_mask ();
   
   if (event->keyval == GDK_slash &&
-      ! (event->state & (~GDK_SHIFT_MASK & gtk_accelerator_get_default_mod_mask ())))
+      ! (event->state & (~GDK_SHIFT_MASK & modifiers)))
     {
       location_popup_handler (impl, "/");
       return TRUE;
@@ -3025,6 +3043,7 @@
        || event->keyval == GDK_ISO_Enter
        || event->keyval == GDK_KP_Enter
        || event->keyval == GDK_space)
+      && ((event->state && modifiers) == 0)
       && !(impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER ||
 	   impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
     {
@@ -3857,9 +3876,12 @@
 	  {
 	    gtk_file_chooser_default_unselect_all (GTK_FILE_CHOOSER (impl));
 	    
-	    if (action == GTK_FILE_CHOOSER_ACTION_SAVE && impl->select_multiple)
+	    if ((action == GTK_FILE_CHOOSER_ACTION_SAVE || action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
+		&& impl->select_multiple)
 	      {
-		g_warning ("Multiple selection mode is not allowed in Save mode");
+		g_warning ("Tried to change the file chooser action to SAVE or CREATE_FOLDER, but "
+			   "this is not allowed in multiple selection mode.  Resetting the file chooser "
+			   "to single selection mode.");
 		set_select_multiple (impl, FALSE, TRUE);
 	      }
 	    impl->action = action;
@@ -3897,9 +3919,12 @@
     case GTK_FILE_CHOOSER_PROP_SELECT_MULTIPLE:
       {
 	gboolean select_multiple = g_value_get_boolean (value);
-	if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE && select_multiple)
+	if ((impl->action == GTK_FILE_CHOOSER_ACTION_SAVE || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
+	    && select_multiple)
 	  {
-	    g_warning ("Multiple selection mode is not allowed in Save mode");
+	    g_warning ("Tried to set the file chooser to multiple selection mode, but this is "
+		       "not allowed in SAVE or CREATE_FOLDER modes.  Ignoring the change and "
+		       "leaving the file chooser in single selection mode.");
 	    return;
 	  }
 
@@ -4700,15 +4725,19 @@
   const GtkFileInfo *info;
   GtkTreeIter iter;
   GtkTreeIter child_iter;
+  gboolean change_entry;
 
-  if (impl->action != GTK_FILE_CHOOSER_ACTION_SAVE)
+  if (!(impl->action == GTK_FILE_CHOOSER_ACTION_SAVE || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER))
     return;
 
   g_assert (!impl->select_multiple);
   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
 
   if (!gtk_tree_selection_get_selected (selection, NULL, &iter))
-    return;
+    {
+      _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry), "");
+      return;
+    }
 
   gtk_tree_model_sort_convert_iter_to_child_iter (impl->sort_model,
 						  &child_iter,
@@ -4716,7 +4745,12 @@
 
   info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
 
-  if (!gtk_file_info_get_is_folder (info))
+  if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
+    change_entry = !gtk_file_info_get_is_folder (info); /* We don't want the name to change when clicking on a folder... */
+  else
+    change_entry = TRUE;                                /* ... unless we are in CREATE_FOLDER mode */
+
+  if (change_entry)
     _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry),
 					   gtk_file_info_get_display_name (info));
 }
@@ -5445,41 +5479,75 @@
 
   if (current_focus == impl->browse_files_tree_view)
     {
+      /* The following array encodes what we do based on the impl->action and the
+       * number of files selected.
+       */
+      typedef enum {
+	NOOP,			/* Do nothing (don't respond) */
+	RESPOND,		/* Respond immediately */
+	RESPOND_OR_SWITCH,      /* Respond immediately if the selected item is a file; switch to it if it is a folder */
+	ALL_FILES,		/* Respond only if everything selected is a file */
+	ALL_FOLDERS,		/* Respond only if everything selected is a folder */
+	SAVE_ENTRY,		/* Go to the code for handling the save entry */
+	NOT_REACHED		/* Sanity check */
+      } ActionToTake;
+      static const ActionToTake what_to_do[4][3] = {
+	/*				  0 selected		1 selected		many selected */
+	/* ACTION_OPEN */		{ NOOP,			RESPOND_OR_SWITCH,	ALL_FILES   },
+	/* ACTION_SAVE */		{ SAVE_ENTRY,		RESPOND_OR_SWITCH,	NOT_REACHED },
+	/* ACTION_SELECT_FOLDER */	{ RESPOND,		ALL_FOLDERS,		ALL_FOLDERS },
+	/* ACTION_CREATE_FOLDER */	{ SAVE_ENTRY,		ALL_FOLDERS,		NOT_REACHED }
+      };
+
       int num_selected;
       gboolean all_files, all_folders;
+      int k;
+      ActionToTake action;
 
     file_list:
 
+      g_assert (impl->action >= GTK_FILE_CHOOSER_ACTION_OPEN && impl->action <= GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER);
+
       selection_check (impl, &num_selected, &all_files, &all_folders);
 
-      if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
+      if (num_selected > 2)
+	k = 2;
+      else
+	k = num_selected;
+
+      action = what_to_do [impl->action] [k];
+
+      switch (action)
 	{
-	  if (num_selected != 1) 	 
-	    return TRUE; /* zero means current folder; more than one means use the whole selection */ 	 
-	  else if (current_focus != impl->browse_files_tree_view) 	 
+	case NOOP:
+	  return FALSE;
+
+	case RESPOND:
+	  return TRUE;
+
+	case RESPOND_OR_SWITCH:
+	  g_assert (num_selected == 1);
+
+	  if (all_folders)
 	    {
-	      /* a single folder is selected and a button was clicked */
-	      switch_to_selected_folder (impl); 	 
-	      return TRUE;
+	      switch_to_selected_folder (impl);
+	      return FALSE;
 	    }
-	}
-
-      if (num_selected == 0)
-	{
-	  if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
-	      || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
-	    goto save_entry; /* it makes sense to use the typed name */
 	  else
-	    return FALSE;
-	}
+	    return TRUE;
 
-      if (num_selected == 1 && all_folders)
-	{
-	  switch_to_selected_folder (impl);
-	  return FALSE;
+	case ALL_FILES:
+	  return all_files;
+
+	case ALL_FOLDERS:
+	  return all_folders;
+
+	case SAVE_ENTRY:
+	  goto save_entry;
+
+	default:
+	  g_assert_not_reached ();
 	}
-      else
-	return all_files;
     }
   else if (current_focus == impl->save_file_name_entry)
     {
@@ -5487,7 +5555,8 @@
       gboolean is_valid, is_empty;
       gboolean is_folder;
       gboolean retval;
-      GtkFileChooserEntry *entry;  
+      GtkFileChooserEntry *entry;
+      GError *error;
 
     save_entry:
 
@@ -5497,32 +5566,67 @@
       entry = GTK_FILE_CHOOSER_ENTRY (impl->save_file_name_entry);
       path = check_save_entry (impl, &is_valid, &is_empty);
 
-      if (!is_empty && !is_valid)
+      if (!is_valid)
 	return FALSE;
 
-      if (is_empty)
-	path = gtk_file_path_copy (_gtk_file_chooser_entry_get_current_folder (entry));
-      
-      is_folder = check_is_folder (impl->file_system, path, NULL);
+      g_assert (!is_empty);
+
+      error = NULL;
+      is_folder = check_is_folder (impl->file_system, path, &error);
       if (is_folder)
 	{
-	  _gtk_file_chooser_entry_set_file_part (entry, "");
-	  change_folder_and_display_error (impl, path);
-	  retval = FALSE;
+	  if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
+	    {
+	      _gtk_file_chooser_entry_set_file_part (entry, "");
+	      change_folder_and_display_error (impl, path);
+	      retval = FALSE;
+	    }
+	  else /* GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER */
+	    {
+	      retval = TRUE;
+	    }
 	}
       else
 	{
-	  /* check that everything up to the last component exists */
-	  gtk_file_path_free (path);
-	  path = gtk_file_path_copy (_gtk_file_chooser_entry_get_current_folder (entry));
-	  is_folder = check_is_folder (impl->file_system, path, NULL);
-	  if (!is_folder)
+	  if (impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
+	      && g_error_matches (error, GTK_FILE_SYSTEM_ERROR, GTK_FILE_SYSTEM_ERROR_NOT_FOLDER))
 	    {
-	      change_folder_and_display_error (impl, path);
+	      /* Oops, the user typed the name of an existing path which is not a folder */
+	      error_creating_folder_over_existing_file_dialog (impl, path, error);
 	      retval = FALSE;
 	    }
 	  else
-	    retval = TRUE;
+	    {
+	      GtkFilePath *parent_path;
+
+	      /* check that everything up to the last component exists */
+
+	      parent_path = gtk_file_path_copy (_gtk_file_chooser_entry_get_current_folder (entry));
+	      is_folder = check_is_folder (impl->file_system, parent_path, NULL);
+	      if (!is_folder)
+		{
+		  change_folder_and_display_error (impl, parent_path);
+		  retval = FALSE;
+		}
+	      else
+		{
+		  GError *create_error;
+
+		  create_error = NULL;
+		  if (gtk_file_system_create_folder (impl->file_system, path, &create_error))
+		    retval = TRUE;
+		  else
+		    {
+		      error_creating_folder_dialog (impl, path, create_error);
+		      retval = FALSE;
+		    }
+		}
+
+	      gtk_file_path_free (parent_path);
+	    }
+
+	  if (error != NULL)
+	    g_error_free (error);
 	}
 
       gtk_file_path_free (path);
Index: gtkfilechooserentry.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkfilechooserentry.c,v
retrieving revision 1.27.2.1
diff -u -r1.27.2.1 gtkfilechooserentry.c
--- gtkfilechooserentry.c	20 Mar 2005 06:25:27 -0000	1.27.2.1
+++ gtkfilechooserentry.c	10 Jun 2005 05:34:30 -0000
@@ -383,28 +383,25 @@
   return display_name;
 }
 
-static gboolean
-check_completion_callback (GtkFileChooserEntry *chooser_entry)
+/* Determines if the completion model has entries with a common prefix relative
+ * to the current contents of the entry.  Also, if there's one and only one such
+ * path, stores it in unique_path_ret.
+ */
+static void
+find_common_prefix (GtkFileChooserEntry *chooser_entry,
+		    gchar               **common_prefix_ret,
+		    GtkFilePath         **unique_path_ret)
 {
   GtkTreeIter iter;
-  gchar *common_prefix = NULL;
-  GtkFilePath *unique_path = NULL;
   gboolean valid;
 
-  GDK_THREADS_ENTER ();
-
-  g_assert (chooser_entry->file_part);
-
-  chooser_entry->check_completion_idle = NULL;
-
-  if (strcmp (chooser_entry->file_part, "") == 0)
-    goto done;
+  *common_prefix_ret = NULL;
+  *unique_path_ret = NULL;
 
   if (chooser_entry->completion_store == NULL)
-    goto done;
+    return;
 
-  valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (chooser_entry->completion_store),
-					 &iter);
+  valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (chooser_entry->completion_store), &iter);
 
   while (valid)
     {
@@ -419,14 +416,14 @@
 
       if (g_str_has_prefix (display_name, chooser_entry->file_part))
 	{
-	  if (!common_prefix)
+	  if (!*common_prefix_ret)
 	    {
-	      common_prefix = g_strdup (display_name);
-	      unique_path = gtk_file_path_copy (path);
+	      *common_prefix_ret = g_strdup (display_name);
+	      *unique_path_ret = gtk_file_path_copy (path);
 	    }
 	  else
 	    {
-	      gchar *p = common_prefix;
+	      gchar *p = *common_prefix_ret;
 	      const gchar *q = display_name;
 		  
 	      while (*p && *p == *q)
@@ -437,16 +434,26 @@
 		  
 	      *p = '\0';
 
-	      gtk_file_path_free (unique_path);
-	      unique_path = NULL;
+	      gtk_file_path_free (*unique_path_ret);
+	      *unique_path_ret = NULL;
 	    }
 	}
 
       g_free (display_name);
       gtk_file_path_free (path);
-      valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (chooser_entry->completion_store),
-					&iter);
+      valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (chooser_entry->completion_store), &iter);
     }
+}
+
+/* Finds a common prefix based on the contents of the entry and mandatorily appends it */
+static void
+append_common_prefix (GtkFileChooserEntry *chooser_entry,
+		      gboolean             highlight)
+{
+  gchar *common_prefix;
+  GtkFilePath *unique_path;
+
+  find_common_prefix (chooser_entry, &common_prefix, &unique_path);
 
   if (unique_path)
     {
@@ -456,19 +463,6 @@
       gtk_file_path_free (unique_path);
     }
 
-  switch (chooser_entry->action)
-    {
-    case GTK_FILE_CHOOSER_ACTION_SAVE:
-    case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
-      if (common_prefix && !g_str_has_suffix (common_prefix, "/"))
-	{
-	  g_free (common_prefix);
-	  common_prefix = NULL;
-	}
-      break;
-    default: ;
-    }
-
   if (common_prefix)
     {
       gint file_part_len;
@@ -488,16 +482,40 @@
 	  gtk_editable_insert_text (GTK_EDITABLE (chooser_entry),
 				    common_prefix, -1, 
 				    &pos);
-	  gtk_editable_select_region (GTK_EDITABLE (chooser_entry),
-				      chooser_entry->file_part_pos + file_part_len,
-				      chooser_entry->file_part_pos + common_prefix_len);
 	  chooser_entry->in_change = FALSE;
 
-	  chooser_entry->has_completion = TRUE;
+	  if (highlight)
+	    {
+	      gtk_editable_select_region (GTK_EDITABLE (chooser_entry),
+					  chooser_entry->file_part_pos + file_part_len,
+					  chooser_entry->file_part_pos + common_prefix_len);
+	      chooser_entry->has_completion = TRUE;
+	    }
 	}
-	  
+
       g_free (common_prefix);
     }
+}
+
+static gboolean
+check_completion_callback (GtkFileChooserEntry *chooser_entry)
+{
+  GDK_THREADS_ENTER ();
+
+  g_assert (chooser_entry->file_part);
+
+  chooser_entry->check_completion_idle = NULL;
+
+  if (strcmp (chooser_entry->file_part, "") == 0)
+    goto done;
+
+  /* We only insert the common prefix without requiring the user to hit Tab in
+   * the "open" modes.  For "save" modes, the user must hit Tab to cause the prefix
+   * to be inserted.  That happens in gtk_file_chooser_entry_focus().
+   */
+  if (chooser_entry->action == GTK_FILE_CHOOSER_ACTION_OPEN
+      || chooser_entry->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
+    append_common_prefix (chooser_entry, TRUE);
 
  done:
 
@@ -659,13 +677,21 @@
 gtk_file_chooser_entry_focus (GtkWidget        *widget,
 			      GtkDirectionType  direction)
 {
-  GtkFileChooserEntry *chooser_entry = GTK_FILE_CHOOSER_ENTRY (widget);
+  GtkFileChooserEntry *chooser_entry;
+  GtkEditable *editable;
+  GtkEntry *entry;
   GdkModifierType state;
-  gboolean control_pressed = FALSE;
+  gboolean control_pressed;
+
+  chooser_entry = GTK_FILE_CHOOSER_ENTRY (widget);
+  editable = GTK_EDITABLE (widget);
+  entry = GTK_ENTRY (widget);
 
   if (!chooser_entry->eat_tabs)
     return GTK_WIDGET_CLASS (parent_class)->focus (widget, direction);
 
+  control_pressed = FALSE;
+
   if (gtk_get_current_event_state (&state))
     {
       if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
@@ -680,13 +706,16 @@
     {
       gint pos = 0;
 
-      if (chooser_entry->has_completion)
-	gtk_editable_set_position (GTK_EDITABLE (widget),
-				   GTK_ENTRY (widget)->text_length);
+      if (!chooser_entry->has_completion
+	  && gtk_editable_get_position (editable) == entry->text_length)
+	append_common_prefix (chooser_entry, FALSE);
+
+      gtk_editable_set_position (editable, entry->text_length);
+
       /* Trigger the completion window to pop up again by a 
        * zero-length insertion, a bit of a hack.
        */
-      gtk_editable_insert_text (GTK_EDITABLE (widget), "", -1, &pos);
+      gtk_editable_insert_text (editable, "", -1, &pos);
 
       return TRUE;
     }
