~ubuntu-branches/ubuntu/precise/ubiquity/precise-proposed

432 by Evan Dandrea, Colin Watson, Evan Dandrea, Brian Murray, Luke Yelavich, Mario Limonciello
[ Colin Watson ]
1
import subprocess
508 by Stéphane Graber, James M. Leddy, Colin Watson, Stéphane Graber, Kent Baxley, Harald Sitter
[ James M. Leddy ]
2
import string
470 by Colin Watson
Cope with pygobject returning unicode objects rather than UTF-8-encoded
3
432 by Evan Dandrea, Colin Watson, Evan Dandrea, Brian Murray, Luke Yelavich, Mario Limonciello
[ Colin Watson ]
4
import dbus
5
from dbus.mainloop.glib import DBusGMainLoop
6
DBusGMainLoop(set_as_default=True)
7
from gi.repository import Gtk, GObject
8
470 by Colin Watson
Cope with pygobject returning unicode objects rather than UTF-8-encoded
9
from ubiquity.misc import utf8
10
432 by Evan Dandrea, Colin Watson, Evan Dandrea, Brian Murray, Luke Yelavich, Mario Limonciello
[ Colin Watson ]
11
NM = 'org.freedesktop.NetworkManager'
12
NM_DEVICE = 'org.freedesktop.NetworkManager.Device'
13
NM_DEVICE_WIFI = 'org.freedesktop.NetworkManager.Device.Wireless'
14
NM_AP = 'org.freedesktop.NetworkManager.AccessPoint'
15
NM_SETTINGS = 'org.freedesktop.NetworkManager.Settings'
16
NM_SETTINGS_CONN = 'org.freedesktop.NetworkManager.Settings.Connection'
17
NM_SETTINGS_PATH = '/org/freedesktop/NetworkManager/Settings'
18
NM_ERROR_NOSECRETS = 'org.freedesktop.NetworkManager.AgentManager.NoSecrets'
19
DEVICE_TYPE_WIFI = 2
20
NM_STATE_DISCONNECTED = 20
21
NM_STATE_CONNECTING = 40
22
NM_STATE_CONNECTED_GLOBAL = 70
23
24
25
# TODO: DBus exceptions.  Catch 'em all.
26
27
def decode_ssid(characters):
477 by Colin Watson
* Cope with stricter dbus.Byte.__str__ implementation in dbus-python 1.0.0
28
    ssid = ''.join([chr(int(char)) for char in characters])
470 by Colin Watson
Cope with pygobject returning unicode objects rather than UTF-8-encoded
29
    return utf8(ssid, errors='replace')
432 by Evan Dandrea, Colin Watson, Evan Dandrea, Brian Murray, Luke Yelavich, Mario Limonciello
[ Colin Watson ]
30
31
def get_prop(obj, iface, prop):
445 by Evan Dandrea
* Add power indicator to the installer panel.
32
    try:
33
        return obj.Get(iface, prop, dbus_interface=dbus.PROPERTIES_IFACE)
34
    except dbus.DBusException, e:
35
        if e.get_dbus_name() == 'org.freedesktop.DBus.Error.UnknownMethod':
36
            return None
37
        else:
38
            raise
432 by Evan Dandrea, Colin Watson, Evan Dandrea, Brian Murray, Luke Yelavich, Mario Limonciello
[ Colin Watson ]
39
40
def get_vendor_and_model(udi):
41
    vendor = ''
42
    model = ''
433 by Evan Dandrea, Colin Watson, Evan Dandrea, Jeremy Bicha
[ Colin Watson ]
43
    cmd = ['/sbin/udevadm', 'info', '--path=%s' % udi, '--query=property']
432 by Evan Dandrea, Colin Watson, Evan Dandrea, Brian Murray, Luke Yelavich, Mario Limonciello
[ Colin Watson ]
44
    with open('/dev/null', 'w') as devnull:
45
        out = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=devnull)
46
        out = out.communicate()
47
    if not out[1]:
48
        for prop in out[0].split('\n'):
49
            if prop.startswith('ID_VENDOR_FROM_DATABASE'):
50
                vendor = prop.split('ID_VENDOR_FROM_DATABASE=')[1]
51
            elif prop.startswith('ID_MODEL_FROM_DATABASE'):
52
                model = prop.split('ID_MODEL_FROM_DATABASE=')[1]
53
    return (vendor, model)
54
55
def wireless_hardware_present():
56
    # NetworkManager keeps DBus objects for wireless devices around even when
57
    # the hardware switch is off.
58
    bus = dbus.SystemBus()
59
    manager = bus.get_object(NM, '/org/freedesktop/NetworkManager')
478 by Colin Watson
* Bump fallback Ubuntu version number in ubi-language to 12.04.
60
    try:
61
        devices = manager.GetDevices()
62
    except dbus.DBusException:
63
        return False
432 by Evan Dandrea, Colin Watson, Evan Dandrea, Brian Murray, Luke Yelavich, Mario Limonciello
[ Colin Watson ]
64
    for device_path in devices:
65
        device_obj = bus.get_object(NM, device_path)
66
        if get_prop(device_obj, NM_DEVICE, 'DeviceType') == DEVICE_TYPE_WIFI:
67
            return True
68
    return False
69
70
class NetworkManager:
71
    def __init__(self, model, state_changed=None):
72
        self.model = model
73
        self.timeout_id = 0
74
        self.start(state_changed)
433 by Evan Dandrea, Colin Watson, Evan Dandrea, Jeremy Bicha
[ Colin Watson ]
75
        self.active_connection = None
432 by Evan Dandrea, Colin Watson, Evan Dandrea, Brian Murray, Luke Yelavich, Mario Limonciello
[ Colin Watson ]
76
77
    def start(self, state_changed=None):
78
        self.bus = dbus.SystemBus()
79
        self.manager = self.bus.get_object(NM, '/org/freedesktop/NetworkManager')
80
        add = self.bus.add_signal_receiver
81
        add(self.queue_build_cache, 'AccessPointAdded', NM_DEVICE_WIFI, NM)
82
        add(self.queue_build_cache, 'AccessPointRemoved', NM_DEVICE_WIFI, NM)
83
        if state_changed:
84
            add(state_changed, 'StateChanged', NM, NM)
85
        add(self.queue_build_cache, 'DeviceAdded', NM, NM)
86
        add(self.queue_build_cache, 'DeviceRemoved', NM, NM)
87
        add(self.properties_changed, 'PropertiesChanged', NM_AP,
88
            path_keyword='path')
89
        self.build_cache()
90
        self.build_passphrase_cache()
91
433 by Evan Dandrea, Colin Watson, Evan Dandrea, Jeremy Bicha
[ Colin Watson ]
92
    def get_state(self):
93
        return self.manager.state()
94
95
    def is_connected(self, device, ap):
96
        device_obj = self.bus.get_object(NM, device)
97
        connectedap = get_prop(device_obj, NM_DEVICE_WIFI, 'ActiveAccessPoint')
98
        if not connectedap:
99
            return False
100
        connect_obj = self.bus.get_object(NM, connectedap)
445 by Evan Dandrea
* Add power indicator to the installer panel.
101
        ssid = get_prop(connect_obj, NM_AP, 'Ssid')
102
        if ssid:
103
            return ap == decode_ssid(ssid)
104
        else:
105
            return False
433 by Evan Dandrea, Colin Watson, Evan Dandrea, Jeremy Bicha
[ Colin Watson ]
106
432 by Evan Dandrea, Colin Watson, Evan Dandrea, Brian Murray, Luke Yelavich, Mario Limonciello
[ Colin Watson ]
107
    def connect_to_ap(self, device, ap, passphrase=None):
108
        device_obj = self.bus.get_object(NM, device)
109
        ap_list = device_obj.GetAccessPoints(dbus_interface=NM_DEVICE_WIFI)
110
        saved_strength = 0
111
        saved_path = ''
112
        for ap_path in ap_list:
113
            ap_obj = self.bus.get_object(NM, ap_path)
445 by Evan Dandrea
* Add power indicator to the installer panel.
114
            ssid = get_prop(ap_obj, NM_AP, 'Ssid')
115
            if ssid:
116
                strength = get_prop(ap_obj, NM_AP, 'Strength')
117
                if decode_ssid(ssid) == ap and saved_strength < strength:
118
                    # Connect to the strongest AP.
119
                    saved_strength = strength
120
                    saved_path = ap_path
432 by Evan Dandrea, Colin Watson, Evan Dandrea, Brian Murray, Luke Yelavich, Mario Limonciello
[ Colin Watson ]
121
        if not saved_path:
122
            return
123
124
        obj = dbus.Dictionary(signature='sa{sv}')
125
        if passphrase:
126
            obj['802-11-wireless-security'] = { 'psk' : passphrase }
433 by Evan Dandrea, Colin Watson, Evan Dandrea, Jeremy Bicha
[ Colin Watson ]
127
        self.active_connection = self.manager.AddAndActivateConnection(
128
            obj, dbus.ObjectPath(device), dbus.ObjectPath(saved_path))[1]
129
130
    def disconnect_from_ap(self):
131
        if self.active_connection is not None:
132
            self.manager.DeactivateConnection(self.active_connection)
432 by Evan Dandrea, Colin Watson, Evan Dandrea, Brian Murray, Luke Yelavich, Mario Limonciello
[ Colin Watson ]
133
134
    def build_passphrase_cache(self):
135
        self.passphrases_cache = {}
136
        settings_obj = self.bus.get_object(NM, NM_SETTINGS_PATH)
137
        for conn in settings_obj.ListConnections(dbus_interface=NM_SETTINGS):
138
            conn_obj = self.bus.get_object(NM, conn)
139
            props = conn_obj.GetSettings(dbus_interface=NM_SETTINGS_CONN)
140
            if '802-11-wireless-security' in props:
141
                try:
142
                    sec = conn_obj.GetSecrets('802-11-wireless-security',
143
                                              dbus_interface=NM_SETTINGS_CONN)
144
                    sec = sec['802-11-wireless-security'].values()[0]
145
                    ssid = decode_ssid(props['802-11-wireless']['ssid'])
146
                    self.passphrases_cache[ssid] = sec
147
                except dbus.exceptions.DBusException, e:
148
                    if e.get_dbus_name() != NM_ERROR_NOSECRETS:
149
                        raise
150
151
    def ssid_in_model(self, iterator, ssid, security):
152
        i = self.model.iter_children(iterator)
153
        while i:
154
            row = self.model[i]
155
            if row[0] == ssid and row[1] == security:
156
                return i
157
            i = self.model.iter_next(i)
158
        return None
159
160
    def prune(self, iterator, ssids):
161
        to_remove = []
162
        while iterator:
163
            ssid = self.model[iterator][0]
164
            if ssid not in ssids:
165
                to_remove.append(iterator)
166
            iterator = self.model.iter_next(iterator)
167
        for iterator in to_remove:
168
            self.model.remove(iterator)
169
170
    def queue_build_cache(self, *args):
171
        if self.timeout_id:
172
            GObject.source_remove(self.timeout_id)
173
        self.timeout_id = GObject.timeout_add(500, self.build_cache)
471 by Colin Watson
* Handle interface change in ICU 4.8: unknown time zones result in
174
432 by Evan Dandrea, Colin Watson, Evan Dandrea, Brian Murray, Luke Yelavich, Mario Limonciello
[ Colin Watson ]
175
    def properties_changed(self, props, path=None):
176
        if 'Strength' in props:
177
            ap_obj = self.bus.get_object(NM, path)
445 by Evan Dandrea
* Add power indicator to the installer panel.
178
            ssid = get_prop(ap_obj, NM_AP, 'Ssid')
179
            if ssid:
180
                ssid = decode_ssid(ssid)
479 by Colin Watson, Colin Watson, Brian Murray
[ Colin Watson ]
181
                security = (get_prop(ap_obj, NM_AP, 'WpaFlags') != 0 or
182
                            get_prop(ap_obj, NM_AP, 'RsnFlags') != 0)
445 by Evan Dandrea
* Add power indicator to the installer panel.
183
                strength = int(props['Strength'])
184
                iterator = self.model.get_iter_first()
185
                while iterator:
186
                    i = self.ssid_in_model(iterator, ssid, security)
187
                    if i:
471 by Colin Watson
* Handle interface change in ICU 4.8: unknown time zones result in
188
                        self.model.set_value(i, 2, strength)
445 by Evan Dandrea
* Add power indicator to the installer panel.
189
                    iterator = self.model.iter_next(iterator)
432 by Evan Dandrea, Colin Watson, Evan Dandrea, Brian Murray, Luke Yelavich, Mario Limonciello
[ Colin Watson ]
190
191
    def build_cache(self):
192
        devices = self.manager.GetDevices()
193
        for device_path in devices:
194
            device_obj = self.bus.get_object(NM, device_path)
195
            if get_prop(device_obj, NM_DEVICE, 'DeviceType') != DEVICE_TYPE_WIFI:
196
                continue
197
            iterator = None
198
            i = self.model.get_iter_first()
199
            while i:
200
                if self.model[i][0] == device_path:
201
                    iterator = i
202
                    break
203
                i = self.model.iter_next(i)
204
            if not iterator:
205
                udi = get_prop(device_obj, NM_DEVICE, 'Udi')
445 by Evan Dandrea
* Add power indicator to the installer panel.
206
                if udi:
207
                    vendor, model = get_vendor_and_model(udi)
208
                else:
209
                    vendor, model = ('', '')
432 by Evan Dandrea, Colin Watson, Evan Dandrea, Brian Murray, Luke Yelavich, Mario Limonciello
[ Colin Watson ]
210
                iterator = self.model.append(None, [device_path, vendor, model])
211
            ap_list = device_obj.GetAccessPoints(dbus_interface=NM_DEVICE_WIFI)
212
            ssids = []
213
            for ap_path in ap_list:
214
                ap_obj = self.bus.get_object(NM, ap_path)
445 by Evan Dandrea
* Add power indicator to the installer panel.
215
                ssid = get_prop(ap_obj, NM_AP, 'Ssid')
216
                if ssid:
217
                    ssid = decode_ssid(ssid)
218
                    strength = int(get_prop(ap_obj, NM_AP, 'Strength') or 0)
479 by Colin Watson, Colin Watson, Brian Murray
[ Colin Watson ]
219
                    security = (get_prop(ap_obj, NM_AP, 'WpaFlags') != 0 or
220
                                get_prop(ap_obj, NM_AP, 'RsnFlags') != 0)
445 by Evan Dandrea
* Add power indicator to the installer panel.
221
                    i = self.ssid_in_model(iterator, ssid, security)
222
                    if not i:
223
                        self.model.append(iterator, [ssid, security, strength])
224
                    else:
471 by Colin Watson
* Handle interface change in ICU 4.8: unknown time zones result in
225
                        self.model.set_value(i, 2, strength)
445 by Evan Dandrea
* Add power indicator to the installer panel.
226
                    ssids.append(ssid)
432 by Evan Dandrea, Colin Watson, Evan Dandrea, Brian Murray, Luke Yelavich, Mario Limonciello
[ Colin Watson ]
227
            i = self.model.iter_children(iterator)
228
            self.prune(i, ssids)
229
        i = self.model.get_iter_first()
230
        self.prune(i, devices)
231
        return False
232
233
class NetworkManagerTreeView(Gtk.TreeView):
234
    __gtype_name__ = 'NetworkManagerTreeView'
235
    def __init__(self, password_entry=None, state_changed=None):
236
        Gtk.TreeView.__init__(self)
237
        self.password_entry = password_entry
238
        self.configure_icons()
239
        model = Gtk.TreeStore(str, object, object)
240
        model.set_sort_column_id(0, Gtk.SortType.ASCENDING)
241
        # TODO eventually this will subclass GenericTreeModel.
242
        self.wifi_model = NetworkManager(model, state_changed)
243
        self.set_model(model)
244
245
        ssid_column = Gtk.TreeViewColumn('')
246
        cell_pixbuf = Gtk.CellRendererPixbuf()
247
        cell_text = Gtk.CellRendererText()
248
        ssid_column.pack_start(cell_pixbuf, False)
249
        ssid_column.pack_start(cell_text, True)
250
        ssid_column.set_cell_data_func(cell_text, self.data_func)
251
        ssid_column.set_cell_data_func(cell_pixbuf, self.pixbuf_func)
252
        self.connect('row-activated', self.row_activated)
253
254
        self.append_column(ssid_column)
255
        self.set_headers_visible(False)
462 by Evan Dandrea
* Expand the list of wireless networks under each card automatically,
256
        self.setup_row_expansion_handling(model)
257
258
    def setup_row_expansion_handling(self, model):
259
        """
260
        If the user collapses a row, save that state. If all the APs go away
261
        and then return, such as when the user toggles the wifi kill switch,
262
        the UI should keep the row collapsed if it already was, or expand it.
263
        """
432 by Evan Dandrea, Colin Watson, Evan Dandrea, Brian Murray, Luke Yelavich, Mario Limonciello
[ Colin Watson ]
264
        self.expand_all()
462 by Evan Dandrea
* Expand the list of wireless networks under each card automatically,
265
        self.rows_changed_id = None
266
        def queue_rows_changed(*args):
267
            if self.rows_changed_id:
268
                GObject.source_remove(self.rows_changed_id)
269
            self.rows_changed_id = GObject.idle_add(self.rows_changed)
270
        model.connect('row-inserted', queue_rows_changed)
271
        model.connect('row-deleted', queue_rows_changed)
272
273
        self.user_collapsed = {}
274
        def collapsed(self, iterator, path, collapse):
275
            udi = model[iterator][0]
276
            self.user_collapsed[udi] = collapse
277
        self.connect('row-collapsed', collapsed, True)
278
        self.connect('row-expanded', collapsed, False)
279
280
    def rows_changed(self, *args):
281
        model = self.get_model()
282
        i = model.get_iter_first()
283
        while i:
284
            udi = model[i][0]
285
            try:
286
                if not self.user_collapsed[udi]:
287
                    path = model.get_path(i)
288
                    self.expand_row(path, False)
289
            except KeyError:
290
                path = model.get_path(i)
291
                self.expand_row(path, False)
292
            i = model.iter_next(i)
432 by Evan Dandrea, Colin Watson, Evan Dandrea, Brian Murray, Luke Yelavich, Mario Limonciello
[ Colin Watson ]
293
433 by Evan Dandrea, Colin Watson, Evan Dandrea, Jeremy Bicha
[ Colin Watson ]
294
    def get_state(self):
295
        return self.wifi_model.get_state()
296
297
    def disconnect_from_ap(self):
298
        self.wifi_model.disconnect_from_ap()
299
432 by Evan Dandrea, Colin Watson, Evan Dandrea, Brian Murray, Luke Yelavich, Mario Limonciello
[ Colin Watson ]
300
    def row_activated(self, unused, path, column):
301
        passphrase = None
302
        if self.password_entry:
303
            passphrase = self.password_entry.get_text()
304
        self.connect_to_selection(passphrase)
305
306
    def configure_icons(self):
307
        it = Gtk.IconTheme()
308
        default = Gtk.IconTheme.get_default()
309
        default = default.load_icon(Gtk.STOCK_MISSING_IMAGE, 22, 0)
310
        it.set_custom_theme('ubuntu-mono-light')
311
        self.icons = []
312
        for n in ['nm-signal-00',
313
                  'nm-signal-25',
314
                  'nm-signal-50',
315
                  'nm-signal-75',
316
                  'nm-signal-100',
317
                  'nm-signal-00-secure',
318
                  'nm-signal-25-secure',
319
                  'nm-signal-50-secure',
320
                  'nm-signal-75-secure',
321
                  'nm-signal-100-secure']:
322
            ico = it.lookup_icon(n, 22, 0)
323
            if ico:
324
                ico = ico.load_icon()
325
            else:
326
                ico = default
327
            self.icons.append(ico)
328
329
    def pixbuf_func(self, column, cell, model, iterator, data):
330
        if not model.iter_parent(iterator):
331
            cell.set_property('pixbuf', None)
332
            return
333
        strength = model[iterator][2]
334
        if strength < 30:
335
            icon = 0
336
        elif strength < 50:
337
            icon = 1
338
        elif strength < 70:
339
            icon = 2
340
        elif strength < 90:
341
            icon = 3
342
        else:
343
            icon = 4
344
        if model[iterator][1]:
345
            icon += 5
346
        cell.set_property('pixbuf', self.icons[icon])
347
348
    def data_func(self, column, cell, model, iterator, data):
349
        ssid = model[iterator][0]
350
351
        if not model.iter_parent(iterator):
352
            txt = '%s %s' % (model[iterator][1], model[iterator][2])
353
            cell.set_property('text', txt)
354
        else:
355
            cell.set_property('text', ssid)
356
357
    def get_passphrase(self, ssid):
358
        try:
359
            cached = self.wifi_model.passphrases_cache[ssid]
360
        except KeyError:
361
            return ''
362
        return cached
363
460 by Evan Dandrea
* Don't crash if the user selects the wireless card header and presses
364
    def is_row_an_ap(self):
365
        model, iterator = self.get_selection().get_selected()
366
        if iterator is None:
367
            return False
368
        return model.iter_parent(iterator) is not None
471 by Colin Watson
* Handle interface change in ICU 4.8: unknown time zones result in
369
433 by Evan Dandrea, Colin Watson, Evan Dandrea, Jeremy Bicha
[ Colin Watson ]
370
    def is_row_connected(self):
371
        model, iterator = self.get_selection().get_selected()
460 by Evan Dandrea
* Don't crash if the user selects the wireless card header and presses
372
        if iterator is None:
373
            return False
433 by Evan Dandrea, Colin Watson, Evan Dandrea, Jeremy Bicha
[ Colin Watson ]
374
        ssid = model[iterator][0]
375
        parent = model.iter_parent(iterator)
376
        if parent and self.wifi_model.is_connected(model[parent][0], ssid):
377
            return True
378
        else:
379
            return False
380
432 by Evan Dandrea, Colin Watson, Evan Dandrea, Brian Murray, Luke Yelavich, Mario Limonciello
[ Colin Watson ]
381
    def connect_to_selection(self, passphrase):
382
        model, iterator = self.get_selection().get_selected()
383
        ssid = model[iterator][0]
384
        parent = model.iter_parent(iterator)
385
        if parent:
386
            self.wifi_model.connect_to_ap(model[parent][0], ssid, passphrase)
471 by Colin Watson
* Handle interface change in ICU 4.8: unknown time zones result in
387
432 by Evan Dandrea, Colin Watson, Evan Dandrea, Brian Murray, Luke Yelavich, Mario Limonciello
[ Colin Watson ]
388
389
GObject.type_register(NetworkManagerTreeView)
390
480 by Colin Watson
* Convert from deprecated GtkHBox/GtkVBox to GtkBox, and from
391
class NetworkManagerWidget(Gtk.Box):
432 by Evan Dandrea, Colin Watson, Evan Dandrea, Brian Murray, Luke Yelavich, Mario Limonciello
[ Colin Watson ]
392
    __gtype_name__ = 'NetworkManagerWidget'
393
    __gsignals__ = { 'connection' : (GObject.SignalFlags.RUN_FIRST,
433 by Evan Dandrea, Colin Watson, Evan Dandrea, Jeremy Bicha
[ Colin Watson ]
394
                                     GObject.TYPE_NONE, (GObject.TYPE_UINT,)),
395
                     'selection_changed' : (GObject.SignalFlags.RUN_FIRST,
508 by Stéphane Graber, James M. Leddy, Colin Watson, Stéphane Graber, Kent Baxley, Harald Sitter
[ James M. Leddy ]
396
                                            GObject.TYPE_NONE, ()),
397
                     'pw_validated' : (GObject.SignalFlags.RUN_FIRST,
398
                                       GObject.TYPE_NONE, (GObject.TYPE_BOOLEAN,))}
432 by Evan Dandrea, Colin Watson, Evan Dandrea, Brian Murray, Luke Yelavich, Mario Limonciello
[ Colin Watson ]
399
    def __init__(self):
480 by Colin Watson
* Convert from deprecated GtkHBox/GtkVBox to GtkBox, and from
400
        Gtk.Box.__init__(self)
401
        self.set_orientation(Gtk.Orientation.VERTICAL)
432 by Evan Dandrea, Colin Watson, Evan Dandrea, Brian Murray, Luke Yelavich, Mario Limonciello
[ Colin Watson ]
402
        self.set_spacing(12)
403
        self.password_entry = Gtk.Entry()
404
        self.view = NetworkManagerTreeView(self.password_entry,
405
                                           self.state_changed)
406
        scrolled_window = Gtk.ScrolledWindow()
407
        scrolled_window.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
408
        scrolled_window.set_shadow_type(Gtk.ShadowType.IN)
409
        scrolled_window.add(self.view)
480 by Colin Watson
* Convert from deprecated GtkHBox/GtkVBox to GtkBox, and from
410
        self.pack_start(scrolled_window, True, True, 0)
411
        self.hbox = Gtk.Box(spacing=6)
432 by Evan Dandrea, Colin Watson, Evan Dandrea, Brian Murray, Luke Yelavich, Mario Limonciello
[ Colin Watson ]
412
        self.pack_start(self.hbox, False, True, 0)
462 by Evan Dandrea
* Expand the list of wireless networks under each card automatically,
413
        self.password_label = Gtk.Label('Password:')
432 by Evan Dandrea, Colin Watson, Evan Dandrea, Brian Murray, Luke Yelavich, Mario Limonciello
[ Colin Watson ]
414
        self.password_entry.set_visibility(False)
415
        self.password_entry.connect('activate', self.connect_to_ap)
508 by Stéphane Graber, James M. Leddy, Colin Watson, Stéphane Graber, Kent Baxley, Harald Sitter
[ James M. Leddy ]
416
        self.password_entry.connect('changed', self.password_entry_changed)
432 by Evan Dandrea, Colin Watson, Evan Dandrea, Brian Murray, Luke Yelavich, Mario Limonciello
[ Colin Watson ]
417
        self.display_password = Gtk.CheckButton('Display password')
418
        self.display_password.connect('toggled', self.display_password_toggled)
462 by Evan Dandrea
* Expand the list of wireless networks under each card automatically,
419
        self.hbox.pack_start(self.password_label, False, True, 0)
432 by Evan Dandrea, Colin Watson, Evan Dandrea, Brian Murray, Luke Yelavich, Mario Limonciello
[ Colin Watson ]
420
        self.hbox.pack_start(self.password_entry, True, True, 0)
421
        self.hbox.pack_start(self.display_password, False, True, 0)
433 by Evan Dandrea, Colin Watson, Evan Dandrea, Jeremy Bicha
[ Colin Watson ]
422
        self.hbox.set_sensitive(False)
423
        self.selection = self.view.get_selection()
424
        self.selection.connect('changed', self.changed)
432 by Evan Dandrea, Colin Watson, Evan Dandrea, Brian Murray, Luke Yelavich, Mario Limonciello
[ Colin Watson ]
425
        self.show_all()
462 by Evan Dandrea
* Expand the list of wireless networks under each card automatically,
426
427
    def translate(self, password_label_text, display_password_text):
428
        self.password_label.set_label(password_label_text)
429
        self.display_password.set_label(display_password_text)
471 by Colin Watson
* Handle interface change in ICU 4.8: unknown time zones result in
430
433 by Evan Dandrea, Colin Watson, Evan Dandrea, Jeremy Bicha
[ Colin Watson ]
431
    def get_state(self):
432
        return self.view.get_state()
433
460 by Evan Dandrea
* Don't crash if the user selects the wireless card header and presses
434
    def is_row_an_ap(self):
435
        return self.view.is_row_an_ap()
436
433 by Evan Dandrea, Colin Watson, Evan Dandrea, Jeremy Bicha
[ Colin Watson ]
437
    def is_row_connected(self):
438
        return self.view.is_row_connected()
439
440
    def select_usable_row(self):
441
        self.selection.select_path('0:0')
442
432 by Evan Dandrea, Colin Watson, Evan Dandrea, Brian Murray, Luke Yelavich, Mario Limonciello
[ Colin Watson ]
443
    def state_changed(self, state):
444
        self.emit('connection', state)
445
508 by Stéphane Graber, James M. Leddy, Colin Watson, Stéphane Graber, Kent Baxley, Harald Sitter
[ James M. Leddy ]
446
    def password_is_valid(self):
447
        passphrase = self.password_entry.get_text()
448
        if len(passphrase) >= 8 and \
449
           len(passphrase) < 64 :
450
            return True
451
        if len(passphrase) == 64:
452
            for c in passphrase:
453
                if not c in string.hexdigits: return False
454
            return True
455
        else:
456
            return False
457
432 by Evan Dandrea, Colin Watson, Evan Dandrea, Brian Murray, Luke Yelavich, Mario Limonciello
[ Colin Watson ]
458
    def connect_to_ap(self, *args):
508 by Stéphane Graber, James M. Leddy, Colin Watson, Stéphane Graber, Kent Baxley, Harald Sitter
[ James M. Leddy ]
459
        if self.password_is_valid():
460
            passphrase = self.password_entry.get_text()
461
            self.view.connect_to_selection(passphrase)
432 by Evan Dandrea, Colin Watson, Evan Dandrea, Brian Murray, Luke Yelavich, Mario Limonciello
[ Colin Watson ]
462
433 by Evan Dandrea, Colin Watson, Evan Dandrea, Jeremy Bicha
[ Colin Watson ]
463
    def disconnect_from_ap(self):
464
        self.view.disconnect_from_ap()
508 by Stéphane Graber, James M. Leddy, Colin Watson, Stéphane Graber, Kent Baxley, Harald Sitter
[ James M. Leddy ]
465
        
466
    def password_entry_changed(self, *args):
467
        self.emit('pw_validated', self.password_is_valid())
433 by Evan Dandrea, Colin Watson, Evan Dandrea, Jeremy Bicha
[ Colin Watson ]
468
432 by Evan Dandrea, Colin Watson, Evan Dandrea, Brian Murray, Luke Yelavich, Mario Limonciello
[ Colin Watson ]
469
    def display_password_toggled(self, *args):
470
        self.password_entry.set_visibility(self.display_password.get_active())
471
472
    def changed(self, selection):
473
        iterator = selection.get_selected()[1]
474
        if not iterator:
475
            return
476
        row = selection.get_tree_view().get_model()[iterator]
477
        secure = row[1]
478
        ssid = row[0]
479
        if secure:
480
            self.hbox.set_sensitive(True)
481
            passphrase = self.view.get_passphrase(ssid)
482
            self.password_entry.set_text(passphrase)
508 by Stéphane Graber, James M. Leddy, Colin Watson, Stéphane Graber, Kent Baxley, Harald Sitter
[ James M. Leddy ]
483
            self.emit('pw_validated', False)
432 by Evan Dandrea, Colin Watson, Evan Dandrea, Brian Murray, Luke Yelavich, Mario Limonciello
[ Colin Watson ]
484
        else:
485
            self.hbox.set_sensitive(False)
486
            self.password_entry.set_text('')
508 by Stéphane Graber, James M. Leddy, Colin Watson, Stéphane Graber, Kent Baxley, Harald Sitter
[ James M. Leddy ]
487
            self.emit('pw_validated', True)
433 by Evan Dandrea, Colin Watson, Evan Dandrea, Jeremy Bicha
[ Colin Watson ]
488
        self.emit('selection_changed')
432 by Evan Dandrea, Colin Watson, Evan Dandrea, Brian Murray, Luke Yelavich, Mario Limonciello
[ Colin Watson ]
489
508 by Stéphane Graber, James M. Leddy, Colin Watson, Stéphane Graber, Kent Baxley, Harald Sitter
[ James M. Leddy ]
490
432 by Evan Dandrea, Colin Watson, Evan Dandrea, Brian Murray, Luke Yelavich, Mario Limonciello
[ Colin Watson ]
491
GObject.type_register(NetworkManagerWidget)
492
493
if __name__ == '__main__':
494
    window = Gtk.Window()
495
    window.connect('destroy', Gtk.main_quit)
496
    window.set_size_request(300, 300)
497
    window.set_border_width(12)
498
    nm = NetworkManagerWidget()
499
    window.add(nm)
500
    window.show_all()
501
    Gtk.main()
502