]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkfilechooserdefault.c
Display an error when we come to the root.
[~andy/gtk] / gtk / gtkfilechooserdefault.c
index 98c0aa18c34fc4df2393b8ce4fe8fbeb9b7acd19..c5338b9b69ef938d9d21ae5188e07bc943c09c88 100644 (file)
@@ -544,8 +544,7 @@ _gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class)
                                GDK_l, GDK_CONTROL_MASK,
                                "location-popup",
                                1, G_TYPE_STRING, "");
-  /* FMQ: remove this? */
-#if 0
+
   gtk_binding_entry_add_signal (binding_set,
                                GDK_slash, 0,
                                "location-popup",
@@ -560,7 +559,6 @@ _gtk_file_chooser_default_class_init (GtkFileChooserDefaultClass *class)
                                GDK_asciitilde, 0,
                                "location-popup",
                                1, G_TYPE_STRING, "~");
-#endif
 #endif
 
   gtk_binding_entry_add_signal (binding_set,
@@ -799,6 +797,8 @@ gtk_file_chooser_default_finalize (GObject *object)
 
   g_object_unref (impl->file_system);
 
+  g_free (impl->browse_files_last_selected_name);
+
   for (l = impl->filters; l; l = l->next)
     {
       GtkFileFilter *filter;
@@ -1905,7 +1905,11 @@ shortcuts_add_current_folder (GtkFileChooserDefault *impl)
          shortcuts_insert_path (impl, pos, TRUE, volume, NULL, NULL, FALSE, SHORTCUTS_CURRENT_FOLDER);
        }
       else
-       shortcuts_insert_path (impl, pos, FALSE, NULL, impl->current_folder, NULL, FALSE, SHORTCUTS_CURRENT_FOLDER);
+        {
+         shortcuts_insert_path (impl, pos, FALSE, NULL, impl->current_folder, NULL, FALSE, SHORTCUTS_CURRENT_FOLDER);
+         if (volume)
+           gtk_file_system_volume_free (impl->file_system, volume);
+       }
 
       if (base_path)
        gtk_file_path_free (base_path);
@@ -1965,7 +1969,7 @@ shortcuts_model_create (GtkFileChooserDefault *impl)
                                              G_TYPE_BOOLEAN,   /* is the previous column a volume? */
                                              G_TYPE_BOOLEAN,   /* removable */
                                              G_TYPE_BOOLEAN,   /* pixbuf cell visibility */
-                                             G_TYPE_OBJECT);   /* GtkFileSystemHandle */
+                                             G_TYPE_POINTER);   /* GtkFileSystemHandle */
 
   if (impl->file_system)
     {
@@ -3158,8 +3162,6 @@ tree_view_keybinding_cb (GtkWidget             *tree_view,
                         GdkEventKey           *event,
                         GtkFileChooserDefault *impl)
 {
-  /* FMQ: remove this? */
-#if 0
   if ((event->keyval == GDK_slash
        || event->keyval == GDK_KP_Divide
 #ifdef G_OS_UNIX
@@ -3170,7 +3172,7 @@ tree_view_keybinding_cb (GtkWidget             *tree_view,
       location_popup_handler (impl, event->string);
       return TRUE;
     }
-#endif
+
   return FALSE;
 }
 
@@ -3542,8 +3544,6 @@ trap_activate_cb (GtkWidget   *widget,
 
   modifiers = gtk_accelerator_get_default_mod_mask ();
 
-  /* FMQ: remove this? */
-#if 0
   if ((event->keyval == GDK_slash
        || event->keyval == GDK_KP_Divide
 #ifdef G_OS_UNIX
@@ -3554,7 +3554,6 @@ trap_activate_cb (GtkWidget   *widget,
       location_popup_handler (impl, event->string);
       return TRUE;
     }
-#endif
 
   if ((event->keyval == GDK_Return
        || event->keyval == GDK_ISO_Enter
@@ -4391,7 +4390,6 @@ location_switch_to_filename_entry (GtkFileChooserDefault *impl)
   /* Configure the entry */
 
   _gtk_file_chooser_entry_set_base_folder (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->current_folder);
-  location_entry_set_initial_text (impl);
 
   /* Done */
 
@@ -4521,6 +4519,7 @@ browse_widgets_create (GtkFileChooserDefault *impl)
   GtkWidget *hpaned;
   GtkWidget *widget;
   GtkSizeGroup *size_group;
+  gchar *text;
 
   /* size group is used by the [+][-] buttons and the filter combo */
   size_group = gtk_size_group_new (GTK_SIZE_GROUP_VERTICAL);
@@ -4551,8 +4550,10 @@ browse_widgets_create (GtkFileChooserDefault *impl)
 
   impl->location_entry_box = gtk_hbox_new (FALSE, 12);
   gtk_box_pack_start (GTK_BOX (vbox), impl->location_entry_box, FALSE, FALSE, 0);
-
-  impl->location_label = gtk_label_new_with_mnemonic ("<b>_Location:</b>");
+  
+  text = g_strconcat ("<b>", _("_Location:"), "</b>", NULL);
+  impl->location_label = gtk_label_new_with_mnemonic (text);
+  g_free (text);
   gtk_label_set_use_markup (GTK_LABEL (impl->location_label), TRUE);
   gtk_widget_show (impl->location_label);
   gtk_box_pack_start (GTK_BOX (impl->location_entry_box), impl->location_label, FALSE, FALSE, 0);
@@ -5640,7 +5641,7 @@ load_timeout_cb (gpointer data)
   return FALSE;
 }
 
-/* Sets up a new load timer for the model and switches to the LOAD_LOADING state */
+/* Sets up a new load timer for the model and switches to the LOAD_PRELOAD state */
 static void
 load_setup_timer (GtkFileChooserDefault *impl)
 {
@@ -5786,6 +5787,7 @@ show_and_select_paths_finished_loading (GtkFileFolder *folder,
 
   browse_files_center_selected_row (data->impl);
 
+  g_object_unref (data->impl);
   gtk_file_paths_free (data->paths);
   g_free (data);
 }
@@ -5866,8 +5868,6 @@ pending_select_paths_process (GtkFileChooserDefault *impl)
   g_assert (impl->browse_files_model != NULL);
   g_assert (impl->sort_model != NULL);
 
-  impl->processing_pending_selections = TRUE;
-
   if (impl->pending_select_paths)
     {
       /* NULL GError */
@@ -5894,8 +5894,6 @@ pending_select_paths_process (GtkFileChooserDefault *impl)
        browse_files_select_first_row (impl);
     }
 
-  impl->processing_pending_selections = FALSE;
-
   g_assert (impl->pending_select_paths == NULL);
 }
 
@@ -5993,6 +5991,24 @@ struct update_chooser_entry_selected_foreach_closure {
   GtkTreeIter first_selected_iter;
 };
 
+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 void
 update_chooser_entry_selected_foreach (GtkTreeModel *model,
                                       GtkTreePath *path,
@@ -6015,9 +6031,6 @@ update_chooser_entry (GtkFileChooserDefault *impl)
   struct update_chooser_entry_selected_foreach_closure closure;
   const char *file_part;
 
-  if (impl->processing_pending_selections)
-    return;
-
   if (!(impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
        || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
        || ((impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
@@ -6028,7 +6041,6 @@ update_chooser_entry (GtkFileChooserDefault *impl)
   g_assert (impl->location_entry != NULL);
 
   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
-
   closure.num_selected = 0;
   gtk_tree_selection_selected_foreach (selection, update_chooser_entry_selected_foreach, &closure);
 
@@ -6036,14 +6048,7 @@ update_chooser_entry (GtkFileChooserDefault *impl)
 
   if (closure.num_selected == 0)
     {
-      /* If nothing is selected, we only reset the file name entry if we are in
-       * CREATE_FOLDER mode.  In SAVE mode, nothing will be selected when the
-       * user starts typeahead in the treeview, and we don't want to clear the
-       * file name entry in that case --- the user could be typing-ahead to look
-       * for a folder name.  See http://bugzilla.gnome.org/show_bug.cgi?id=308332
-       */
-      if (impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
-       file_part = "";
+      goto maybe_clear_entry;
     }
   else if (closure.num_selected == 1)
     {
@@ -6057,24 +6062,66 @@ update_chooser_entry (GtkFileChooserDefault *impl)
 
       info = _gtk_file_system_model_get_info (impl->browse_files_model, &child_iter);
 
+      g_free (impl->browse_files_last_selected_name);
+      impl->browse_files_last_selected_name = g_strdup (gtk_file_info_get_display_name (info));
+
       if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN
-         || impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
-         || impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
+         || 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 */
+       change_entry = TRUE;                                /* ... unless we are in one of the folder modes */
 
       if (change_entry)
-       file_part = gtk_file_info_get_display_name (info);
+       _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), impl->browse_files_last_selected_name);
+
+      return;
     }
   else
     {
+      g_assert (!(impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
+                 || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER));
+
       /* Multiple selection, so just clear the entry. */
-      file_part = "";
+
+      g_free (impl->browse_files_last_selected_name);
+      impl->browse_files_last_selected_name = NULL;
+
+      _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), "");
+      return;
     }
 
-  if (file_part)
-    _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), file_part);
+ maybe_clear_entry:
+
+  if (impl->browse_files_last_selected_name)
+    {
+      const char *entry_text;
+      int len;
+      gboolean clear_entry;
+
+      entry_text = gtk_entry_get_text (GTK_ENTRY (impl->location_entry));
+      len = strlen (entry_text);
+      if (len != 0)
+       {
+         /* The file chooser entry may have appended a "/" to its text.  So
+          * take it out, and compare the result to the old selection.
+          */
+         if (entry_text[len - 1] == G_DIR_SEPARATOR)
+           {
+             char *tmp;
+
+             tmp = g_strndup (entry_text, len - 1);
+             clear_entry = (compare_utf8_filenames (impl->browse_files_last_selected_name, tmp) == 0);
+             g_free (tmp);
+           }
+         else
+           clear_entry = (compare_utf8_filenames (impl->browse_files_last_selected_name, entry_text) == 0);
+       }
+      else
+       clear_entry = FALSE;
+
+      if (clear_entry)
+       _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), "");
+    }
 }
 
 static gboolean
@@ -6091,6 +6138,8 @@ struct UpdateCurrentFolderData
   GtkFileChooserDefault *impl;
   GtkFilePath *path;
   gboolean keep_trail;
+  GtkFilePath *original_path;
+  GError *original_error;
 };
 
 static void
@@ -6116,8 +6165,52 @@ update_current_folder_get_info_cb (GtkFileSystemHandle *handle,
 
   if (error)
     {
-      error_changing_folder_dialog (impl, data->path, g_error_copy (error));
-      goto out;
+      GtkFilePath *parent_path;
+
+      if (!data->original_path)
+        {
+         data->original_path = gtk_file_path_copy (data->path);
+         data->original_error = g_error_copy (error);
+       }
+
+      /* get parent path and try to change the folder to that */
+      if (gtk_file_system_get_parent (impl->file_system, data->path, &parent_path, NULL) &&
+         parent_path != NULL)
+        {
+         gtk_file_path_free (data->path);
+         data->path = parent_path;
+
+         g_object_unref (handle);
+
+         /* restart the update current folder operation */
+         impl->reload_state = RELOAD_HAS_FOLDER;
+
+         impl->update_current_folder_handle =
+           gtk_file_system_get_info (impl->file_system, data->path,
+                                     GTK_FILE_INFO_IS_FOLDER,
+                                     update_current_folder_get_info_cb,
+                                     data);
+
+         set_busy_cursor (impl, TRUE);
+
+         return;
+       }
+      else
+        {
+         /* error and bail out */
+         error_changing_folder_dialog (impl, data->original_path, data->original_error);
+
+         gtk_file_path_free (data->original_path);
+
+         goto out;
+       }
+    }
+
+  if (data->original_path)
+    {
+      error_changing_folder_dialog (impl, data->original_path, data->original_error);
+
+      gtk_file_path_free (data->original_path);
     }
 
   if (!gtk_file_info_get_is_folder (info))
@@ -6555,18 +6648,19 @@ gtk_file_chooser_default_get_paths (GtkFileChooser *chooser)
 
       check_save_entry (impl, &info.path_from_entry, &is_well_formed, &is_empty, &is_file_part_empty, &is_folder);
 
+      if (is_empty)
+       goto out;
+
       if (!is_well_formed)
        return NULL;
 
-      if (!is_empty)
+      if (is_file_part_empty && impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
        {
-         if (is_file_part_empty && impl->action == GTK_FILE_CHOOSER_ACTION_SAVE)
-           {
-             gtk_file_path_free (info.path_from_entry);
-             return NULL;
-           }
+         gtk_file_path_free (info.path_from_entry);
+         return NULL;
        }
 
+      g_assert (info.path_from_entry != NULL);
       info.result = g_slist_prepend (info.result, info.path_from_entry);
     }
   else if (impl->toplevel_last_focus_widget == impl->browse_files_tree_view)
@@ -6583,6 +6677,8 @@ gtk_file_chooser_default_get_paths (GtkFileChooser *chooser)
        goto file_list; 
     }
 
+ out:
+
   /* If there's no folder selected, and we're in SELECT_FOLDER mode, then we
    * fall back to the current directory */
   if (impl->action == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER &&
@@ -7277,7 +7373,7 @@ out:
   g_object_unref (handle);
 }
 
-struct SaveEntryData
+struct FileExistsData
 {
   GtkFileChooserDefault *impl;
   gboolean file_exists_and_is_not_folder;
@@ -7293,7 +7389,7 @@ save_entry_get_info_cb (GtkFileSystemHandle *handle,
 {
   gboolean parent_is_folder;
   gboolean cancelled = handle->cancelled;
-  struct SaveEntryData *data = user_data;
+  struct FileExistsData *data = user_data;
 
   if (handle != data->impl->should_respond_get_info_handle)
     goto out;
@@ -7356,6 +7452,72 @@ out:
   g_object_unref (handle);
 }
 
+static void
+file_exists_get_info_cb (GtkFileSystemHandle *handle,
+                        const GtkFileInfo   *info,
+                        const GError        *error,
+                        gpointer             user_data)
+{
+  gboolean data_ownership_taken = FALSE;
+  gboolean cancelled = handle->cancelled;
+  gboolean file_exists_and_is_not_folder;
+  struct FileExistsData *data = user_data;
+
+  if (handle != data->impl->file_exists_get_info_handle)
+    goto out;
+
+  data->impl->file_exists_get_info_handle = NULL;
+
+  set_busy_cursor (data->impl, FALSE);
+
+  if (cancelled)
+    goto out;
+
+  file_exists_and_is_not_folder = info && !gtk_file_info_get_is_folder (info);
+
+  if (data->impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
+    /* user typed a filename; we are done */
+    g_signal_emit_by_name (data->impl, "response-requested");
+  else if (data->impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
+          && file_exists_and_is_not_folder)
+    {
+      /* Oops, the user typed the name of an existing path which is not
+       * a folder
+       */
+      error_creating_folder_over_existing_file_dialog (data->impl, data->path,
+                                                      g_error_copy (error));
+    }
+  else
+    {
+      /* check that everything up to the last component exists */
+
+      data->file_exists_and_is_not_folder = file_exists_and_is_not_folder;
+      data_ownership_taken = TRUE;
+
+      if (data->impl->should_respond_get_info_handle)
+       gtk_file_system_cancel_operation (data->impl->should_respond_get_info_handle);
+
+      data->impl->should_respond_get_info_handle =
+       gtk_file_system_get_info (data->impl->file_system,
+                                 data->parent_path,
+                                 GTK_FILE_INFO_IS_FOLDER,
+                                 save_entry_get_info_cb,
+                                 data);
+      set_busy_cursor (data->impl, TRUE);
+    }
+
+out:
+  if (!data_ownership_taken)
+    {
+      g_object_unref (data->impl);
+      gtk_file_path_free (data->path);
+      gtk_file_path_free (data->parent_path);
+      g_free (data);
+    }
+
+  g_object_unref (handle);
+}
+
 static void
 paste_text_received (GtkClipboard          *clipboard,
                     const gchar           *text,
@@ -7543,46 +7705,26 @@ gtk_file_chooser_default_should_respond (GtkFileChooserEmbed *chooser_embed)
        }
       else
        {
-         gboolean file_exists_and_is_not_folder;
-
-         file_exists_and_is_not_folder = g_error_matches (error, GTK_FILE_SYSTEM_ERROR, GTK_FILE_SYSTEM_ERROR_NOT_FOLDER);
+         struct FileExistsData *data;
 
-         if (impl->action == GTK_FILE_CHOOSER_ACTION_OPEN)
-           retval = TRUE; /* user typed a filename; we are done */
-         else if (impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER && file_exists_and_is_not_folder)
-           {
-             /* 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);
-             error = NULL; /* as it will be freed below for the general case */
-             retval = FALSE;
-           }
-         else
-           {
-             GtkFilePath *parent_path;
-             struct SaveEntryData *data;
+         /* We need to check whether path exists and is not a folder */
 
-             /* check that everything up to the last component exists */
-
-             parent_path = gtk_file_path_copy (_gtk_file_chooser_entry_get_current_folder (entry));
-
-             data = g_new0 (struct SaveEntryData, 1);
-             data->impl = g_object_ref (impl);
-             data->file_exists_and_is_not_folder = file_exists_and_is_not_folder;
-             data->parent_path = parent_path; /* Takes ownership */
-             data->path = gtk_file_path_copy (path);
+         data = g_new0 (struct FileExistsData, 1);
+         data->impl = g_object_ref (impl);
+         data->path = gtk_file_path_copy (path);
+         data->parent_path = gtk_file_path_copy (_gtk_file_chooser_entry_get_current_folder (entry));
 
-             if (impl->should_respond_get_info_handle)
-               gtk_file_system_cancel_operation (impl->should_respond_get_info_handle);
+         if (impl->file_exists_get_info_handle)
+           gtk_file_system_cancel_operation (impl->file_exists_get_info_handle);
 
-             impl->should_respond_get_info_handle =
-               gtk_file_system_get_info (impl->file_system, parent_path,
-                                         GTK_FILE_INFO_IS_FOLDER,
-                                         save_entry_get_info_cb,
-                                         data);
-             set_busy_cursor (impl, TRUE);
+         impl->file_exists_get_info_handle =
+           gtk_file_system_get_info (impl->file_system, path,
+                                     GTK_FILE_INFO_IS_FOLDER,
+                                     file_exists_get_info_cb,
+                                     data);
 
-             retval = FALSE;
-           }
+         set_busy_cursor (impl, TRUE);
+         retval = FALSE;
 
          if (error != NULL)
            g_error_free (error);
@@ -7891,6 +8033,9 @@ shortcuts_activate_iter (GtkFileChooserDefault *impl,
   gpointer col_data;
   gboolean is_volume;
 
+  if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY && impl->action != GTK_FILE_CHOOSER_ACTION_SAVE)
+    _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), "");
+
   gtk_tree_model_get (GTK_TREE_MODEL (impl->shortcuts_model), iter,
                      SHORTCUTS_COL_DATA, &col_data,
                      SHORTCUTS_COL_IS_VOLUME, &is_volume,
@@ -8326,6 +8471,14 @@ _gtk_file_chooser_default_new (const char *file_system)
                        NULL);
 }
 
+static void
+location_set_user_text (GtkFileChooserDefault *impl,
+                       const gchar           *path)
+{
+  _gtk_file_chooser_entry_set_file_part (GTK_FILE_CHOOSER_ENTRY (impl->location_entry), path);
+  gtk_editable_set_position (GTK_EDITABLE (impl->location_entry), -1);
+}
+
 static void
 location_popup_handler (GtkFileChooserDefault *impl,
                        const gchar           *path)
@@ -8335,7 +8488,12 @@ location_popup_handler (GtkFileChooserDefault *impl,
     {
       LocationMode new_mode;
 
-      if (impl->location_mode == LOCATION_MODE_PATH_BAR)
+      if (path != NULL)
+       {
+         /* since the user typed something, we unconditionally want to turn on the entry */
+         new_mode = LOCATION_MODE_FILENAME_ENTRY;
+       }
+      else if (impl->location_mode == LOCATION_MODE_PATH_BAR)
        new_mode = LOCATION_MODE_FILENAME_ENTRY;
       else if (impl->location_mode == LOCATION_MODE_FILENAME_ENTRY)
        new_mode = LOCATION_MODE_PATH_BAR;
@@ -8346,10 +8504,24 @@ location_popup_handler (GtkFileChooserDefault *impl,
        }
 
       location_mode_set (impl, new_mode, TRUE);
+      if (new_mode == LOCATION_MODE_FILENAME_ENTRY)
+       {
+         if (path != NULL)
+           location_set_user_text (impl, path);
+         else
+           {
+             location_entry_set_initial_text (impl);
+             gtk_editable_select_region (GTK_EDITABLE (impl->location_entry), 0, -1);
+           }
+       }
     }
   else if (impl->action == GTK_FILE_CHOOSER_ACTION_SAVE
           || impl->action == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER)
-    gtk_widget_grab_focus (impl->location_entry);
+    {
+      gtk_widget_grab_focus (impl->location_entry);
+      if (path != NULL)
+       location_set_user_text (impl, path);
+    }
   else
     g_assert_not_reached ();
 }
@@ -8499,6 +8671,3 @@ shortcuts_model_filter_new (GtkFileChooserDefault *impl,
 
   return GTK_TREE_MODEL (model);
 }
-
-#define __GTK_FILE_CHOOSER_DEFAULT_C__
-#include "gtkaliasdef.c"