gnome-control-center/panels/network/cc-wifi-connection-list.c
Hendrik Müller 48497080b3 wifi: Fix bug in Known Wi-Fi Networks dialog
The "Forget" button would only update it's sensitivity after the first
select and deselect, when selecting and deselecting rows in the
"Known Wi-Fi Networks" dialog.
When selecting the first row, it would go from disabled to enabled.
Then deselecting that row would cause the button to go from enabled to
disabled.
Selecting any rows after that would no longer update the sensitivity and
make the dialog essentially useless.

The issue was, that the signals "add" and "remove" where being
expected to be emitted when the connection list updates its rows.
However, neither CcWifiConnectionList nor GtkListBox emit these signals.
The fix was, to emit these two signals at the appropriate locations.
The signals have also been renamed to "add-row" and "remove-row" to
make their purpose more clear.

Closes https://gitlab.gnome.org/GNOME/gnome-control-center/-/issues/1824
2022-08-03 11:05:16 +00:00

799 lines
25 KiB
C

/*
* 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
{
AdwBin parent_instance;
GtkListBox *listbox;
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 (CcWifiConnectionList *self,
CcWifiConnectionRow *row);
G_DEFINE_TYPE (CcWifiConnectionList, cc_wifi_connection_list, ADW_TYPE_BIN)
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,
gboolean known_connection)
{
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,
known_connection);
gtk_list_box_append (self->listbox, GTK_WIDGET (res));
g_signal_connect_object (res, "configure", G_CALLBACK (on_row_configured_cb), self, G_CONNECT_SWAPPED);
g_signal_emit_by_name (self, "add-row", res);
return res;
}
static void
clear_widget (CcWifiConnectionList *self)
{
const GPtrArray *aps;
GHashTableIter iter;
CcWifiConnectionRow *row;
gint i;
/* 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))
{
g_hash_table_iter_remove (&iter);
g_signal_emit_by_name (self, "remove-row", row);
gtk_list_box_remove (self->listbox, 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))
continue;
row = g_ptr_array_index (self->connections_row, i);
g_ptr_array_index (self->connections_row, i) = NULL;
g_signal_emit_by_name (self, "remove-row", row);
gtk_list_box_remove (self->listbox, GTK_WIDGET (row));
}
/* 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);
}
static void
update_connections (CcWifiConnectionList *self)
{
const GPtrArray *aps;
const GPtrArray *acs_client;
g_autoptr(GPtrArray) acs = NULL;
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_widget (self);
/* 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, TRUE));
}
/* Coldplug all known APs again */
aps = nm_device_wifi_get_access_points (self->device);
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 (CcWifiConnectionList *self, CcWifiConnectionRow *row)
{
g_signal_emit_by_name (self, "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;
NM80211ApSecurityFlags rsn_flags;
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 internally 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++)
{
gboolean found = g_ptr_array_find (self->connections, g_ptr_array_index (connections, i), &j);
g_assert (found);
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, TRUE);
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
* or a hidden OWE transition network.
*/
ap_ssid = nm_access_point_get_ssid (ap);
if (ap_ssid == NULL)
return;
/* Skip OWE-TM network with OWE RSN */
rsn_flags = nm_access_point_get_rsn_flags (ap);
if (rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_OWE && rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_OWE_TM)
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, FALSE);
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)
{
g_ptr_array_index (self->connections_row, i) = NULL;
g_signal_emit_by_name (self, "remove-row", row);
gtk_list_box_remove (self->listbox, GTK_WIDGET (row));
}
}
}
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);
g_assert (row != NULL);
if (cc_wifi_connection_row_remove_access_point (row, ap))
{
g_hash_table_remove (self->ssid_to_row, ssid);
g_signal_emit_by_name (self, "remove-row", row);
gtk_list_box_remove (self->listbox, GTK_WIDGET (row));
}
}
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 &&
g_ptr_array_find (self->connections, connection, &idx) &&
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
on_device_active_ap_changed_cb (CcWifiConnectionList *self,
GParamSpec *pspec,
NMDeviceWifi *device)
{
NMAccessPoint *ap;
/* We need to make sure the active AP is grouped with the active connection.
* Do so by simply removing and adding it.
*
* This is necessary because the AP is added before this property
* is updated. */
ap = nm_device_wifi_get_active_access_point (self->device);
if (ap)
{
g_debug ("Simulating add/remove for active AP change");
on_device_ap_removed_cb (self, ap, self->device);
on_device_ap_added_cb (self, ap, self->device);
}
}
static void
cc_wifi_connection_list_dispose (GObject *object)
{
CcWifiConnectionList *self = (CcWifiConnectionList *)object;
/* Prevent any further updates; clear_widget must not indirectly recurse
* through updates_connections */
self->updating = TRUE;
/* Drop all external references */
clear_widget (self);
G_OBJECT_CLASS (cc_wifi_connection_list_parent_class)->dispose (object);
}
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);
g_signal_connect_object (self->device, "notify::active-access-point",
G_CALLBACK (on_device_active_ap_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->dispose = cc_wifi_connection_list_dispose;
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);
g_signal_new ("add-row",
CC_TYPE_WIFI_CONNECTION_LIST,
G_SIGNAL_RUN_LAST,
0, NULL, NULL, NULL,
G_TYPE_NONE, 1, CC_TYPE_WIFI_CONNECTION_ROW);
g_signal_new ("remove-row",
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->listbox = GTK_LIST_BOX (gtk_list_box_new ());
gtk_list_box_set_selection_mode (GTK_LIST_BOX (self->listbox), GTK_SELECTION_NONE);
gtk_widget_set_valign (GTK_WIDGET (self->listbox), GTK_ALIGN_START);
gtk_widget_add_css_class (GTK_WIDGET (self->listbox), "boxed-list");
adw_bin_set_child (ADW_BIN (self), GTK_WIDGET (self->listbox));
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 *self)
{
g_return_if_fail (CC_WIFI_CONNECTION_LIST (self));
if (self->freeze_count == 0)
g_debug ("wifi connection list has been frozen");
self->freeze_count += 1;
}
void
cc_wifi_connection_list_thaw (CcWifiConnectionList *self)
{
g_return_if_fail (CC_WIFI_CONNECTION_LIST (self));
g_return_if_fail (self->freeze_count > 0);
self->freeze_count -= 1;
if (self->freeze_count == 0)
{
g_debug ("wifi connection list has been thawed");
update_connections (self);
}
}
GtkListBox *
cc_wifi_connection_list_get_list_box (CcWifiConnectionList *self)
{
g_return_val_if_fail (CC_IS_WIFI_CONNECTION_LIST (self), NULL);
return self->listbox;
}