[gnome-control-center/wip/benzea/wifi-panel: 8/9] wifi: Add new connection list widget



commit b1c62d19f344fb1a5b3b96f7362f51a200ff31e6
Author: Benjamin Berg <bberg redhat com>
Date:   Sat Dec 15 17:51:38 2018 +0100

    wifi: Add new connection list widget
    
    This widget keep track of connections and APs and automatically
    creates/refreshes the corresponding list entries.

 panels/network/cc-wifi-connection-list.c | 703 +++++++++++++++++++++++++++++++
 panels/network/cc-wifi-connection-list.h |  39 ++
 panels/network/meson.build               |   1 +
 3 files changed, 743 insertions(+)
---
diff --git a/panels/network/cc-wifi-connection-list.c b/panels/network/cc-wifi-connection-list.c
new file mode 100644
index 000000000..87efed005
--- /dev/null
+++ b/panels/network/cc-wifi-connection-list.c
@@ -0,0 +1,703 @@
+/*
+ * Copyright © 2018 Red Hat Inc.
+ *
+ * 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 2 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 "cc-wifi-connection-list.h"
+#include "cc-wifi-connection-row.h"
+
+struct _CcWifiConnectionList
+{
+  GtkListBox parent_instance;
+
+  guint          freeze_count;
+  gboolean       updating;
+
+  gboolean       checkable;
+  gboolean       hide_unavailable;
+  gboolean       show_aps;
+
+  NMClient      *client;
+  NMDeviceWifi  *device;
+
+  NMConnection  *last_active;
+
+  GPtrArray     *connections;
+  GPtrArray     *connections_row;
+
+  /* AP SSID cache stores the APs SSID used for assigning it to a row.
+   * This is necessary to efficiently remove it when its SSID changes.
+   *
+   * Note that we only group APs that cannot be assigned to a connection
+   * by the SSID. In principle this is wrong, because other attributes may
+   * be different rendering them separate networks.
+   * In practice this will almost never happen, and if it does, we just
+   * show and select the strongest AP.
+   */
+  GHashTable    *ap_ssid_cache;
+  GHashTable    *ssid_to_row;
+};
+
+static void on_device_ap_added_cb   (CcWifiConnectionList *self,
+                                     NMAccessPoint        *ap,
+                                     NMDeviceWifi         *device);
+static void on_device_ap_removed_cb (CcWifiConnectionList *self,
+                                     NMAccessPoint        *ap,
+                                     NMDeviceWifi         *device);
+static void on_row_configured_cb    (CcWifiConnectionRow  *row,
+                                     CcWifiConnectionList *list);
+
+G_DEFINE_TYPE (CcWifiConnectionList, cc_wifi_connection_list, GTK_TYPE_LIST_BOX)
+
+enum {
+  PROP_0,
+  PROP_CHECKABLE,
+  PROP_HIDE_UNAVAILABLE,
+  PROP_SHOW_APS,
+  PROP_CLIENT,
+  PROP_DEVICE,
+  PROP_LAST
+};
+
+static GParamSpec *props [PROP_LAST];
+
+static GBytes*
+new_hashable_ssid (GBytes *ssid)
+{
+  GBytes *res;
+  const guint8 *data;
+  gsize size;
+
+  /* This is what nm_utils_same_ssid does, but returning it so that we can
+   * use the result in other ways (i.e. hash table lookups). */
+  data = g_bytes_get_data ((GBytes*) ssid, &size);
+  if (data[size-1] == '\0')
+    size -= 1;
+  res = g_bytes_new (data, size);
+
+  return res;
+}
+
+static gboolean
+connection_ignored (NMConnection *connection)
+{
+  NMSettingWireless *sw;
+
+  /* Ignore AP and adhoc modes (i.e. accept infrastructure or empty) */
+  sw = nm_connection_get_setting_wireless (connection);
+  if (!sw)
+    return TRUE;
+  if (g_strcmp0 (nm_setting_wireless_get_mode (sw), "adhoc") == 0 ||
+      g_strcmp0 (nm_setting_wireless_get_mode (sw), "ap") == 0)
+    return TRUE;
+
+  return FALSE;
+}
+
+static CcWifiConnectionRow*
+cc_wifi_connection_list_row_add (CcWifiConnectionList *self,
+                                 NMConnection         *connection,
+                                 NMAccessPoint        *ap)
+{
+  CcWifiConnectionRow *res;
+  g_autoptr(GPtrArray) aps = NULL;
+
+  if (ap)
+    {
+      aps = g_ptr_array_new ();
+      g_ptr_array_add (aps, ap);
+    }
+
+  res = cc_wifi_connection_row_new (self->device,
+                                    connection,
+                                    aps,
+                                    self->checkable);
+  gtk_container_add (GTK_CONTAINER (self), GTK_WIDGET (res));
+  gtk_widget_show (GTK_WIDGET (res));
+
+  g_signal_connect (res, "configure", G_CALLBACK (on_row_configured_cb), self);
+
+  return res;
+}
+
+static void
+update_connections (CcWifiConnectionList *self)
+{
+  const GPtrArray *aps;
+  const GPtrArray *acs_client;
+  g_autoptr(GPtrArray) acs;
+  GHashTableIter iter;
+  CcWifiConnectionRow *row;
+  NMActiveConnection *ac;
+  NMConnection *ac_con = NULL;
+  gint i;
+
+  /* We don't want full UI rebuilds during some UI interactions, so allow freezing the list. */
+  if (self->freeze_count > 0)
+    return;
+
+  /* Prevent recursion (maybe move this into an idle handler instead?) */
+  if (self->updating)
+    return;
+  self->updating = TRUE;
+
+  /* Clear everything; disconnect all AP signals first */
+  aps = nm_device_wifi_get_access_points (self->device);
+  for (i = 0; i < aps->len; i++)
+    g_signal_handlers_disconnect_by_data (g_ptr_array_index (aps, i), self);
+
+  /* Remove all AP only rows */
+  g_hash_table_iter_init (&iter, self->ssid_to_row);
+  while (g_hash_table_iter_next (&iter, NULL, (gpointer*) &row))
+    gtk_container_remove (GTK_CONTAINER (self), GTK_WIDGET (row));
+
+  /* Remove all connection rows */
+  for (i = 0; i < self->connections_row->len; i++)
+    if (g_ptr_array_index (self->connections_row, i))
+      gtk_container_remove (GTK_CONTAINER (self),
+                            GTK_WIDGET (g_ptr_array_index (self->connections_row, i)));
+
+  /* Reset the internal state */
+  g_ptr_array_set_size (self->connections, 0);
+  g_ptr_array_set_size (self->connections_row, 0);
+  g_hash_table_remove_all (self->ssid_to_row);
+  g_hash_table_remove_all (self->ap_ssid_cache);
+
+
+  /* Copy the new connections; also create a row if we show unavailable
+   * connections */
+  acs_client = nm_client_get_connections (self->client);
+
+  acs = g_ptr_array_new_full (acs_client->len + 1, NULL);
+  for (i = 0; i < acs_client->len; i++)
+    g_ptr_array_add (acs, g_ptr_array_index (acs_client, i));
+
+  ac = nm_device_get_active_connection (NM_DEVICE (self->device));
+  if (ac)
+    ac_con = NM_CONNECTION (nm_active_connection_get_connection (ac));
+
+  if (ac_con && !g_ptr_array_find (acs, ac_con, NULL))
+    {
+      g_debug ("Adding remote connection for active connection");
+      g_ptr_array_add (acs, g_object_ref (ac_con));
+    }
+
+  for (i = 0; i < acs->len; i++)
+    {
+      NMConnection *con;
+      con = g_ptr_array_index (acs, i);
+      if (connection_ignored (con))
+        continue;
+
+      g_ptr_array_add (self->connections, g_object_ref (con));
+      if (self->hide_unavailable && con != ac_con)
+        g_ptr_array_add (self->connections_row, NULL);
+      else
+        g_ptr_array_add (self->connections_row,
+                         cc_wifi_connection_list_row_add (self, con,
+                         NULL));
+    }
+
+  /* Coldplug all known APs again */
+  for (i = 0; i < aps->len; i++)
+    on_device_ap_added_cb (self, g_ptr_array_index (aps, i), self->device);
+
+  self->updating = FALSE;
+}
+
+static void
+on_row_configured_cb (CcWifiConnectionRow *row, CcWifiConnectionList *list)
+{
+  g_signal_emit_by_name (list, "configure", row);
+}
+
+static void
+on_access_point_property_changed (CcWifiConnectionList *self,
+                                  GParamSpec           *pspec,
+                                  NMAccessPoint        *ap)
+{
+  CcWifiConnectionRow *row;
+  GBytes *ssid;
+  gboolean has_connection = FALSE;
+  gint i;
+
+  /* If the SSID changed then the AP needs to be added/removed from rows.
+   * Do this by simulating an AP addition/removal.  */
+  if (g_str_equal (pspec->name, NM_ACCESS_POINT_SSID))
+    {
+      g_debug ("Simulating add/remove for SSID change");
+      on_device_ap_removed_cb (self, ap, self->device);
+      on_device_ap_added_cb (self, ap, self->device);
+      return;
+    }
+
+  /* Otherwise, find all rows that contain the AP and update it. Do this by
+   * first searching all rows with connections, and then looking it up in the
+   * SSID rows if not found. */
+  for (i = 0; i < self->connections_row->len; i++)
+    {
+      row = g_ptr_array_index (self->connections_row, i);
+      if (row && cc_wifi_connection_row_has_access_point (row, ap))
+        {
+          cc_wifi_connection_row_update (row);
+          has_connection = TRUE;
+        }
+    }
+
+  if (!self->show_aps || has_connection)
+    return;
+
+  ssid = g_hash_table_lookup (self->ap_ssid_cache, ap);
+  if (!ssid)
+    return;
+
+  row = g_hash_table_lookup (self->ssid_to_row, ssid);
+  if (!row)
+    g_assert_not_reached ();
+  else
+    cc_wifi_connection_row_update (row);
+}
+
+static void
+on_device_ap_added_cb (CcWifiConnectionList *self,
+                       NMAccessPoint        *ap,
+                       NMDeviceWifi         *device)
+{
+  g_autoptr(GPtrArray) connections = NULL;
+  CcWifiConnectionRow *row;
+  GBytes *ap_ssid;
+  g_autoptr(GBytes) ssid = NULL;
+  guint i, j;
+
+  g_signal_connect_object (ap, "notify",
+                           G_CALLBACK (on_access_point_property_changed),
+                           self, G_CONNECT_SWAPPED);
+
+  connections = nm_access_point_filter_connections (ap, self->connections);
+
+  /* If this is the active AP, then add the active connection to the list. This
+   * is a workaround because nm_access_pointer_filter_connections() will not
+   * include it otherwise.
+   * So it seems like the dummy AP entry that NM creates internaly is not actually
+   * compatible with the connection that is being activated.
+   */
+  if (ap == nm_device_wifi_get_active_access_point (device))
+    {
+      NMActiveConnection *ac;
+      NMConnection *ac_con;
+      ac = nm_device_get_active_connection (NM_DEVICE (self->device));
+
+      if (ac)
+        {
+          guint idx;
+
+          ac_con = NM_CONNECTION (nm_active_connection_get_connection (ac));
+
+          if (!g_ptr_array_find (connections, ac_con, NULL) && g_ptr_array_find (self->connections, ac_con, 
&idx))
+            {
+              g_debug ("Adding active connection to list of valid connections for AP");
+              g_ptr_array_add (connections, g_object_ref (ac_con));
+            }
+        }
+    }
+
+  /* Add the AP to all connection related rows, creating the row if neccessary. */
+  for (i = 0; i < connections->len; i++)
+    {
+      g_assert (g_ptr_array_find (self->connections, g_ptr_array_index (connections, i), &j));
+      row = g_ptr_array_index (self->connections_row, j);
+      if (!row)
+        row = cc_wifi_connection_list_row_add (self, g_ptr_array_index (connections, i), NULL);
+      cc_wifi_connection_row_add_access_point (row, ap);
+      g_ptr_array_index (self->connections_row, j) = row;
+    }
+
+  if (connections->len > 0)
+    return;
+
+  if (!self->show_aps)
+    return;
+
+  /* The AP is not compatible to any known connection, generate an entry for the
+   * SSID or add to existing one. However, not for hidden APs that don't have an SSID.
+   */
+  ap_ssid = nm_access_point_get_ssid (ap);
+  if (ap_ssid == NULL)
+    return;
+  ssid = new_hashable_ssid (ap_ssid);
+
+  g_hash_table_insert (self->ap_ssid_cache, ap, g_bytes_ref (ssid));
+
+  row = g_hash_table_lookup (self->ssid_to_row, ssid);
+  if (!row)
+    {
+      row = cc_wifi_connection_list_row_add (self, NULL, ap);
+
+      g_hash_table_insert (self->ssid_to_row, g_bytes_ref (ssid), row);
+    }
+  else
+    {
+      cc_wifi_connection_row_add_access_point (row, ap);
+    }
+}
+
+static void
+on_device_ap_removed_cb (CcWifiConnectionList *self,
+                         NMAccessPoint        *ap,
+                         NMDeviceWifi         *device)
+{
+  CcWifiConnectionRow *row;
+  g_autoptr(GBytes) ssid = NULL;
+  gboolean found = FALSE;
+  gint i;
+
+  g_signal_handlers_disconnect_by_data (ap, self);
+
+  /* Find any connection related row with the AP and remove the AP from it. Remove the
+   * row if it was the last AP and we are hiding unavailable connections. */
+  for (i = 0; i < self->connections_row->len; i++)
+    {
+      row = g_ptr_array_index (self->connections_row, i);
+      if (row && cc_wifi_connection_row_remove_access_point (row, ap))
+        {
+          found = TRUE;
+
+          if (self->hide_unavailable)
+            {
+              gtk_container_remove (GTK_CONTAINER (self), GTK_WIDGET (row));
+              g_ptr_array_index (self->connections_row, i) = NULL;
+            }
+        }
+    }
+
+  if (found || !self->show_aps)
+    return;
+
+  /* If the AP was inserted into a row without a connection, then we will get an
+   * SSID for it here. */
+  g_hash_table_steal_extended (self->ap_ssid_cache, ap, NULL, (gpointer*) &ssid);
+  if (!ssid)
+    return;
+
+  /* And we can update the row (possibly removing it) */
+  row = g_hash_table_lookup (self->ssid_to_row, ssid);
+  if (row)
+    {
+      if (cc_wifi_connection_row_remove_access_point (row, ap))
+        {
+          gtk_container_remove (GTK_CONTAINER (self), GTK_WIDGET (row));
+          g_hash_table_remove (self->ssid_to_row, ssid);
+        }
+    }
+  else
+    {
+      g_assert_not_reached ();
+    }
+
+}
+
+static void
+on_client_connection_added_cb (CcWifiConnectionList *self,
+                               NMConnection         *connection,
+                               NMClient             *client)
+{
+  if (!nm_device_connection_compatible (NM_DEVICE (self->device), connection, NULL))
+    return;
+
+  if (connection_ignored (connection))
+    return;
+
+  /* The approach we take to handle connection changes is to do a full rebuild.
+   * It happens seldom enough to make this feasible.
+   */
+  update_connections (self);
+}
+
+static void
+on_client_connection_removed_cb (CcWifiConnectionList *self,
+                                 NMConnection         *connection,
+                                 NMClient             *client)
+{
+  if (!g_ptr_array_find (self->connections, connection, NULL))
+    return;
+
+  /* The approach we take to handle connection changes is to do a full rebuild.
+   * It happens seldom enough to make this feasible.
+   */
+  update_connections (self);
+}
+
+static void
+on_device_state_changed_cb (CcWifiConnectionList *self,
+                            GParamSpec           *pspec,
+                            NMDeviceWifi         *device)
+{
+  NMActiveConnection *ac;
+  NMConnection *connection = NULL;
+  guint idx;
+
+  ac = nm_device_get_active_connection (NM_DEVICE (self->device));
+  if (ac)
+    connection = NM_CONNECTION (nm_active_connection_get_connection (ac));
+
+  /* Just update the corresponding row if the AC is still the same. */
+  if (self->last_active == connection)
+    {
+      if (g_ptr_array_find (self->connections, connection, &idx))
+        {
+          if (g_ptr_array_index (self->connections_row, idx))
+            {
+              cc_wifi_connection_row_update (g_ptr_array_index (self->connections_row, idx));
+              return;
+            }
+        }
+    }
+
+  /* Give up and do a full update. */
+  update_connections (self);
+  self->last_active = connection;
+}
+
+static void
+cc_wifi_connection_list_finalize (GObject *object)
+{
+  CcWifiConnectionList *self = (CcWifiConnectionList *)object;
+
+  g_clear_object (&self->client);
+  g_clear_object (&self->device);
+
+  g_clear_pointer (&self->connections, g_ptr_array_unref);
+  g_clear_pointer (&self->connections_row, g_ptr_array_unref);
+  g_clear_pointer (&self->ssid_to_row, g_hash_table_unref);
+  g_clear_pointer (&self->ap_ssid_cache, g_hash_table_unref);
+
+  G_OBJECT_CLASS (cc_wifi_connection_list_parent_class)->finalize (object);
+}
+
+static void
+cc_wifi_connection_list_constructed (GObject *object)
+{
+  CcWifiConnectionList *self = (CcWifiConnectionList *)object;
+
+  G_OBJECT_CLASS (cc_wifi_connection_list_parent_class)->constructed (object);
+
+  g_assert (self->client);
+  g_assert (self->device);
+
+  g_signal_connect_object (self->client, "connection-added",
+                           G_CALLBACK (on_client_connection_added_cb),
+                           self, G_CONNECT_SWAPPED);
+  g_signal_connect_object (self->client, "connection-removed",
+                           G_CALLBACK (on_client_connection_removed_cb),
+                           self, G_CONNECT_SWAPPED);
+
+  g_signal_connect_object (self->device, "access-point-added",
+                           G_CALLBACK (on_device_ap_added_cb),
+                           self, G_CONNECT_SWAPPED);
+  g_signal_connect_object (self->device, "access-point-removed",
+                           G_CALLBACK (on_device_ap_removed_cb),
+                           self, G_CONNECT_SWAPPED);
+
+  g_signal_connect_object (self->device, "notify::state",
+                           G_CALLBACK (on_device_state_changed_cb),
+                           self, G_CONNECT_SWAPPED);
+  g_signal_connect_object (self->device, "notify::active-connection",
+                           G_CALLBACK (on_device_state_changed_cb),
+                           self, G_CONNECT_SWAPPED);
+  on_device_state_changed_cb (self, NULL, self->device);
+
+  /* Simulate a change notification on the available connections.
+   * This uses the implementation detail that the list is rebuild
+   * completely in this case. */
+  update_connections (self);
+}
+
+static void
+cc_wifi_connection_list_get_property (GObject    *object,
+                                      guint       prop_id,
+                                      GValue     *value,
+                                      GParamSpec *pspec)
+{
+  CcWifiConnectionList *self = CC_WIFI_CONNECTION_LIST (object);
+
+  switch (prop_id)
+    {
+    case PROP_CHECKABLE:
+      g_value_set_boolean (value, self->checkable);
+      break;
+
+    case PROP_HIDE_UNAVAILABLE:
+      g_value_set_boolean (value, self->hide_unavailable);
+      break;
+
+    case PROP_SHOW_APS:
+      g_value_set_boolean (value, self->show_aps);
+      break;
+
+    case PROP_CLIENT:
+      g_value_set_object (value, self->client);
+      break;
+
+    case PROP_DEVICE:
+      g_value_set_object (value, self->device);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+cc_wifi_connection_list_set_property (GObject      *object,
+                                      guint         prop_id,
+                                      const GValue *value,
+                                      GParamSpec   *pspec)
+{
+  CcWifiConnectionList *self = CC_WIFI_CONNECTION_LIST (object);
+
+  switch (prop_id)
+    {
+    case PROP_CHECKABLE:
+      self->checkable = g_value_get_boolean (value);
+      break;
+
+    case PROP_HIDE_UNAVAILABLE:
+      self->hide_unavailable = g_value_get_boolean (value);
+      break;
+
+    case PROP_SHOW_APS:
+      self->show_aps = g_value_get_boolean (value);
+      break;
+
+    case PROP_CLIENT:
+      self->client = g_value_dup_object (value);
+      break;
+
+    case PROP_DEVICE:
+      self->device = g_value_dup_object (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+cc_wifi_connection_list_class_init (CcWifiConnectionListClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->constructed = cc_wifi_connection_list_constructed;
+  object_class->finalize = cc_wifi_connection_list_finalize;
+  object_class->get_property = cc_wifi_connection_list_get_property;
+  object_class->set_property = cc_wifi_connection_list_set_property;
+
+  props[PROP_CHECKABLE] =
+    g_param_spec_boolean ("checkable", "checkable",
+                          "Passed to the created rows to show/hide the checkbox for deletion",
+                          FALSE,
+                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+  props[PROP_HIDE_UNAVAILABLE] =
+    g_param_spec_boolean ("hide-unavailable", "HideUnavailable",
+                          "Whether to show or hide unavailable connections",
+                          TRUE,
+                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+  props[PROP_SHOW_APS] =
+    g_param_spec_boolean ("show-aps", "ShowAPs",
+                          "Whether to show available SSIDs/APs without a connection",
+                          TRUE,
+                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+  props[PROP_CLIENT] =
+    g_param_spec_object ("client", "NMClient",
+                         "The NM Client",
+                         NM_TYPE_CLIENT,
+                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+  props[PROP_DEVICE] =
+    g_param_spec_object ("device", "WiFi Device",
+                         "The WiFi Device for this connection list",
+                         NM_TYPE_DEVICE_WIFI,
+                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (object_class,
+                                     PROP_LAST,
+                                     props);
+
+  g_signal_new ("configure",
+                CC_TYPE_WIFI_CONNECTION_LIST,
+                G_SIGNAL_RUN_LAST,
+                0, NULL, NULL, NULL,
+                G_TYPE_NONE, 1, CC_TYPE_WIFI_CONNECTION_ROW);
+}
+
+static void
+cc_wifi_connection_list_init (CcWifiConnectionList *self)
+{
+  self->hide_unavailable = TRUE;
+  self->show_aps = TRUE;
+
+  self->connections = g_ptr_array_new_with_free_func (g_object_unref);
+  self->connections_row = g_ptr_array_new ();
+  self->ssid_to_row = g_hash_table_new_full (g_bytes_hash, g_bytes_equal,
+                                             (GDestroyNotify) g_bytes_unref, NULL);
+  self->ap_ssid_cache = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+                                               NULL, (GDestroyNotify) g_bytes_unref);
+}
+
+CcWifiConnectionList *
+cc_wifi_connection_list_new (NMClient     *client,
+                             NMDeviceWifi *device,
+                             gboolean      hide_unavailable,
+                             gboolean      show_aps,
+                             gboolean      checkable)
+{
+  return g_object_new (CC_TYPE_WIFI_CONNECTION_LIST,
+                       "client", client,
+                       "device", device,
+                       "hide-unavailable", hide_unavailable,
+                       "show-aps", show_aps,
+                       "checkable", checkable,
+                       NULL);
+}
+
+void
+cc_wifi_connection_list_freeze (CcWifiConnectionList  *list)
+{
+  g_return_if_fail (CC_WIFI_CONNECTION_LIST (list));
+
+  if (list->freeze_count == 0)
+    g_debug ("wifi connection list has been frozen");
+
+  list->freeze_count += 1;
+}
+
+void
+cc_wifi_connection_list_thaw (CcWifiConnectionList  *list)
+{
+  g_return_if_fail (CC_WIFI_CONNECTION_LIST (list));
+
+  g_return_if_fail (list->freeze_count > 0);
+
+  list->freeze_count -= 1;
+
+  if (list->freeze_count == 0)
+    {
+      g_debug ("wifi connection list has been thawed");
+      update_connections (list);
+    }
+}
diff --git a/panels/network/cc-wifi-connection-list.h b/panels/network/cc-wifi-connection-list.h
new file mode 100644
index 000000000..67dc23d81
--- /dev/null
+++ b/panels/network/cc-wifi-connection-list.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright © 2018 Red Hat Inc.
+ *
+ * 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 2 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/>.
+ */
+
+#pragma once
+
+#include <gtk/gtk.h>
+#include <NetworkManager.h>
+
+G_BEGIN_DECLS
+
+#define CC_TYPE_WIFI_CONNECTION_LIST (cc_wifi_connection_list_get_type())
+
+G_DECLARE_FINAL_TYPE (CcWifiConnectionList, cc_wifi_connection_list, CC, WIFI_CONNECTION_LIST, GtkListBox)
+
+CcWifiConnectionList *cc_wifi_connection_list_new (NMClient     *client,
+                                                   NMDeviceWifi *device,
+                                                   gboolean      hide_unavailable,
+                                                   gboolean      show_aps,
+                                                   gboolean      checkable);
+
+
+void                  cc_wifi_connection_list_freeze (CcWifiConnectionList  *list);
+void                  cc_wifi_connection_list_thaw   (CcWifiConnectionList  *list);
+
+G_END_DECLS
diff --git a/panels/network/meson.build b/panels/network/meson.build
index 151b589b6..ec9df3263 100644
--- a/panels/network/meson.build
+++ b/panels/network/meson.build
@@ -36,6 +36,7 @@ endforeach
 sources = files(
   'cc-network-panel.c',
   'cc-wifi-connection-row.c',
+  'cc-wifi-connection-list.c',
   'cc-wifi-panel.c',
   'net-device.c',
   'net-device-ethernet.c',


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