/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ /* * Nautilus * * Copyright (C) 2011, Red Hat, Inc. * * Nautilus 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 2 of the * License, or (at your option) any later version. * * Nautilus 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 . * * Author: Cosimo Cecchi * */ #include #include "nautilus-toolbar.h" #include "nautilus-location-entry.h" #include "nautilus-pathbar.h" #include "nautilus-window.h" #include "nautilus-progress-info-widget.h" #include #include #include #include #include #include #define OPERATION_MINIMUM_TIME 5 //s #define REMOVE_FINISHED_OPERATIONS_TIEMOUT 2 //s typedef enum { NAUTILUS_NAVIGATION_DIRECTION_NONE, NAUTILUS_NAVIGATION_DIRECTION_BACK, NAUTILUS_NAVIGATION_DIRECTION_FORWARD } NautilusNavigationDirection; struct _NautilusToolbarPrivate { NautilusWindow *window; GtkWidget *path_bar_container; GtkWidget *location_entry_container; GtkWidget *path_bar; GtkWidget *location_entry; gboolean show_location_entry; guint popup_timeout_id; guint start_operations_timeout_id; guint remove_finished_operations_timeout_id; guint operations_button_attention_timeout_id; GtkWidget *operations_button; GtkWidget *view_button; GtkWidget *action_button; GtkWidget *operations_popover; GtkWidget *operations_container; GtkWidget *operations_revealer; GtkWidget *operations_icon; GtkWidget *view_menu_widget; GtkWidget *sort_menu; GtkWidget *sort_trash_time; GtkWidget *sort_search_relevance; GtkWidget *visible_columns; GtkWidget *stop; GtkWidget *reload; GtkAdjustment *zoom_adjustment; GtkWidget *zoom_level_scale; GMenu *action_menu; GtkWidget *forward_button; GtkWidget *back_button; NautilusProgressInfoManager *progress_manager; }; enum { PROP_WINDOW = 1, PROP_SHOW_LOCATION_ENTRY, NUM_PROPERTIES }; static GParamSpec *properties[NUM_PROPERTIES] = { NULL, }; G_DEFINE_TYPE_WITH_PRIVATE(NautilusToolbar, nautilus_toolbar, GTK_TYPE_HEADER_BAR); static void unschedule_menu_popup_timeout (NautilusToolbar *self); static void update_operations (NautilusToolbar *self); static void toolbar_update_appearance (NautilusToolbar *self) { gboolean show_location_entry; show_location_entry = self->priv->show_location_entry || g_settings_get_boolean (nautilus_preferences, NAUTILUS_PREFERENCES_ALWAYS_USE_LOCATION_ENTRY); gtk_widget_set_visible (self->priv->location_entry, show_location_entry); gtk_widget_set_visible (self->priv->path_bar, !show_location_entry); } static void activate_back_or_forward_menu_item (GtkMenuItem *menu_item, NautilusWindow *window, gboolean back) { int index; g_assert (GTK_IS_MENU_ITEM (menu_item)); index = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (menu_item), "user_data")); nautilus_window_back_or_forward (window, back, index, nautilus_event_get_window_open_flags ()); } static void activate_back_menu_item_callback (GtkMenuItem *menu_item, NautilusWindow *window) { activate_back_or_forward_menu_item (menu_item, window, TRUE); } static void activate_forward_menu_item_callback (GtkMenuItem *menu_item, NautilusWindow *window) { activate_back_or_forward_menu_item (menu_item, window, FALSE); } static void fill_menu (NautilusWindow *window, GtkWidget *menu, gboolean back) { NautilusWindowSlot *slot; GtkWidget *menu_item; int index; GList *list; slot = nautilus_window_get_active_slot (window); list = back ? nautilus_window_slot_get_back_history (slot) : nautilus_window_slot_get_forward_history (slot); index = 0; while (list != NULL) { menu_item = nautilus_bookmark_menu_item_new (NAUTILUS_BOOKMARK (list->data)); g_object_set_data (G_OBJECT (menu_item), "user_data", GINT_TO_POINTER (index)); gtk_widget_show (GTK_WIDGET (menu_item)); g_signal_connect_object (menu_item, "activate", back ? G_CALLBACK (activate_back_menu_item_callback) : G_CALLBACK (activate_forward_menu_item_callback), window, 0); gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item); list = g_list_next (list); ++index; } } /* adapted from gtk/gtkmenubutton.c */ static void menu_position_func (GtkMenu *menu, gint *x, gint *y, gboolean *push_in, GtkWidget *widget) { GtkWidget *toplevel; GtkRequisition menu_req; GdkRectangle monitor; gint monitor_num; GdkScreen *screen; GdkWindow *window; GtkAllocation allocation; /* Set the dropdown menu hint on the toplevel, so the WM can omit the top side * of the shadows. */ toplevel = gtk_widget_get_toplevel (GTK_WIDGET (menu)); gtk_window_set_type_hint (GTK_WINDOW (toplevel), GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU); window = gtk_widget_get_window (widget); screen = gtk_widget_get_screen (GTK_WIDGET (menu)); monitor_num = gdk_screen_get_monitor_at_window (screen, window); if (monitor_num < 0) { monitor_num = 0; } gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor); gtk_widget_get_preferred_size (GTK_WIDGET (menu), &menu_req, NULL); gtk_widget_get_allocation (widget, &allocation); gdk_window_get_origin (window, x, y); *x += allocation.x; *y += allocation.y; if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) { *x -= MAX (menu_req.width - allocation.width, 0); } else { *x += MAX (allocation.width - menu_req.width, 0); } if ((*y + allocation.height + menu_req.height) <= monitor.y + monitor.height) { *y += allocation.height; } else if ((*y - menu_req.height) >= monitor.y) { *y -= menu_req.height; } else if (monitor.y + monitor.height - (*y + allocation.height) > *y) { *y += allocation.height; } else { *y -= menu_req.height; } *push_in = FALSE; } static void show_menu (NautilusToolbar *self, GtkWidget *widget, guint button, guint32 event_time) { NautilusWindow *window; GtkWidget *menu; NautilusNavigationDirection direction; window = self->priv->window; menu = gtk_menu_new (); direction = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (widget), "nav-direction")); switch (direction) { case NAUTILUS_NAVIGATION_DIRECTION_FORWARD: fill_menu (window, menu, FALSE); break; case NAUTILUS_NAVIGATION_DIRECTION_BACK: fill_menu (window, menu, TRUE); break; default: g_assert_not_reached (); break; } gtk_menu_popup (GTK_MENU (menu), NULL, NULL, (GtkMenuPositionFunc) menu_position_func, widget, button, event_time); } static void action_view_mode_state_changed (GActionGroup *action_group, gchar *action_name, GVariant *value, gpointer user_data) { NautilusToolbar *self = user_data; const gchar *view_mode = g_variant_get_string (value, NULL); const gchar *name; GtkWidget *image; if (g_strcmp0 (view_mode, "list") == 0) { name = "view-list-symbolic"; } else if (g_strcmp0 (view_mode, "grid") == 0) { name = "view-grid-symbolic"; } else { g_assert_not_reached (); } image = gtk_image_new (); gtk_button_set_image (GTK_BUTTON (self->priv->view_button), image); gtk_image_set_from_icon_name (GTK_IMAGE (image), name, GTK_ICON_SIZE_MENU); } static void action_reload_enabled_changed (GActionGroup *action_group, gchar *action_name, gboolean enabled, gpointer user_data) { NautilusToolbar *self = user_data; gtk_widget_set_visible (self->priv->reload, enabled); } static void action_stop_enabled_changed (GActionGroup *action_group, gchar *action_name, gboolean enabled, gpointer user_data) { NautilusToolbar *self = user_data; gtk_widget_set_visible (self->priv->stop, enabled); } static void nautilus_toolbar_set_window (NautilusToolbar *self, NautilusWindow *window) { self->priv->window = window; g_signal_connect (self->priv->window, "action-enabled-changed::stop", G_CALLBACK (action_stop_enabled_changed), self); g_signal_connect (self->priv->window, "action-enabled-changed::reload", G_CALLBACK (action_reload_enabled_changed), self); g_signal_connect (self->priv->window, "action-state-changed::view-mode", G_CALLBACK (action_view_mode_state_changed), self); } #define MENU_POPUP_TIMEOUT 1200 typedef struct { NautilusToolbar *self; GtkWidget *widget; } ScheduleMenuData; static void schedule_menu_data_free (ScheduleMenuData *data) { g_slice_free (ScheduleMenuData, data); } static gboolean popup_menu_timeout_cb (gpointer user_data) { ScheduleMenuData *data = user_data; show_menu (data->self, data->widget, 1, gtk_get_current_event_time ()); return FALSE; } static void unschedule_menu_popup_timeout (NautilusToolbar *self) { if (self->priv->popup_timeout_id != 0) { g_source_remove (self->priv->popup_timeout_id); self->priv->popup_timeout_id = 0; } } static void schedule_menu_popup_timeout (NautilusToolbar *self, GtkWidget *widget) { ScheduleMenuData *data; /* unschedule any previous timeouts */ unschedule_menu_popup_timeout (self); data = g_slice_new0 (ScheduleMenuData); data->self = self; data->widget = widget; self->priv->popup_timeout_id = g_timeout_add_full (G_PRIORITY_DEFAULT, MENU_POPUP_TIMEOUT, popup_menu_timeout_cb, data, (GDestroyNotify) schedule_menu_data_free); } static gboolean navigation_button_press_cb (GtkButton *button, GdkEventButton *event, gpointer user_data) { NautilusToolbar *self = user_data; if (event->button == 3) { /* right click */ show_menu (self, GTK_WIDGET (button), event->button, event->time); return TRUE; } if (event->button == 1) { schedule_menu_popup_timeout (self, GTK_WIDGET (button)); } return FALSE; } static gboolean navigation_button_release_cb (GtkButton *button, GdkEventButton *event, gpointer user_data) { NautilusToolbar *self = user_data; unschedule_menu_popup_timeout (self); return FALSE; } static void zoom_level_changed (GtkRange *range, NautilusToolbar *self) { NautilusWindowSlot *slot; NautilusView *view; gdouble zoom_level; zoom_level = gtk_range_get_value (range); slot = nautilus_window_get_active_slot (self->priv->window); view = nautilus_window_slot_get_current_view (slot); g_action_group_change_action_state (nautilus_view_get_action_group (view), "zoom-to-level", g_variant_new_int32 ((gint) zoom_level)); } static void view_menu_popover_closed (GtkPopover *popover, NautilusToolbar *self) { NautilusWindowSlot *slot; NautilusView *view; slot = nautilus_window_get_active_slot (self->priv->window); view = nautilus_window_slot_get_current_view (slot); nautilus_view_grab_focus (view); } static gboolean should_hide_operations_button (NautilusToolbar *self) { GList *progress_infos; GList *l; progress_infos = nautilus_progress_info_manager_get_all_infos (self->priv->progress_manager); for (l = progress_infos; l != NULL; l = l->next) { if (nautilus_progress_info_get_elapsed_time (l->data) + nautilus_progress_info_get_remaining_time (l->data) > OPERATION_MINIMUM_TIME && !nautilus_progress_info_get_is_cancelled (l->data) && !nautilus_progress_info_get_is_finished (l->data)) { return FALSE; } } return TRUE; } static gboolean remove_finished_operations (NautilusToolbar *self) { nautilus_progress_info_manager_remove_finished_or_cancelled_infos (self->priv->progress_manager); if (should_hide_operations_button (self)) { gtk_revealer_set_reveal_child (GTK_REVEALER (self->priv->operations_revealer), FALSE); } else { update_operations (self); } self->priv->remove_finished_operations_timeout_id = 0; return G_SOURCE_REMOVE; } static void unschedule_remove_finished_operations (NautilusToolbar *self) { if (self->priv->remove_finished_operations_timeout_id != 0) { g_source_remove (self->priv->remove_finished_operations_timeout_id); self->priv->remove_finished_operations_timeout_id = 0; } } static void schedule_remove_finished_operations (NautilusToolbar *self) { if (self->priv->remove_finished_operations_timeout_id == 0) { self->priv->remove_finished_operations_timeout_id = g_timeout_add_seconds (REMOVE_FINISHED_OPERATIONS_TIEMOUT, (GSourceFunc) remove_finished_operations, self); } } static gboolean remove_operations_button_attention_style (NautilusToolbar *self) { GtkStyleContext *style_context; style_context = gtk_widget_get_style_context (self->priv->operations_button); gtk_style_context_remove_class (style_context, "suggested-action"); self->priv->operations_button_attention_timeout_id = 0; return G_SOURCE_REMOVE; } static void on_progress_info_cancelled (NautilusToolbar *self) { /* Update the pie chart progress */ gtk_widget_queue_draw (self->priv->operations_icon); if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->priv->operations_button))) { schedule_remove_finished_operations (self); } } static void on_progress_info_progress_changed (NautilusToolbar *self) { /* Update the pie chart progress */ gtk_widget_queue_draw (self->priv->operations_icon); } static void on_progress_info_finished (NautilusToolbar *self, NautilusProgressInfo *info) { GtkStyleContext *style_context; gchar *main_label; GFile *folder_to_open; /* Update the pie chart progress */ gtk_widget_queue_draw (self->priv->operations_icon); if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->priv->operations_button))) { schedule_remove_finished_operations (self); } folder_to_open = nautilus_progress_info_get_destination (info); /* If destination is null, don't show a notification. This happens when the * operation is a trash operation, which we already show a diferent kind of * notification */ if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->priv->operations_button)) && folder_to_open != NULL) { style_context = gtk_widget_get_style_context (self->priv->operations_button); gtk_style_context_add_class (style_context, "suggested-action"); self->priv->operations_button_attention_timeout_id = g_timeout_add_seconds (1, (GSourceFunc) remove_operations_button_attention_style, self); main_label = nautilus_progress_info_get_status (info); nautilus_window_show_operation_notification (self->priv->window, main_label, folder_to_open); g_free (main_label); } g_clear_object (&folder_to_open); } static void disconnect_progress_infos (NautilusToolbar *self) { GList *progress_infos; GList *l; progress_infos = nautilus_progress_info_manager_get_all_infos (self->priv->progress_manager); for (l = progress_infos; l != NULL; l = l->next) { g_signal_handlers_disconnect_by_data (l->data, self); } } static void update_operations (NautilusToolbar *self) { GList *progress_infos; GList *l; GtkWidget *progress; guint total_remaining_time = 0; gtk_container_foreach (GTK_CONTAINER (self->priv->operations_container), (GtkCallback) gtk_widget_destroy, NULL); disconnect_progress_infos (self); progress_infos = nautilus_progress_info_manager_get_all_infos (self->priv->progress_manager); for (l = progress_infos; l != NULL; l = l->next) { if (nautilus_progress_info_get_elapsed_time (l->data) + nautilus_progress_info_get_remaining_time (l->data) > OPERATION_MINIMUM_TIME) { total_remaining_time = nautilus_progress_info_get_remaining_time (l->data); g_signal_connect_swapped (l->data, "finished", G_CALLBACK (on_progress_info_finished), self); g_signal_connect_swapped (l->data, "cancelled", G_CALLBACK (on_progress_info_cancelled), self); g_signal_connect_swapped (l->data, "progress-changed", G_CALLBACK (on_progress_info_progress_changed), self); progress = nautilus_progress_info_widget_new (l->data); gtk_box_pack_start (GTK_BOX (self->priv->operations_container), progress, FALSE, FALSE, 0); } } /* Either we are already showing the button, so keep showing it until the user * toggle it to hide the operations popover, or, if we want now to show it, * we have to have at least one operation that its total stimated time * is longer than OPERATION_MINIMUM_TIME seconds, or if we failed to get * a correct stimated time and it's around OPERATION_MINIMUM_TIME, * showing the button for just for a moment because now we realized the * estimated time is longer than a OPERATION_MINIMUM_TIME is odd, so show * it only if the remaining time is bigger than again OPERATION_MINIMUM_TIME. */ if (total_remaining_time > OPERATION_MINIMUM_TIME || gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->priv->operations_button))) { gtk_revealer_set_reveal_child (GTK_REVEALER (self->priv->operations_revealer), TRUE); gtk_widget_queue_draw (self->priv->operations_icon); } } static gboolean on_progress_info_started_timeout (NautilusToolbar *self) { update_operations (self); /* In case we didn't show the operations button because the operation total * time stimation is not good enough, update again to make sure we don't miss * a long time operation because of that */ if (!nautilus_progress_manager_are_all_infos_finished_or_cancelled (self->priv->progress_manager)) { return G_SOURCE_CONTINUE; } else { self->priv->start_operations_timeout_id = 0; return G_SOURCE_REMOVE; } } static void schedule_operations_start (NautilusToolbar *self) { if (self->priv->start_operations_timeout_id == 0) { /* Timeout is a little more than what we require for a stimated operation * total time, to make sure the stimated total time is correct */ self->priv->start_operations_timeout_id = g_timeout_add (SECONDS_NEEDED_FOR_APROXIMATE_TRANSFER_RATE * 1000 + 500, (GSourceFunc) on_progress_info_started_timeout, self); } } static void unschedule_operations_start (NautilusToolbar *self) { if (self->priv->start_operations_timeout_id != 0) { g_source_remove (self->priv->start_operations_timeout_id); self->priv->start_operations_timeout_id = 0; } } static void on_progress_info_started (NautilusProgressInfo *info, NautilusToolbar *self) { g_signal_handlers_disconnect_by_data (info, self); schedule_operations_start (self); } static void on_new_progress_info (NautilusProgressInfoManager *manager, NautilusProgressInfo *info, NautilusToolbar *self) { g_signal_connect (info, "started", G_CALLBACK (on_progress_info_started), self); } static void on_operations_icon_draw (GtkWidget *widget, cairo_t *cr, NautilusToolbar *self) { gfloat elapsed_progress = 0; gint remaining_progress = 0; gint total_progress; gdouble ratio; GList *progress_infos; GList *l; guint width; guint height; gboolean all_cancelled; GdkRGBA background = {.red = 0, .green = 0, .blue = 0, .alpha = 0.2 }; GdkRGBA foreground = {.red = 0, .green = 0, .blue = 0, .alpha = 0.7 }; all_cancelled = TRUE; progress_infos = nautilus_progress_info_manager_get_all_infos (self->priv->progress_manager); for (l = progress_infos; l != NULL; l = l->next) { if (!nautilus_progress_info_get_is_cancelled (l->data)) { all_cancelled = FALSE; remaining_progress += nautilus_progress_info_get_remaining_time (l->data); elapsed_progress += nautilus_progress_info_get_elapsed_time (l->data); } } total_progress = remaining_progress + elapsed_progress; if (all_cancelled) { ratio = 1.0; } else { if (total_progress > 0) { ratio = MAX (0.05, elapsed_progress / total_progress); } else { ratio = 0.05; } } width = gtk_widget_get_allocated_width (widget); height = gtk_widget_get_allocated_height (widget); gdk_cairo_set_source_rgba(cr, &background); cairo_arc (cr, width / 2.0, height / 2.0, MIN (width, height) / 2.0, 0, 2 *G_PI); cairo_fill (cr); cairo_move_to (cr, width / 2.0, height / 2.0); gdk_cairo_set_source_rgba (cr, &foreground); cairo_arc (cr, width / 2.0, height / 2.0, MIN (width, height) / 2.0, -G_PI / 2.0, ratio * 2 * G_PI - G_PI / 2.0); cairo_fill (cr); } static void on_operations_button_toggled (NautilusToolbar *self) { unschedule_remove_finished_operations (self); if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (self->priv->operations_button))) { schedule_remove_finished_operations (self); } else { update_operations (self); } } static void nautilus_toolbar_init (NautilusToolbar *self) { GtkBuilder *builder; self->priv = nautilus_toolbar_get_instance_private (self); gtk_widget_init_template (GTK_WIDGET (self)); self->priv->path_bar = g_object_new (NAUTILUS_TYPE_PATH_BAR, NULL); gtk_container_add (GTK_CONTAINER (self->priv->path_bar_container), self->priv->path_bar); self->priv->location_entry = nautilus_location_entry_new (); gtk_container_add (GTK_CONTAINER (self->priv->location_entry_container), self->priv->location_entry); builder = gtk_builder_new_from_resource ("/org/gnome/nautilus/nautilus-toolbar-view-menu.xml"); self->priv->view_menu_widget = GTK_WIDGET (gtk_builder_get_object (builder, "view_menu_widget")); self->priv->zoom_level_scale = GTK_WIDGET (gtk_builder_get_object (builder, "zoom_level_scale")); self->priv->zoom_adjustment = GTK_ADJUSTMENT (gtk_builder_get_object (builder, "zoom_adjustment")); self->priv->sort_menu = GTK_WIDGET (gtk_builder_get_object (builder, "sort_menu")); self->priv->sort_trash_time = GTK_WIDGET (gtk_builder_get_object (builder, "sort_trash_time")); self->priv->sort_search_relevance = GTK_WIDGET (gtk_builder_get_object (builder, "sort_search_relevance")); self->priv->visible_columns = GTK_WIDGET (gtk_builder_get_object (builder, "visible_columns")); self->priv->reload = GTK_WIDGET (gtk_builder_get_object (builder, "reload")); self->priv->stop = GTK_WIDGET (gtk_builder_get_object (builder, "stop")); g_signal_connect (self->priv->view_menu_widget, "closed", G_CALLBACK (view_menu_popover_closed), self); gtk_menu_button_set_popover (GTK_MENU_BUTTON (self->priv->view_button), self->priv->view_menu_widget); g_object_unref (builder); builder = gtk_builder_new_from_resource ("/org/gnome/nautilus/nautilus-toolbar-action-menu.xml"); self->priv->action_menu = G_MENU (gtk_builder_get_object (builder, "action-menu")); gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (self->priv->action_button), G_MENU_MODEL (self->priv->action_menu)); g_object_unref (builder); self->priv->progress_manager = nautilus_progress_info_manager_dup_singleton (); g_signal_connect (self->priv->progress_manager, "new-progress-info", G_CALLBACK (on_new_progress_info), self); update_operations (self); g_object_set_data (G_OBJECT (self->priv->back_button), "nav-direction", GUINT_TO_POINTER (NAUTILUS_NAVIGATION_DIRECTION_BACK)); g_object_set_data (G_OBJECT (self->priv->forward_button), "nav-direction", GUINT_TO_POINTER (NAUTILUS_NAVIGATION_DIRECTION_FORWARD)); g_signal_connect (self->priv->back_button, "button-press-event", G_CALLBACK (navigation_button_press_cb), self); g_signal_connect (self->priv->back_button, "button-release-event", G_CALLBACK (navigation_button_release_cb), self); g_signal_connect (self->priv->forward_button, "button-press-event", G_CALLBACK (navigation_button_press_cb), self); g_signal_connect (self->priv->forward_button, "button-release-event", G_CALLBACK (navigation_button_release_cb), self); g_signal_connect (self->priv->zoom_level_scale, "value-changed", G_CALLBACK (zoom_level_changed), self); gtk_widget_show_all (GTK_WIDGET (self)); toolbar_update_appearance (self); } static void nautilus_toolbar_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { NautilusToolbar *self = NAUTILUS_TOOLBAR (object); switch (property_id) { case PROP_SHOW_LOCATION_ENTRY: g_value_set_boolean (value, self->priv->show_location_entry); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void nautilus_toolbar_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { NautilusToolbar *self = NAUTILUS_TOOLBAR (object); switch (property_id) { case PROP_WINDOW: nautilus_toolbar_set_window (self, g_value_get_object (value)); break; case PROP_SHOW_LOCATION_ENTRY: nautilus_toolbar_set_show_location_entry (self, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void nautilus_toolbar_dispose (GObject *obj) { NautilusToolbar *self = NAUTILUS_TOOLBAR (obj); g_signal_handlers_disconnect_by_func (nautilus_preferences, toolbar_update_appearance, self); disconnect_progress_infos (self); unschedule_menu_popup_timeout (self); unschedule_remove_finished_operations (self); unschedule_operations_start (self); if (self->priv->operations_button_attention_timeout_id != 0) { g_source_remove (self->priv->operations_button_attention_timeout_id); self->priv->operations_button_attention_timeout_id = 0; } g_signal_handlers_disconnect_by_data (self->priv->progress_manager, self); g_clear_object (&self->priv->progress_manager); G_OBJECT_CLASS (nautilus_toolbar_parent_class)->dispose (obj); } static void nautilus_toolbar_class_init (NautilusToolbarClass *klass) { GObjectClass *oclass; GtkWidgetClass *widget_class; widget_class = GTK_WIDGET_CLASS (klass); oclass = G_OBJECT_CLASS (klass); oclass->get_property = nautilus_toolbar_get_property; oclass->set_property = nautilus_toolbar_set_property; oclass->dispose = nautilus_toolbar_dispose; properties[PROP_WINDOW] = g_param_spec_object ("window", "The NautilusWindow", "The NautilusWindow this toolbar is part of", NAUTILUS_TYPE_WINDOW, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS); properties[PROP_SHOW_LOCATION_ENTRY] = g_param_spec_boolean ("show-location-entry", "Whether to show the location entry", "Whether to show the location entry instead of the pathbar", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (oclass, NUM_PROPERTIES, properties); gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/nautilus/nautilus-toolbar-ui.xml"); gtk_widget_class_bind_template_child_private (widget_class, NautilusToolbar, operations_button); gtk_widget_class_bind_template_child_private (widget_class, NautilusToolbar, operations_icon); gtk_widget_class_bind_template_child_private (widget_class, NautilusToolbar, operations_container); gtk_widget_class_bind_template_child_private (widget_class, NautilusToolbar, operations_revealer); gtk_widget_class_bind_template_child_private (widget_class, NautilusToolbar, view_button); gtk_widget_class_bind_template_child_private (widget_class, NautilusToolbar, action_button); gtk_widget_class_bind_template_child_private (widget_class, NautilusToolbar, path_bar_container); gtk_widget_class_bind_template_child_private (widget_class, NautilusToolbar, location_entry_container); gtk_widget_class_bind_template_child_private (widget_class, NautilusToolbar, back_button); gtk_widget_class_bind_template_child_private (widget_class, NautilusToolbar, forward_button); gtk_widget_class_bind_template_callback (widget_class, on_operations_icon_draw); gtk_widget_class_bind_template_callback (widget_class, on_operations_button_toggled); } void nautilus_toolbar_reset_menus (NautilusToolbar *self) { NautilusWindowSlot *slot; NautilusView *view; GActionGroup *view_action_group; GVariant *variant; GVariantIter iter; gboolean show_sort_trash, show_sort_search, show_sort_access, show_sort_modification, enable_sort; const gchar *hint; /* Allow actions from the current view to be activated through * the view menu and action menu of the toolbar */ slot = nautilus_window_get_active_slot (self->priv->window); view = nautilus_window_slot_get_current_view (slot); view_action_group = nautilus_view_get_action_group (view); gtk_widget_insert_action_group (GTK_WIDGET (self), "view", G_ACTION_GROUP (view_action_group)); gtk_widget_set_visible (self->priv->visible_columns, g_action_group_has_action (view_action_group, "visible-columns")); enable_sort = g_action_group_get_action_enabled (view_action_group, "sort"); show_sort_trash = show_sort_search = show_sort_modification = show_sort_access = FALSE; gtk_widget_set_visible (self->priv->sort_menu, enable_sort); if (enable_sort) { variant = g_action_group_get_action_state_hint (view_action_group, "sort"); g_variant_iter_init (&iter, variant); while (g_variant_iter_next (&iter, "&s", &hint)) { if (g_strcmp0 (hint, "trash-time") == 0) show_sort_trash = TRUE; if (g_strcmp0 (hint, "search-relevance") == 0) show_sort_search = TRUE; } g_variant_unref (variant); } gtk_widget_set_visible (self->priv->sort_trash_time, show_sort_trash); gtk_widget_set_visible (self->priv->sort_search_relevance, show_sort_search); variant = g_action_group_get_action_state (view_action_group, "zoom-to-level"); gtk_adjustment_set_value (self->priv->zoom_adjustment, g_variant_get_int32 (variant)); g_variant_unref (variant); } GtkWidget * nautilus_toolbar_new () { return g_object_new (NAUTILUS_TYPE_TOOLBAR, "show-close-button", TRUE, "custom-title", gtk_label_new (NULL), "valign", GTK_ALIGN_CENTER, NULL); } GMenu * nautilus_toolbar_get_action_menu (NautilusToolbar *self) { return self->priv->action_menu; } GtkWidget * nautilus_toolbar_get_path_bar (NautilusToolbar *self) { return self->priv->path_bar; } GtkWidget * nautilus_toolbar_get_location_entry (NautilusToolbar *self) { return self->priv->location_entry; } void nautilus_toolbar_set_show_location_entry (NautilusToolbar *self, gboolean show_location_entry) { if (show_location_entry != self->priv->show_location_entry) { self->priv->show_location_entry = show_location_entry; toolbar_update_appearance (self); g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SHOW_LOCATION_ENTRY]); } }