diff options
Diffstat (limited to 'src/nautilus-file-name-widget-controller.c')
-rw-r--r-- | src/nautilus-file-name-widget-controller.c | 387 |
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)); +} |