Merge lp:~naaando/wingpanel-indicator-network/vpn into lp:~wingpanel-devs/wingpanel-indicator-network/trunk

Proposed by Fernando da Silva Sousa
Status: Merged
Approved by: Danielle Foré
Approved revision: 230
Merged at revision: 233
Proposed branch: lp:~naaando/wingpanel-indicator-network/vpn
Merge into: lp:~wingpanel-devs/wingpanel-indicator-network/trunk
Diff against target: 860 lines (+619/-171)
6 files modified
src/CMakeLists.txt (+3/-0)
src/Widgets/PopoverWidget.vala (+7/-0)
src/Widgets/VpnInterface.vala (+96/-0)
src/common/Widgets/AbstractVpnInterface.vala (+206/-0)
src/common/Widgets/NMVisualizer.vala (+176/-171)
src/common/Widgets/VpnMenuItem.vala (+131/-0)
To merge this branch: bzr merge lp:~naaando/wingpanel-indicator-network/vpn
Reviewer Review Type Date Requested Status
Adam Bieńkowski (community) code Approve
Danielle Foré ux Approve
Kirill Romanov (community) test Approve
Review via email: mp+321350@code.launchpad.net

Commit message

Show configured VPNs

Description of the change

Adds VPN menu

To post a comment you must log in.
Revision history for this message
Danielle Foré (danrabbit) wrote :

I don't have any VPN connections set up, but I still get the header in the menu. It should probably only show if you actually have VPN connections configured. Especially since toggling VPN to on does nothing. This is kind of dangerous honestly because it could make people think they have a VPN just by hitting this switch.

It also shows as "Vpn" instead of "VPN"

review: Needs Fixing
226. By Fernando da Silva Sousa

Fix small typos

227. By Fernando da Silva Sousa

* Fix wingpanel crashing when a VPN is added all being removed
* Hide VPN menu if there's no VPN setup

Revision history for this message
Fernando da Silva Sousa (naaando) wrote :

I made the fixes, under the hood the menu is hidden when there are no VPN connections now.

Revision history for this message
Danielle Foré (danrabbit) wrote :

I can confirm that the VPN section isn't present while I have no VPN configured :)

Revision history for this message
Kirill Romanov (djaler1) wrote :

I can confirm that it works good for me!

review: Approve (test)
Revision history for this message
Kirill Romanov (djaler1) wrote :

I have some VPN connections, and I succesfully managed to connect and switch between them

Revision history for this message
Adam Bieńkowski (donadigo) wrote :

One little thing in the diff comment.

review: Needs Fixing (code)
Revision history for this message
Danielle Foré (danrabbit) :
review: Approve (ux)
228. By Fernando da Silva Sousa

Rename function that create VPN

229. By Fernando da Silva Sousa

Fix coding style

230. By Fernando da Silva Sousa

Merge trunk

Revision history for this message
Adam Bieńkowski (donadigo) wrote :

Thanks. I can let that go now.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/CMakeLists.txt'
2--- src/CMakeLists.txt 2016-10-29 20:28:52 +0000
3+++ src/CMakeLists.txt 2017-04-23 18:39:47 +0000
4@@ -17,15 +17,18 @@
5 Widgets/DisplayWidget.vala
6 Widgets/PopoverWidget.vala
7 Widgets/EtherInterface.vala
8+ Widgets/VpnInterface.vala
9 Widgets/WifiInterface.vala
10 common/Utils.vala
11 common/Widgets/WifiMenuItem.vala
12+ common/Widgets/VpnMenuItem.vala
13 common/Widgets/NMVisualizer.vala
14 common/Widgets/WidgetNMInterface.vala
15 Services/SettingsManager.vala
16 ${CMAKE_CURRENT_BINARY_DIR}/config.vala
17 common/rfkill.vala
18 common/Widgets/AbstractWifiInterface.vala
19+ common/Widgets/AbstractVpnInterface.vala
20 common/Widgets/AbstractEtherInterface.vala
21
22 PACKAGES
23
24=== modified file 'src/Widgets/PopoverWidget.vala'
25--- src/Widgets/PopoverWidget.vala 2016-11-19 14:50:46 +0000
26+++ src/Widgets/PopoverWidget.vala 2017-04-23 18:39:47 +0000
27@@ -19,6 +19,7 @@
28 public class Network.Widgets.PopoverWidget : Network.Widgets.NMVisualizer {
29 private Gtk.Box other_box;
30 private Gtk.Box wifi_box;
31+ private Gtk.Box vpn_box;
32 private Wingpanel.Widgets.Button show_settings_button;
33 private Wingpanel.Widgets.Button hidden_item;
34
35@@ -47,8 +48,10 @@
36
37 other_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
38 wifi_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
39+ vpn_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
40 add (other_box);
41 add (wifi_box);
42+ add (vpn_box);
43
44 if (!is_dm ()) {
45 hidden_item = new Wingpanel.Widgets.Button (_("Connect to Hidden Network…"));
46@@ -90,6 +93,10 @@
47 });
48 }
49
50+ if (widget_interface is Network.VpnInterface) {
51+ container_box = vpn_box;
52+ }
53+
54 if (!is_dm () || get_children ().length () > 0) {
55 widget_interface.sep = new Wingpanel.Widgets.Separator ();
56 container_box.pack_end (widget_interface.sep);
57
58=== added file 'src/Widgets/VpnInterface.vala'
59--- src/Widgets/VpnInterface.vala 1970-01-01 00:00:00 +0000
60+++ src/Widgets/VpnInterface.vala 2017-04-23 18:39:47 +0000
61@@ -0,0 +1,96 @@
62+/*
63+ * Copyright (c) 2017 elementary LLC. (http://launchpad.net/elementary)
64+ *
65+ * This program is free software: you can redistribute it and/or modify
66+ * it under the terms of the GNU Library General Public License as published by
67+ * the Free Software Foundation, either version 2.1 of the License, or
68+ * (at your option) any later version.
69+ *
70+ * This program is distributed in the hope that it will be useful,
71+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
72+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
73+ * GNU Library General Public License for more details.
74+ *
75+ * You should have received a copy of the GNU Library General Public License
76+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
77+ */
78+
79+public class Network.VpnInterface : Network.AbstractVpnInterface {
80+ private Wingpanel.Widgets.Switch vpn_item;
81+ Gtk.Revealer revealer;
82+
83+ public VpnInterface (NM.Client nm_client, NM.RemoteSettings nm_settings) {
84+ init_vpn_interface (nm_client, nm_settings);
85+ vpn_item.set_caption (display_title);
86+ debug ("Starting VPN Interface");
87+
88+ vpn_item.get_style_context ().add_class ("h4");
89+ vpn_item.switched.connect (() => {
90+ revealer.reveal_child = vpn_item.get_active ();
91+ if (!vpn_item.get_active ()) {
92+ vpn_deactivate_cb ();
93+ }
94+ });
95+
96+ vpn_list.add.connect (check_vpn_availability);
97+ vpn_list.remove.connect (check_vpn_availability);
98+
99+ notify["vpn_state"].connect (update);
100+ }
101+
102+ construct {
103+ orientation = Gtk.Orientation.VERTICAL;
104+ vpn_item = new Wingpanel.Widgets.Switch ("");
105+ vpn_item.get_style_context ().add_class ("h4");
106+ pack_start (vpn_item);
107+
108+ var scrolled_box = new Wingpanel.Widgets.AutomaticScrollBox (null, null);
109+ scrolled_box.set_policy (Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
110+ scrolled_box.add (vpn_list);
111+
112+ revealer = new Gtk.Revealer ();
113+ revealer.add (scrolled_box);
114+ pack_start (revealer);
115+ }
116+
117+ public override void update () {
118+ base.update ();
119+
120+ check_vpn_availability ();
121+ if (active_vpn_item != null) {
122+ vpn_item.set_active (true);
123+ }
124+ }
125+
126+ private void check_vpn_availability () {
127+ var length = vpn_list.get_children ().length ();
128+ // The first item is the blank item
129+ show_vpn (length > 1);
130+ }
131+
132+ private void show_vpn (bool show) {
133+ no_show_all = sep.no_show_all = !show;
134+ visible = sep.visible = show;
135+ }
136+
137+ protected override void vpn_activate_cb (VpnMenuItem item) {
138+ warning ("Activating connection");
139+ vpn_deactivate_cb ();
140+
141+ debug ("Connecting to VPN : %s", item.connection.get_id ());
142+
143+ nm_client.activate_connection (item.connection, null, null, null);
144+ active_vpn_item = item;
145+ Idle.add (() => { update (); return false; });
146+ }
147+
148+ protected override void vpn_deactivate_cb () {
149+ if (active_vpn_connection == null) {
150+ update ();
151+ return;
152+ }
153+ debug ("Deactivating VPN : %s", active_vpn_connection.get_id ());
154+ nm_client.deactivate_connection (active_vpn_connection);
155+ Idle.add (() => { update (); return false; });
156+ }
157+}
158
159=== added file 'src/common/Widgets/AbstractVpnInterface.vala'
160--- src/common/Widgets/AbstractVpnInterface.vala 1970-01-01 00:00:00 +0000
161+++ src/common/Widgets/AbstractVpnInterface.vala 2017-04-23 18:39:47 +0000
162@@ -0,0 +1,206 @@
163+/*
164+ * Copyright (c) 2017 elementary LLC. (http://launchpad.net/elementary)
165+ *
166+ * This program is free software: you can redistribute it and/or modify
167+ * it under the terms of the GNU Library General Public License as published by
168+ * the Free Software Foundation, either version 2.1 of the License, or
169+ * (at your option) any later version.
170+ *
171+ * This program is distributed in the hope that it will be useful,
172+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
173+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
174+ * GNU Library General Public License for more details.
175+ *
176+ * You should have received a copy of the GNU Library General Public License
177+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
178+ */
179+
180+public abstract class Network.AbstractVpnInterface : Network.WidgetNMInterface {
181+ protected NM.VPNConnection? active_vpn_connection = null;
182+
183+ protected Gtk.ListBox vpn_list;
184+
185+ protected NM.Client nm_client;
186+ public NM.RemoteSettings nm_settings;
187+
188+ protected VpnMenuItem? active_vpn_item { get; set; }
189+ protected VpnMenuItem? blank_item = null;
190+ protected Gtk.Stack placeholder;
191+
192+ /**
193+ * If we want to add a visual feedback on DisplayWidget later,
194+ * we just need to remove vpn_state and swap it to state on the code
195+ **/
196+ public Network.State vpn_state { get; protected set; default = Network.State.DISCONNECTED; }
197+
198+ public void init_vpn_interface (NM.Client _nm_client, NM.RemoteSettings _nm_settings) {
199+ nm_client = _nm_client;
200+ nm_settings = _nm_settings;
201+ display_title = _("VPN");
202+
203+ blank_item = new VpnMenuItem.blank ();
204+ vpn_list.add (blank_item);
205+ active_vpn_item = null;
206+
207+ /* Advices that no Vpn has been configured */
208+ var no_vpn_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 6);
209+ no_vpn_box.visible = true;
210+ no_vpn_box.valign = Gtk.Align.CENTER;
211+
212+ var no_vpn = construct_placeholder_label (_("No VPN Available"), true);
213+ no_vpn_box.add (no_vpn);
214+
215+ placeholder.add_named (no_vpn_box, "no-vpn");
216+ placeholder.visible_child_name = "no-vpn";
217+
218+ nm_settings.connections_read.connect (update);
219+ nm_client.notify["active-connections"].connect (update);
220+ nm_settings.new_connection.connect (vpn_added_cb);
221+
222+ update ();
223+ }
224+
225+ construct {
226+ placeholder = new Gtk.Stack ();
227+ placeholder.visible = true;
228+
229+ vpn_list = new Gtk.ListBox ();
230+ // Single click is disabled because it's being handled by VpnMenuItem
231+ vpn_list.activate_on_single_click = false;
232+ vpn_list.visible = true;
233+ vpn_list.set_placeholder (placeholder);
234+ }
235+
236+ public override void update () {
237+ update_active_connection ();
238+
239+ VpnMenuItem? item = null;
240+
241+ if (active_vpn_connection != null) {
242+ switch (active_vpn_connection.vpn_state) {
243+ case NM.VPNConnectionState.UNKNOWN:
244+ case NM.VPNConnectionState.DISCONNECTED:
245+ vpn_state = State.DISCONNECTED;
246+ active_vpn_item = null;
247+ break;
248+ case NM.VPNConnectionState.PREPARE:
249+ case NM.VPNConnectionState.IP_CONFIG_GET:
250+ case NM.VPNConnectionState.CONNECT:
251+ vpn_state = State.CONNECTING_VPN;
252+ item = get_item_by_uuid (active_vpn_connection.get_uuid ());
253+ break;
254+ case NM.VPNConnectionState.FAILED:
255+ vpn_state = State.FAILED_VPN;
256+ active_vpn_item = null;
257+ break;
258+ case NM.VPNConnectionState.ACTIVATED:
259+ vpn_state = State.CONNECTED_VPN;
260+ item = get_item_by_uuid (active_vpn_connection.get_uuid ());
261+ sensitive = true;
262+ break;
263+ }
264+ } else {
265+ vpn_state = State.DISCONNECTED;
266+ }
267+
268+ if (item == null) {
269+ blank_item.set_active (true);
270+
271+ if (active_vpn_item != null) {
272+ active_vpn_item.no_show_all = false;
273+ active_vpn_item.visible = true;
274+ active_vpn_item.vpn_state = vpn_state;
275+ }
276+ }
277+
278+ base.update ();
279+ }
280+
281+ protected Gtk.Label construct_placeholder_label (string text, bool title = false) {
282+ var label = new Gtk.Label (text);
283+ label.visible = true;
284+ label.use_markup = true;
285+ label.wrap = true;
286+ label.wrap_mode = Pango.WrapMode.WORD_CHAR;
287+ label.max_width_chars = 30;
288+ label.justify = Gtk.Justification.CENTER;
289+
290+ if (title) {
291+#if PLUG_NETWORK
292+ label.get_style_context ().add_class ("h2");
293+#endif
294+ }
295+
296+ return label;
297+ }
298+
299+ /**
300+ * The vpn_added_cb is called on new_connection signal,
301+ * (we get the vpn connections from there)
302+ * then we filter the connection that make sense for us.
303+ */
304+ void vpn_added_cb (Object obj) {
305+ var vpn = (NM.RemoteConnection)obj;
306+ switch (vpn.get_connection_type ()) {
307+ case NM.SettingVpn.SETTING_NAME:
308+ // Remove vpn when it's removed in switchboard-plug-networking
309+ vpn.removed.connect (vpn_removed_cb);
310+
311+ // Add the item to vpn_list
312+ var item = new VpnMenuItem (vpn);
313+ item.set_visible (true);
314+ item.user_action.connect (vpn_activate_cb);
315+
316+ vpn_list.add (item);
317+ update ();
318+ break;
319+ default:
320+ break;
321+ }
322+ }
323+
324+ // Removed vpn, from removed signal attached to connection when it get added.
325+ void vpn_removed_cb (NM.RemoteConnection vpn_) {
326+ var item = get_item_by_uuid (vpn_.get_uuid ());
327+ item.destroy ();
328+ }
329+
330+ private VpnMenuItem? get_item_by_uuid (string uuid) {
331+ VpnMenuItem? item = null;
332+ foreach (var child in vpn_list.get_children ()) {
333+ var _item = (VpnMenuItem)child;
334+ if (_item.connection != null && _item.connection.get_uuid () == uuid && item == null) {
335+ item = (VpnMenuItem)child;
336+ }
337+ }
338+
339+ return item;
340+ }
341+
342+ /**
343+ * Loop through each active connection to find out the vpn.
344+ */
345+ protected void update_active_connection () {
346+ active_vpn_connection = null;
347+
348+ nm_client.get_active_connections ().foreach ((ac) => {
349+ if (ac.get_vpn () && active_vpn_connection == null) {
350+ active_vpn_connection = (NM.VPNConnection)ac;
351+ active_vpn_connection.vpn_state_changed.connect (update);
352+
353+ foreach (var v in vpn_list.get_children ()) {
354+ var menu_item = (VpnMenuItem) v;
355+
356+ if (menu_item.connection.get_uuid () == active_vpn_connection.uuid) {
357+ menu_item.set_active (true);
358+ active_vpn_item = menu_item;
359+ active_vpn_item.vpn_state = vpn_state;
360+ }
361+ }
362+ }
363+ });
364+ }
365+
366+ protected abstract void vpn_activate_cb (VpnMenuItem i);
367+ protected abstract void vpn_deactivate_cb ();
368+}
369
370=== modified file 'src/common/Widgets/NMVisualizer.vala'
371--- src/common/Widgets/NMVisualizer.vala 2017-01-06 14:22:24 +0000
372+++ src/common/Widgets/NMVisualizer.vala 2017-04-23 18:39:47 +0000
373@@ -16,175 +16,180 @@
374 */
375
376 public abstract class Network.Widgets.NMVisualizer : Gtk.Grid {
377- protected NM.Client nm_client;
378- protected NM.RemoteSettings nm_settings;
379- protected NM.VPNConnection? active_vpn_connection = null;
380-
381- protected GLib.List<WidgetNMInterface>? network_interface;
382-
383- public bool secure { private set; get; default = false; }
384- public Network.State state { private set; get; default = Network.State.CONNECTING_WIRED; }
385-
386- construct {
387- network_interface = new GLib.List<WidgetNMInterface>();
388-
389- build_ui ();
390-
391- /* Monitor network manager */
392- nm_client = new NM.Client ();
393- nm_settings = new NM.RemoteSettings (null);
394-
395- nm_client.notify["active-connections"].connect (update_vpn_connection);
396-
397- nm_client.device_added.connect (device_added_cb);
398- nm_client.device_removed.connect (device_removed_cb);
399-
400- nm_client.notify["networking-enabled"].connect (update_state);
401-
402- var devices = nm_client.get_devices ();
403- for (var i = 0; i < devices.length; i++)
404- device_added_cb (devices.get (i));
405-
406- show_all();
407- update_vpn_connection ();
408- }
409-
410- protected abstract void build_ui ();
411- protected abstract void add_interface (WidgetNMInterface widget_interface);
412- protected abstract void remove_interface (WidgetNMInterface widget_interface);
413-
414- void device_removed_cb (NM.Device device) {
415- foreach (var widget_interface in network_interface) {
416- if (widget_interface.is_device (device)) {
417- network_interface.remove (widget_interface);
418-
419- // Implementation call
420- remove_interface (widget_interface);
421- break;
422- }
423- }
424-
425- update_interfaces_names ();
426- }
427-
428- void update_interfaces_names () {
429- var count_type = new Gee.HashMap<string, int?> ();
430- foreach (var iface in network_interface) {
431- var type = iface.get_type ().name ();
432- if (count_type.has_key (type)) {
433- count_type[type] = count_type[type] + 1;
434- } else {
435- count_type[type] = 1;
436- }
437- }
438-
439- foreach (var iface in network_interface) {
440- var type = iface.get_type ().name ();
441- iface.update_name (count_type [type]);
442- }
443- }
444-
445- private void device_added_cb (NM.Device device) {
446- if (device.get_iface ().has_prefix ("vmnet") ||
447- device.get_iface ().has_prefix ("lo") ||
448- device.get_iface ().has_prefix ("veth")) {
449- return;
450- }
451-
452- WidgetNMInterface? widget_interface = null;
453-#if PLUG_NETWORK
454- WidgetNMInterface? hotspot_interface = null;
455-#endif
456-
457- if (device is NM.DeviceWifi) {
458- widget_interface = new WifiInterface (nm_client, nm_settings, device);
459-#if PLUG_NETWORK
460- hotspot_interface = new HotspotInterface((WifiInterface)widget_interface);
461-#endif
462-
463- debug ("Wifi interface added");
464- } else if (device is NM.DeviceEthernet) {
465- widget_interface = new EtherInterface (nm_client, nm_settings, device);
466- debug ("Wired interface added");
467- } else {
468- debug ("Unknown device: %s\n", device.get_device_type().to_string());
469- }
470-
471- if (widget_interface != null) {
472- // Implementation call
473- network_interface.append (widget_interface);
474- add_interface(widget_interface);
475- widget_interface.notify["state"].connect(update_state);
476-
477- }
478-
479-#if PLUG_NETWORK
480- if (hotspot_interface != null) {
481- // Implementation call
482- network_interface.append (hotspot_interface);
483- add_interface(hotspot_interface);
484- hotspot_interface.notify["state"].connect(update_state);
485-
486- }
487-#endif
488-
489- update_interfaces_names ();
490- update_all ();
491- show_all ();
492- }
493-
494- void update_all () {
495- foreach(var inter in network_interface) {
496- inter.update ();
497- }
498- }
499-
500- void update_state () {
501- if (!nm_client.networking_get_enabled ()) {
502- state = Network.State.DISCONNECTED_AIRPLANE_MODE;
503- } else {
504- var next_state = Network.State.DISCONNECTED;
505- var best_score = int.MAX;
506-
507- foreach (var inter in network_interface) {
508- var score = inter.state.get_priority();
509-
510- if (score < best_score) {
511- next_state = inter.state;
512- best_score = score;
513- }
514- }
515-
516- state = next_state;
517- }
518- }
519-
520- void update_vpn_connection () {
521- active_vpn_connection = null;
522-
523- nm_client.get_active_connections ().foreach ((ac) => {
524- if (active_vpn_connection == null && ac.get_vpn ()) {
525- active_vpn_connection = (NM.VPNConnection) ac;
526- update_vpn_state (active_vpn_connection.get_vpn_state ());
527- active_vpn_connection.vpn_state_changed.connect (() => {
528- update_vpn_state (active_vpn_connection.get_vpn_state ());
529- });
530- }
531- });
532- }
533-
534- void update_vpn_state (NM.VPNConnectionState state) {
535- switch (state) {
536- case NM.VPNConnectionState.DISCONNECTED:
537- case NM.VPNConnectionState.PREPARE:
538- case NM.VPNConnectionState.IP_CONFIG_GET:
539- case NM.VPNConnectionState.CONNECT:
540- case NM.VPNConnectionState.FAILED:
541- secure = false;
542- break;
543- case NM.VPNConnectionState.ACTIVATED:
544- secure = true;
545- break;
546- }
547- }
548+ protected NM.Client nm_client;
549+ protected NM.RemoteSettings nm_settings;
550+ protected NM.VPNConnection? active_vpn_connection = null;
551+
552+ protected GLib.List<WidgetNMInterface>? network_interface;
553+
554+ public bool secure { private set; get; default = false; }
555+ public Network.State state { private set; get; default = Network.State.CONNECTING_WIRED; }
556+
557+ construct {
558+ network_interface = new GLib.List<WidgetNMInterface>();
559+
560+ build_ui ();
561+
562+ /* Monitor network manager */
563+ nm_client = new NM.Client ();
564+ nm_settings = new NM.RemoteSettings (null);
565+
566+ nm_client.notify["active-connections"].connect (update_vpn_connection);
567+
568+ nm_client.device_added.connect (device_added_cb);
569+ nm_client.device_removed.connect (device_removed_cb);
570+
571+ nm_client.notify["networking-enabled"].connect (update_state);
572+
573+ var devices = nm_client.get_devices ();
574+ for (var i = 0; i < devices.length; i++)
575+ device_added_cb (devices.get (i));
576+
577+ // Vpn interface
578+ create_vpn_interface ();
579+
580+ show_all();
581+ update_vpn_connection ();
582+ }
583+
584+ protected abstract void build_ui ();
585+ protected abstract void add_interface (WidgetNMInterface widget_interface);
586+ protected abstract void remove_interface (WidgetNMInterface widget_interface);
587+
588+ void device_removed_cb (NM.Device device) {
589+ foreach (var widget_interface in network_interface) {
590+ if (widget_interface.is_device (device)) {
591+ network_interface.remove (widget_interface);
592+
593+ // Implementation call
594+ remove_interface (widget_interface);
595+ break;
596+ }
597+ }
598+
599+ update_interfaces_names ();
600+ }
601+
602+ void update_interfaces_names () {
603+ var count_type = new Gee.HashMap<string, int?> ();
604+ foreach (var iface in network_interface) {
605+ var type = iface.get_type ().name ();
606+ if (count_type.has_key (type)) {
607+ count_type[type] = count_type[type] + 1;
608+ } else {
609+ count_type[type] = 1;
610+ }
611+ }
612+
613+ foreach (var iface in network_interface) {
614+ var type = iface.get_type ().name ();
615+ iface.update_name (count_type [type]);
616+ }
617+ }
618+
619+ private void device_added_cb (NM.Device device) {
620+ if (device.get_iface ().has_prefix ("vmnet") ||
621+ device.get_iface ().has_prefix ("lo") ||
622+ device.get_iface ().has_prefix ("veth")) {
623+ return;
624+ }
625+
626+ WidgetNMInterface? widget_interface = null;
627+#if PLUG_NETWORK
628+ WidgetNMInterface? hotspot_interface = null;
629+#endif
630+
631+ if (device is NM.DeviceWifi) {
632+ widget_interface = new WifiInterface (nm_client, nm_settings, device);
633+#if PLUG_NETWORK
634+ hotspot_interface = new HotspotInterface((WifiInterface)widget_interface);
635+#endif
636+
637+ debug ("Wifi interface added");
638+ } else if (device is NM.DeviceEthernet) {
639+ widget_interface = new EtherInterface (nm_client, nm_settings, device);
640+ debug ("Wired interface added");
641+ } else {
642+ debug ("Unknown device: %s\n", device.get_device_type().to_string());
643+ }
644+
645+ if (widget_interface != null) {
646+ // Implementation call
647+ network_interface.append (widget_interface);
648+ add_interface(widget_interface);
649+ widget_interface.notify["state"].connect(update_state);
650+
651+ }
652+
653+#if PLUG_NETWORK
654+ if (hotspot_interface != null) {
655+ // Implementation call
656+ network_interface.append (hotspot_interface);
657+ add_interface(hotspot_interface);
658+ hotspot_interface.notify["state"].connect(update_state);
659+
660+ }
661+#endif
662+
663+ update_interfaces_names ();
664+ update_all ();
665+ show_all ();
666+ }
667+
668+ private void create_vpn_interface () {
669+ WidgetNMInterface widget_interface = new VpnInterface (nm_client, nm_settings);
670+ network_interface.append (widget_interface);
671+ add_interface (widget_interface);
672+ widget_interface.notify["state"].connect (update_state);
673+ }
674+
675+ void update_all () {
676+ foreach(var inter in network_interface) {
677+ inter.update ();
678+ }
679+ }
680+
681+ void update_state () {
682+ if (!nm_client.networking_get_enabled ()) {
683+ state = Network.State.DISCONNECTED_AIRPLANE_MODE;
684+ } else {
685+ var next_state = Network.State.DISCONNECTED;
686+ foreach (var inter in network_interface) {
687+ if (inter.state != Network.State.DISCONNECTED) {
688+ next_state = inter.state;
689+ }
690+ }
691+
692+ state = next_state;
693+ }
694+ }
695+
696+ void update_vpn_connection () {
697+ active_vpn_connection = null;
698+
699+ nm_client.get_active_connections ().foreach ((ac) => {
700+ if (active_vpn_connection == null && ac.get_vpn ()) {
701+ active_vpn_connection = (NM.VPNConnection) ac;
702+ update_vpn_state (active_vpn_connection.get_vpn_state ());
703+ active_vpn_connection.vpn_state_changed.connect (() => {
704+ update_vpn_state (active_vpn_connection.get_vpn_state ());
705+ });
706+ }
707+ });
708+ }
709+
710+ void update_vpn_state (NM.VPNConnectionState state) {
711+ switch (state) {
712+ case NM.VPNConnectionState.DISCONNECTED:
713+ case NM.VPNConnectionState.PREPARE:
714+ case NM.VPNConnectionState.IP_CONFIG_GET:
715+ case NM.VPNConnectionState.CONNECT:
716+ case NM.VPNConnectionState.FAILED:
717+ secure = false;
718+ break;
719+ case NM.VPNConnectionState.ACTIVATED:
720+ secure = true;
721+ break;
722+ }
723+ }
724 }
725
726=== added file 'src/common/Widgets/VpnMenuItem.vala'
727--- src/common/Widgets/VpnMenuItem.vala 1970-01-01 00:00:00 +0000
728+++ src/common/Widgets/VpnMenuItem.vala 2017-04-23 18:39:47 +0000
729@@ -0,0 +1,131 @@
730+/*
731+ * Copyright (c) 2017 elementary LLC. (http://launchpad.net/elementary)
732+ *
733+ * This program is free software: you can redistribute it and/or modify
734+ * it under the terms of the GNU Library General Public License as published by
735+ * the Free Software Foundation, either version 2.1 of the License, or
736+ * (at your option) any later version.
737+ *
738+ * This program is distributed in the hope that it will be useful,
739+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
740+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
741+ * GNU Library General Public License for more details.
742+ *
743+ * You should have received a copy of the GNU Library General Public License
744+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
745+ */
746+
747+public class Network.VpnMenuItem : Gtk.ListBoxRow {
748+ private static unowned Gtk.RadioButton? blank_button = null;
749+
750+ public signal void user_action ();
751+ public NM.RemoteConnection? connection { get; private set; }
752+ public string id {
753+ get {
754+ return connection.get_id ();
755+ }
756+ }
757+ public Network.State vpn_state { get; set; default = Network.State.DISCONNECTED; }
758+
759+ public Gtk.RadioButton radio_button { get; private set; }
760+ Gtk.Spinner spinner;
761+ Gtk.Image error_img;
762+
763+ public VpnMenuItem (NM.RemoteConnection? _connection) {
764+ connection = _connection;
765+ connection.changed.connect (update);
766+
767+ var main_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 6);
768+ main_box.margin_start = main_box.margin_end = 6;
769+
770+ radio_button = new Gtk.RadioButton (null);
771+ if (blank_button != null) {
772+ radio_button.join_group (blank_button);
773+ }
774+
775+ radio_button.button_release_event.connect ((b, ev) => {
776+ user_action ();
777+ return false;
778+ });
779+
780+ error_img = new Gtk.Image.from_icon_name ("process-error-symbolic", Gtk.IconSize.MENU);
781+ error_img.margin_start = 6;
782+ error_img.set_tooltip_text (_("This Virtual Private Network could not be connected to."));
783+
784+ spinner = new Gtk.Spinner ();
785+ spinner.start ();
786+ spinner.visible = false;
787+ spinner.no_show_all = !spinner.visible;
788+
789+ main_box.pack_start (radio_button, true, true);
790+ main_box.pack_start (spinner, false, false);
791+ main_box.pack_start (error_img, false, false);
792+
793+ notify["vpn_state"].connect (update);
794+ radio_button.notify["active"].connect (update);
795+
796+ add (main_box);
797+ get_style_context ().add_class ("menuitem");
798+
799+ update ();
800+ }
801+
802+ /**
803+ * Only used for an item which is not displayed: hacky way to have no radio button selected.
804+ **/
805+ public VpnMenuItem.blank () {
806+ radio_button = new Gtk.RadioButton (null);
807+ blank_button = radio_button;
808+ }
809+
810+ private void update () {
811+ radio_button.label = connection.get_id ();
812+
813+#if PLUG_NETWORK
814+ if (show_icons) {
815+#endif
816+ hide_item (error_img);
817+ hide_item (spinner);
818+
819+#if PLUG_NETWORK
820+ }
821+#endif
822+ switch (vpn_state) {
823+ case State.FAILED_VPN:
824+ show_item (error_img);
825+ break;
826+ case State.CONNECTING_VPN:
827+ show_item (spinner);
828+ if (!radio_button.active) {
829+ critical ("An VPN is being connected but not active.");
830+ }
831+ break;
832+ }
833+ }
834+
835+ public void set_active (bool active) {
836+ radio_button.set_active (active);
837+ }
838+
839+ public void hide_icons (bool show_remove_button = true) {
840+#if PLUG_NETWORK
841+ show_icons = false;
842+ hide_item (error_img);
843+ hide_item (spinner);
844+ if (!show_remove_button) {
845+ hide_item (remove_button);
846+ }
847+#endif
848+ }
849+
850+ void show_item (Gtk.Widget w) {
851+ w.visible = true;
852+ w.no_show_all = w.visible;
853+ }
854+
855+ void hide_item (Gtk.Widget w) {
856+ w.visible = false;
857+ w.no_show_all = !w.visible;
858+ w.hide ();
859+ }
860+}

Subscribers

People subscribed via source and target branches