GCC Code Coverage Report


Directory: ./
File: panels/network/cc-wifi-connection-list.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 0 346 0.0%
Functions: 0 31 0.0%
Branches: 0 177 0.0%

Line Branch Exec Source
1 /*
2 * Copyright © 2018 Red Hat Inc.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <glib/gi18n.h>
19
20 #include "cc-wifi-connection-list.h"
21 #include "cc-wifi-connection-row.h"
22
23 struct _CcWifiConnectionList
24 {
25 AdwBin parent_instance;
26
27 GtkListBox *listbox;
28
29 guint freeze_count;
30 gboolean updating;
31
32 gboolean checkable;
33 gboolean forgettable;
34 gboolean hide_unavailable;
35 gboolean show_aps;
36
37 NMClient *client;
38 NMDeviceWifi *device;
39
40 NMConnection *last_active;
41
42 GPtrArray *connections;
43 GPtrArray *connections_row;
44
45 /* AP SSID cache stores the APs SSID used for assigning it to a row.
46 * This is necessary to efficiently remove it when its SSID changes.
47 *
48 * Note that we only group APs that cannot be assigned to a connection
49 * by the SSID. In principle this is wrong, because other attributes may
50 * be different rendering them separate networks.
51 * In practice this will almost never happen, and if it does, we just
52 * show and select the strongest AP.
53 */
54 GHashTable *ap_ssid_cache;
55 GHashTable *ssid_to_row;
56 };
57
58 static void on_device_ap_added_cb (CcWifiConnectionList *self,
59 NMAccessPoint *ap,
60 NMDeviceWifi *device);
61 static void on_device_ap_removed_cb (CcWifiConnectionList *self,
62 NMAccessPoint *ap,
63 NMDeviceWifi *device);
64 static void on_row_configured_cb (CcWifiConnectionList *self,
65 CcWifiConnectionRow *row);
66 static void on_row_forget_cb (CcWifiConnectionList *self,
67 CcWifiConnectionRow *row);
68 static void on_row_show_qr_code_cb (CcWifiConnectionList *self,
69 CcWifiConnectionRow *row);
70
71 G_DEFINE_TYPE (CcWifiConnectionList, cc_wifi_connection_list, ADW_TYPE_BIN)
72
73 enum
74 {
75 PROP_0,
76 PROP_CHECKABLE,
77 PROP_HIDE_UNAVAILABLE,
78 PROP_SHOW_APS,
79 PROP_CLIENT,
80 PROP_DEVICE,
81 PROP_FORGETTABLE,
82 PROP_LAST
83 };
84
85 static GParamSpec *props [PROP_LAST];
86
87 static GBytes*
88 new_hashable_ssid (GBytes *ssid)
89 {
90 GBytes *res;
91 const guint8 *data;
92 gsize size;
93
94 /* This is what nm_utils_same_ssid does, but returning it so that we can
95 * use the result in other ways (i.e. hash table lookups). */
96 data = g_bytes_get_data ((GBytes*) ssid, &size);
97 if (data[size-1] == '\0')
98 size -= 1;
99 res = g_bytes_new (data, size);
100
101 return res;
102 }
103
104 static gboolean
105 connection_ignored (NMConnection *connection)
106 {
107 NMSettingWireless *sw;
108
109 /* Ignore AP and adhoc modes (i.e. accept infrastructure or empty) */
110 sw = nm_connection_get_setting_wireless (connection);
111 if (!sw)
112 return TRUE;
113 if (g_strcmp0 (nm_setting_wireless_get_mode (sw), "adhoc") == 0 ||
114 g_strcmp0 (nm_setting_wireless_get_mode (sw), "ap") == 0)
115 {
116 return TRUE;
117 }
118
119 return FALSE;
120 }
121
122 static CcWifiConnectionRow*
123 cc_wifi_connection_list_row_add (CcWifiConnectionList *self,
124 NMConnection *connection,
125 NMAccessPoint *ap,
126 gboolean known_connection)
127 {
128 CcWifiConnectionRow *res;
129 g_autoptr(GPtrArray) aps = NULL;
130
131 if (ap)
132 {
133 aps = g_ptr_array_new ();
134 g_ptr_array_add (aps, ap);
135 }
136
137 res = cc_wifi_connection_row_new (self->device,
138 connection,
139 aps,
140 self->checkable,
141 known_connection,
142 self->forgettable);
143 gtk_list_box_append (self->listbox, GTK_WIDGET (res));
144
145 g_signal_connect_object (res, "configure", G_CALLBACK (on_row_configured_cb), self, G_CONNECT_SWAPPED);
146 g_signal_connect_object (res, "forget", G_CALLBACK (on_row_forget_cb), self, G_CONNECT_SWAPPED);
147 g_signal_connect_object (res, "show-qr-code", G_CALLBACK (on_row_show_qr_code_cb), self, G_CONNECT_SWAPPED);
148
149 g_signal_emit_by_name (self, "add-row", res);
150
151 return res;
152 }
153
154 static void
155 clear_widget (CcWifiConnectionList *self)
156 {
157 const GPtrArray *aps;
158 GHashTableIter iter;
159 CcWifiConnectionRow *row;
160 gint i;
161
162 /* Clear everything; disconnect all AP signals first */
163 aps = nm_device_wifi_get_access_points (self->device);
164 for (i = 0; i < aps->len; i++)
165 g_signal_handlers_disconnect_by_data (g_ptr_array_index (aps, i), self);
166
167 /* Remove all AP only rows */
168 g_hash_table_iter_init (&iter, self->ssid_to_row);
169 while (g_hash_table_iter_next (&iter, NULL, (gpointer*) &row))
170 {
171 g_hash_table_iter_remove (&iter);
172 g_signal_emit_by_name (self, "remove-row", row);
173 gtk_list_box_remove (self->listbox, GTK_WIDGET (row));
174 }
175
176 /* Remove all connection rows */
177 for (i = 0; i < self->connections_row->len; i++)
178 {
179 if (!g_ptr_array_index (self->connections_row, i))
180 continue;
181
182 row = g_ptr_array_index (self->connections_row, i);
183 g_ptr_array_index (self->connections_row, i) = NULL;
184 g_signal_emit_by_name (self, "remove-row", row);
185 gtk_list_box_remove (self->listbox, GTK_WIDGET (row));
186 }
187
188 /* Reset the internal state */
189 g_ptr_array_set_size (self->connections, 0);
190 g_ptr_array_set_size (self->connections_row, 0);
191 g_hash_table_remove_all (self->ssid_to_row);
192 g_hash_table_remove_all (self->ap_ssid_cache);
193 }
194
195 static void
196 update_connections (CcWifiConnectionList *self)
197 {
198 const GPtrArray *aps;
199 const GPtrArray *acs_client;
200 g_autoptr(GPtrArray) acs = NULL;
201 NMActiveConnection *ac;
202 NMConnection *ac_con = NULL;
203 gint i;
204
205 /* We don't want full UI rebuilds during some UI interactions, so allow freezing the list. */
206 if (self->freeze_count > 0)
207 return;
208
209 /* Prevent recursion (maybe move this into an idle handler instead?) */
210 if (self->updating)
211 return;
212 self->updating = TRUE;
213
214 clear_widget (self);
215
216 /* Copy the new connections; also create a row if we show unavailable
217 * connections */
218 acs_client = nm_client_get_connections (self->client);
219
220 acs = g_ptr_array_new_full (acs_client->len + 1, NULL);
221 for (i = 0; i < acs_client->len; i++)
222 g_ptr_array_add (acs, g_ptr_array_index (acs_client, i));
223
224 ac = nm_device_get_active_connection (NM_DEVICE (self->device));
225 if (ac)
226 ac_con = NM_CONNECTION (nm_active_connection_get_connection (ac));
227
228 if (ac_con && !g_ptr_array_find (acs, ac_con, NULL))
229 {
230 g_debug ("Adding remote connection for active connection");
231 g_ptr_array_add (acs, g_object_ref (ac_con));
232 }
233
234 for (i = 0; i < acs->len; i++)
235 {
236 NMConnection *con;
237
238 con = g_ptr_array_index (acs, i);
239 if (connection_ignored (con))
240 continue;
241
242 g_ptr_array_add (self->connections, g_object_ref (con));
243 if (self->hide_unavailable && con != ac_con)
244 g_ptr_array_add (self->connections_row, NULL);
245 else
246 g_ptr_array_add (self->connections_row,
247 cc_wifi_connection_list_row_add (self, con,
248 NULL, TRUE));
249 }
250
251 /* Coldplug all known APs again */
252 aps = nm_device_wifi_get_access_points (self->device);
253 for (i = 0; i < aps->len; i++)
254 on_device_ap_added_cb (self, g_ptr_array_index (aps, i), self->device);
255
256 self->updating = FALSE;
257 }
258
259 static void
260 on_row_configured_cb (CcWifiConnectionList *self, CcWifiConnectionRow *row)
261 {
262 g_signal_emit_by_name (self, "configure", row);
263 }
264
265 static void
266 on_row_forget_cb (CcWifiConnectionList *self, CcWifiConnectionRow *row)
267 {
268 g_signal_emit_by_name (self, "forget", row);
269 }
270
271 static void
272 on_row_show_qr_code_cb (CcWifiConnectionList *self, CcWifiConnectionRow *row)
273 {
274 g_signal_emit_by_name (self, "show_qr_code", row);
275 }
276
277 static void
278 on_access_point_property_changed (CcWifiConnectionList *self,
279 GParamSpec *pspec,
280 NMAccessPoint *ap)
281 {
282 CcWifiConnectionRow *row;
283 GBytes *ssid;
284 gboolean has_connection = FALSE;
285 gint i;
286
287 /* If the SSID changed then the AP needs to be added/removed from rows.
288 * Do this by simulating an AP addition/removal. */
289 if (g_str_equal (pspec->name, NM_ACCESS_POINT_SSID))
290 {
291 g_debug ("Simulating add/remove for SSID change");
292 on_device_ap_removed_cb (self, ap, self->device);
293 on_device_ap_added_cb (self, ap, self->device);
294 return;
295 }
296
297 /* Otherwise, find all rows that contain the AP and update it. Do this by
298 * first searching all rows with connections, and then looking it up in the
299 * SSID rows if not found. */
300 for (i = 0; i < self->connections_row->len; i++)
301 {
302 row = g_ptr_array_index (self->connections_row, i);
303 if (row && cc_wifi_connection_row_has_access_point (row, ap))
304 {
305 cc_wifi_connection_row_update (row);
306 has_connection = TRUE;
307 }
308 }
309
310 if (!self->show_aps || has_connection)
311 return;
312
313 ssid = g_hash_table_lookup (self->ap_ssid_cache, ap);
314 if (!ssid)
315 return;
316
317 row = g_hash_table_lookup (self->ssid_to_row, ssid);
318 if (!row)
319 g_assert_not_reached ();
320 else
321 cc_wifi_connection_row_update (row);
322 }
323
324 static void
325 on_device_ap_added_cb (CcWifiConnectionList *self,
326 NMAccessPoint *ap,
327 NMDeviceWifi *device)
328 {
329 g_autoptr(GPtrArray) connections = NULL;
330 NM80211ApSecurityFlags rsn_flags;
331 CcWifiConnectionRow *row;
332 GBytes *ap_ssid;
333 g_autoptr(GBytes) ssid = NULL;
334 guint i, j;
335
336 g_signal_connect_object (ap, "notify",
337 G_CALLBACK (on_access_point_property_changed),
338 self, G_CONNECT_SWAPPED);
339
340 connections = nm_access_point_filter_connections (ap, self->connections);
341
342 /* If this is the active AP, then add the active connection to the list. This
343 * is a workaround because nm_access_pointer_filter_connections() will not
344 * include it otherwise.
345 * So it seems like the dummy AP entry that NM creates internally is not actually
346 * compatible with the connection that is being activated.
347 */
348 if (ap == nm_device_wifi_get_active_access_point (device))
349 {
350 NMActiveConnection *ac;
351 NMConnection *ac_con;
352
353 ac = nm_device_get_active_connection (NM_DEVICE (self->device));
354
355 if (ac)
356 {
357 guint idx;
358
359 ac_con = NM_CONNECTION (nm_active_connection_get_connection (ac));
360
361 if (!g_ptr_array_find (connections, ac_con, NULL) &&
362 g_ptr_array_find (self->connections, ac_con, &idx))
363 {
364 g_debug ("Adding active connection to list of valid connections for AP");
365 g_ptr_array_add (connections, g_object_ref (ac_con));
366 }
367 }
368 }
369
370 /* Add the AP to all connection related rows, creating the row if neccessary. */
371 for (i = 0; i < connections->len; i++)
372 {
373 gboolean found = g_ptr_array_find (self->connections, g_ptr_array_index (connections, i), &j);
374
375 g_assert (found);
376
377 row = g_ptr_array_index (self->connections_row, j);
378 if (!row)
379 row = cc_wifi_connection_list_row_add (self, g_ptr_array_index (connections, i), NULL, TRUE);
380 cc_wifi_connection_row_add_access_point (row, ap);
381 g_ptr_array_index (self->connections_row, j) = row;
382 }
383
384 if (connections->len > 0)
385 return;
386
387 if (!self->show_aps)
388 return;
389
390 /* The AP is not compatible to any known connection, generate an entry for the
391 * SSID or add to existing one. However, not for hidden APs that don't have an SSID
392 * or a hidden OWE transition network.
393 */
394 ap_ssid = nm_access_point_get_ssid (ap);
395 if (ap_ssid == NULL)
396 return;
397
398 /* Skip OWE-TM network with OWE RSN */
399 rsn_flags = nm_access_point_get_rsn_flags (ap);
400 if (rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_OWE && rsn_flags & NM_802_11_AP_SEC_KEY_MGMT_OWE_TM)
401 return;
402
403 ssid = new_hashable_ssid (ap_ssid);
404
405 g_hash_table_insert (self->ap_ssid_cache, ap, g_bytes_ref (ssid));
406
407 row = g_hash_table_lookup (self->ssid_to_row, ssid);
408 if (!row)
409 {
410 row = cc_wifi_connection_list_row_add (self, NULL, ap, FALSE);
411
412 g_hash_table_insert (self->ssid_to_row, g_bytes_ref (ssid), row);
413 }
414 else
415 {
416 cc_wifi_connection_row_add_access_point (row, ap);
417 }
418 }
419
420 static void
421 on_device_ap_removed_cb (CcWifiConnectionList *self,
422 NMAccessPoint *ap,
423 NMDeviceWifi *device)
424 {
425 CcWifiConnectionRow *row;
426 g_autoptr(GBytes) ssid = NULL;
427 gboolean found = FALSE;
428 gint i;
429
430 g_signal_handlers_disconnect_by_data (ap, self);
431
432 /* Find any connection related row with the AP and remove the AP from it. Remove the
433 * row if it was the last AP and we are hiding unavailable connections. */
434 for (i = 0; i < self->connections_row->len; i++)
435 {
436 row = g_ptr_array_index (self->connections_row, i);
437 if (row && cc_wifi_connection_row_remove_access_point (row, ap))
438 {
439 found = TRUE;
440
441 if (self->hide_unavailable)
442 {
443 g_ptr_array_index (self->connections_row, i) = NULL;
444 g_signal_emit_by_name (self, "remove-row", row);
445 gtk_list_box_remove (self->listbox, GTK_WIDGET (row));
446 }
447 }
448 }
449
450 if (found || !self->show_aps)
451 return;
452
453 /* If the AP was inserted into a row without a connection, then we will get an
454 * SSID for it here. */
455 g_hash_table_steal_extended (self->ap_ssid_cache, ap, NULL, (gpointer*) &ssid);
456 if (!ssid)
457 return;
458
459 /* And we can update the row (possibly removing it) */
460 row = g_hash_table_lookup (self->ssid_to_row, ssid);
461 g_assert (row != NULL);
462
463 if (cc_wifi_connection_row_remove_access_point (row, ap))
464 {
465 g_hash_table_remove (self->ssid_to_row, ssid);
466 g_signal_emit_by_name (self, "remove-row", row);
467 gtk_list_box_remove (self->listbox, GTK_WIDGET (row));
468 }
469 }
470
471 static void
472 on_client_connection_added_cb (CcWifiConnectionList *self,
473 NMConnection *connection,
474 NMClient *client)
475 {
476 if (!nm_device_connection_compatible (NM_DEVICE (self->device), connection, NULL))
477 return;
478
479 if (connection_ignored (connection))
480 return;
481
482 /* The approach we take to handle connection changes is to do a full rebuild.
483 * It happens seldom enough to make this feasible.
484 */
485 update_connections (self);
486 }
487
488 static void
489 on_client_connection_removed_cb (CcWifiConnectionList *self,
490 NMConnection *connection,
491 NMClient *client)
492 {
493 if (!g_ptr_array_find (self->connections, connection, NULL))
494 return;
495
496 /* The approach we take to handle connection changes is to do a full rebuild.
497 * It happens seldom enough to make this feasible.
498 */
499 update_connections (self);
500 }
501
502 static void
503 on_device_state_changed_cb (CcWifiConnectionList *self,
504 GParamSpec *pspec,
505 NMDeviceWifi *device)
506 {
507 NMActiveConnection *ac;
508 NMConnection *connection = NULL;
509 guint idx;
510
511 ac = nm_device_get_active_connection (NM_DEVICE (self->device));
512 if (ac)
513 connection = NM_CONNECTION (nm_active_connection_get_connection (ac));
514
515 /* Just update the corresponding row if the AC is still the same. */
516 if (self->last_active == connection &&
517 g_ptr_array_find (self->connections, connection, &idx) &&
518 g_ptr_array_index (self->connections_row, idx))
519 {
520 cc_wifi_connection_row_update (g_ptr_array_index (self->connections_row, idx));
521 return;
522 }
523
524 /* Give up and do a full update. */
525 update_connections (self);
526 self->last_active = connection;
527 }
528
529 static void
530 on_device_active_ap_changed_cb (CcWifiConnectionList *self,
531 GParamSpec *pspec,
532 NMDeviceWifi *device)
533 {
534 NMAccessPoint *ap;
535 /* We need to make sure the active AP is grouped with the active connection.
536 * Do so by simply removing and adding it.
537 *
538 * This is necessary because the AP is added before this property
539 * is updated. */
540 ap = nm_device_wifi_get_active_access_point (self->device);
541 if (ap)
542 {
543 g_debug ("Simulating add/remove for active AP change");
544 on_device_ap_removed_cb (self, ap, self->device);
545 on_device_ap_added_cb (self, ap, self->device);
546 }
547 }
548
549 static void
550 cc_wifi_connection_list_dispose (GObject *object)
551 {
552 CcWifiConnectionList *self = (CcWifiConnectionList *)object;
553
554 /* Prevent any further updates; clear_widget must not indirectly recurse
555 * through updates_connections */
556 self->updating = TRUE;
557
558 /* Drop all external references */
559 clear_widget (self);
560
561 G_OBJECT_CLASS (cc_wifi_connection_list_parent_class)->dispose (object);
562 }
563
564 static void
565 cc_wifi_connection_list_finalize (GObject *object)
566 {
567 CcWifiConnectionList *self = (CcWifiConnectionList *)object;
568
569 g_clear_object (&self->client);
570 g_clear_object (&self->device);
571
572 g_clear_pointer (&self->connections, g_ptr_array_unref);
573 g_clear_pointer (&self->connections_row, g_ptr_array_unref);
574 g_clear_pointer (&self->ssid_to_row, g_hash_table_unref);
575 g_clear_pointer (&self->ap_ssid_cache, g_hash_table_unref);
576
577 G_OBJECT_CLASS (cc_wifi_connection_list_parent_class)->finalize (object);
578 }
579
580 static void
581 cc_wifi_connection_list_constructed (GObject *object)
582 {
583 CcWifiConnectionList *self = (CcWifiConnectionList *)object;
584
585 G_OBJECT_CLASS (cc_wifi_connection_list_parent_class)->constructed (object);
586
587 g_assert (self->client);
588 g_assert (self->device);
589
590 g_signal_connect_object (self->client, "connection-added",
591 G_CALLBACK (on_client_connection_added_cb),
592 self, G_CONNECT_SWAPPED);
593 g_signal_connect_object (self->client, "connection-removed",
594 G_CALLBACK (on_client_connection_removed_cb),
595 self, G_CONNECT_SWAPPED);
596
597 g_signal_connect_object (self->device, "access-point-added",
598 G_CALLBACK (on_device_ap_added_cb),
599 self, G_CONNECT_SWAPPED);
600 g_signal_connect_object (self->device, "access-point-removed",
601 G_CALLBACK (on_device_ap_removed_cb),
602 self, G_CONNECT_SWAPPED);
603
604 g_signal_connect_object (self->device, "notify::state",
605 G_CALLBACK (on_device_state_changed_cb),
606 self, G_CONNECT_SWAPPED);
607 g_signal_connect_object (self->device, "notify::active-connection",
608 G_CALLBACK (on_device_state_changed_cb),
609 self, G_CONNECT_SWAPPED);
610 g_signal_connect_object (self->device, "notify::active-access-point",
611 G_CALLBACK (on_device_active_ap_changed_cb),
612 self, G_CONNECT_SWAPPED);
613 on_device_state_changed_cb (self, NULL, self->device);
614
615 /* Simulate a change notification on the available connections.
616 * This uses the implementation detail that the list is rebuild
617 * completely in this case. */
618 update_connections (self);
619 }
620
621 static void
622 cc_wifi_connection_list_get_property (GObject *object,
623 guint prop_id,
624 GValue *value,
625 GParamSpec *pspec)
626 {
627 CcWifiConnectionList *self = CC_WIFI_CONNECTION_LIST (object);
628
629 switch (prop_id)
630 {
631 case PROP_CHECKABLE:
632 g_value_set_boolean (value, self->checkable);
633 break;
634
635 case PROP_HIDE_UNAVAILABLE:
636 g_value_set_boolean (value, self->hide_unavailable);
637 break;
638
639 case PROP_SHOW_APS:
640 g_value_set_boolean (value, self->show_aps);
641 break;
642
643 case PROP_CLIENT:
644 g_value_set_object (value, self->client);
645 break;
646
647 case PROP_DEVICE:
648 g_value_set_object (value, self->device);
649 break;
650
651 case PROP_FORGETTABLE:
652 g_value_set_boolean (value, self->forgettable);
653 break;
654
655 default:
656 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
657 }
658 }
659
660 static void
661 cc_wifi_connection_list_set_property (GObject *object,
662 guint prop_id,
663 const GValue *value,
664 GParamSpec *pspec)
665 {
666 CcWifiConnectionList *self = CC_WIFI_CONNECTION_LIST (object);
667
668 switch (prop_id)
669 {
670 case PROP_CHECKABLE:
671 self->checkable = g_value_get_boolean (value);
672 break;
673
674 case PROP_HIDE_UNAVAILABLE:
675 self->hide_unavailable = g_value_get_boolean (value);
676 break;
677
678 case PROP_SHOW_APS:
679 self->show_aps = g_value_get_boolean (value);
680 break;
681
682 case PROP_CLIENT:
683 self->client = g_value_dup_object (value);
684 break;
685
686 case PROP_DEVICE:
687 self->device = g_value_dup_object (value);
688 break;
689
690 case PROP_FORGETTABLE:
691 self->forgettable = g_value_get_boolean (value);
692 break;
693
694 default:
695 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
696 }
697 }
698
699 static void
700 cc_wifi_connection_list_class_init (CcWifiConnectionListClass *klass)
701 {
702 GObjectClass *object_class = G_OBJECT_CLASS (klass);
703
704 object_class->constructed = cc_wifi_connection_list_constructed;
705 object_class->dispose = cc_wifi_connection_list_dispose;
706 object_class->finalize = cc_wifi_connection_list_finalize;
707 object_class->get_property = cc_wifi_connection_list_get_property;
708 object_class->set_property = cc_wifi_connection_list_set_property;
709
710 props[PROP_CHECKABLE] =
711 g_param_spec_boolean ("checkable", "checkable",
712 "Passed to the created rows to show/hide the checkbox for deletion",
713 FALSE,
714 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
715
716 props[PROP_HIDE_UNAVAILABLE] =
717 g_param_spec_boolean ("hide-unavailable", "HideUnavailable",
718 "Whether to show or hide unavailable connections",
719 TRUE,
720 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
721
722 props[PROP_SHOW_APS] =
723 g_param_spec_boolean ("show-aps", "ShowAPs",
724 "Whether to show available SSIDs/APs without a connection",
725 TRUE,
726 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
727
728 props[PROP_CLIENT] =
729 g_param_spec_object ("client", "NMClient",
730 "The NM Client",
731 NM_TYPE_CLIENT,
732 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
733
734 props[PROP_DEVICE] =
735 g_param_spec_object ("device", "WiFi Device",
736 "The WiFi Device for this connection list",
737 NM_TYPE_DEVICE_WIFI,
738 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
739 props[PROP_FORGETTABLE] =
740 g_param_spec_boolean ("forgettable", "forgettable",
741 "Passed to the created rows to show/hide the checkbox for deletion",
742 FALSE,
743 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
744
745 g_object_class_install_properties (object_class,
746 PROP_LAST,
747 props);
748
749 g_signal_new ("configure",
750 CC_TYPE_WIFI_CONNECTION_LIST,
751 G_SIGNAL_RUN_LAST,
752 0, NULL, NULL, NULL,
753 G_TYPE_NONE, 1, CC_TYPE_WIFI_CONNECTION_ROW);
754 g_signal_new ("show_qr_code",
755 CC_TYPE_WIFI_CONNECTION_LIST,
756 G_SIGNAL_RUN_LAST,
757 0, NULL, NULL, NULL,
758 G_TYPE_NONE, 1, CC_TYPE_WIFI_CONNECTION_ROW);
759 g_signal_new ("forget",
760 CC_TYPE_WIFI_CONNECTION_LIST,
761 G_SIGNAL_RUN_LAST,
762 0, NULL, NULL, NULL,
763 G_TYPE_NONE, 1, CC_TYPE_WIFI_CONNECTION_ROW);
764 g_signal_new ("add-row",
765 CC_TYPE_WIFI_CONNECTION_LIST,
766 G_SIGNAL_RUN_LAST,
767 0, NULL, NULL, NULL,
768 G_TYPE_NONE, 1, CC_TYPE_WIFI_CONNECTION_ROW);
769 g_signal_new ("remove-row",
770 CC_TYPE_WIFI_CONNECTION_LIST,
771 G_SIGNAL_RUN_LAST,
772 0, NULL, NULL, NULL,
773 G_TYPE_NONE, 1, CC_TYPE_WIFI_CONNECTION_ROW);
774 }
775
776 static void
777 cc_wifi_connection_list_init (CcWifiConnectionList *self)
778 {
779 self->listbox = GTK_LIST_BOX (gtk_list_box_new ());
780 gtk_list_box_set_selection_mode (GTK_LIST_BOX (self->listbox), GTK_SELECTION_NONE);
781 gtk_widget_set_valign (GTK_WIDGET (self->listbox), GTK_ALIGN_START);
782 gtk_widget_add_css_class (GTK_WIDGET (self->listbox), "boxed-list");
783 adw_bin_set_child (ADW_BIN (self), GTK_WIDGET (self->listbox));
784
785 self->hide_unavailable = TRUE;
786 self->show_aps = TRUE;
787
788 self->connections = g_ptr_array_new_with_free_func (g_object_unref);
789 self->connections_row = g_ptr_array_new ();
790 self->ssid_to_row = g_hash_table_new_full (g_bytes_hash, g_bytes_equal,
791 (GDestroyNotify) g_bytes_unref, NULL);
792 self->ap_ssid_cache = g_hash_table_new_full (g_direct_hash, g_direct_equal,
793 NULL, (GDestroyNotify) g_bytes_unref);
794 }
795
796 CcWifiConnectionList *
797 cc_wifi_connection_list_new (NMClient *client,
798 NMDeviceWifi *device,
799 gboolean hide_unavailable,
800 gboolean show_aps,
801 gboolean checkable,
802 gboolean forgettable)
803 {
804 return g_object_new (CC_TYPE_WIFI_CONNECTION_LIST,
805 "client", client,
806 "device", device,
807 "hide-unavailable", hide_unavailable,
808 "show-aps", show_aps,
809 "checkable", checkable,
810 "forgettable", forgettable,
811 NULL);
812 }
813
814 void
815 cc_wifi_connection_list_freeze (CcWifiConnectionList *self)
816 {
817 g_return_if_fail (CC_WIFI_CONNECTION_LIST (self));
818
819 if (self->freeze_count == 0)
820 g_debug ("wifi connection list has been frozen");
821
822 self->freeze_count += 1;
823 }
824
825 void
826 cc_wifi_connection_list_thaw (CcWifiConnectionList *self)
827 {
828 g_return_if_fail (CC_WIFI_CONNECTION_LIST (self));
829
830 g_return_if_fail (self->freeze_count > 0);
831
832 self->freeze_count -= 1;
833
834 if (self->freeze_count == 0)
835 {
836 g_debug ("wifi connection list has been thawed");
837 update_connections (self);
838 }
839 }
840
841 GtkListBox *
842 cc_wifi_connection_list_get_list_box (CcWifiConnectionList *self)
843 {
844 g_return_val_if_fail (CC_IS_WIFI_CONNECTION_LIST (self), NULL);
845
846 return self->listbox;
847 }
848
849 gboolean
850 cc_wifi_connection_list_is_empty (CcWifiConnectionList *self)
851 {
852 g_return_val_if_fail (CC_IS_WIFI_CONNECTION_LIST (self), TRUE);
853
854 return self->connections->len == 0;
855 }
856
857 void
858 cc_wifi_connection_list_set_placeholder_text (CcWifiConnectionList *self,
859 const gchar *placeholder_text)
860 {
861 GtkWidget *listbox_placeholder;
862
863 g_return_if_fail (CC_IS_WIFI_CONNECTION_LIST (self));
864
865 listbox_placeholder = gtk_label_new (placeholder_text);
866
867 gtk_label_set_wrap (GTK_LABEL (listbox_placeholder), TRUE);
868 gtk_label_set_max_width_chars (GTK_LABEL (listbox_placeholder), 50);
869 gtk_widget_add_css_class (listbox_placeholder, "dim-label");
870 gtk_widget_add_css_class (listbox_placeholder, "cc-placeholder-row");
871
872 gtk_list_box_set_placeholder (self->listbox, listbox_placeholder);
873 }
874