summaryrefslogtreecommitdiff
path: root/src/nautilus-file-name-widget-controller.c
diff options
context:
space:
mode:
authorRazvan Chitu <razvan.ch95@gmail.com>2016-07-30 18:50:10 +0300
committerRazvan Chitu <razvan.ch95@gmail.com>2016-08-19 15:52:16 +0300
commitfd873c5086e53ee8624a6412c3dd6ff4d263c04b (patch)
tree00cd4b873d1c479010a61c9b01d445304ae35c74 /src/nautilus-file-name-widget-controller.c
parent58f62f331889aca434f5f4831c2747eaa89c7756 (diff)
downloadnautilus-fd873c5086e53ee8624a6412c3dd6ff4d263c04b.tar.gz
files-view: move file name widget logic to separate controllers
The rename file popover and the new folder dialog share common logic for validating file names entered by the user. The control logic was implemented with a simple structure in files-view. Besides common logic, the structure also held parameters specific to only one of the operations. Another problem is that the current implementation does not allow flexibility in obtaining the file name from the widgets and displaying error messages. In order to fix this, reimplement the structure as an abstract class and create two subclasses for the "Rename" and "New Folder" widgets. https://bugzilla.gnome.org/show_bug.cgi?id=769336
Diffstat (limited to 'src/nautilus-file-name-widget-controller.c')
-rw-r--r--src/nautilus-file-name-widget-controller.c387
1 files changed, 387 insertions, 0 deletions
diff --git a/src/nautilus-file-name-widget-controller.c b/src/nautilus-file-name-widget-controller.c
new file mode 100644
index 000000000..6f0e4c03c
--- /dev/null
+++ b/src/nautilus-file-name-widget-controller.c
@@ -0,0 +1,387 @@
+#include <glib/gi18n.h>
+
+#include "nautilus-file-name-widget-controller.h"
+
+
+#define FILE_NAME_DUPLICATED_LABEL_TIMEOUT 500
+
+typedef struct {
+ GtkWidget *error_label;
+ GtkWidget *name_entry;
+ GtkWidget *activate_button;
+ NautilusDirectory *containing_directory;
+
+ gboolean duplicated_is_folder;
+ gint duplicated_label_timeout_id;
+} NautilusFileNameWidgetControllerPrivate;
+
+enum {
+ NAME_ACCEPTED,
+ CANCELLED,
+ LAST_SIGNAL
+};
+
+enum {
+ PROP_ERROR_LABEL = 1,
+ PROP_NAME_ENTRY,
+ PROP_ACTION_BUTTON,
+ PROP_CONTAINING_DIRECTORY,
+ NUM_PROPERTIES
+};
+
+static guint signals[LAST_SIGNAL];
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (NautilusFileNameWidgetController, nautilus_file_name_widget_controller, G_TYPE_OBJECT)
+
+gchar *
+nautilus_file_name_widget_controller_get_new_name (NautilusFileNameWidgetController *self)
+{
+ return NAUTILUS_FILE_NAME_WIDGET_CONTROLLER_GET_CLASS (self)->get_new_name (self);
+}
+
+static gboolean
+nautilus_file_name_widget_controller_name_is_valid (NautilusFileNameWidgetController *self,
+ gchar *name,
+ gchar **error_message)
+{
+ return NAUTILUS_FILE_NAME_WIDGET_CONTROLLER_GET_CLASS (self)->name_is_valid (self,
+ name,
+ error_message);
+}
+
+static gboolean
+nautilus_file_name_widget_controller_ignore_existing_file (NautilusFileNameWidgetController *self,
+ NautilusFile *existing_file)
+{
+ return NAUTILUS_FILE_NAME_WIDGET_CONTROLLER_GET_CLASS (self)->ignore_existing_file (self,
+ existing_file);
+}
+
+static gchar *
+real_get_new_name (NautilusFileNameWidgetController *self)
+{
+ NautilusFileNameWidgetControllerPrivate *priv;
+
+ priv = nautilus_file_name_widget_controller_get_instance_private (self);
+
+ return g_strstrip (g_strdup (gtk_entry_get_text (GTK_ENTRY (priv->name_entry))));
+}
+
+static gboolean
+real_name_is_valid (NautilusFileNameWidgetController *self,
+ gchar *name,
+ gchar **error_message)
+{
+ if (strlen (name) == 0) {
+ return FALSE;
+ }
+
+ if (strstr (name, "/") != NULL) {
+ *error_message = _("File names cannot contain “/”.");
+ } else if (strcmp (name, ".") == 0){
+ *error_message = _("A file cannot be called “.”.");
+ } else if (strcmp (name, "..") == 0){
+ *error_message = _("A file cannot be called “..”.");
+ }
+
+ return *error_message == NULL;
+}
+
+static gboolean
+real_ignore_existing_file (NautilusFileNameWidgetController *self,
+ NautilusFile *existing_file)
+{
+ return FALSE;
+}
+
+static gboolean
+duplicated_file_label_show (NautilusFileNameWidgetController *self)
+{
+ NautilusFileNameWidgetControllerPrivate *priv;
+
+ priv = nautilus_file_name_widget_controller_get_instance_private (self);
+ if (priv->duplicated_is_folder) {
+ gtk_label_set_label (GTK_LABEL (priv->error_label),
+ _("A folder with that name already exists."));
+ } else {
+ gtk_label_set_label (GTK_LABEL (priv->error_label),
+ _("A file with that name already exists."));
+ }
+
+ priv->duplicated_label_timeout_id = 0;
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+file_name_widget_controller_process_new_name (NautilusFileNameWidgetController *controller,
+ gboolean *duplicated_name,
+ gboolean *valid_name)
+{
+ NautilusFileNameWidgetControllerPrivate *priv;
+ g_autofree gchar *name;
+ gchar *error_message = NULL;
+ NautilusFile *existing_file;
+ priv = nautilus_file_name_widget_controller_get_instance_private (controller);
+
+ name = nautilus_file_name_widget_controller_get_new_name (controller);
+ *valid_name = nautilus_file_name_widget_controller_name_is_valid (controller,
+ name,
+ &error_message);
+
+ gtk_label_set_label (GTK_LABEL (priv->error_label), error_message);
+
+ existing_file = nautilus_directory_get_file_by_name (priv->containing_directory, name);
+ *duplicated_name = existing_file != NULL &&
+ !nautilus_file_name_widget_controller_ignore_existing_file (controller,
+ existing_file);
+
+ gtk_widget_set_sensitive (priv->activate_button, *valid_name && !*duplicated_name);
+
+ if (priv->duplicated_label_timeout_id != 0) {
+ g_source_remove (priv->duplicated_label_timeout_id);
+ priv->duplicated_label_timeout_id = 0;
+ }
+
+ if (*duplicated_name) {
+ priv->duplicated_is_folder = nautilus_file_is_directory (existing_file);
+ }
+
+ if (existing_file != NULL) {
+ nautilus_file_unref (existing_file);
+ }
+}
+
+static void
+file_name_widget_controller_on_changed_directory_info_ready (NautilusDirectory *directory,
+ GList *files,
+ gpointer user_data)
+{
+ NautilusFileNameWidgetController *controller;
+ NautilusFileNameWidgetControllerPrivate *priv;
+ gboolean duplicated_name;
+ gboolean valid_name;
+
+ controller = NAUTILUS_FILE_NAME_WIDGET_CONTROLLER (user_data);
+ priv = nautilus_file_name_widget_controller_get_instance_private (controller);
+
+ file_name_widget_controller_process_new_name (controller,
+ &duplicated_name,
+ &valid_name);
+
+ /* 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 or if the name is the same as the file
+ * we are renaming also don't report as a duplicated */
+ if (duplicated_name && valid_name) {
+ priv->duplicated_label_timeout_id = g_timeout_add (FILE_NAME_DUPLICATED_LABEL_TIMEOUT,
+ (GSourceFunc)duplicated_file_label_show,
+ controller);
+ }
+}
+
+static void
+file_name_widget_controller_on_changed (gpointer user_data)
+{
+ NautilusFileNameWidgetController *controller;
+ NautilusFileNameWidgetControllerPrivate *priv;
+
+ controller = NAUTILUS_FILE_NAME_WIDGET_CONTROLLER (user_data);
+ priv = nautilus_file_name_widget_controller_get_instance_private (controller);
+
+ nautilus_directory_call_when_ready (priv->containing_directory,
+ NAUTILUS_FILE_ATTRIBUTE_INFO,
+ TRUE,
+ file_name_widget_controller_on_changed_directory_info_ready,
+ controller);
+}
+
+static void
+file_name_widget_controller_on_activate_directory_info_ready (NautilusDirectory *directory,
+ GList *files,
+ gpointer user_data)
+{
+ NautilusFileNameWidgetController *controller;
+ gboolean duplicated_name;
+ gboolean valid_name;
+
+ controller = NAUTILUS_FILE_NAME_WIDGET_CONTROLLER (user_data);
+
+ file_name_widget_controller_process_new_name (controller,
+ &duplicated_name,
+ &valid_name);
+
+ if (valid_name && !duplicated_name) {
+ g_signal_emit (controller, signals[NAME_ACCEPTED], 0);
+ } 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 (duplicated_name && valid_name) {
+ /* Show it inmediatily since the user tried to trigger the action */
+ duplicated_file_label_show (controller);
+ }
+ }
+}
+
+static void
+file_name_widget_controller_on_activate (gpointer user_data)
+{
+ NautilusFileNameWidgetController *controller;
+ NautilusFileNameWidgetControllerPrivate *priv;
+
+ controller = NAUTILUS_FILE_NAME_WIDGET_CONTROLLER (user_data);
+ priv = nautilus_file_name_widget_controller_get_instance_private (controller);
+
+ nautilus_directory_call_when_ready (priv->containing_directory,
+ NAUTILUS_FILE_ATTRIBUTE_INFO,
+ TRUE,
+ file_name_widget_controller_on_activate_directory_info_ready,
+ controller);
+}
+
+static void
+nautilus_file_name_widget_controller_init (NautilusFileNameWidgetController *self)
+{
+
+}
+
+static void
+nautilus_file_name_widget_controller_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NautilusFileNameWidgetController *controller;
+ NautilusFileNameWidgetControllerPrivate *priv;
+
+ controller = NAUTILUS_FILE_NAME_WIDGET_CONTROLLER (object);
+ priv = nautilus_file_name_widget_controller_get_instance_private (controller);
+
+ switch (prop_id) {
+ case PROP_ERROR_LABEL:
+ priv->error_label = GTK_WIDGET (g_value_get_object (value));
+ break;
+ case PROP_NAME_ENTRY:
+ priv->name_entry = GTK_WIDGET (g_value_get_object (value));
+
+ g_signal_connect_swapped (G_OBJECT (priv->name_entry),
+ "activate",
+ (GCallback)file_name_widget_controller_on_activate,
+ controller);
+ g_signal_connect_swapped (G_OBJECT (priv->name_entry),
+ "changed",
+ (GCallback)file_name_widget_controller_on_changed,
+ controller);
+ break;
+ case PROP_ACTION_BUTTON:
+ priv->activate_button = GTK_WIDGET (g_value_get_object (value));
+
+ g_signal_connect_swapped (G_OBJECT (priv->activate_button),
+ "clicked",
+ (GCallback)file_name_widget_controller_on_activate,
+ controller);
+ break;
+ case PROP_CONTAINING_DIRECTORY:
+ priv->containing_directory = NAUTILUS_DIRECTORY (g_value_get_object (value));
+ nautilus_directory_ref (priv->containing_directory);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nautilus_file_name_widget_controller_finalize (GObject *object)
+{
+ NautilusFileNameWidgetController *self;
+ NautilusFileNameWidgetControllerPrivate *priv;
+
+ self = NAUTILUS_FILE_NAME_WIDGET_CONTROLLER (object);
+ priv = nautilus_file_name_widget_controller_get_instance_private (self);
+
+ if (priv->containing_directory != NULL) {
+ nautilus_directory_cancel_callback (priv->containing_directory,
+ file_name_widget_controller_on_changed_directory_info_ready,
+ self);
+ nautilus_directory_cancel_callback (priv->containing_directory,
+ file_name_widget_controller_on_activate_directory_info_ready,
+ self);
+ g_clear_object (&priv->containing_directory);
+ }
+
+ if (priv->duplicated_label_timeout_id > 0) {
+ g_source_remove (priv->duplicated_label_timeout_id);
+ priv->duplicated_label_timeout_id = 0;
+ }
+
+ G_OBJECT_CLASS (nautilus_file_name_widget_controller_parent_class)->finalize (object);
+}
+
+static void
+nautilus_file_name_widget_controller_class_init (NautilusFileNameWidgetControllerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->set_property = nautilus_file_name_widget_controller_set_property;
+ object_class->finalize = nautilus_file_name_widget_controller_finalize;
+
+ klass->get_new_name = real_get_new_name;
+ klass->name_is_valid = real_name_is_valid;
+ klass->ignore_existing_file = real_ignore_existing_file;
+
+ signals[NAME_ACCEPTED] =
+ g_signal_new ("name-accepted",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NautilusFileNameWidgetControllerClass, name_accepted),
+ NULL, NULL,
+ g_cclosure_marshal_generic,
+ G_TYPE_NONE, 0);
+ signals[CANCELLED] =
+ g_signal_new ("cancelled",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_generic,
+ G_TYPE_NONE, 0);
+
+ g_object_class_install_property (
+ object_class,
+ PROP_ERROR_LABEL,
+ g_param_spec_object ("error-label",
+ "Error Label",
+ "The label used for displaying errors",
+ GTK_TYPE_WIDGET,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (
+ object_class,
+ PROP_NAME_ENTRY,
+ g_param_spec_object ("name-entry",
+ "Name Entry",
+ "The entry for the file name",
+ GTK_TYPE_WIDGET,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (
+ object_class,
+ PROP_ACTION_BUTTON,
+ g_param_spec_object ("activate-button",
+ "Activate Button",
+ "The activate button of the widget",
+ GTK_TYPE_WIDGET,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (
+ object_class,
+ PROP_CONTAINING_DIRECTORY,
+ g_param_spec_object ("containing-directory",
+ "Containing Directory",
+ "The directory used to check for duplicate names",
+ NAUTILUS_TYPE_DIRECTORY,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY));
+}