[nautilus] nautilus-view: use popover for renaming



commit 328348c6f76bc72823c1a8464937b7f4208674d9
Author: Carlos Soriano <csoriano gnome org>
Date:   Sat Aug 15 17:20:21 2015 +0200

    nautilus-view: use popover for renaming
    
    Some users felt it quite disruptive to us a dialog, even
    if the attention has to be on the name entry.
    Also it's the way that GtkFileChooser implemented it
    and it actually feels less disruptive and desigerns agreed
    it works better.
    So continue to use a dialog for the new folder creation, but use
    a popover for renaming.

 libnautilus-private/nautilus-canvas-container.c    |   41 ++
 libnautilus-private/nautilus-canvas-container.h    |    1 +
 libnautilus-private/nautilus-directory.c           |   22 +
 libnautilus-private/nautilus-directory.h           |    2 +
 src/nautilus-canvas-view.c                         |   18 +
 ...-dialog.ui => nautilus-create-folder-dialog.ui} |    6 +-
 src/nautilus-list-view.c                           |   42 ++
 src/nautilus-rename-file-popover.ui                |   66 +++
 src/nautilus-view.c                                |  436 +++++++++++---------
 src/nautilus-view.h                                |    2 +
 src/nautilus.gresource.xml                         |    3 +-
 11 files changed, 430 insertions(+), 209 deletions(-)
---
diff --git a/libnautilus-private/nautilus-canvas-container.c b/libnautilus-private/nautilus-canvas-container.c
index a2620dc..d40b77a 100644
--- a/libnautilus-private/nautilus-canvas-container.c
+++ b/libnautilus-private/nautilus-canvas-container.c
@@ -6380,6 +6380,47 @@ nautilus_canvas_container_get_icon_locations (NautilusCanvasContainer *container
        return result;
 }
 
+/* Returns an array of GdkRectangles of the icons. */
+static GArray *
+nautilus_canvas_container_get_icons_bounding_box (NautilusCanvasContainer *container,
+                                                  GList                   *icons)
+{
+        GArray *result;
+        GList *node;
+        int index;
+        int x1, x2, y1, y2;
+
+        result = g_array_new (FALSE, TRUE, sizeof (GdkRectangle));
+        result = g_array_set_size (result, g_list_length (icons));
+
+        for (index = 0, node = icons; node != NULL; index++, node = node->next) {
+               icon_get_bounding_box ((NautilusCanvasIcon *)node->data,
+                                       &x1, &y1, &x2, &y2,
+                                      BOUNDS_USAGE_FOR_ENTIRE_ITEM);
+                g_array_index (result, GdkRectangle, index).x = x1;
+                g_array_index (result, GdkRectangle, index).width = x2 - x1;
+                g_array_index (result, GdkRectangle, index).y = y1;
+                g_array_index (result, GdkRectangle, index).height = y2 - y1;
+        }
+
+        return result;
+}
+
+GArray *
+nautilus_canvas_container_get_selected_icons_bounding_box (NautilusCanvasContainer *container)
+{
+        GArray *result;
+        GList *icons;
+
+        g_return_val_if_fail (NAUTILUS_IS_CANVAS_CONTAINER (container), NULL);
+
+        icons = nautilus_canvas_container_get_selected_icons (container);
+       result = nautilus_canvas_container_get_icons_bounding_box (container, icons);
+       g_list_free (icons);
+
+       return result;
+}
+
 /**
  * nautilus_canvas_container_get_selected_icon_locations:
  * @container: An canvas container widget.
diff --git a/libnautilus-private/nautilus-canvas-container.h b/libnautilus-private/nautilus-canvas-container.h
index 9519b4a..711b3ea 100644
--- a/libnautilus-private/nautilus-canvas-container.h
+++ b/libnautilus-private/nautilus-canvas-container.h
@@ -270,6 +270,7 @@ void                          nautilus_canvas_container_invert_selection                  
          (NautilusCanvasContainer
 void              nautilus_canvas_container_set_selection                 (NautilusCanvasContainer  *view,
                                                                           GList                  *selection);
 GArray    *       nautilus_canvas_container_get_selected_icon_locations   (NautilusCanvasContainer  *view);
+GArray    *       nautilus_canvas_container_get_selected_icons_bounding_box (NautilusCanvasContainer 
*container);
 gboolean          nautilus_canvas_container_has_stretch_handles           (NautilusCanvasContainer  
*container);
 gboolean          nautilus_canvas_container_is_stretched                  (NautilusCanvasContainer  
*container);
 void              nautilus_canvas_container_show_stretch_handles          (NautilusCanvasContainer  
*container);
diff --git a/libnautilus-private/nautilus-directory.c b/libnautilus-private/nautilus-directory.c
index 4f40808..e150e81 100644
--- a/libnautilus-private/nautilus-directory.c
+++ b/libnautilus-private/nautilus-directory.c
@@ -1447,6 +1447,28 @@ nautilus_directory_contains_file (NautilusDirectory *directory,
        return NAUTILUS_DIRECTORY_CLASS (G_OBJECT_GET_CLASS (directory))->contains_file (directory, file);
 }
 
+NautilusFile*
+nautilus_directory_get_file_by_name (NautilusDirectory *directory,
+                                     const gchar       *name)
+{
+        GList *files;
+        GList *l;
+        NautilusFile *result = NULL;
+
+        files = nautilus_directory_get_file_list (directory);
+
+        for (l = files; l != NULL; l = l->next) {
+                if (nautilus_file_compare_display_name (l->data, name) == 0) {
+                     result = nautilus_file_ref (l->data);
+                     break;
+                }
+        }
+
+        nautilus_file_list_free (files);
+
+        return result;
+}
+
 void
 nautilus_directory_call_when_ready (NautilusDirectory *directory,
                                    NautilusFileAttributes file_attributes,
diff --git a/libnautilus-private/nautilus-directory.h b/libnautilus-private/nautilus-directory.h
index 46040d7..5f6c0cd 100644
--- a/libnautilus-private/nautilus-directory.h
+++ b/libnautilus-private/nautilus-directory.h
@@ -167,6 +167,8 @@ GFile *            nautilus_directory_get_location             (NautilusDirector
 gboolean           nautilus_directory_contains_file            (NautilusDirectory         *directory,
                                                                NautilusFile              *file);
 
+NautilusFile*           nautilus_directory_get_file_by_name            (NautilusDirectory *directory,
+                                                                        const gchar       *name);
 /* Get (and ref) a NautilusFile object for this directory. */
 NautilusFile *     nautilus_directory_get_corresponding_file   (NautilusDirectory         *directory);
 
diff --git a/src/nautilus-canvas-view.c b/src/nautilus-canvas-view.c
index 53e37d9..8d42d6f 100644
--- a/src/nautilus-canvas-view.c
+++ b/src/nautilus-canvas-view.c
@@ -1172,6 +1172,23 @@ nautilus_canvas_view_reveal_selection (NautilusView *view)
         nautilus_file_list_free (selection);
 }
 
+static GdkRectangle*
+nautilus_canvas_view_compute_rename_popover_relative_to (NautilusView *view)
+{
+        GArray *bounding_boxes;
+        GdkRectangle *bounding_box;
+        NautilusCanvasContainer *canvas_container;
+
+        canvas_container = get_canvas_container (NAUTILUS_CANVAS_VIEW (view));
+        bounding_boxes = nautilus_canvas_container_get_selected_icons_bounding_box (canvas_container);
+        /* We only allow renaming one item at once */
+        bounding_box = &g_array_index (bounding_boxes, GdkRectangle, 0);
+
+        g_array_free (bounding_boxes, FALSE);
+
+        return bounding_box;
+}
+
 static GArray *
 nautilus_canvas_view_get_selected_icon_locations (NautilusView *view)
 {
@@ -1877,6 +1894,7 @@ nautilus_canvas_view_class_init (NautilusCanvasViewClass *klass)
        nautilus_view_class->end_loading = nautilus_canvas_view_end_loading;
        nautilus_view_class->file_changed = nautilus_canvas_view_file_changed;
        nautilus_view_class->get_selected_icon_locations = nautilus_canvas_view_get_selected_icon_locations;
+       nautilus_view_class->compute_rename_popover_relative_to = 
nautilus_canvas_view_compute_rename_popover_relative_to;
        nautilus_view_class->get_selection = nautilus_canvas_view_get_selection;
        nautilus_view_class->get_selection_for_file_transfer = nautilus_canvas_view_get_selection;
        nautilus_view_class->is_empty = nautilus_canvas_view_is_empty;
diff --git a/src/nautilus-file-name-dialog.ui b/src/nautilus-create-folder-dialog.ui
similarity index 91%
rename from src/nautilus-file-name-dialog.ui
rename to src/nautilus-create-folder-dialog.ui
index 4e2c4c7..21bf6b2 100644
--- a/src/nautilus-file-name-dialog.ui
+++ b/src/nautilus-create-folder-dialog.ui
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
   <requires lib="gtk+" version="3.14"/>
-  <object class="GtkDialog" id="file_name_dialog">
+  <object class="GtkDialog" id="create_folder_dialog">
     <property name="resizable">False</property>
     <property name="modal">True</property>
     <property name="window_position">center-on-parent</property>
@@ -32,8 +32,8 @@
           <object class="GtkEntry" id="name_entry">
             <property name="visible">True</property>
             <property name="can_focus">True</property>
-            <signal name="notify::text" handler="nautilus_view_file_name_dialog_entry_on_validate" 
swapped="no" />
-            <signal name="activate" handler="nautilus_view_file_name_dialog_entry_on_activate" swapped="no" 
/>
+            <signal name="changed" handler="create_folder_dialog_entry_on_changed" swapped="no" />
+            <signal name="activate" handler="create_folder_dialog_entry_on_activate" swapped="no" />
           </object>
           <packing>
             <property name="expand">False</property>
diff --git a/src/nautilus-list-view.c b/src/nautilus-list-view.c
index 20a4882..35a7472 100644
--- a/src/nautilus-list-view.c
+++ b/src/nautilus-list-view.c
@@ -59,6 +59,12 @@
 #define DEBUG_FLAG NAUTILUS_DEBUG_LIST_VIEW
 #include <libnautilus-private/nautilus-debug.h>
 
+/* We use a rectangle to make the popover point to the right column. In an
+ * ideal world with GtkListBox we would just point to the GtkListBoxRow. In our case, we
+ * need to use a rectangle and we provide some width to not make the popover arrow pointy
+ * in the edges if the window is small */
+#define RENAME_POPOVER_RELATIVE_TO_RECTANGLE_WIDTH 40
+
 struct NautilusListViewDetails {
        GtkTreeView *tree_view;
        NautilusListModel *model;
@@ -3240,6 +3246,41 @@ nautilus_list_view_get_id (NautilusView *view)
        return NAUTILUS_LIST_VIEW_ID;
 }
 
+static GdkRectangle*
+nautilus_list_view_compute_rename_popover_relative_to (NautilusView *view)
+{
+        GtkTreeSelection *selection;
+        GtkTreePath *path;
+        GdkRectangle *rect;
+        GtkTreeModel *model;
+        GtkTreeView *tree_view;
+        GList *list;
+        NautilusListView *list_view;
+
+        rect = g_malloc0 (sizeof(GdkRectangle));
+        list_view = NAUTILUS_LIST_VIEW (view);
+        tree_view = list_view->details->tree_view;
+        selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list_view->details->tree_view));
+        model = GTK_TREE_MODEL (list_view->details->model);
+        list = gtk_tree_selection_get_selected_rows (selection, &model);
+        path = list->data;
+        gtk_tree_view_get_cell_area (tree_view, path, NULL, rect);
+        gtk_tree_view_convert_bin_window_to_widget_coords (tree_view,
+                                                           rect->x, rect->y,
+                                                           &rect->x, &rect->y);
+
+        rect->x = CLAMP (gtk_widget_get_allocated_width (GTK_WIDGET (tree_view)) * 0.5 -
+                         RENAME_POPOVER_RELATIVE_TO_RECTANGLE_WIDTH * 0.5,
+                         0,
+                         gtk_widget_get_allocated_width (GTK_WIDGET (tree_view)) -
+                         RENAME_POPOVER_RELATIVE_TO_RECTANGLE_WIDTH);
+        rect->width = RENAME_POPOVER_RELATIVE_TO_RECTANGLE_WIDTH;
+
+        g_list_free_full (list, (GDestroyNotify) gtk_tree_path_free);
+
+        return rect;
+}
+
 static void
 nautilus_list_view_class_init (NautilusListViewClass *class)
 {
@@ -3277,6 +3318,7 @@ nautilus_list_view_class_init (NautilusListViewClass *class)
        nautilus_view_class->get_view_id = nautilus_list_view_get_id;
        nautilus_view_class->get_first_visible_file = nautilus_list_view_get_first_visible_file;
        nautilus_view_class->scroll_to_file = list_view_scroll_to_file;
+       nautilus_view_class->compute_rename_popover_relative_to = 
nautilus_list_view_compute_rename_popover_relative_to;
 }
 
 static void
diff --git a/src/nautilus-rename-file-popover.ui b/src/nautilus-rename-file-popover.ui
new file mode 100644
index 0000000..5214857
--- /dev/null
+++ b/src/nautilus-rename-file-popover.ui
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <object class="GtkPopover" id="rename_file_popover">
+    <property name="position">bottom</property>
+    <signal name="closed" handler="rename_file_popover_on_closed"/>
+    <child>
+      <object class="GtkGrid">
+        <property name="visible">True</property>
+        <property name="margin">10</property>
+        <property name="row-spacing">6</property>
+        <property name="column-spacing">6</property>
+        <child>
+          <object class="GtkLabel" id="name_label">
+            <property name="visible">True</property>
+            <property name="label" translatable="yes">Name</property>
+            <property name="halign">start</property>
+            <property name="mnemonic_widget">name_entry</property>
+            <attributes>
+              <attribute name="weight" value="bold"/>
+            </attributes>
+          </object>
+          <packing>
+            <property name="width">2</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkEntry" id="name_entry">
+            <property name="visible">True</property>
+            <property name="activates-default">True</property>
+          </object>
+          <packing>
+            <property name="left-attach">0</property>
+            <property name="top-attach">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkButton" id="rename_button">
+            <property name="visible">True</property>
+            <property name="sensitive">False</property>
+            <property name="label" translatable="yes">_Rename</property>
+            <property name="use_underline">True</property>
+            <property name="can_default">True</property>
+            <style>
+              <class name="suggested-action"/>
+            </style>
+          </object>
+          <packing>
+            <property name="left-attach">1</property>
+            <property name="top-attach">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel" id="error_label">
+            <property name="visible">True</property>
+            <property name="halign">start</property>
+          </object>
+          <packing>
+            <property name="left-attach">0</property>
+            <property name="top-attach">2</property>
+            <property name="width">2</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/src/nautilus-view.c b/src/nautilus-view.c
index 073bacc..d5ec6a1 100644
--- a/src/nautilus-view.c
+++ b/src/nautilus-view.c
@@ -116,8 +116,8 @@
 #define MAX_MENU_LEVELS 5
 #define TEMPLATE_LIMIT 30
 
-/* Time to show the duplicated folder label */
-#define DIALOG_DUPLICATED_NAME_ERROR_LABEL_TIMEOUT 500
+/* Delay to show the duplicated label when creating a folder */
+#define CREATE_FOLDER_DUPLICATED_LABEL_TIMEOUT 500
 
 /* Delay to show the Loading... floating bar */
 #define FLOATING_BAR_LOADING_DELAY 500 /* ms */
@@ -159,7 +159,8 @@ struct NautilusViewDetails
        NautilusFile *directory_as_file;
        guint dir_merge_id;
 
-       gint dialog_duplicated_name_label_timeout_id;
+       gint create_folder_duplicated_label_timeout_id;
+        GtkWidget *rename_file_popover;
 
        gboolean supports_zooming;
 
@@ -1699,16 +1700,18 @@ context_menu_to_file_operation_position (NautilusView *view)
 
 typedef struct {
        NautilusView *view;
-       GtkWidget *dialog;
+        GtkWidget *widget;
        GtkWidget *error_label;
        GtkWidget *name_entry;
        gboolean target_is_folder;
        NautilusFile *target_file;
+        GtkWidget *activate_button;
        gboolean duplicated_is_folder;
-} FileNameDialogData;
+} FileNameWidgetData;
 
 typedef struct {
        NautilusView *view;
+        GtkWidget *widget;
        GtkWidget *name_entry;
        NautilusFile *target_file;
 } RenameDialogData;
@@ -1720,143 +1723,152 @@ typedef struct {
 } NewFolderDialogData;
 
 static gboolean
-duplicated_file_label_show (FileNameDialogData *data)
+duplicated_file_label_show (FileNameWidgetData *data)
 {
        if (data->duplicated_is_folder)
                gtk_label_set_label (GTK_LABEL (data->error_label), _("A folder with that name already 
exists."));
        else
                gtk_label_set_label (GTK_LABEL (data->error_label), _("A file with that name already 
exists."));
 
-       data->view->details->dialog_duplicated_name_label_timeout_id = 0;
-       return FALSE;
+       data->view->details->create_folder_duplicated_label_timeout_id = 0;
+
+       return G_SOURCE_REMOVE;
 }
 
-static void
-nautilus_view_file_name_dialog_validate_name (FileNameDialogData *data)
+static gchar*
+validate_file_name (const gchar *name,
+                    gboolean     is_folder)
 {
-       gboolean duplicated_name;
-       gboolean valid_name;
-       gchar *name;
-       GList *files;
-       GList *node;
-       NautilusFile *file;
-
-       g_assert (data != NULL);
-       g_assert (GTK_IS_ENTRY (data->name_entry));
-       g_assert (GTK_IS_LABEL (data->error_label));
-       g_assert (GTK_IS_DIALOG (data->dialog));
-       g_assert (NAUTILUS_IS_VIEW (data->view));
+        gchar *error_message = NULL;
 
-       name = g_strstrip (g_strdup (gtk_entry_get_text (GTK_ENTRY (data->name_entry))));
-       duplicated_name = FALSE;
-       valid_name = FALSE;
-       files = nautilus_directory_get_file_list (data->view->details->model);
-
-       for (node = files; node != NULL; node = node->next) {
-               file = node->data;
-
-               if (nautilus_file_compare_display_name (file, name) == 0) {
-                        /* If we are renaming, we don't want to block the user to
-                         * rename the file with the current file name */
-                       if (data->target_file == NULL ||
-                            nautilus_file_compare_display_name (data->target_file, name) != 0) {
-                               duplicated_name = TRUE;
-                               data->duplicated_is_folder = nautilus_file_is_directory (file);
-                               break;
-                            }
-               }
-       }
-
-       nautilus_file_list_free (files);
-
-       /* Remove any sources left behind by
-        * previous calls of this function.
-        */
-       if (data->view->details->dialog_duplicated_name_label_timeout_id > 0) {
-               g_source_remove (data->view->details->dialog_duplicated_name_label_timeout_id);
-               data->view->details->dialog_duplicated_name_label_timeout_id = 0;
-       }
-
-       if (duplicated_name) {
-               data->view->details->dialog_duplicated_name_label_timeout_id =
-                   g_timeout_add (DIALOG_DUPLICATED_NAME_ERROR_LABEL_TIMEOUT,
-                                  (GSourceFunc)duplicated_file_label_show,
-                                  data);
-       } else if (strstr (name, "/") != NULL) {
-               if (data->target_is_folder)
-                       gtk_label_set_label (GTK_LABEL (data->error_label), _("Folder names cannot contain 
“/”."));
+       if (strstr (name, "/") != NULL) {
+               if (is_folder)
+                        error_message = _("Folder names cannot contain “/”.");
                else
-                       gtk_label_set_label (GTK_LABEL (data->error_label), _("Files names cannot contain 
“/”."));
+                       error_message = _("Files names cannot contain “/”.");
        } else if (strcmp (name, ".") == 0){
-               if (data->target_is_folder)
-                       gtk_label_set_label (GTK_LABEL (data->error_label), _("A folder can not be called 
“.”."));
+               if (is_folder)
+                       error_message = _("A folder can not be called “.”.");
                else
-                       gtk_label_set_label (GTK_LABEL (data->error_label), _("A file can not be called 
“.”."));
+                       error_message = _("A file can not be called “.”.");
        } else if (strcmp (name, "..") == 0){
-               if (data->target_is_folder)
-                       gtk_label_set_label (GTK_LABEL (data->error_label), _("A folder can not be called 
“..”."));
+               if (is_folder)
+                       error_message = _("A folder can not be called “..”.");
                else
-                       gtk_label_set_label (GTK_LABEL (data->error_label), _("A file can not be called 
“..”."));
-       } else {
-               /* No errors detected, empty the label */
-               gtk_label_set_label (GTK_LABEL (data->error_label), NULL);
-               valid_name = strlen (name) > 0;
+                       error_message = _("A file can not be called “..”.");
        }
 
-       gtk_dialog_set_response_sensitive (GTK_DIALOG (data->dialog),
-                                           GTK_RESPONSE_OK,
-                                           valid_name);
-       g_free (name);
+        return error_message;
 }
 
 static void
-nautilus_view_file_name_dialog_entry_on_validate (GObject    *object,
-                                                  GParamSpec *params,
-                                                  gpointer    user_data)
+create_folder_dialog_entry_on_changed (GtkEntry *entry,
+                                       gpointer  user_data)
 {
-       FileNameDialogData *data;
+       FileNameWidgetData *data;
+        NautilusFile *existing_file;
+        gchar *name;
+        gchar *error_message;
+        gboolean valid_name;
+        gboolean can_create_folder;
+
+        data = (FileNameWidgetData *) user_data;
+        name = g_strstrip (g_strdup (gtk_entry_get_text (GTK_ENTRY (data->name_entry))));
+        error_message = validate_file_name (name, TRUE);
+        gtk_label_set_label (GTK_LABEL (data->error_label), error_message);
+
+        existing_file = nautilus_directory_get_file_by_name (data->view->details->model, name);
+
+        valid_name = strlen (name) > 0 && error_message == NULL;
+        can_create_folder = existing_file == NULL && valid_name;
+        gtk_widget_set_sensitive (data->activate_button, can_create_folder);
 
-       data = (FileNameDialogData *) user_data;
+        if (data->view->details->create_folder_duplicated_label_timeout_id > 0) {
+                g_source_remove (data->view->details->create_folder_duplicated_label_timeout_id);
+                data->view->details->create_folder_duplicated_label_timeout_id = 0;
+        }
+
+        /* Report duplicated file only if not other message shown (for instance,
+         * folders like "." or ".." will always exists, but we consider it as an
+         * error, not as a duplicated file) */
+        if (existing_file != NULL && valid_name) {
+                data->duplicated_is_folder = nautilus_file_is_directory (existing_file);
+                data->view->details->create_folder_duplicated_label_timeout_id =
+                        g_timeout_add (CREATE_FOLDER_DUPLICATED_LABEL_TIMEOUT,
+                                       (GSourceFunc)duplicated_file_label_show,
+                                       data);
+        }
 
-        nautilus_view_file_name_dialog_validate_name (data);
+        if (existing_file != NULL)
+                nautilus_file_unref (existing_file);
 }
 
 static void
-nautilus_view_file_name_dialog_entry_on_activate (GtkWidget *entry,
-                                                  gpointer   user_data)
+rename_file_popover_entry_on_changed (GtkEntry *entry,
+                                      gpointer  user_data)
 {
-       FileNameDialogData *data;
-       GtkWidget *create_button;
+       FileNameWidgetData *data;
+        gboolean valid_name;
+        gchar *name;
+        gchar *error_message;
 
-       data = (FileNameDialogData *) user_data;
-       create_button = gtk_dialog_get_widget_for_response (GTK_DIALOG (data->dialog),
-                                                            GTK_RESPONSE_OK);
+        data = (FileNameWidgetData *) user_data;
+        name = g_strstrip (g_strdup (gtk_entry_get_text (GTK_ENTRY (data->name_entry))));
+        error_message = validate_file_name (name, nautilus_file_is_directory (data->target_file));
+        gtk_label_set_label (GTK_LABEL (data->error_label), error_message);
 
-       /* nautilus_view_new_folder_dialog_validate_name performs
-        * all the necessary validation, and it's not needed to check
-        * it all again. Checking if the "Create" button is sensitive
-        * is enough.
-        */
-       if (gtk_widget_get_sensitive (create_button)) {
-               gtk_dialog_response (GTK_DIALOG (data->dialog),
+        valid_name = strlen (name) > 0 && error_message == NULL;
+        gtk_widget_set_sensitive (data->activate_button, valid_name);
+
+        g_free (name);
+}
+
+static void
+create_folder_dialog_entry_on_activate (GtkWidget *entry,
+                                        gpointer   user_data)
+{
+       FileNameWidgetData *data;
+        NautilusFile *existing_file;
+        gchar *name;
+        gchar *error_message;
+        gboolean valid_name;
+        gboolean can_create_folder;
+
+        data = (FileNameWidgetData *) user_data;
+        name = g_strstrip (g_strdup (gtk_entry_get_text (GTK_ENTRY (data->name_entry))));
+        existing_file = nautilus_directory_get_file_by_name (data->view->details->model, name);
+        error_message = validate_file_name (name, TRUE);
+        valid_name = strlen (name) > 0 && error_message == NULL;
+        can_create_folder = existing_file == NULL && valid_name;
+
+        if (data->view->details->create_folder_duplicated_label_timeout_id > 0) {
+                g_source_remove (data->view->details->create_folder_duplicated_label_timeout_id);
+                data->view->details->create_folder_duplicated_label_timeout_id = 0;
+        }
+
+        if (can_create_folder) {
+                gtk_dialog_response (GTK_DIALOG (data->widget),
                                      GTK_RESPONSE_OK);
-       } else {
-               NautilusView *view = data->view;
+        } else {
+                /* Report duplicated file only if not other message shown (for instance,
+                 * folders like "." or ".." will always exists, but we consider it as an
+                 * error, not as a duplicated file) */
+                if (existing_file != NULL && valid_name) {
+                        data->duplicated_is_folder = nautilus_file_is_directory (existing_file);
+                        /* Show it inmediatily since the user tried to trigger the action */
+                        duplicated_file_label_show (data);
+                }
+        }
 
-               /* Since typos are immediately shown, only
-                * handle name collisions here.
-                */
-               if (view->details->dialog_duplicated_name_label_timeout_id > 0) {
-                       g_source_remove (view->details->dialog_duplicated_name_label_timeout_id);
-                       duplicated_file_label_show (data);
-               }
-       }
+        if (existing_file != NULL)
+                nautilus_file_unref (existing_file);
+
+        g_free (name);
 }
 
 static void
-nautilus_view_rename_dialog_on_response (GtkDialog *dialog,
-                                         gint       response_id,
-                                         gpointer   user_data)
+rename_file_popover_rename_button_on_clicked (GtkButton *entry,
+                                              gpointer  *user_data)
 {
        RenameDialogData *rename_data;
        gchar *name;
@@ -1867,96 +1879,113 @@ nautilus_view_rename_dialog_on_response (GtkDialog *dialog,
        g_assert (GTK_IS_ENTRY (rename_data->name_entry));
        g_assert (NAUTILUS_IS_FILE (rename_data->target_file));
 
-       if (response_id == GTK_RESPONSE_OK) {
-               name = g_strstrip (g_strdup (gtk_entry_get_text (GTK_ENTRY (rename_data->name_entry))));
-               nautilus_rename_file (rename_data->target_file, name, NULL, NULL);
-               g_free (name);
-       }
+       name = g_strstrip (g_strdup (gtk_entry_get_text (GTK_ENTRY (rename_data->name_entry))));
+       nautilus_rename_file (rename_data->target_file, name, NULL, NULL);
+       g_free (name);
+
        nautilus_view_select_file (rename_data->view, rename_data->target_file);
        nautilus_view_reveal_selection (rename_data->view);
 
-       /* If there's any resources left from the delayed
-        * message, it should be removed before it gets
-        * triggered.
-        */
-       if (rename_data->view->details->dialog_duplicated_name_label_timeout_id > 0) {
-               g_source_remove (rename_data->view->details->dialog_duplicated_name_label_timeout_id);
-               rename_data->view->details->dialog_duplicated_name_label_timeout_id = 0;
-       }
+        gtk_widget_hide (rename_data->widget);
 
-       g_free (user_data);
-       gtk_widget_destroy (GTK_WIDGET (dialog));
+       g_free (rename_data);
+}
+
+static void
+rename_file_popover_on_closed (GtkPopover *popover,
+                               gpointer    user_data)
+{
+        FileNameWidgetData *widget_data;
+
+        widget_data = (FileNameWidgetData *) user_data;
+        widget_data->view->details->rename_file_popover = NULL;
+       g_free (widget_data);
+}
+
+static GdkRectangle*
+nautilus_view_compute_rename_popover_relative_to (NautilusView *view)
+{
+       return NAUTILUS_VIEW_CLASS (G_OBJECT_GET_CLASS (view))->compute_rename_popover_relative_to (view);
 }
 
 static void
-nautilus_view_rename_dialog_new (NautilusView *view,
-                                 NautilusFile *target_file)
+nautilus_view_rename_file_popover_new (NautilusView *view,
+                                       NautilusFile *target_file)
 {
-       FileNameDialogData *dialog_data;
-       RenameDialogData *user_data;
-       GtkWidget *button_ok;
+       FileNameWidgetData *widget_data;
+       RenameDialogData *rename_data;
        GtkWidget *label_file_name;
        GtkBuilder *builder;
        gint start_offset, end_offset;
-       gchar *file_name;
-       gchar *title;
+        GdkRectangle *relative_to;
 
-       builder = gtk_builder_new_from_resource ("/org/gnome/nautilus/nautilus-file-name-dialog.ui");
-       button_ok = GTK_WIDGET (gtk_builder_get_object (builder, "ok_button"));
-       label_file_name = GTK_WIDGET (gtk_builder_get_object (builder, "name_label"));
+        if (view->details->rename_file_popover != NULL)
+          return;
 
-       dialog_data = g_new (FileNameDialogData, 1);
-       dialog_data->view = view;
-       dialog_data->dialog = GTK_WIDGET (gtk_builder_get_object (builder, "file_name_dialog"));
-       dialog_data->error_label = GTK_WIDGET (gtk_builder_get_object (builder, "error_label"));
-       dialog_data->name_entry = GTK_WIDGET (gtk_builder_get_object (builder, "name_entry"));
-       dialog_data->target_is_folder = nautilus_file_is_directory (target_file);
-       dialog_data->target_file = target_file;
+       builder = gtk_builder_new_from_resource ("/org/gnome/nautilus/nautilus-rename-file-popover.ui");
+       label_file_name = GTK_WIDGET (gtk_builder_get_object (builder, "name_label"));
 
-       user_data = g_new (RenameDialogData, 1);
-       user_data->view = view;
-       user_data->target_file = target_file;
-       user_data->name_entry = dialog_data->name_entry;
+       widget_data = g_new (FileNameWidgetData, 1);
+       widget_data->view = view;
+       widget_data->widget = GTK_WIDGET (gtk_builder_get_object (builder, "rename_file_popover"));
+       widget_data->activate_button = GTK_WIDGET (gtk_builder_get_object (builder, "rename_button"));
+       widget_data->error_label = GTK_WIDGET (gtk_builder_get_object (builder, "error_label"));
+       widget_data->name_entry = GTK_WIDGET (gtk_builder_get_object (builder, "name_entry"));
+       widget_data->target_is_folder = nautilus_file_is_directory (target_file);
+       widget_data->target_file = target_file;
 
-       gtk_window_set_transient_for (GTK_WINDOW (dialog_data->dialog),
-                                      GTK_WINDOW (nautilus_view_get_window (view)));
+        view->details->rename_file_popover = widget_data->widget;
 
-       /* Connect signals */
-       gtk_builder_add_callback_symbols (builder,
-                                          "nautilus_view_file_name_dialog_entry_on_validate",
-                                          G_CALLBACK (nautilus_view_file_name_dialog_entry_on_validate),
-                                          "nautilus_view_file_name_dialog_entry_on_activate",
-                                          G_CALLBACK (nautilus_view_file_name_dialog_entry_on_activate),
-                                          NULL);
+       rename_data = g_new (RenameDialogData, 1);
+       rename_data->view = view;
+       rename_data->widget = widget_data->widget;
+       rename_data->target_file = target_file;
+       rename_data->name_entry = widget_data->name_entry;
 
-       dialog_data->target_is_folder = nautilus_file_is_directory (target_file);
-       gtk_button_set_label (GTK_BUTTON (button_ok), _("Rename"));
-       file_name = nautilus_file_get_display_name (target_file);
-        title = g_strdup_printf (_("Rename “%s”"), file_name);
-        gtk_window_set_title (GTK_WINDOW (dialog_data->dialog), title);
-        g_free (file_name);
-        g_free (title);
-       if (dialog_data->target_is_folder)
+       if (widget_data->target_is_folder)
                gtk_label_set_text (GTK_LABEL (label_file_name), _("Folder name"));
        else
                gtk_label_set_text (GTK_LABEL (label_file_name), _("File name"));
-       gtk_entry_set_text (GTK_ENTRY (dialog_data->name_entry), nautilus_file_get_name (target_file));
-       gtk_builder_connect_signals (builder, dialog_data);
-
-       g_signal_connect (dialog_data->dialog,
-                         "response",
-                          G_CALLBACK (nautilus_view_rename_dialog_on_response),
-                          user_data);
+       gtk_entry_set_text (GTK_ENTRY (widget_data->name_entry), nautilus_file_get_name (target_file));
+
+       g_signal_connect (widget_data->activate_button,
+                         "clicked",
+                          G_CALLBACK (rename_file_popover_rename_button_on_clicked),
+                          rename_data);
+
+       g_signal_connect (widget_data->name_entry,
+                         "changed",
+                          G_CALLBACK (rename_file_popover_entry_on_changed),
+                          widget_data);
+
+       g_signal_connect (GTK_POPOVER (widget_data->widget),
+                         "closed",
+                          G_CALLBACK (rename_file_popover_on_closed),
+                          widget_data);
+
+       g_signal_connect (GTK_POPOVER (widget_data->widget),
+                         "unmap",
+                          G_CALLBACK (gtk_widget_destroy),
+                          NULL);
+
+        relative_to = nautilus_view_compute_rename_popover_relative_to (view);
+        gtk_popover_set_default_widget (GTK_POPOVER (widget_data->widget),
+                                        widget_data->activate_button);
+        gtk_popover_set_pointing_to (GTK_POPOVER (widget_data->widget), relative_to);
+        gtk_popover_set_relative_to (GTK_POPOVER (widget_data->widget),
+                                     GTK_WIDGET (view));
+       gtk_widget_show (widget_data->widget);
 
-       gtk_widget_show_all (dialog_data->dialog);
        /* Select the name part withouth the file extension */
        eel_filename_get_rename_region (nautilus_file_get_name (target_file),
                                         &start_offset, &end_offset);
-       gtk_editable_select_region (GTK_EDITABLE (dialog_data->name_entry),
+       gtk_editable_select_region (GTK_EDITABLE (widget_data->name_entry),
                                     start_offset, end_offset);
 
-        /* Update the ok button status */
-        nautilus_view_file_name_dialog_validate_name (dialog_data);
+        /* Update the rename button status */
+        rename_file_popover_entry_on_changed (GTK_ENTRY (widget_data->name_entry),
+                                              widget_data);
+
 
        g_object_unref (builder);
 }
@@ -2000,70 +2029,67 @@ nautilus_view_new_folder_dialog_on_response (GtkDialog *dialog,
                g_free (name);
        }
 
-       /* If there's any resources left from the delayed
-        * message, it should be removed before it gets
-        * triggered.
-        */
-       if (new_folder_data->view->details->dialog_duplicated_name_label_timeout_id > 0) {
-               g_source_remove (new_folder_data->view->details->dialog_duplicated_name_label_timeout_id);
-               new_folder_data->view->details->dialog_duplicated_name_label_timeout_id = 0;
-       }
+        if (new_folder_data->view->details->create_folder_duplicated_label_timeout_id > 0) {
+                g_source_remove (new_folder_data->view->details->create_folder_duplicated_label_timeout_id);
+                new_folder_data->view->details->create_folder_duplicated_label_timeout_id = 0;
+        }
 
-       g_free (user_data);
        gtk_widget_destroy (GTK_WIDGET (dialog));
+       g_free (user_data);
 }
 
 static void
 nautilus_view_new_folder_dialog_new (NautilusView *view,
                                      gboolean      with_selection)
 {
-       FileNameDialogData *dialog_data;
+       FileNameWidgetData *widget_data;
        NewFolderDialogData *user_data;
-       GtkWidget *button_ok;
        GtkWidget *label_file_name;
        GtkBuilder *builder;
 
-       builder = gtk_builder_new_from_resource ("/org/gnome/nautilus/nautilus-file-name-dialog.ui");
-       button_ok = GTK_WIDGET (gtk_builder_get_object (builder, "ok_button"));
+       builder = gtk_builder_new_from_resource ("/org/gnome/nautilus/nautilus-create-folder-dialog.ui");
        label_file_name = GTK_WIDGET (gtk_builder_get_object (builder, "name_label"));
 
-       dialog_data = g_new (FileNameDialogData, 1);
-       dialog_data->view = view;
-       dialog_data->dialog = GTK_WIDGET (gtk_builder_get_object (builder, "file_name_dialog"));
-       dialog_data->error_label = GTK_WIDGET (gtk_builder_get_object (builder, "error_label"));
-       dialog_data->name_entry = GTK_WIDGET (gtk_builder_get_object (builder, "name_entry"));
-       dialog_data->target_is_folder = TRUE;
-       dialog_data->target_file = NULL;
+       widget_data = g_new (FileNameWidgetData, 1);
+       widget_data->view = view;
+       widget_data->widget = GTK_WIDGET (gtk_builder_get_object (builder, "create_folder_dialog"));
+       widget_data->activate_button = GTK_WIDGET (gtk_builder_get_object (builder, "ok_button"));
+       widget_data->error_label = GTK_WIDGET (gtk_builder_get_object (builder, "error_label"));
+       widget_data->name_entry = GTK_WIDGET (gtk_builder_get_object (builder, "name_entry"));
+       widget_data->target_is_folder = TRUE;
+       widget_data->target_file = NULL;
 
        user_data = g_new (NewFolderDialogData, 1);
        user_data->view = view;
        user_data->with_selection = with_selection;
-       user_data->name_entry = dialog_data->name_entry;
+       user_data->name_entry = widget_data->name_entry;
 
-       gtk_window_set_transient_for (GTK_WINDOW (dialog_data->dialog),
+       gtk_window_set_transient_for (GTK_WINDOW (widget_data->widget),
                                       GTK_WINDOW (nautilus_view_get_window (view)));
 
        /* Connect signals */
        gtk_builder_add_callback_symbols (builder,
-                                          "nautilus_view_file_name_dialog_entry_on_validate",
-                                          G_CALLBACK (nautilus_view_file_name_dialog_entry_on_validate),
-                                          "nautilus_view_file_name_dialog_entry_on_activate",
-                                          G_CALLBACK (nautilus_view_file_name_dialog_entry_on_activate),
+                                          "create_folder_dialog_entry_on_changed",
+                                          G_CALLBACK (create_folder_dialog_entry_on_changed),
+                                          "create_folder_dialog_entry_on_activate",
+                                          G_CALLBACK (create_folder_dialog_entry_on_activate),
                                           NULL);
 
-       gtk_builder_connect_signals (builder, dialog_data);
-       gtk_button_set_label (GTK_BUTTON (button_ok), _("Create"));
+       gtk_builder_connect_signals (builder, widget_data);
+       gtk_button_set_label (GTK_BUTTON (widget_data->activate_button),
+                              _("Create"));
        gtk_label_set_text (GTK_LABEL (label_file_name), _("Folder name"));
-       gtk_window_set_title (GTK_WINDOW (dialog_data->dialog), _("New Folder"));
+       gtk_window_set_title (GTK_WINDOW (widget_data->widget), _("New Folder"));
 
-       g_signal_connect (dialog_data->dialog,
+       g_signal_connect (widget_data->widget,
                           "response",
                           G_CALLBACK (nautilus_view_new_folder_dialog_on_response),
                           user_data);
 
-       gtk_widget_show_all (dialog_data->dialog);
+       gtk_widget_show_all (widget_data->widget);
         /* Update the ok button status */
-        nautilus_view_file_name_dialog_validate_name (dialog_data);
+        create_folder_dialog_entry_on_changed (GTK_ENTRY (widget_data->name_entry),
+                                               widget_data);
 
        g_object_unref (builder);
 }
@@ -5488,7 +5514,7 @@ real_action_rename (NautilusView *view,
                                 * they are always pre-selected as a whole */
                                select_all = nautilus_file_is_directory (file);
                        }
-                       nautilus_view_rename_dialog_new (view, file);
+                        nautilus_view_rename_file_popover_new (view, file);
                }
        }
 
diff --git a/src/nautilus-view.h b/src/nautilus-view.h
index 93fbc89..3cc87da 100644
--- a/src/nautilus-view.h
+++ b/src/nautilus-view.h
@@ -276,6 +276,8 @@ struct NautilusViewClass {
                                                   const char            *uri);
 
        NautilusWindow * (*get_window)            (NautilusView *view);
+
+        GdkRectangle * (*compute_rename_popover_relative_to) (NautilusView *view);
 };
 
 /* GObject support */
diff --git a/src/nautilus.gresource.xml b/src/nautilus.gresource.xml
index acab5ff..c80a529 100644
--- a/src/nautilus.gresource.xml
+++ b/src/nautilus.gresource.xml
@@ -8,7 +8,8 @@
     <file>nautilus-toolbar-ui.xml</file>
     <file>nautilus-toolbar-view-menu.xml</file>
     <file>nautilus-toolbar-action-menu.xml</file>
-    <file>nautilus-file-name-dialog.ui</file>
+    <file>nautilus-create-folder-dialog.ui</file>
+    <file>nautilus-rename-file-popover.ui</file>
     <file>nautilus-view-context-menus.xml</file>
     <file>nautilus-progress-info-widget.xml</file>
     <file>nautilus-move-to-trash-shortcut-changed.ui</file>



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]