summaryrefslogtreecommitdiffstats
path: root/src/nautilus-file-name-widget-controller.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nautilus-file-name-widget-controller.c')
-rw-r--r--src/nautilus-file-name-widget-controller.c522
1 files changed, 522 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 0000000..db048b7
--- /dev/null
+++ b/src/nautilus-file-name-widget-controller.c
@@ -0,0 +1,522 @@
+/* nautilus-file-name-widget-controller.c
+ *
+ * Copyright (C) 2016 the Nautilus developers
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <glib/gi18n.h>
+
+#include "nautilus-file-name-widget-controller.h"
+#include "nautilus-file-utilities.h"
+
+#define FILE_NAME_DUPLICATED_LABEL_TIMEOUT 500
+
+typedef struct
+{
+ GtkWidget *error_revealer;
+ 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_REVEALER = 1,
+ PROP_ERROR_LABEL,
+ 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);
+}
+
+void
+nautilus_file_name_widget_controller_set_containing_directory (NautilusFileNameWidgetController *self,
+ NautilusDirectory *directory)
+{
+ g_assert (NAUTILUS_IS_DIRECTORY (directory));
+
+ g_object_set (self, "containing-directory", directory, NULL);
+}
+
+gboolean
+nautilus_file_name_widget_controller_is_name_too_long (NautilusFileNameWidgetController *self,
+ gchar *name)
+{
+ NautilusFileNameWidgetControllerPrivate *priv;
+ size_t name_length;
+ g_autoptr (GFile) location = NULL;
+ glong max_name_length;
+
+ priv = nautilus_file_name_widget_controller_get_instance_private (self);
+ name_length = strlen (name);
+ location = nautilus_directory_get_location (priv->containing_directory);
+ max_name_length = nautilus_get_max_child_name_length_for_location (location);
+
+ if (max_name_length == -1)
+ {
+ /* We don't know, so let's give it a chance */
+ return FALSE;
+ }
+ else
+ {
+ return name_length > max_name_length + 1;
+ }
+}
+
+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_editable_get_text (GTK_EDITABLE (priv->name_entry))));
+}
+
+static gboolean
+real_name_is_valid (NautilusFileNameWidgetController *self,
+ gchar *name,
+ gchar **error_message)
+{
+ gboolean is_valid;
+
+ is_valid = TRUE;
+ if (strlen (name) == 0)
+ {
+ is_valid = FALSE;
+ }
+ else if (strstr (name, "/") != NULL)
+ {
+ is_valid = FALSE;
+ *error_message = _("File names cannot contain “/”.");
+ }
+ else if (strcmp (name, ".") == 0)
+ {
+ is_valid = FALSE;
+ *error_message = _("A file cannot be called “.”.");
+ }
+ else if (strcmp (name, "..") == 0)
+ {
+ is_valid = FALSE;
+ *error_message = _("A file cannot be called “..”.");
+ }
+ else if (nautilus_file_name_widget_controller_is_name_too_long (self, name))
+ {
+ is_valid = FALSE;
+ *error_message = _("File name is too long.");
+ }
+
+ if (is_valid && g_str_has_prefix (name, "."))
+ {
+ /* We must warn about the side effect */
+ *error_message = _("Files with “.” at the beginning of their name are hidden.");
+ }
+
+ return is_valid;
+}
+
+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."));
+ }
+
+ gtk_revealer_set_reveal_child (GTK_REVEALER (priv->error_revealer),
+ TRUE);
+
+ 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 = NULL;
+ gchar *error_message = NULL;
+ NautilusFile *existing_file;
+ priv = nautilus_file_name_widget_controller_get_instance_private (controller);
+
+ g_return_if_fail (NAUTILUS_IS_DIRECTORY (priv->containing_directory));
+
+ 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);
+ gtk_revealer_set_reveal_child (GTK_REVEALER (priv->error_revealer),
+ error_message != NULL);
+
+ 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)
+{
+ NautilusFileNameWidgetControllerPrivate *priv;
+
+ priv = nautilus_file_name_widget_controller_get_instance_private (self);
+
+ priv->containing_directory = NULL;
+}
+
+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_REVEALER:
+ {
+ priv->error_revealer = GTK_WIDGET (g_value_get_object (value));
+ }
+ break;
+
+ 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:
+ {
+ g_clear_object (&priv->containing_directory);
+
+ priv->containing_directory = NAUTILUS_DIRECTORY (g_value_dup_object (value));
+ }
+ 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_REVEALER,
+ g_param_spec_object ("error-revealer",
+ "Error Revealer",
+ "The error label revealer",
+ GTK_TYPE_WIDGET,
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY));
+
+ 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));
+}