[gnome-flashback] desktop: add New Folder to desktop menu



commit eeee72c9b29e395de9f63f135c6d98af95026959
Author: Alberts Muktupāvels <alberts muktupavels gmail com>
Date:   Wed Nov 13 04:37:52 2019 +0200

    desktop: add New Folder to desktop menu

 gnome-flashback/libdesktop/Makefile.am             |   2 +
 .../libdesktop/gf-create-folder-dialog.c           | 258 +++++++++++++++++++++
 .../libdesktop/gf-create-folder-dialog.h           |  35 +++
 gnome-flashback/libdesktop/gf-icon-view.c          | 189 ++++++++++++++-
 gnome-flashback/libdesktop/gf-icon.c               |   6 +
 gnome-flashback/libdesktop/gf-icon.h               |  18 +-
 po/POTFILES.in                                     |   1 +
 7 files changed, 492 insertions(+), 17 deletions(-)
---
diff --git a/gnome-flashback/libdesktop/Makefile.am b/gnome-flashback/libdesktop/Makefile.am
index cfc0215..744244f 100644
--- a/gnome-flashback/libdesktop/Makefile.am
+++ b/gnome-flashback/libdesktop/Makefile.am
@@ -18,6 +18,8 @@ libdesktop_la_CFLAGS = \
 libdesktop_la_SOURCES = \
        gf-background.c \
        gf-background.h \
+       gf-create-folder-dialog.c \
+       gf-create-folder-dialog.h \
        gf-desktop-enums.h \
        gf-desktop-window.c \
        gf-desktop-window.h \
diff --git a/gnome-flashback/libdesktop/gf-create-folder-dialog.c 
b/gnome-flashback/libdesktop/gf-create-folder-dialog.c
new file mode 100644
index 0000000..2057453
--- /dev/null
+++ b/gnome-flashback/libdesktop/gf-create-folder-dialog.c
@@ -0,0 +1,258 @@
+/*
+ * 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-create-folder-dialog.h"
+
+#include <glib/gi18n.h>
+
+struct _GfCreateFolderDialog
+{
+  GtkDialog  parent;
+
+  GtkWidget *create_button;
+
+  GtkWidget *name_entry;
+
+  GtkWidget *error_revealer;
+  GtkWidget *error_label;
+};
+
+enum
+{
+  VALIDATE,
+
+  LAST_SIGNAL
+};
+
+static guint dialog_signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (GfCreateFolderDialog, gf_create_folder_dialog, GTK_TYPE_DIALOG)
+
+static void
+cancel_clicked_cb (GtkWidget            *widget,
+                   GfCreateFolderDialog *self)
+{
+  gtk_widget_destroy (GTK_WIDGET (self));
+}
+
+static void
+create_clicked_cb (GtkWidget            *widget,
+                   GfCreateFolderDialog *self)
+{
+  gtk_dialog_response (GTK_DIALOG (self), GTK_RESPONSE_ACCEPT);
+}
+
+static void
+name_changed_cb (GtkEditable          *editable,
+                 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);
+}
+
+static void
+name_activate_cb (GtkWidget            *widget,
+                  GfCreateFolderDialog *self)
+{
+  if (!gtk_widget_get_sensitive (self->create_button))
+    return;
+
+  gtk_dialog_response (GTK_DIALOG (self), GTK_RESPONSE_ACCEPT);
+}
+
+static void
+setup_header_bar (GfCreateFolderDialog *self)
+{
+  GtkWidget *header_bar;
+  GtkWidget *cancel_button;
+  GtkStyleContext *style;
+
+  header_bar = gtk_dialog_get_header_bar (GTK_DIALOG (self));
+  gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (header_bar), FALSE);
+  gtk_header_bar_set_title (GTK_HEADER_BAR (header_bar), _("New Folder"));
+
+  cancel_button = gtk_button_new_with_label (_("Cancel"));
+  gtk_header_bar_pack_start (GTK_HEADER_BAR (header_bar), cancel_button);
+  gtk_widget_show (cancel_button);
+
+  g_signal_connect (cancel_button, "clicked",
+                    G_CALLBACK (cancel_clicked_cb),
+                    self);
+
+  self->create_button = gtk_button_new_with_label (_("Create"));
+  gtk_header_bar_pack_end (GTK_HEADER_BAR (header_bar), self->create_button);
+  gtk_widget_show (self->create_button);
+
+  style = gtk_widget_get_style_context (self->create_button);
+  gtk_style_context_add_class (style, GTK_STYLE_CLASS_SUGGESTED_ACTION);
+
+  gtk_widget_set_sensitive (self->create_button, FALSE);
+
+  g_signal_connect (self->create_button, "clicked",
+                    G_CALLBACK (create_clicked_cb),
+                    self);
+}
+
+static void
+setup_content_area (GfCreateFolderDialog *self)
+{
+  GtkWidget *content;
+  GtkWidget *label;
+
+  content = gtk_dialog_get_content_area (GTK_DIALOG (self));
+
+  g_object_set (content,
+                "margin", 18,
+                "margin-bottom", 12,
+                "spacing", 6,
+                NULL);
+
+  label = gtk_label_new (_("Folder name"));
+  gtk_label_set_xalign (GTK_LABEL (label), 0.0);
+  gtk_container_add (GTK_CONTAINER (content), label);
+  gtk_widget_show (label);
+
+  self->name_entry = gtk_entry_new ();
+  gtk_container_add (GTK_CONTAINER (content), self->name_entry);
+  gtk_widget_show (self->name_entry);
+
+  g_signal_connect (self->name_entry, "changed",
+                    G_CALLBACK (name_changed_cb),
+                    self);
+
+  g_signal_connect (self->name_entry, "activate",
+                    G_CALLBACK (name_activate_cb),
+                    self);
+
+  self->error_revealer = gtk_revealer_new ();
+  gtk_container_add (GTK_CONTAINER (content), 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);
+}
+
+static void
+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);
+}
+
+static void
+gf_create_folder_dialog_class_init (GfCreateFolderDialogClass *self_class)
+{
+  install_signals ();
+}
+
+static void
+gf_create_folder_dialog_init (GfCreateFolderDialog *self)
+{
+  setup_header_bar (self);
+  setup_content_area (self);
+}
+
+GtkWidget *
+gf_create_folder_dialog_new (void)
+{
+  return g_object_new (GF_TYPE_CREATE_FOLDER_DIALOG,
+                       "use-header-bar", TRUE,
+                       "width-request", 450,
+                       "resizable", FALSE,
+                       NULL);
+}
+
+char *
+gf_create_folder_dialog_get_folder_name (GfCreateFolderDialog *self)
+{
+  const char *text;
+  char *folder_name;
+
+  text = gtk_entry_get_text (GTK_ENTRY (self->name_entry));
+  folder_name = g_strdup (text);
+
+  return g_strstrip (folder_name);
+}
diff --git a/gnome-flashback/libdesktop/gf-create-folder-dialog.h 
b/gnome-flashback/libdesktop/gf-create-folder-dialog.h
new file mode 100644
index 0000000..ffc5669
--- /dev/null
+++ b/gnome-flashback/libdesktop/gf-create-folder-dialog.h
@@ -0,0 +1,35 @@
+/*
+ * 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_CREATE_FOLDER_DIALOG_H
+#define GF_CREATE_FOLDER_DIALOG_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GF_TYPE_CREATE_FOLDER_DIALOG (gf_create_folder_dialog_get_type ())
+G_DECLARE_FINAL_TYPE (GfCreateFolderDialog, gf_create_folder_dialog,
+                      GF, CREATE_FOLDER_DIALOG, GtkDialog)
+
+GtkWidget  *gf_create_folder_dialog_new             (void);
+
+char       *gf_create_folder_dialog_get_folder_name (GfCreateFolderDialog *self);
+
+G_END_DECLS
+
+#endif
diff --git a/gnome-flashback/libdesktop/gf-icon-view.c b/gnome-flashback/libdesktop/gf-icon-view.c
index 35cc8cb..75318a9 100644
--- a/gnome-flashback/libdesktop/gf-icon-view.c
+++ b/gnome-flashback/libdesktop/gf-icon-view.c
@@ -21,9 +21,11 @@
 #include <gdk/gdkx.h>
 #include <glib/gi18n.h>
 
+#include "gf-create-folder-dialog.h"
 #include "gf-desktop-enum-types.h"
 #include "gf-icon.h"
 #include "gf-monitor-view.h"
+#include "gf-nautilus-gen.h"
 #include "gf-utils.h"
 
 typedef struct
@@ -59,6 +61,10 @@ struct _GfIconView
   GtkStyleContext *rubberband_style;
   GdkRectangle     rubberband_rect;
   GList           *rubberband_icons;
+
+  GfNautilusGen   *nautilus;
+
+  GtkWidget       *create_folder_dialog;
 };
 
 enum
@@ -76,7 +82,8 @@ G_DEFINE_TYPE (GfIconView, gf_icon_view, GTK_TYPE_EVENT_BOX)
 static char *
 get_required_attributes (void)
 {
-  return gf_build_attributes_list (G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
+  return gf_build_attributes_list (G_FILE_ATTRIBUTE_STANDARD_NAME,
+                                   G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
                                    G_FILE_ATTRIBUTE_STANDARD_ICON,
                                    G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN,
                                    G_FILE_ATTRIBUTE_STANDARD_IS_BACKUP,
@@ -391,20 +398,120 @@ desktop_changed_cb (GFileMonitor      *monitor,
     }
 }
 
+static char *
+create_folder_dialog_validate_cb (GfCreateFolderDialog *dialog,
+                                  const char           *folder_name,
+                                  GfIconView           *self)
+{
+  GList *l;
+
+  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."));
+
+    }
+
+  return NULL;
+}
+
 static void
-open_terminal_cb (GtkMenuItem *item,
-                  GfIconView  *self)
+create_folder_cb (GObject      *object,
+                  GAsyncResult *res,
+                  gpointer      user_data)
 {
   GError *error;
 
   error = NULL;
-  if (!gf_launch_desktop_file ("org.gnome.Terminal.desktop", &error))
+  gf_nautilus_gen_call_create_folder_finish (GF_NAUTILUS_GEN (object),
+                                             res, &error);
+
+  if (error != NULL)
     {
-      g_warning ("%s", error->message);
+      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+        g_warning ("Error creating new folder: %s", error->message);
       g_error_free (error);
     }
 }
 
+static void
+create_folder_dialog_response_cb (GtkDialog  *dialog,
+                                  gint        response_id,
+                                  GfIconView *self)
+{
+  GfCreateFolderDialog *folder_dialog;
+  char *folder_name;
+  GFile *new_file;
+  char *uri;
+
+  if (response_id != GTK_RESPONSE_ACCEPT)
+    return;
+
+  folder_dialog = GF_CREATE_FOLDER_DIALOG (dialog);
+  folder_name = gf_create_folder_dialog_get_folder_name (folder_dialog);
+
+  new_file = g_file_get_child (self->desktop, folder_name);
+  g_free (folder_name);
+
+  uri = g_file_get_uri (new_file);
+  g_object_unref (new_file);
+
+  gf_nautilus_gen_call_create_folder (self->nautilus, uri,
+                                      self->cancellable,
+                                      create_folder_cb,
+                                      NULL);
+
+  gtk_widget_destroy (GTK_WIDGET (dialog));
+  g_free (uri);
+}
+
+static void
+create_folder_dialog_destroy_cb (GtkWidget   *widget,
+                                 GfIconView  *self)
+{
+  self->create_folder_dialog = NULL;
+}
+
+static void
+new_folder_cb (GtkMenuItem *item,
+               GfIconView  *self)
+{
+  GtkWidget *dialog;
+
+  if (self->nautilus == NULL)
+    return;
+
+  if (self->create_folder_dialog != NULL)
+    {
+      gtk_window_present (GTK_WINDOW (self->create_folder_dialog));
+      return;
+    }
+
+  dialog = gf_create_folder_dialog_new ();
+  self->create_folder_dialog = dialog;
+
+  g_signal_connect (dialog, "validate",
+                    G_CALLBACK (create_folder_dialog_validate_cb),
+                    self);
+
+  g_signal_connect (dialog, "response",
+                    G_CALLBACK (create_folder_dialog_response_cb),
+                    self);
+
+  g_signal_connect (dialog, "destroy",
+                    G_CALLBACK (create_folder_dialog_destroy_cb),
+                    self);
+
+  gtk_window_present (GTK_WINDOW (dialog));
+}
+
 static void
 change_background_cb (GtkMenuItem *item,
                       GfIconView  *self)
@@ -419,6 +526,20 @@ change_background_cb (GtkMenuItem *item,
     }
 }
 
+static void
+open_terminal_cb (GtkMenuItem *item,
+                  GfIconView  *self)
+{
+  GError *error;
+
+  error = NULL;
+  if (!gf_launch_desktop_file ("org.gnome.Terminal.desktop", &error))
+    {
+      g_warning ("%s", error->message);
+      g_error_free (error);
+    }
+}
+
 static GtkWidget *
 create_popup_menu (GfIconView *self)
 {
@@ -431,12 +552,12 @@ create_popup_menu (GfIconView *self)
   context = gtk_widget_get_style_context (popup_menu);
   gtk_style_context_add_class (context, GTK_STYLE_CLASS_CONTEXT_MENU);
 
-  item = gtk_menu_item_new_with_label (_("Open Terminal"));
+  item = gtk_menu_item_new_with_label (_("New Folder"));
   gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), item);
   gtk_widget_show (item);
 
   g_signal_connect (item, "activate",
-                    G_CALLBACK (open_terminal_cb),
+                    G_CALLBACK (new_folder_cb),
                     self);
 
   item = gtk_separator_menu_item_new ();
@@ -451,6 +572,18 @@ create_popup_menu (GfIconView *self)
                     G_CALLBACK (change_background_cb),
                     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 (_("Open Terminal"));
+  gtk_menu_shell_append (GTK_MENU_SHELL (popup_menu), item);
+  gtk_widget_show (item);
+
+  g_signal_connect (item, "activate",
+                    G_CALLBACK (open_terminal_cb),
+                    self);
+
   return popup_menu;
 }
 
@@ -771,8 +904,6 @@ enumerate_desktop (GfIconView *self)
 
   attributes = get_required_attributes ();
 
-  self->cancellable = g_cancellable_new ();
-
   g_file_enumerate_children_async (self->desktop,
                                    attributes,
                                    G_FILE_QUERY_INFO_NONE,
@@ -784,6 +915,32 @@ enumerate_desktop (GfIconView *self)
   g_free (attributes);
 }
 
+static void
+nautilus_ready_cb (GObject     *object,
+                   GAsyncResult *res,
+                   gpointer      user_data)
+
+{
+  GError *error;
+  GfNautilusGen *nautilus;
+  GfIconView *self;
+
+  error = NULL;
+  nautilus = gf_nautilus_gen_proxy_new_for_bus_finish (res, &error);
+
+  if (error != NULL)
+    {
+      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+        g_warning ("%s", error->message);
+
+      g_error_free (error);
+      return;
+    }
+
+  self = GF_ICON_VIEW (user_data);
+  self->nautilus = nautilus;
+}
+
 static GtkWidget *
 find_monitor_view_by_monitor (GfIconView *self,
                               GdkMonitor *monitor)
@@ -1015,6 +1172,10 @@ gf_icon_view_dispose (GObject *object)
   g_clear_object (&self->rubberband_style);
   g_clear_pointer (&self->rubberband_icons, g_list_free);
 
+  g_clear_object (&self->nautilus);
+
+  g_clear_pointer (&self->create_folder_dialog, gtk_widget_destroy);
+
   G_OBJECT_CLASS (gf_icon_view_parent_class)->dispose (object);
 }
 
@@ -1210,6 +1371,16 @@ gf_icon_view_init (GfIconView *self)
   gtk_container_add (GTK_CONTAINER (self), self->fixed);
   gtk_widget_show (self->fixed);
 
+  self->cancellable = g_cancellable_new ();
+
+  gf_nautilus_gen_proxy_new_for_bus (G_BUS_TYPE_SESSION,
+                                     G_DBUS_PROXY_FLAGS_NONE,
+                                     "org.gnome.Nautilus",
+                                     "/org/gnome/Nautilus",
+                                     self->cancellable,
+                                     nautilus_ready_cb,
+                                     self);
+
   display = gdk_display_get_default ();
   n_monitors = gdk_display_get_n_monitors (display);
 
diff --git a/gnome-flashback/libdesktop/gf-icon.c b/gnome-flashback/libdesktop/gf-icon.c
index 356e0ad..3d241a8 100644
--- a/gnome-flashback/libdesktop/gf-icon.c
+++ b/gnome-flashback/libdesktop/gf-icon.c
@@ -469,6 +469,12 @@ gf_icon_get_file (GfIcon *self)
   return self->file;
 }
 
+const char *
+gf_icon_get_name (GfIcon *self)
+{
+  return g_file_info_get_name (self->info);
+}
+
 gboolean
 gf_icon_is_hidden (GfIcon *self)
 {
diff --git a/gnome-flashback/libdesktop/gf-icon.h b/gnome-flashback/libdesktop/gf-icon.h
index ae43da4..72a0df0 100644
--- a/gnome-flashback/libdesktop/gf-icon.h
+++ b/gnome-flashback/libdesktop/gf-icon.h
@@ -33,18 +33,20 @@ typedef enum
 #define GF_TYPE_ICON (gf_icon_get_type ())
 G_DECLARE_FINAL_TYPE (GfIcon, gf_icon, GF, ICON, GtkButton)
 
-GtkWidget *gf_icon_new          (GFile               *file,
-                                 GFileInfo           *info);
+GtkWidget  *gf_icon_new          (GFile               *file,
+                                  GFileInfo           *info);
 
-GFile     *gf_icon_get_file     (GfIcon              *self);
+GFile      *gf_icon_get_file     (GfIcon              *self);
 
-gboolean   gf_icon_is_hidden    (GfIcon              *self);
+const char *gf_icon_get_name     (GfIcon              *self);
 
-void       gf_icon_set_selected (GfIcon              *self,
-                                 gboolean             selected,
-                                 GfIconSelectedFlags  flags);
+gboolean    gf_icon_is_hidden    (GfIcon              *self);
 
-gboolean   gf_icon_get_selected (GfIcon              *self);
+void        gf_icon_set_selected (GfIcon              *self,
+                                  gboolean             selected,
+                                  GfIconSelectedFlags  flags);
+
+gboolean    gf_icon_get_selected (GfIcon              *self);
 
 G_END_DECLS
 
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 846e7dd..fea8f1f 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -21,6 +21,7 @@ gnome-flashback/libaudio-device-selection/gf-audio-device-selection-dialog.c
 gnome-flashback/libautomount-manager/gsd-automount-manager.c
 gnome-flashback/libautomount-manager/gsd-autorun.c
 gnome-flashback/libbluetooth-applet/gf-bluetooth-applet.c
+gnome-flashback/libdesktop/gf-create-folder-dialog.c
 gnome-flashback/libdesktop/gf-icon.c
 gnome-flashback/libdesktop/gf-icon-view.c
 gnome-flashback/libend-session-dialog/gf-inhibit-dialog.c


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