760 lines
24 KiB
C
760 lines
24 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
|
||
|
{
|
||
|
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
|
||
|
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);
|
||
|
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))
|
||
|
continue;
|
||
|
|
||
|
row = g_ptr_array_index (self->connections_row, i);
|
||
|
g_ptr_array_index (self->connections_row, i) = NULL;
|
||
|
gtk_container_remove (GTK_CONTAINER (self),
|
||
|
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));
|
||
|
}
|
||
|
|
||
|
/* 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 (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++)
|
||
|
{
|
||
|
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);
|
||
|
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)
|
||
|
{
|
||
|
g_ptr_array_index (self->connections_row, i) = NULL;
|
||
|
gtk_container_remove (GTK_CONTAINER (self), 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);
|
||
|
gtk_container_remove (GTK_CONTAINER (self), 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);
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
}
|