[gnome-flashback] desktop: add Rename... to icon menu



commit 43c37cf7b2d9f356a67a3773ff3697cda7ac099f
Author: Alberts Muktupāvels <alberts muktupavels gmail com>
Date:   Fri Nov 15 23:24:30 2019 +0200

    desktop: add Rename... to icon menu

 gnome-flashback/libdesktop/Makefile.am             |   2 +
 .../libdesktop/gf-create-folder-dialog.c           |  99 ++-----
 .../libdesktop/gf-create-folder-dialog.h           |   8 +-
 gnome-flashback/libdesktop/gf-home-icon.c          |  11 +
 gnome-flashback/libdesktop/gf-icon-view.c          | 130 +++++++--
 gnome-flashback/libdesktop/gf-icon-view.h          |  25 +-
 gnome-flashback/libdesktop/gf-icon.c               | 134 +++++++++
 gnome-flashback/libdesktop/gf-icon.h               |   2 +
 gnome-flashback/libdesktop/gf-rename-popover.c     | 312 +++++++++++++++++++++
 gnome-flashback/libdesktop/gf-rename-popover.h     |  41 +++
 gnome-flashback/libdesktop/gf-trash-icon.c         |  10 +
 po/POTFILES.in                                     |   1 +
 12 files changed, 677 insertions(+), 98 deletions(-)
---
diff --git a/gnome-flashback/libdesktop/Makefile.am b/gnome-flashback/libdesktop/Makefile.am
index 82a8799..8667d0f 100644
--- a/gnome-flashback/libdesktop/Makefile.am
+++ b/gnome-flashback/libdesktop/Makefile.am
@@ -37,6 +37,8 @@ libdesktop_la_SOURCES = \
        gf-icon.h \
        gf-monitor-view.c \
        gf-monitor-view.h \
+       gf-rename-popover.c \
+       gf-rename-popover.h \
        gf-utils.c \
        gf-utils.h \
        $(BUILT_SOURCES) \
diff --git a/gnome-flashback/libdesktop/gf-create-folder-dialog.c 
b/gnome-flashback/libdesktop/gf-create-folder-dialog.c
index 214af55..6584ec2 100644
--- a/gnome-flashback/libdesktop/gf-create-folder-dialog.c
+++ b/gnome-flashback/libdesktop/gf-create-folder-dialog.c
@@ -43,75 +43,13 @@ static guint dialog_signals[LAST_SIGNAL] = { 0 };
 
 G_DEFINE_TYPE (GfCreateFolderDialog, gf_create_folder_dialog, GTK_TYPE_DIALOG)
 
-static gboolean
-is_valid (GfCreateFolderDialog *self)
+static void
+validate (GfCreateFolderDialog *self)
 {
-  GtkRevealer *revealer;
   const char *text;
-  char *folder_name;
-  char *validate_error;
-  const char *error;
-  gboolean valid;
 
-  revealer = GTK_REVEALER (self->error_revealer);
   text = gtk_entry_get_text (GTK_ENTRY (self->name_entry));
-  folder_name = g_strdup (text);
-  error = NULL;
-  valid = TRUE;
-
-  folder_name = g_strstrip (folder_name);
-  validate_error = NULL;
-
-  if (*folder_name == '\0')
-    {
-      error = NULL;
-      valid = FALSE;
-    }
-  else if (g_strstr_len (folder_name, -1, "/") != NULL)
-    {
-      error = _("Folder names cannot contain “/”.");
-      valid = FALSE;
-    }
-  else if (g_strcmp0 (folder_name, ".") == 0)
-    {
-      error = _("A folder cannot be called “.”.");
-      valid = FALSE;
-    }
-  else if (g_strcmp0 (folder_name, "..") == 0)
-    {
-      error = _("A folder cannot be called “..”.");
-      valid = FALSE;
-    }
-
-  if (valid)
-    {
-      g_assert_true (error == NULL);
-
-      g_signal_emit (self, dialog_signals[VALIDATE], 0,
-                     folder_name, &validate_error);
-
-      if (validate_error != NULL)
-        {
-          error = validate_error;
-          valid = FALSE;
-        }
-    }
-
-  if (error == NULL &&
-      g_str_has_prefix (folder_name, "."))
-    {
-      error = _("Folders with “.” at the beginning of their name are hidden.");
-    }
-
-  gtk_label_set_text (GTK_LABEL (self->error_label), error);
-  gtk_revealer_set_reveal_child (revealer, error != NULL);
-
-  gtk_widget_set_sensitive (self->create_button, valid);
-
-  g_free (validate_error);
-  g_free (folder_name);
-
-  return valid;
+  g_signal_emit (self, dialog_signals[VALIDATE], 0, text);
 }
 
 static void
@@ -125,7 +63,9 @@ static void
 create_clicked_cb (GtkWidget            *widget,
                    GfCreateFolderDialog *self)
 {
-  if (!is_valid (self))
+  validate (self);
+
+  if (!gtk_widget_get_sensitive (self->create_button))
     return;
 
   gtk_dialog_response (GTK_DIALOG (self), GTK_RESPONSE_ACCEPT);
@@ -135,15 +75,16 @@ static void
 name_changed_cb (GtkEditable          *editable,
                  GfCreateFolderDialog *self)
 {
-  is_valid (self);
+  validate (self);
 }
 
 static void
 name_activate_cb (GtkWidget            *widget,
                   GfCreateFolderDialog *self)
 {
-  if (!gtk_widget_get_sensitive (self->create_button) ||
-      !is_valid (self))
+  validate (self);
+
+  if (!gtk_widget_get_sensitive (self->create_button))
     return;
 
   gtk_dialog_response (GTK_DIALOG (self), GTK_RESPONSE_ACCEPT);
@@ -229,9 +170,8 @@ install_signals (void)
   dialog_signals[VALIDATE] =
     g_signal_new ("validate", GF_TYPE_CREATE_FOLDER_DIALOG,
                   G_SIGNAL_RUN_LAST, 0,
-                  g_signal_accumulator_first_wins,
-                  NULL, NULL,
-                  G_TYPE_STRING, 1, G_TYPE_STRING);
+                  NULL, NULL, NULL,
+                  G_TYPE_NONE, 1, G_TYPE_STRING);
 }
 
 static void
@@ -257,6 +197,21 @@ gf_create_folder_dialog_new (void)
                        NULL);
 }
 
+void
+gf_create_folder_dialog_set_valid (GfCreateFolderDialog *self,
+                                   gboolean              valid,
+                                   const char           *message)
+{
+  GtkRevealer *revealer;
+
+  revealer = GTK_REVEALER (self->error_revealer);
+
+  gtk_label_set_text (GTK_LABEL (self->error_label), message);
+  gtk_revealer_set_reveal_child (revealer, message != NULL);
+
+  gtk_widget_set_sensitive (self->create_button, valid);
+}
+
 char *
 gf_create_folder_dialog_get_folder_name (GfCreateFolderDialog *self)
 {
diff --git a/gnome-flashback/libdesktop/gf-create-folder-dialog.h 
b/gnome-flashback/libdesktop/gf-create-folder-dialog.h
index ffc5669..d302a9e 100644
--- a/gnome-flashback/libdesktop/gf-create-folder-dialog.h
+++ b/gnome-flashback/libdesktop/gf-create-folder-dialog.h
@@ -26,9 +26,13 @@ G_BEGIN_DECLS
 G_DECLARE_FINAL_TYPE (GfCreateFolderDialog, gf_create_folder_dialog,
                       GF, CREATE_FOLDER_DIALOG, GtkDialog)
 
-GtkWidget  *gf_create_folder_dialog_new             (void);
+GtkWidget *gf_create_folder_dialog_new             (void);
 
-char       *gf_create_folder_dialog_get_folder_name (GfCreateFolderDialog *self);
+void       gf_create_folder_dialog_set_valid       (GfCreateFolderDialog *self,
+                                                    gboolean              valid,
+                                                    const char           *message);
+
+char      *gf_create_folder_dialog_get_folder_name (GfCreateFolderDialog *self);
 
 G_END_DECLS
 
diff --git a/gnome-flashback/libdesktop/gf-home-icon.c b/gnome-flashback/libdesktop/gf-home-icon.c
index f31b2aa..7e11bdb 100644
--- a/gnome-flashback/libdesktop/gf-home-icon.c
+++ b/gnome-flashback/libdesktop/gf-home-icon.c
@@ -25,9 +25,20 @@ struct _GfHomeIcon
 
 G_DEFINE_TYPE (GfHomeIcon, gf_home_icon, GF_TYPE_ICON)
 
+static gboolean
+gf_home_icon_can_rename (GfIcon *icon)
+{
+  return FALSE;
+}
+
 static void
 gf_home_icon_class_init (GfHomeIconClass *self_class)
 {
+  GfIconClass *icon_class;
+
+  icon_class = GF_ICON_CLASS (self_class);
+
+  icon_class->can_rename = gf_home_icon_can_rename;
 }
 
 static void
diff --git a/gnome-flashback/libdesktop/gf-icon-view.c b/gnome-flashback/libdesktop/gf-icon-view.c
index 8d455ba..d49f6e1 100644
--- a/gnome-flashback/libdesktop/gf-icon-view.c
+++ b/gnome-flashback/libdesktop/gf-icon-view.c
@@ -486,6 +486,25 @@ empty_trash_cb (GObject      *object,
     }
 }
 
+static void
+rename_file_cb (GObject      *object,
+                GAsyncResult *res,
+                gpointer      user_data)
+{
+  GError *error;
+
+  error = NULL;
+  gf_nautilus_gen_call_rename_file_finish (GF_NAUTILUS_GEN (object),
+                                           res, &error);
+
+  if (error != NULL)
+    {
+      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+        g_warning ("Error renaming file: %s", error->message);
+      g_error_free (error);
+    }
+}
+
 static GfIconInfo *
 create_icon_info (GfIconView *self,
                   GtkWidget  *icon)
@@ -683,28 +702,22 @@ desktop_changed_cb (GFileMonitor      *monitor,
     }
 }
 
-static char *
+static void
 create_folder_dialog_validate_cb (GfCreateFolderDialog *dialog,
                                   const char           *folder_name,
                                   GfIconView           *self)
 {
-  GList *l;
+  char *message;
+  gboolean valid;
 
-  for (l = self->icons; l != NULL; l = l->next)
-    {
-      GfIconInfo *info;
-      const char *name;
-
-      info = l->data;
-
-      name = gf_icon_get_name (GF_ICON (info->icon));
-
-      if (g_strcmp0 (name, folder_name) == 0)
-        return g_strdup (_("A folder with that name already exists."));
+  message = NULL;
+  valid = gf_icon_view_validate_new_name (self,
+                                          G_FILE_TYPE_DIRECTORY,
+                                          folder_name,
+                                          &message);
 
-    }
-
-  return NULL;
+  gf_create_folder_dialog_set_valid (dialog, valid, message);
+  g_free (message);
 }
 
 static void
@@ -2310,3 +2323,88 @@ gf_icon_view_empty_trash (GfIconView  *self)
                                     empty_trash_cb,
                                     NULL);
 }
+
+gboolean
+gf_icon_view_validate_new_name (GfIconView  *self,
+                                GFileType    file_type,
+                                const char  *new_name,
+                                char       **message)
+{
+  gboolean is_dir;
+  char *text;
+  gboolean valid;
+  GList *l;
+
+  g_assert (message != NULL && *message == NULL);
+
+  is_dir = file_type == G_FILE_TYPE_DIRECTORY;
+  text = g_strstrip (g_strdup (new_name));
+  valid = TRUE;
+
+  if (*text == '\0')
+    {
+      valid = FALSE;
+    }
+  else if (g_strstr_len (text, -1, "/") != NULL)
+    {
+      valid = FALSE;
+      *message = g_strdup_printf (_("%s names cannot contain “/”."),
+                                  is_dir ? "Folder" : "File");
+    }
+  else if (g_strcmp0 (text, ".") == 0)
+    {
+      valid = FALSE;
+      *message = g_strdup_printf (_("A %s cannot be called “.”."),
+                                  is_dir ? "folder" : "file");
+    }
+  else if (g_strcmp0 (text, "..") == 0)
+    {
+      valid = FALSE;
+      *message = g_strdup_printf (_("A %s cannot be called “..”."),
+                                  is_dir ? "folder" : "file");
+    }
+
+  for (l = self->icons; l != NULL; l = l->next)
+    {
+      GfIconInfo *info;
+      const char *name;
+
+      info = l->data;
+
+      name = gf_icon_get_name (GF_ICON (info->icon));
+      if (g_strcmp0 (name, text) == 0)
+        {
+          valid = FALSE;
+          *message = g_strdup_printf (_("A %s with that name already exists."),
+                                      is_dir ? "folder" : "file");
+          break;
+        }
+    }
+
+  if (*message == NULL &&
+      g_str_has_prefix (text, "."))
+    {
+      *message = g_strdup_printf (_("%s with “.” at the beginning of their name are hidden."),
+                                  is_dir ? "Folders" : "Files");
+    }
+
+  g_free (text);
+
+  return valid;
+}
+
+void
+gf_icon_view_rename_file (GfIconView *self,
+                          const char *uri,
+                          const char *new_name)
+{
+  if (self->nautilus == NULL)
+    return;
+
+  gf_nautilus_gen_call_rename_file (self->nautilus,
+                                    uri,
+                                    new_name,
+                                    self->cancellable,
+                                    rename_file_cb,
+                                    NULL);
+}
diff --git a/gnome-flashback/libdesktop/gf-icon-view.h b/gnome-flashback/libdesktop/gf-icon-view.h
index b72a32e..b75bd9d 100644
--- a/gnome-flashback/libdesktop/gf-icon-view.h
+++ b/gnome-flashback/libdesktop/gf-icon-view.h
@@ -27,19 +27,28 @@ G_DECLARE_FINAL_TYPE (GfIconView, gf_icon_view, GF, ICON_VIEW, GtkEventBox)
 
 GtkWidget *gf_icon_view_new                      (void);
 
-char      *gf_icon_view_get_file_attributes      (GfIconView         *self);
+char      *gf_icon_view_get_file_attributes      (GfIconView          *self);
 
-void       gf_icon_view_set_representative_color (GfIconView         *self,
-                                                  GdkRGBA            *color);
+void       gf_icon_view_set_representative_color (GfIconView          *self,
+                                                  GdkRGBA             *color);
 
-void       gf_icon_view_clear_selection          (GfIconView         *self);
+void       gf_icon_view_clear_selection          (GfIconView          *self);
 
-GList     *gf_icon_view_get_selected_icons       (GfIconView         *self);
+GList     *gf_icon_view_get_selected_icons       (GfIconView          *self);
 
-void       gf_icon_view_show_item_properties     (GfIconView         *self,
-                                                  const char * const *uris);
+void       gf_icon_view_show_item_properties     (GfIconView          *self,
+                                                  const char * const  *uris);
 
-void       gf_icon_view_empty_trash              (GfIconView         *self);
+void       gf_icon_view_empty_trash              (GfIconView          *self);
+
+gboolean   gf_icon_view_validate_new_name        (GfIconView          *self,
+                                                  GFileType            file_type,
+                                                  const char          *new_name,
+                                                  char               **message);
+
+void       gf_icon_view_rename_file              (GfIconView          *self,
+                                                  const char          *uri,
+                                                  const char          *new_name);
 
 G_END_DECLS
 
diff --git a/gnome-flashback/libdesktop/gf-icon.c b/gnome-flashback/libdesktop/gf-icon.c
index 41463ef..24ae67a 100644
--- a/gnome-flashback/libdesktop/gf-icon.c
+++ b/gnome-flashback/libdesktop/gf-icon.c
@@ -23,6 +23,7 @@
 
 #include "gf-desktop-enums.h"
 #include "gf-desktop-enum-types.h"
+#include "gf-rename-popover.h"
 #include "gf-trash-icon.h"
 #include "gf-utils.h"
 
@@ -48,7 +49,10 @@ typedef struct
 
   GDesktopAppInfo *app_info;
 
+  char            *name;
   char            *name_collated;
+
+  GtkWidget       *popover;
 } GfIconPrivate;
 
 enum
@@ -127,6 +131,74 @@ icon_open (GfIcon *self)
   g_free (uri);
 }
 
+static void
+rename_validate_cb (GfRenamePopover *popover,
+                    const char      *new_name,
+                    GfIcon          *self)
+{
+  GfIconPrivate *priv;
+  char *message;
+  gboolean valid;
+
+  priv = gf_icon_get_instance_private (self);
+
+  if (g_strcmp0 (new_name, gf_icon_get_name (self)) == 0)
+    {
+      gf_rename_popover_set_valid (popover, TRUE, "");
+      return;
+    }
+
+  message = NULL;
+  valid = gf_icon_view_validate_new_name (priv->icon_view,
+                                          gf_icon_get_file_type (self),
+                                          new_name,
+                                          &message);
+
+  gf_rename_popover_set_valid (popover, valid, message);
+  g_free (message);
+}
+
+static void
+rename_do_rename_cb (GfRenamePopover *popover,
+                     GfIcon          *self)
+{
+  GfIconPrivate *priv;
+  char *new_name;
+
+  priv = gf_icon_get_instance_private (self);
+  new_name = gf_rename_popover_get_name (popover);
+
+  if (g_strcmp0 (new_name, priv->name) != 0)
+    {
+      char *uri;
+
+      uri = g_file_get_uri (priv->file);
+      gf_icon_view_rename_file (priv->icon_view, uri, new_name);
+      g_free (uri);
+    }
+
+  gtk_popover_popdown (GTK_POPOVER (popover));
+  g_free (new_name);
+}
+
+static void
+rename_closed_cb (GtkPopover *popover,
+                  GfIcon     *self)
+{
+  gtk_widget_destroy (GTK_WIDGET (popover));
+}
+
+static void
+rename_destroy_cb (GtkWidget *widget,
+                   GfIcon    *self)
+{
+  GfIconPrivate *priv;
+
+  priv = gf_icon_get_instance_private (self);
+
+  priv->popover = NULL;
+}
+
 static void
 open_cb (GtkMenuItem *item,
          GfIcon      *self)
@@ -134,6 +206,38 @@ open_cb (GtkMenuItem *item,
   icon_open (self);
 }
 
+static void
+rename_cb (GtkMenuItem *item,
+           GfIcon      *self)
+{
+  GfIconPrivate *priv;
+
+  priv = gf_icon_get_instance_private (self);
+
+  g_assert (priv->popover == NULL);
+  priv->popover = gf_rename_popover_new (GTK_WIDGET (self),
+                                         gf_icon_get_file_type (self),
+                                         gf_icon_get_name (self));
+
+  g_signal_connect (priv->popover, "validate",
+                    G_CALLBACK (rename_validate_cb),
+                    self);
+
+  g_signal_connect (priv->popover, "do-rename",
+                    G_CALLBACK (rename_do_rename_cb),
+                    self);
+
+  g_signal_connect (priv->popover, "closed",
+                    G_CALLBACK (rename_closed_cb),
+                    self);
+
+  g_signal_connect (priv->popover, "destroy",
+                    G_CALLBACK (rename_destroy_cb),
+                    self);
+
+  gtk_popover_popup (GTK_POPOVER (priv->popover));
+}
+
 static void
 empty_trash_cb (GtkMenuItem *item,
                 GfIcon      *self)
@@ -216,6 +320,21 @@ create_popup_menu (GfIcon *self)
                     G_CALLBACK (open_cb),
                     self);
 
+  if (GF_ICON_GET_CLASS (self)->can_rename (GF_ICON (self)))
+    {
+      item = gtk_separator_menu_item_new ();
+      gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), item);
+      gtk_widget_show (item);
+
+      item = gtk_menu_item_new_with_label (_("Rename..."));
+      gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), item);
+      gtk_widget_show (item);
+
+      g_signal_connect (item, "activate",
+                        G_CALLBACK (rename_cb),
+                        self);
+    }
+
   if (GF_IS_TRASH_ICON (self) &&
       n_selected_icons == 1)
     {
@@ -373,6 +492,7 @@ update_text (GfIcon *self)
   if (name == NULL)
     name = g_file_info_get_display_name (priv->info);
 
+  priv->name = g_strdup (name);
   gtk_label_set_text (GTK_LABEL (priv->label), name);
 
   g_clear_pointer (&priv->name_collated, g_free);
@@ -473,8 +593,12 @@ gf_icon_finalize (GObject *object)
   priv = gf_icon_get_instance_private (self);
 
   g_clear_pointer (&priv->css_class, g_free);
+
+  g_clear_pointer (&priv->name, g_free);
   g_clear_pointer (&priv->name_collated, g_free);
 
+  g_clear_pointer (&priv->popover, gtk_widget_destroy);
+
   G_OBJECT_CLASS (gf_icon_parent_class)->finalize (object);
 }
 
@@ -570,6 +694,12 @@ gf_icon_get_preferred_width (GtkWidget *widget,
   *natural_width += priv->extra_text_width;
 }
 
+static gboolean
+gf_icon_can_rename (GfIcon *self)
+{
+  return TRUE;
+}
+
 static void
 install_properties (GObjectClass *object_class)
 {
@@ -650,6 +780,8 @@ gf_icon_class_init (GfIconClass *self_class)
 
   widget_class->get_preferred_width = gf_icon_get_preferred_width;
 
+  self_class->can_rename = gf_icon_can_rename;
+
   install_properties (object_class);
   install_signals ();
 
@@ -728,6 +860,8 @@ gf_icon_set_file (GfIcon *self,
 
   priv = gf_icon_get_instance_private (self);
 
+  g_clear_pointer (&priv->popover, gtk_widget_destroy);
+
   g_clear_object (&priv->file);
   priv->file = g_object_ref (file);
 
diff --git a/gnome-flashback/libdesktop/gf-icon.h b/gnome-flashback/libdesktop/gf-icon.h
index 5c446c2..c416740 100644
--- a/gnome-flashback/libdesktop/gf-icon.h
+++ b/gnome-flashback/libdesktop/gf-icon.h
@@ -28,6 +28,8 @@ G_DECLARE_DERIVABLE_TYPE (GfIcon, gf_icon, GF, ICON, GtkButton)
 struct _GfIconClass
 {
   GtkButtonClass parent_class;
+
+  gboolean (* can_rename) (GfIcon *self);
 };
 
 GtkWidget  *gf_icon_new               (GfIconView *icon_view,
diff --git a/gnome-flashback/libdesktop/gf-rename-popover.c b/gnome-flashback/libdesktop/gf-rename-popover.c
new file mode 100644
index 0000000..408c977
--- /dev/null
+++ b/gnome-flashback/libdesktop/gf-rename-popover.c
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2019 Alberts Muktupāvels
+ *
+ * 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 3 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 "config.h"
+#include "gf-rename-popover.h"
+
+#include <glib/gi18n.h>
+
+struct _GfRenamePopover
+{
+  GtkPopover  parent;
+
+  GFileType   file_type;
+  char       *name;
+
+  GtkWidget  *title_label;
+
+  GtkWidget  *rename_button;
+
+  GtkWidget  *name_entry;
+
+  GtkWidget  *error_revealer;
+  GtkWidget  *error_label;
+};
+
+enum
+{
+  PROP_0,
+
+  PROP_FILE_TYPE,
+  PROP_NAME,
+
+  LAST_PROP
+};
+
+static GParamSpec *popover_properties[LAST_PROP] = { NULL };
+
+enum
+{
+  VALIDATE,
+
+  DO_RENAME,
+
+  LAST_SIGNAL
+};
+
+static guint popover_signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (GfRenamePopover, gf_rename_popover, GTK_TYPE_POPOVER)
+
+static void
+validate (GfRenamePopover *self)
+{
+  const char *text;
+
+  text = gtk_entry_get_text (GTK_ENTRY (self->name_entry));
+  g_signal_emit (self, popover_signals[VALIDATE], 0, text);
+}
+
+static void
+name_changed_cb (GtkEditable     *editable,
+                 GfRenamePopover *self)
+{
+  validate (self);
+}
+
+static void
+name_activate_cb (GtkWidget       *widget,
+                  GfRenamePopover *self)
+{
+  validate (self);
+
+  if (!gtk_widget_get_sensitive (self->rename_button))
+    return;
+
+  g_signal_emit (self, popover_signals[DO_RENAME], 0);
+}
+
+static void
+rename_clicked_cb (GtkWidget       *widget,
+                   GfRenamePopover *self)
+{
+  validate (self);
+
+  if (!gtk_widget_get_sensitive (self->rename_button))
+    return;
+
+  g_signal_emit (self, popover_signals[DO_RENAME], 0);
+}
+
+static void
+gf_rename_popover_constructed (GObject *object)
+{
+  GfRenamePopover *self;
+  gboolean is_dir;
+  const char *title;
+
+  self = GF_RENAME_POPOVER (object);
+
+  G_OBJECT_CLASS (gf_rename_popover_parent_class)->constructed (object);
+
+  is_dir = self->file_type == G_FILE_TYPE_DIRECTORY;
+  title = is_dir ? _("Folder name") : _("File name");
+
+  gtk_label_set_text (GTK_LABEL (self->title_label), title);
+  gtk_entry_set_text (GTK_ENTRY (self->name_entry), self->name);
+
+  g_signal_connect (self->name_entry, "changed",
+                    G_CALLBACK (name_changed_cb),
+                    self);
+}
+
+static void
+gf_rename_popover_finalize (GObject *object)
+{
+  GfRenamePopover *self;
+
+  self = GF_RENAME_POPOVER (object);
+
+  g_clear_pointer (&self->name, g_free);
+
+  G_OBJECT_CLASS (gf_rename_popover_parent_class)->finalize (object);
+}
+
+static void
+gf_rename_popover_set_property (GObject      *object,
+                                guint         property_id,
+                                const GValue *value,
+                                GParamSpec   *pspec)
+{
+  GfRenamePopover *self;
+
+  self = GF_RENAME_POPOVER (object);
+
+  switch (property_id)
+    {
+      case PROP_FILE_TYPE:
+        self->file_type = g_value_get_enum (value);
+        break;
+
+      case PROP_NAME:
+        g_assert (self->name == NULL);
+        self->name = g_value_dup_string (value);
+        break;
+
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+        break;
+    }
+}
+
+static void
+install_properties (GObjectClass *object_class)
+{
+  popover_properties[PROP_FILE_TYPE] =
+    g_param_spec_enum ("file-type",
+                       "file-type",
+                       "file-type",
+                       G_TYPE_FILE_TYPE,
+                       G_FILE_TYPE_UNKNOWN,
+                       G_PARAM_CONSTRUCT_ONLY |
+                       G_PARAM_WRITABLE |
+                       G_PARAM_EXPLICIT_NOTIFY |
+                       G_PARAM_STATIC_STRINGS);
+
+  popover_properties[PROP_NAME] =
+    g_param_spec_string ("name",
+                         "name",
+                         "name",
+                         NULL,
+                         G_PARAM_CONSTRUCT_ONLY |
+                         G_PARAM_WRITABLE |
+                         G_PARAM_EXPLICIT_NOTIFY |
+                         G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (object_class, LAST_PROP,
+                                     popover_properties);
+}
+
+static void
+install_signals (void)
+{
+  popover_signals[VALIDATE] =
+    g_signal_new ("validate", GF_TYPE_RENAME_POPOVER,
+                  G_SIGNAL_RUN_LAST, 0,
+                  NULL, NULL, NULL,
+                  G_TYPE_NONE, 1, G_TYPE_STRING);
+
+  popover_signals[DO_RENAME] =
+    g_signal_new ("do-rename", GF_TYPE_RENAME_POPOVER,
+                  G_SIGNAL_RUN_LAST, 0,
+                  NULL, NULL, NULL,
+                  G_TYPE_NONE, 0);
+}
+
+static void
+gf_rename_popover_class_init (GfRenamePopoverClass *self_class)
+{
+  GObjectClass *object_class;
+
+  object_class = G_OBJECT_CLASS (self_class);
+
+  object_class->constructed = gf_rename_popover_constructed;
+  object_class->finalize = gf_rename_popover_finalize;
+  object_class->set_property = gf_rename_popover_set_property;
+
+  install_properties (object_class);
+  install_signals ();
+}
+
+static void
+gf_rename_popover_init (GfRenamePopover *self)
+{
+  GtkWidget *vbox;
+  GtkWidget *hbox;
+  GtkStyleContext *style;
+
+  vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
+  g_object_set (vbox, "margin", 10, NULL);
+  gtk_container_add (GTK_CONTAINER (self), vbox);
+  gtk_widget_show (vbox);
+
+  self->title_label = gtk_label_new (NULL);
+  gtk_label_set_xalign (GTK_LABEL (self->title_label), 0.0);
+  gtk_container_add (GTK_CONTAINER (vbox), self->title_label);
+  gtk_widget_show (self->title_label);
+
+  hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
+  gtk_container_add (GTK_CONTAINER (vbox), hbox);
+  gtk_widget_show (hbox);
+
+  self->name_entry = gtk_entry_new ();
+  gtk_container_add (GTK_CONTAINER (hbox), self->name_entry);
+  gtk_widget_show (self->name_entry);
+
+  g_signal_connect (self->name_entry, "activate",
+                    G_CALLBACK (name_activate_cb),
+                    self);
+
+  self->rename_button = gtk_button_new_with_label (_("Rename"));
+  gtk_container_add (GTK_CONTAINER (hbox), self->rename_button);
+  gtk_widget_show (self->rename_button);
+
+  style = gtk_widget_get_style_context (self->rename_button);
+  gtk_style_context_add_class (style, GTK_STYLE_CLASS_SUGGESTED_ACTION);
+
+  g_signal_connect (self->rename_button, "clicked",
+                    G_CALLBACK (rename_clicked_cb),
+                    self);
+
+  self->error_revealer = gtk_revealer_new ();
+  gtk_container_add (GTK_CONTAINER (vbox), self->error_revealer);
+  gtk_widget_show (self->error_revealer);
+
+  self->error_label = gtk_label_new (NULL);
+  gtk_label_set_xalign (GTK_LABEL (self->error_label), 0.0);
+  gtk_container_add (GTK_CONTAINER (self->error_revealer), self->error_label);
+  gtk_widget_show (self->error_label);
+}
+
+GtkWidget *
+gf_rename_popover_new (GtkWidget  *relative_to,
+                       GFileType   file_type,
+                       const char *name)
+{
+  return g_object_new (GF_TYPE_RENAME_POPOVER,
+                       "relative-to", relative_to,
+                       "file-type", file_type,
+                       "name", name,
+                       NULL);
+}
+
+void
+gf_rename_popover_set_valid (GfRenamePopover *self,
+                             gboolean         valid,
+                             const char      *message)
+{
+  GtkRevealer *revealer;
+
+  revealer = GTK_REVEALER (self->error_revealer);
+
+  gtk_label_set_text (GTK_LABEL (self->error_label), message);
+  gtk_revealer_set_reveal_child (revealer, message != NULL);
+
+  gtk_widget_set_sensitive (self->rename_button, valid);
+}
+
+char *
+gf_rename_popover_get_name (GfRenamePopover *self)
+{
+  const char *text;
+  char *name;
+
+  text = gtk_entry_get_text (GTK_ENTRY (self->name_entry));
+  name = g_strdup (text);
+
+  return g_strstrip (name);
+}
diff --git a/gnome-flashback/libdesktop/gf-rename-popover.h b/gnome-flashback/libdesktop/gf-rename-popover.h
new file mode 100644
index 0000000..b7c0e63
--- /dev/null
+++ b/gnome-flashback/libdesktop/gf-rename-popover.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 Alberts Muktupāvels
+ *
+ * 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 3 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/>.
+ */
+
+#ifndef GF_RENAME_POPOVER_H
+#define GF_RENAME_POPOVER_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GF_TYPE_RENAME_POPOVER (gf_rename_popover_get_type ())
+G_DECLARE_FINAL_TYPE (GfRenamePopover, gf_rename_popover,
+                      GF, RENAME_POPOVER, GtkPopover)
+
+GtkWidget *gf_rename_popover_new       (GtkWidget       *relative_to,
+                                        GFileType        file_type,
+                                        const char      *name);
+
+void       gf_rename_popover_set_valid (GfRenamePopover *self,
+                                        gboolean         valid,
+                                        const char      *message);
+
+char      *gf_rename_popover_get_name  (GfRenamePopover *self);
+
+G_END_DECLS
+
+#endif
diff --git a/gnome-flashback/libdesktop/gf-trash-icon.c b/gnome-flashback/libdesktop/gf-trash-icon.c
index 55b8bf6..93a725c 100644
--- a/gnome-flashback/libdesktop/gf-trash-icon.c
+++ b/gnome-flashback/libdesktop/gf-trash-icon.c
@@ -186,15 +186,25 @@ gf_trash_icon_dispose (GObject *object)
   G_OBJECT_CLASS (gf_trash_icon_parent_class)->dispose (object);
 }
 
+static gboolean
+gf_trash_icon_can_rename (GfIcon *icon)
+{
+  return FALSE;
+}
+
 static void
 gf_trash_icon_class_init (GfTrashIconClass *self_class)
 {
   GObjectClass *object_class;
+  GfIconClass *icon_class;
 
   object_class = G_OBJECT_CLASS (self_class);
+  icon_class = GF_ICON_CLASS (self_class);
 
   object_class->constructed = gf_trash_icon_constructed;
   object_class->dispose = gf_trash_icon_dispose;
+
+  icon_class->can_rename = gf_trash_icon_can_rename;
 }
 
 static void
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 70b8628..ddeb8e3 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -25,6 +25,7 @@ gnome-flashback/libdesktop/gf-create-folder-dialog.c
 gnome-flashback/libdesktop/gf-desktop-window.c
 gnome-flashback/libdesktop/gf-icon.c
 gnome-flashback/libdesktop/gf-icon-view.c
+gnome-flashback/libdesktop/gf-rename-popover.c
 gnome-flashback/libend-session-dialog/gf-inhibit-dialog.c
 gnome-flashback/libend-session-dialog/gf-inhibit-dialog.ui
 gnome-flashback/libinput-sources/gf-input-sources.c


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