From 2774b8552dcc89ae744700af5832dbf76c138a9e Mon Sep 17 00:00:00 2001 From: Neil Herald Date: Sun, 17 Apr 2016 09:08:29 +0100 Subject: toolbar: fix ops button so it gets removed when multiple windows open In some cases, the operations button doesn't get removed from every Nautilus window. And if clicked, an empty popover will appear. One case is when the user starts a long operation, and then closes the popovers in all windows once the operations have completed (and before the buttons are due to be removed). All windows get notified that the operations have finished. But if there's a popover open in any window at that point, the windows don't schedule removal of the button - as the logic is to keep the buttons visible while there are popovers open. When the user then closes the popover in the last window, that window knows there are no popovers in the other windows, so it removes the button from it's toolbar. But there's nothing to notify the other windows to remove their buttons. The fix is to implement a more robust solution; instead of the windows checking the other windows for popovers (windows shouldn't know about the other windows anyway), the progress info manager maintains a list of viewers. When an popover is open or closed, the window tells the manager to update it's list of viewers. When there are no entries in the list, the info manager notifies all listeners (the windows), so they all know when to schedule removal of their buttons. https://bugzilla.gnome.org/show_bug.cgi?id=765019 --- .../nautilus-progress-info-manager.c | 62 ++++++++++++++++++++++ .../nautilus-progress-info-manager.h | 4 ++ src/nautilus-toolbar.c | 61 ++++++++++----------- 3 files changed, 95 insertions(+), 32 deletions(-) diff --git a/libnautilus-private/nautilus-progress-info-manager.c b/libnautilus-private/nautilus-progress-info-manager.c index 5dc26d1b0..29046c933 100644 --- a/libnautilus-private/nautilus-progress-info-manager.c +++ b/libnautilus-private/nautilus-progress-info-manager.c @@ -26,10 +26,12 @@ struct _NautilusProgressInfoManagerPriv { GList *progress_infos; + GList *current_viewers; }; enum { NEW_PROGRESS_INFO, + HAS_VIEWERS_CHANGED, LAST_SIGNAL }; @@ -40,15 +42,23 @@ static guint signals[LAST_SIGNAL] = { 0, }; G_DEFINE_TYPE (NautilusProgressInfoManager, nautilus_progress_info_manager, G_TYPE_OBJECT); +static void remove_viewer (NautilusProgressInfoManager *self, GObject *viewer); + static void nautilus_progress_info_manager_finalize (GObject *obj) { + GList *l; NautilusProgressInfoManager *self = NAUTILUS_PROGRESS_INFO_MANAGER (obj); if (self->priv->progress_infos != NULL) { g_list_free_full (self->priv->progress_infos, g_object_unref); } + for (l = self->priv->current_viewers; l != NULL; l = l->next) { + g_object_weak_unref (l->data, (GWeakNotify) remove_viewer, self); + } + g_list_free (self->priv->current_viewers); + G_OBJECT_CLASS (nautilus_progress_info_manager_parent_class)->finalize (obj); } @@ -98,6 +108,15 @@ nautilus_progress_info_manager_class_init (NautilusProgressInfoManagerClass *kla 1, NAUTILUS_TYPE_PROGRESS_INFO); + signals[HAS_VIEWERS_CHANGED] = + g_signal_new ("has-viewers-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); + g_type_class_add_private (klass, sizeof (NautilusProgressInfoManagerPriv)); } @@ -160,3 +179,46 @@ nautilus_progress_manager_are_all_infos_finished_or_cancelled (NautilusProgressI return TRUE; } + +static void +remove_viewer (NautilusProgressInfoManager *self, + GObject *viewer) +{ + self->priv->current_viewers = g_list_remove (self->priv->current_viewers, viewer); + + if (self->priv->current_viewers == NULL) + g_signal_emit (self, signals[HAS_VIEWERS_CHANGED], 0); +} + +void +nautilus_progress_manager_add_viewer (NautilusProgressInfoManager *self, + GObject *viewer) +{ + GList *viewers; + + viewers = self->priv->current_viewers; + if (g_list_find (viewers, viewer) == NULL) { + g_object_weak_ref (viewer, (GWeakNotify) remove_viewer, self); + viewers = g_list_append (viewers, viewer); + self->priv->current_viewers = viewers; + + if (g_list_length (viewers) == 1) + g_signal_emit (self, signals[HAS_VIEWERS_CHANGED], 0); + } +} + +void +nautilus_progress_manager_remove_viewer (NautilusProgressInfoManager *self, + GObject *viewer) +{ + if (g_list_find (self->priv->current_viewers, viewer) != NULL) { + g_object_weak_unref (viewer, (GWeakNotify) remove_viewer, self); + remove_viewer (self, viewer); + } +} + +gboolean +nautilus_progress_manager_has_viewers (NautilusProgressInfoManager *self) +{ + return self->priv->current_viewers != NULL; +} diff --git a/libnautilus-private/nautilus-progress-info-manager.h b/libnautilus-private/nautilus-progress-info-manager.h index ef91d3d95..54d2ce676 100644 --- a/libnautilus-private/nautilus-progress-info-manager.h +++ b/libnautilus-private/nautilus-progress-info-manager.h @@ -64,6 +64,10 @@ GList *nautilus_progress_info_manager_get_all_infos (NautilusProgressInfoManager void nautilus_progress_info_manager_remove_finished_or_cancelled_infos (NautilusProgressInfoManager *self); gboolean nautilus_progress_manager_are_all_infos_finished_or_cancelled (NautilusProgressInfoManager *self); +void nautilus_progress_manager_add_viewer (NautilusProgressInfoManager *self, GObject *viewer); +void nautilus_progress_manager_remove_viewer (NautilusProgressInfoManager *self, GObject *viewer); +gboolean nautilus_progress_manager_has_viewers (NautilusProgressInfoManager *self); + G_END_DECLS #endif /* __NAUTILUS_PROGRESS_INFO_MANAGER_H__ */ diff --git a/src/nautilus-toolbar.c b/src/nautilus-toolbar.c index d36b56c6a..3c1255888 100644 --- a/src/nautilus-toolbar.c +++ b/src/nautilus-toolbar.c @@ -473,38 +473,13 @@ add_operations_button_attention_style (NautilusToolbar *self) self); } -/* It's not the most beautiful solution, but we need to check wheter all windows - * have it's button inactive, so the toolbar can schedule to remove the operations - * only in that case to avoid other windows to show an empty popover in the oposite - * case */ -static gboolean -is_all_windows_operations_buttons_inactive () -{ - GApplication *application; - GList *windows; - GList *l; - GtkWidget *toolbar; - - application = g_application_get_default (); - windows = nautilus_application_get_windows (NAUTILUS_APPLICATION (application)); - - for (l = windows; l != NULL; l = l->next) { - toolbar = nautilus_window_get_toolbar (NAUTILUS_WINDOW (l->data)); - if (nautilus_toolbar_is_operations_button_active (NAUTILUS_TOOLBAR (toolbar))) { - return FALSE; - } - } - - return TRUE; -} - static void on_progress_info_cancelled (NautilusToolbar *self) { /* Update the pie chart progress */ gtk_widget_queue_draw (self->priv->operations_icon); - if (is_all_windows_operations_buttons_inactive ()) { + if (!nautilus_progress_manager_has_viewers (self->priv->progress_manager)) { schedule_remove_finished_operations (self); } } @@ -526,7 +501,7 @@ on_progress_info_finished (NautilusToolbar *self, /* Update the pie chart progress */ gtk_widget_queue_draw (self->priv->operations_icon); - if (is_all_windows_operations_buttons_inactive ()){ + if (!nautilus_progress_manager_has_viewers (self->priv->progress_manager)) { schedule_remove_finished_operations (self); } @@ -747,13 +722,32 @@ on_operations_icon_draw (GtkWidget *widget, } static void -on_operations_button_toggled (NautilusToolbar *self) +on_operations_button_toggled (NautilusToolbar *self, + GtkToggleButton *button) { - unschedule_remove_finished_operations (self); - if (is_all_windows_operations_buttons_inactive ()) { + if (gtk_toggle_button_get_active (button)) { + unschedule_remove_finished_operations (self); + nautilus_progress_manager_add_viewer (self->priv->progress_manager, + G_OBJECT (self)); + } + else { + nautilus_progress_manager_remove_viewer (self->priv->progress_manager, + G_OBJECT (self)); + } +} + +static void +on_progress_has_viewers_changed (NautilusProgressInfoManager *manager, + NautilusToolbar *self) +{ + if (nautilus_progress_manager_has_viewers (manager)) { + unschedule_remove_finished_operations (self); + return; + } + + if (nautilus_progress_manager_are_all_infos_finished_or_cancelled (manager)) { + unschedule_remove_finished_operations (self); schedule_remove_finished_operations (self); - } else { - update_operations (self); } } @@ -782,6 +776,9 @@ nautilus_toolbar_init (NautilusToolbar *self) 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); + g_signal_connect (self->priv->progress_manager, "has-viewers-changed", + G_CALLBACK (on_progress_has_viewers_changed), self); + update_operations (self); g_object_set_data (G_OBJECT (self->priv->back_button), "nav-direction", -- cgit v1.2.1