Commit 43c37cf7 authored by Alberts Muktupāvels's avatar Alberts Muktupāvels
Browse files

desktop: add Rename... to icon menu

parent 78914e9a
Pipeline #131314 passed with stage
in 14 minutes and 34 seconds
......@@ -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) \
......
......@@ -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)
{
......
......@@ -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
......
......@@ -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
......
......@@ -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);
}
......@@ -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
......
......@@ -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);
......
......@@ -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,
......
/*
* 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"