[nautilus] toolbar: fix ops button so it gets removed when multiple windows open



commit 2774b8552dcc89ae744700af5832dbf76c138a9e
Author: Neil Herald <neil herald gmail com>
Date:   Sun Apr 17 09:08:29 2016 +0100

    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 5dc26d1..29046c9 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 ef91d3d..54d2ce6 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 d36b56c..3c12558 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",


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]