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

desktop: add New Folder to desktop menu

parent 4109f15b
Pipeline #130656 passed with stage
in 14 minutes and 35 seconds
......@@ -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 \
......
/*
* 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);
}
/*
* 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
......@@ -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);
......
......@@ -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)
{
......
......@@ -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,