diff options
Diffstat (limited to 'src/nautilus-action-bar.c')
-rw-r--r-- | src/nautilus-action-bar.c | 526 |
1 files changed, 526 insertions, 0 deletions
diff --git a/src/nautilus-action-bar.c b/src/nautilus-action-bar.c new file mode 100644 index 000000000..f2f59532b --- /dev/null +++ b/src/nautilus-action-bar.c @@ -0,0 +1,526 @@ +/* nautilus-action-bar.c + * + * Copyright (C) 2016 Georges Basile Stavracas Neto <georges.stavracas@gmail.com> + * + * 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 "nautilus-action-bar.h" +#include "nautilus-clipboard.h" +#include "nautilus-file.h" +#include "nautilus-previewer.h" + +#include <gdk/gdkx.h> + +#include <glib/gi18n.h> + +#define UPDATE_STATUS_TIMEOUT 200 //ms + +struct _NautilusActionBar +{ + GtkFrame parent; + + GtkWidget *loading_label; + GtkWidget *paste_button; + GtkWidget *primary_label; + GtkWidget *secondary_label; + GtkWidget *stack; + + NautilusView *view; + gint update_status_timeout_id; +}; + +G_DEFINE_TYPE (NautilusActionBar, nautilus_action_bar, GTK_TYPE_FRAME) + +enum { + PROP_0, + PROP_VIEW, + N_PROPS +}; + +static void +open_preview_cb (NautilusActionBar *actionbar) +{ + GtkWidget *toplevel; + GdkWindow *window; + GList *selection; + gchar *uri; + guint xid; + + xid = 0; + uri = NULL; + selection = nautilus_view_get_selection (actionbar->view); + + /* Only preview if exact 1 file is selected */ + if (g_list_length (selection) != 1) + goto out; + + uri = nautilus_file_get_uri (selection->data); + toplevel = gtk_widget_get_toplevel (GTK_WIDGET (actionbar)); + +#ifdef GDK_WINDOWING_X11 + window = gtk_widget_get_window (toplevel); + if (GDK_IS_X11_WINDOW (window)) + xid = gdk_x11_window_get_xid (gtk_widget_get_window (toplevel)); +#endif + + nautilus_previewer_call_show_file (uri, xid, TRUE); + +out: + g_clear_pointer (&selection, nautilus_file_list_free); + g_clear_pointer (&uri, g_free); +} + +#if 0 +static void +update_paste_button (NautilusActionBar *self) +{ + NautilusClipboardMonitor *monitor; + NautilusClipboardInfo *info; + + monitor = nautilus_clipboard_monitor_get (); + info = nautilus_clipboard_monitor_get_clipboard_info (monitor); + + if (info) + { + gchar *label; + gint length; + + length = g_list_length (info->files); + + if (info->cut) + label = g_strdup_printf (g_dngettext(NULL, "Move %d file", "Move %d files", length), length); + else + label = g_strdup_printf (g_dngettext(NULL, "Paste %d file", "Paste %d files", length), length); + + gtk_widget_set_tooltip_text (self->paste_button, label); + + g_free (label); + } +} +#endif + +static void +setup_multiple_files_selection (NautilusActionBar *actionbar, + GList *selection) +{ + NautilusFile *file; + goffset non_folder_size; + gboolean non_folder_size_known; + guint non_folder_count, folder_count, folder_item_count; + gboolean folder_item_count_known; + guint file_item_count; + GList *p; + char *first_item_name; + char *non_folder_count_str; + char *non_folder_item_count_str; + char *folder_count_str; + char *folder_item_count_str; + gchar *primary_text; + gchar *secondary_text; + + folder_item_count_known = TRUE; + folder_count = 0; + folder_item_count = 0; + non_folder_count = 0; + non_folder_size_known = FALSE; + non_folder_size = 0; + first_item_name = NULL; + folder_count_str = NULL; + folder_item_count_str = NULL; + non_folder_count_str = NULL; + non_folder_item_count_str = NULL; + + for (p = selection; p != NULL; p = p->next) + { + file = p->data; + + if (nautilus_file_is_directory (file)) + { + folder_count++; + + if (nautilus_file_get_directory_item_count (file, &file_item_count, NULL)) + folder_item_count += file_item_count; + else + folder_item_count_known = FALSE; + } + else + { + non_folder_count++; + + if (!nautilus_file_can_get_size (file)) + { + non_folder_size_known = TRUE; + non_folder_size += nautilus_file_get_size (file); + } + } + + if (first_item_name == NULL) + first_item_name = nautilus_file_get_display_name (file); + } + + nautilus_file_list_free (selection); + + /* + * Break out cases for localization's sake. But note that there are still pieces + * being assembled in a particular order, which may be a problem for some localizers. + */ + if (folder_count != 0) + { + if (folder_count == 1 && non_folder_count == 0) + { + folder_count_str = g_strdup_printf (_("ā%sā selected"), first_item_name); + } + else + { + folder_count_str = g_strdup_printf (ngettext("%'d folder selected", + "%'d folders selected", + folder_count), + folder_count); + } + + if (folder_count == 1) + { + if (!folder_item_count_known) + folder_item_count_str = g_strdup (""); + else + folder_item_count_str = g_strdup_printf (ngettext("(containing %'d item)", "(containing %'d items)", folder_item_count), + folder_item_count); + } + else + { + if (!folder_item_count_known) + { + folder_item_count_str = g_strdup (""); + } + else + { + /* translators: this is preceded with a string of form 'N folders' (N more than 1) */ + folder_item_count_str = g_strdup_printf (ngettext("(containing a total of %'d item)", + "(containing a total of %'d items)", + folder_item_count), + folder_item_count); + } + } + } + + if (non_folder_count != 0) + { + if (folder_count == 0) + { + if (non_folder_count == 1) { + non_folder_count_str = g_strdup_printf (_("ā%sā selected"), first_item_name); + } else { + non_folder_count_str = g_strdup_printf (ngettext("%'d item selected", + "%'d items selected", + non_folder_count), + non_folder_count); + } + } + else + { + /* Folders selected also, use "other" terminology */ + non_folder_count_str = g_strdup_printf (ngettext("%'d other item selected", + "%'d other items selected", + non_folder_count), + non_folder_count); + } + + if (non_folder_size_known) + { + char *size_string; + + size_string = g_format_size (non_folder_size); + /* This is marked for translation in case a localiser + * needs to use something other than parentheses. The + * the message in parentheses is the size of the selected items. + */ + non_folder_item_count_str = g_strdup_printf (_("(%s)"), size_string); + g_free (size_string); + } + else + { + non_folder_item_count_str = g_strdup (""); + } + } + + if (folder_count == 0 && non_folder_count == 0) + { + primary_text = secondary_text = NULL; + } + else if (folder_count == 0) + { + primary_text = g_strdup_printf ("%s, %s", non_folder_count_str, non_folder_item_count_str); + secondary_text = NULL; + } + else if (non_folder_count == 0) + { + primary_text = g_strdup_printf ("%s %s", folder_count_str, folder_item_count_str); + secondary_text = NULL; + } + else + { + primary_text = g_strdup_printf ("%s %s", folder_count_str, folder_item_count_str); + secondary_text = g_strdup_printf ("%s, %s", non_folder_count_str, non_folder_item_count_str); + } + + gtk_label_set_label (GTK_LABEL (actionbar->primary_label), primary_text ? primary_text : ""); + gtk_label_set_label (GTK_LABEL (actionbar->secondary_label), secondary_text ? secondary_text : ""); + + g_free (first_item_name); + g_free (folder_count_str); + g_free (folder_item_count_str); + g_free (non_folder_count_str); + g_free (non_folder_item_count_str); + g_free (secondary_text); + g_free (primary_text); +} + +static void +setup_single_file_selection (NautilusActionBar *actionbar, + NautilusFile *file) +{ + gboolean is_directory; + gchar *description; + + description = NULL; + is_directory = nautilus_file_is_directory (file); + + /* Primary label is the file name */ + gtk_label_set_label (GTK_LABEL (actionbar->primary_label), nautilus_file_get_display_name (file)); + + /* + * If the selected item is a folder, display the number of + * children. Otherwise, display the file size. + */ + if (is_directory) + { + guint folder_children; + + if (nautilus_file_get_directory_item_count (file, &folder_children, NULL)) + { + description = g_strdup_printf (ngettext("Contains %'d item", "Contains %'d items", folder_children), + folder_children); + } + } + else + { + description = g_format_size (nautilus_file_get_size (file)); + } + + /* + * If there is no description available, we hide the second label so + * the filename is vertically centralized against the icon. + */ + gtk_widget_set_visible (actionbar->secondary_label, description != NULL); + gtk_label_set_label (GTK_LABEL (actionbar->secondary_label), description ? description : ""); + + g_clear_pointer (&description, g_free); + g_clear_pointer (&file, nautilus_file_unref); +} + +static gboolean +real_update_status (gpointer data) +{ + NautilusActionBar *actionbar = data; + + if (nautilus_view_is_loading (actionbar->view)) + { + gtk_label_set_label (GTK_LABEL (actionbar->loading_label), + nautilus_view_is_searching (actionbar->view) ? _("Searching") : _("Loading")); + + gtk_stack_set_visible_child_name (GTK_STACK (actionbar->stack), "loading"); + } + else + { + GList *selection; + gint number_of_files; + + selection = nautilus_view_get_selection (actionbar->view); + number_of_files = g_list_length (selection); + + if (number_of_files == 0) + { + gtk_label_set_label (GTK_LABEL (actionbar->primary_label), ""); + gtk_label_set_label (GTK_LABEL (actionbar->secondary_label), ""); + } + else if (number_of_files == 1) + { + setup_single_file_selection (actionbar, selection->data); + } + else + { + setup_multiple_files_selection (actionbar, selection); + } + + gtk_stack_set_visible_child_name (GTK_STACK (actionbar->stack), "main"); + } + + actionbar->update_status_timeout_id = 0; + + return G_SOURCE_REMOVE; +} + +static void +update_status (NautilusActionBar *actionbar) +{ + if (actionbar->update_status_timeout_id > 0) + { + g_source_remove (actionbar->update_status_timeout_id); + actionbar->update_status_timeout_id = 0; + } + + actionbar->update_status_timeout_id = g_timeout_add (UPDATE_STATUS_TIMEOUT, + real_update_status, + actionbar); +} + +static void +nautilus_action_bar_dispose (GObject *object) +{ + NautilusActionBar *self = NAUTILUS_ACTION_BAR (object); + + if (self->update_status_timeout_id > 0) + { + g_source_remove (self->update_status_timeout_id); + self->update_status_timeout_id = 0; + } + if (self->view != NULL) + { + g_signal_handlers_disconnect_by_data (self->view, self); + g_clear_object (&self->view); + } + + G_OBJECT_CLASS (nautilus_action_bar_parent_class)->dispose (object); +} + +static void +nautilus_action_bar_finalize (GObject *object) +{ + //g_signal_handlers_disconnect_by_func (nautilus_clipboard_monitor_get (), update_paste_button, self); + + + G_OBJECT_CLASS (nautilus_action_bar_parent_class)->finalize (object); +} + +static void +nautilus_action_bar_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NautilusActionBar *self = NAUTILUS_ACTION_BAR (object); + + switch (prop_id) + { + case PROP_VIEW: + g_value_set_object (value, self->view); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +nautilus_action_bar_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NautilusActionBar *self = NAUTILUS_ACTION_BAR (object); + + switch (prop_id) + { + case PROP_VIEW: + if (g_set_object (&self->view, g_value_get_object (value))) + { + g_signal_connect_swapped (self->view, "notify::selection", G_CALLBACK (update_status), self); + g_signal_connect_swapped (self->view, "notify::is-loading", G_CALLBACK (update_status), self); + g_signal_connect_swapped (self->view, "notify::is-searching", G_CALLBACK (update_status), self); + g_object_notify (object, "view"); + } + + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +nautilus_action_bar_class_init (NautilusActionBarClass *klass) +{ + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = nautilus_action_bar_finalize; + object_class->dispose = nautilus_action_bar_dispose; + object_class->get_property = nautilus_action_bar_get_property; + object_class->set_property = nautilus_action_bar_set_property; + + /** + * NautilusActionBar::view: + * + * The view related to this actionbar. + */ + g_object_class_install_property (object_class, + PROP_VIEW, + g_param_spec_object ("view", + "View of the actionbar", + "The view related to this actionbar", + NAUTILUS_TYPE_VIEW, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/nautilus/ui/nautilus-action-bar.ui"); + + gtk_widget_class_bind_template_child (widget_class, NautilusActionBar, loading_label); + gtk_widget_class_bind_template_child (widget_class, NautilusActionBar, paste_button); + gtk_widget_class_bind_template_child (widget_class, NautilusActionBar, primary_label); + gtk_widget_class_bind_template_child (widget_class, NautilusActionBar, secondary_label); + gtk_widget_class_bind_template_child (widget_class, NautilusActionBar, stack); + + gtk_widget_class_bind_template_callback (widget_class, open_preview_cb); + + gtk_widget_class_set_css_name (widget_class, "actionbar"); +} + +static void +nautilus_action_bar_init (NautilusActionBar *self) +{ + gtk_widget_init_template (GTK_WIDGET (self)); + +#if 0 + update_paste_button (self); + + g_signal_connect_swapped (nautilus_clipboard_monitor_get (), "clipboard-changed", + G_CALLBACK (update_paste_button), self); +#endif +} + +/** + * nautilus_action_bar_new: + * @view: a #NautilusView + * + * Creates a new actionbar related to @view. + * + * Returns: (transfer full): a #NautilusActionBar + */ +GtkWidget* +nautilus_action_bar_new (NautilusView *view) +{ + return g_object_new (NAUTILUS_TYPE_ACTION_BAR, + "view", view, + NULL); +} |