2 # -*- coding: utf-8 -*-
4 # gui/g2/__init__.py - collection of classes for main UI with PyGTK
6 # Part of WiFi Radar: A utility for managing WiFi profiles on GNU/Linux.
8 # Copyright (C) 2004-2005 Ahmad Baitalmal <ahmad@baitalmal.com>
9 # Copyright (C) 2005 Nicolas Brouard <nicolas.brouard@mandrake.org>
10 # Copyright (C) 2005-2009 Brian Elliott Finley <brian@thefinleys.com>
11 # Copyright (C) 2006 David Decotigny <com.d2@free.fr>
12 # Copyright (C) 2006 Simon Gerber <gesimu@gmail.com>
13 # Copyright (C) 2006-2007 Joey Hurst <jhurst@lucubrate.org>
14 # Copyright (C) 2012 Anari Jalakas <anari.jalakas@gmail.com>
15 # Copyright (C) 2006, 2009 Ante Karamatic <ivoks@ubuntu.com>
16 # Copyright (C) 2009-2010,2014 Sean Robinson <robinson@tuxfamily.org>
17 # Copyright (C) 2010 Prokhor Shuchalov <p@shuchalov.ru>
19 # This program is free software; you can redistribute it and/or modify
20 # it under the terms of the GNU General Public License as published by
21 # the Free Software Foundation; version 2 of the License.
23 # This program is distributed in the hope that it will be useful,
24 # but WITHOUT ANY WARRANTY; without even the implied warranty of
25 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 # GNU General Public License in LICENSE.GPL for more details.
28 # You should have received a copy of the GNU General Public License
29 # along with this program; if not, write to:
30 # Free Software Foundation, Inc.
31 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
35 from __future__
import unicode_literals
45 from wifiradar
.config
import make_section_name
46 import wifiradar
.connections
as connections
47 from wifiradar
.misc
import _
, PipeError
, get_new_profile
48 from wifiradar
.pubsub
import Message
50 from . import profile
as profile_ed
51 from . import transients
54 logger
= logging
.getLogger(__name__
)
57 # Create a bunch of icons from files in the package.
58 known_profile_icon
= gtk
.gdk
.pixbuf_new_from_file('pixmaps/known_profile.png')
59 unknown_profile_icon
= gtk
.gdk
.pixbuf_new_from_file('pixmaps/unknown_profile.png')
60 signal_none_pb
= gtk
.gdk
.pixbuf_new_from_file('pixmaps/signal_none.xpm')
61 signal_low_pb
= gtk
.gdk
.pixbuf_new_from_file('pixmaps/signal_low.xpm')
62 signal_barely_pb
= gtk
.gdk
.pixbuf_new_from_file('pixmaps/signal_barely.xpm')
63 signal_ok_pb
= gtk
.gdk
.pixbuf_new_from_file('pixmaps/signal_ok.xpm')
64 signal_best_pb
= gtk
.gdk
.pixbuf_new_from_file('pixmaps/signal_best.xpm')
66 def pixbuf_from_known(known
):
67 """ Return a :class:`gtk.gdk.Pixbuf` icon to represent :data:`known`.
68 Any true :data:`known` value returns the icon showing previous
72 return known_profile_icon
73 return unknown_profile_icon
75 def pixbuf_from_signal(signal
):
76 """ Return a :class:`gtk.gdk.Pixbuf` icon to indicate the :data:`signal`
77 level. :data:`signal` is as reported by iwlist (may be arbitrary
78 scale in 0-100 or -X dBm)
81 # Shift signal up by 80 to convert dBm scale to arbitrary scale.
90 return signal_barely_pb
97 """ Function to boot-strap the UI. :data:`ui_pipe` is one half of a
98 :class:`multiprocessing.Pipe` which will be given to the main UI
99 element for intra-app communication.
101 # Reset SIGINT handler so that Ctrl+C in launching terminal
102 # will kill the application.
103 signal
.signal(signal
.SIGINT
, signal
.SIG_DFL
)
104 gtk
.gdk
.threads_init()
105 ui
= RadarWindow(ui_pipe
)
111 class RadarWindow(gtk
.Dialog
, object):
112 def __init__(self
, msg_pipe
):
113 """ Create a new RadarWindow wanting to communicate through
114 :data:`msg_pipe`, a :class:`multiprocessing.Connection`.
116 gtk
.Dialog
.__init
__(self
, 'WiFi Radar', None, gtk
.DIALOG_MODAL
)
117 self
.msg_pipe
= msg_pipe
119 self
.icon
= gtk
.gdk
.pixbuf_new_from_file('pixmaps/wifi-radar.png')
121 self
.set_icon(self
.icon
)
122 self
.set_border_width(10)
123 self
.set_size_request(550, 300)
124 self
.connect('delete_event', self
.delete_event
)
125 # let's create all our widgets
126 self
.current_network
= gtk
.Label()
127 self
.current_network
.set_property('justify', gtk
.JUSTIFY_CENTER
)
128 self
.current_network
.show()
129 self
.close_button
= gtk
.Button(_('Close'), gtk
.STOCK_CLOSE
)
130 self
.close_button
.show()
131 self
.close_button
.connect('clicked', self
.delete_event
, None)
132 self
.about_button
= gtk
.Button(_('About'), gtk
.STOCK_ABOUT
)
133 self
.about_button
.show()
134 self
.about_button
.connect('clicked', self
.show_about_info
, None)
135 self
.preferences_button
= gtk
.Button(_('Preferences'), gtk
.STOCK_PREFERENCES
)
136 self
.preferences_button
.show()
137 self
.preferences_button
.connect('clicked', self
.request_preferences_edit
)
138 # essid bssid known_icon known available wep_icon signal_level mode protocol channel
139 self
.pstore
= gtk
.ListStore(str, str, gtk
.gdk
.Pixbuf
, bool, bool, str, gtk
.gdk
.Pixbuf
, str, str, str)
140 self
.plist
= gtk
.TreeView(self
.pstore
)
141 # The icons column, known and encryption
142 self
.pix_cell
= gtk
.CellRendererPixbuf()
143 self
.wep_cell
= gtk
.CellRendererPixbuf()
144 self
.icons_cell
= gtk
.CellRendererText()
145 self
.icons_col
= gtk
.TreeViewColumn()
146 self
.icons_col
.pack_start(self
.pix_cell
, False)
147 self
.icons_col
.pack_start(self
.wep_cell
, False)
148 self
.icons_col
.add_attribute(self
.pix_cell
, 'pixbuf', 2)
149 self
.icons_col
.add_attribute(self
.wep_cell
, 'stock-id', 5)
150 self
.plist
.append_column(self
.icons_col
)
152 self
.ap_cell
= gtk
.CellRendererText()
153 self
.ap_col
= gtk
.TreeViewColumn(_('Access Point'))
154 self
.ap_col
.pack_start(self
.ap_cell
, True)
155 self
.ap_col
.set_cell_data_func(self
.ap_cell
, self
._set
_ap
_col
_value
)
156 self
.plist
.append_column(self
.ap_col
)
158 self
.sig_cell
= gtk
.CellRendererPixbuf()
159 self
.signal_col
= gtk
.TreeViewColumn(_('Signal'))
160 self
.signal_col
.pack_start(self
.sig_cell
, True)
161 self
.signal_col
.add_attribute(self
.sig_cell
, 'pixbuf', 6)
162 self
.plist
.append_column(self
.signal_col
)
164 self
.mode_cell
= gtk
.CellRendererText()
165 self
.mode_col
= gtk
.TreeViewColumn(_('Mode'))
166 self
.mode_col
.pack_start(self
.mode_cell
, True)
167 self
.mode_col
.add_attribute(self
.mode_cell
, 'text', 7)
168 self
.plist
.append_column(self
.mode_col
)
169 # The protocol column
170 self
.prot_cell
= gtk
.CellRendererText()
171 self
.protocol_col
= gtk
.TreeViewColumn('802.11')
172 self
.protocol_col
.pack_start(self
.prot_cell
, True)
173 self
.protocol_col
.add_attribute(self
.prot_cell
, 'text', 8)
174 self
.plist
.append_column(self
.protocol_col
)
176 self
.channel_cell
= gtk
.CellRendererText()
177 self
.channel_col
= gtk
.TreeViewColumn(_('Channel'))
178 self
.channel_col
.pack_start(self
.channel_cell
, True)
179 self
.channel_col
.add_attribute(self
.channel_cell
, 'text', 9)
180 self
.plist
.append_column(self
.channel_col
)
182 self
.plist
.set_reorderable(True)
183 # detect d-n-d of AP in round-about way, since rows-reordered does not work as advertised
184 self
.pstore
.connect('row-deleted', self
.update_auto_profile_order
)
185 # enable/disable buttons based on the selected network
186 self
.selected_network
= self
.plist
.get_selection()
187 self
.selected_network
.connect('changed', self
.on_network_selection
, None)
188 # the list scroll bar
189 sb
= gtk
.ScrolledWindow()
190 sb
.set_policy(gtk
.POLICY_AUTOMATIC
, gtk
.POLICY_AUTOMATIC
)
193 self
.new_button
= gtk
.Button(_('_New'))
194 self
.new_button
.connect('clicked', self
.create_new_profile
)
195 self
.new_button
.show()
196 # Add Configure button
197 self
.edit_button
= gtk
.Button(_('C_onfigure'))
198 self
.edit_button
.connect('clicked', self
.request_profile_edit
)
199 self
.edit_button
.show()
200 self
.edit_button
.set_sensitive(False)
202 self
.delete_button
= gtk
.Button(_('_Delete'))
203 self
.delete_button
.connect('clicked', self
.request_profile_delete
)
204 self
.delete_button
.show()
205 self
.delete_button
.set_sensitive(False)
207 self
.connect_button
= gtk
.Button(_('Co_nnect'))
208 self
.connect_button
.connect('clicked', self
.connect_profile
, None)
209 # Add Disconnect button
210 self
.disconnect_button
= gtk
.Button(_('D_isconnect'))
211 self
.disconnect_button
.connect('clicked', self
.disconnect_profile
, None)
212 # lets add our widgets
213 rows
= gtk
.VBox(False, 3)
214 net_list
= gtk
.HBox(False, 0)
215 listcols
= gtk
.HBox(False, 0)
216 prows
= gtk
.VBox(False, 0)
219 net_list
.pack_start(sb
, True, True, 0)
222 rows
.pack_start(net_list
, True, True, 0)
223 rows
.pack_start(self
.current_network
, False, True, 0)
225 listcols
.pack_start(rows
, True, True, 0)
226 listcols
.pack_start(prows
, False, False, 5)
228 prows
.pack_start(self
.new_button
, False, False, 2)
229 prows
.pack_start(self
.edit_button
, False, False, 2)
230 prows
.pack_start(self
.delete_button
, False, False, 2)
231 prows
.pack_end(self
.connect_button
, False, False, 2)
232 prows
.pack_end(self
.disconnect_button
, False, False, 2)
234 self
.action_area
.pack_start(self
.about_button
)
235 self
.action_area
.pack_start(self
.preferences_button
)
236 self
.action_area
.pack_start(self
.close_button
)
241 self
.vbox
.add(listcols
)
242 self
.vbox
.set_spacing(3)
245 # Now, immediately hide these two. The proper one will be
246 # displayed later, based on interface state. -BEF-
247 self
.disconnect_button
.hide()
248 self
.connect_button
.hide()
249 self
.connect_button
.set_sensitive(False)
251 # set up status window for later use
252 self
.status_window
= transients
.StatusWindow(self
)
253 self
.status_window
.cancel_button
.connect('clicked', self
.disconnect_profile
, 'cancel')
256 # Check for incoming messages every 25 ms, a.k.a. 40 Hz.
257 glib
.timeout_add(25, self
.run
)
260 """ Watch for incoming messages.
262 if self
.msg_pipe
.poll():
264 msg
= self
.msg_pipe
.recv()
265 except (EOFError, IOError) as e
:
266 # This is bad, really bad.
267 logger
.critical(_('read on closed Pipe ({PIPE}), '
268 'failing...').format(PIPE
=self
.msg_pipe
))
271 self
._check
_message
(msg
)
272 # Update the UI before returning.
273 self
.update_network_info()
274 self
.update_connect_buttons()
277 def _check_message(self
, msg
):
278 """ Process incoming messages.
280 if msg
.topic
== 'EXIT':
282 elif msg
.topic
== 'CONFIG-UPDATE':
283 # Replace configuration manager with the one in msg.details.
284 self
.config
= msg
.details
285 elif msg
.topic
== 'PROFILE-EDIT':
287 self
.edit_profile(msg
.details
)
288 elif msg
.topic
== 'PROFILE-UPDATE':
290 self
.update_profile(msg
.details
)
291 elif msg
.topic
== 'PROFILE-UNLIST':
293 self
.delete_profile(msg
.details
)
294 elif msg
.topic
== 'PROFILE-MOVE':
295 new_position
, profile
= msg
.details
297 if profile
['roaming']:
298 old_position
= self
.get_row_by_ap(profile
['essid'])
300 old_position
= self
.get_row_by_ap(profile
['essid'],
302 self
.pstore
.move_before(old_position
, self
.pstore
[new_position
].iter)
303 elif msg
.topic
== 'PREFS-EDIT':
305 self
.edit_preferences(msg
.details
)
306 elif msg
.topic
== 'ERROR':
308 transients
.ErrorDialog(self
, msg
.details
)
310 logger
.warning(_('unrecognized Message: "{MSG}"').format(MSG
=msg
))
312 def destroy(self
, widget
=None):
313 """ Quit the Gtk event loop. :data:`widget` is the widget
314 sending the signal, but it is ignored.
316 if self
.status_window
:
317 self
.status_window
.destroy()
320 def delete_event(self
, widget
=None, data
=None):
321 """ Shutdown the application. :data:`widget` is the widget sending
322 the signal and :data:`data` is a list of arbitrary arguments,
323 both are ignored. Always returns False to not propigate the
324 signal which called :func:`delete_event`.
326 self
._running
= False
327 self
.msg_pipe
.send(Message('EXIT', ''))
328 self
.msg_pipe
.close()
330 # process GTK events so that window hides more quickly
331 if sys
.modules
.has_key('gtk'):
332 while gtk
.events_pending():
333 gtk
.main_iteration(False)
337 def update_network_info(self
, profile
=None, ip
=None):
338 """ Update the current ip and essid shown to the user.
340 if (profile
is None) and (ip
is None):
341 self
.current_network
.set_text(_('Not Connected.'))
343 self
.current_network
.set_text(_('Connected to {PROFILE}\n'
344 'IP Address {IP}').format(PROFILE
=profile
, IP
=ip
))
346 def update_connect_buttons(self
, connected
=False):
347 """ Set the state of connect/disconnect buttons to reflect the
348 current connected state.
351 self
.connect_button
.hide()
352 self
.disconnect_button
.show()
354 self
.disconnect_button
.hide()
355 self
.connect_button
.show()
357 def _set_ap_col_value(self
, column
, cell
, model
, iter):
358 """ Set the text attribute of :data:`column` to the first two
359 :data:`model` values joined by a newline. This is for
360 displaying the :data:`essid` and :data:`bssid` in a single
363 essid
= model
.get_value(iter, 0)
364 bssid
= model
.get_value(iter, 1)
365 cell
.set_property('text', '\n'.join([essid
, bssid
]))
367 def get_row_by_ap(self
, essid
, bssid
=_(' Multiple APs')):
368 """ Returns a :class:`gtk.TreeIter` for the row which holds
369 :data:`essid` and :data:`bssid`.
371 :data:`bssid` is optional. If not given, :func:`get_row_by_ap`
372 will try to match a roaming profile with the given :data:`essid`.
374 If no match is found, it returns None.
376 for row
in self
.pstore
:
377 if (row
[0] == essid
) and (row
[1] == bssid
):
381 def on_network_selection(self
, widget
=None, data
=None):
382 """ Enable/disable buttons based on the selected network.
383 :data:`widget` is the widget sending the signal and :data:`data`
384 is a list of arbitrary arguments, both are ignored.
386 store
, selected_iter
= self
.selected_network
.get_selected()
387 if selected_iter
is None:
388 # No row is selected, disable all buttons except New.
389 # This occurs after a drag-and-drop.
390 self
.edit_button
.set_sensitive(False)
391 self
.delete_button
.set_sensitive(False)
392 self
.connect_button
.set_sensitive(False)
394 # One row is selected, so enable or disable buttons.
395 self
.connect_button
.set_sensitive(True)
396 if store
.get_value(selected_iter
, 3):
398 self
.edit_button
.set_sensitive(True)
399 self
.delete_button
.set_sensitive(True)
402 self
.edit_button
.set_sensitive(True)
403 self
.delete_button
.set_sensitive(False)
405 def show_about_info(self
, widget
=None, data
=None):
406 """ Handle the life-cycle of the About dialog. :data:`widget` is
407 the widget sending the signal and :data:`data` is a list of
408 arbitrary arguments, both are ignored.
410 about
= transients
.AboutDialog()
414 def request_preferences_edit(self
, widget
=None, data
=None):
415 """ Respond to a request to edit the application preferences.
416 :data:`widget` is the widget sending the signal and :data:`data`
417 is a list of arbitrary arguments, both are ignored.
419 self
.msg_pipe
.send(Message('PREFS-EDIT-REQUEST', ''))
421 def edit_preferences(self
, config
):
422 """ Allow the user to edit :data:`config`.
424 prefs_editor
= prefs
.PreferencesEditor(self
, config
)
425 response
, config_copy
= prefs_editor
.run()
426 if response
== gtk
.RESPONSE_APPLY
:
427 self
.msg_pipe
.send(Message('PREFS-UPDATE', config_copy
))
428 prefs_editor
.destroy()
430 def update_profile(self
, profile
):
431 """ Updates the display of :data:`profile`.
433 if profile
['roaming']:
434 prow_iter
= self
.get_row_by_ap(profile
['essid'])
436 prow_iter
= self
.get_row_by_ap(profile
['essid'], profile
['bssid'])
438 if prow_iter
is None:
439 # the AP is not in the list of APs on the screen
440 self
._add
_profile
(profile
)
442 # the AP is in the list of APs on the screen
443 self
._update
_row
(profile
, prow_iter
)
445 def _add_profile(self
, profile
):
446 """ Add :data:`profile` to the list of APs shown to the user.
448 if profile
['roaming']:
449 profile
['bssid'] = _(' Multiple APs')
452 if profile
['encrypted']:
453 wep
= gtk
.STOCK_DIALOG_AUTHENTICATION
455 self
.pstore
.append([profile
['essid'], profile
['bssid'],
456 known_profile_icon
, profile
['known'], profile
['available'],
457 wep
, signal_none_pb
, profile
['mode'], profile
['protocol'],
460 def _update_row(self
, profile
, row_iter
):
461 """ Change the values displayed in :data:`row_iter` (a
462 :class:`gtk.TreeIter`) using :data:`profile`.
465 if profile
['encrypted']:
466 wep
= gtk
.STOCK_DIALOG_AUTHENTICATION
467 # Update the Gtk objects.
468 self
.pstore
.set_value(row_iter
, 2, pixbuf_from_known(profile
['known']))
469 self
.pstore
.set_value(row_iter
, 3, profile
['known'])
470 self
.pstore
.set_value(row_iter
, 4, profile
['available'])
471 self
.pstore
.set_value(row_iter
, 5, wep
)
472 self
.pstore
.set_value(row_iter
, 6, pixbuf_from_signal(profile
['signal']))
473 self
.pstore
.set_value(row_iter
, 7, profile
['mode'])
474 self
.pstore
.set_value(row_iter
, 8, profile
['protocol'])
475 self
.pstore
.set_value(row_iter
, 9, profile
['channel'])
477 def create_new_profile(self
, widget
=None, profile
=None, data
=None):
478 """ Respond to a user request to create a new AP profile.
479 :data:`widget` is the widget sending the signal. :data:profile`
480 is an AP profile to use as the basis for the new profile. It
481 is likely empty or mostly empty. :data:`data` is a list of
482 arbitrary arguments. :data:`widget` and "data"`data` are both
485 The order of parameters is important. Because when this method
486 is called from a signal handler, :data:`widget` is always the
490 profile
= get_new_profile()
492 profile_editor
= profile_ed
.ProfileEditor(self
, profile
)
494 edited_profile
= profile_editor
.run()
496 self
.msg_pipe
.send(Message('ERROR', _('Cannot save empty ESSID')))
499 self
.msg_pipe
.send(Message('PROFILE-EDITED', (edited_profile
, profile
)))
501 profile_editor
.destroy()
503 def request_profile_edit(self
, widget
=None, data
=None):
504 """ Respond to a request to edit an AP profile. :data:`widget`
505 is the widget sending the signal and :data:`data` is a list
506 of arbitrary arguments, both are ignored.
508 store
, selected_iter
= self
.plist
.get_selection().get_selected()
509 if selected_iter
is not None:
510 essid
= self
.pstore
.get_value(selected_iter
, 0)
511 bssid
= self
.pstore
.get_value(selected_iter
, 1)
512 if bssid
== _(' Multiple APs'):
513 # AP list says this is a roaming profile
515 self
.msg_pipe
.send(Message('PROFILE-EDIT-REQUEST', (essid
, bssid
)))
517 def edit_profile(self
, profile
):
518 """ Allow the user to edit :data:`profile`.
520 profile_editor
= profile_ed
.ProfileEditor(self
, profile
)
521 edited_profile
= profile_editor
.run()
522 profile_editor
.destroy()
524 if edited_profile
is not None:
525 # Replace old profile.
526 self
.msg_pipe
.send(Message('PROFILE-EDITED',
527 (edited_profile
, profile
)))
529 def request_profile_delete(self
, widget
=None, data
=None):
530 """ Respond to a request to delete an AP profile (i.e. make the
531 profile unknown). Trying to delete an AP which is not configured
532 is a NOOP. Check with the user before deleting the profile.
533 :data:`widget` is the widget sending the signal and :data:`data`
534 is a list of arbitrary arguments, both are ignored.
536 store
, selected_iter
= self
.plist
.get_selection().get_selected()
537 if selected_iter
is not None:
538 if store
.get_value(selected_iter
, 3):
539 # The selected AP is configured (a.k.a. 'known').
540 essid
= self
.pstore
.get_value(selected_iter
, 0)
541 bssid
= self
.pstore
.get_value(selected_iter
, 1)
542 if bssid
== _(' Multiple APs'):
543 # AP list says this is a roaming profile
547 profile_name
= '{ESSID} ({BSSID})'.format(ESSID
=essid
,
550 dialog
= gtk
.MessageDialog(self
,
551 gtk
.DIALOG_DESTROY_WITH_PARENT | gtk
.DIALOG_MODAL
,
552 gtk
.MESSAGE_QUESTION
, gtk
.BUTTONS_YES_NO
,
553 _('Are you sure you want to delete the '
554 '{NAME} profile?').format(NAME
=profile_name
))
556 result
= dialog
.run()
560 if result
== gtk
.RESPONSE_YES
:
561 apname
= make_section_name(essid
, bssid
)
562 self
.msg_pipe
.send(Message('PROFILE-REMOVE', apname
))
564 def delete_profile(self
, profile
):
565 """ Remove :data:`profile` from the list of APs shown to the user.
567 if profile
['roaming']:
568 prow_iter
= self
.get_row_by_ap(profile
['essid'])
570 prow_iter
= self
.get_row_by_ap(profile
['essid'], profile
['bssid'])
571 if prow_iter
is not None:
572 self
.pstore
.remove(prow_iter
)
574 def connect_profile(self
, widget
, profile
, data
=None):
575 """ Respond to a request to connect to an AP.
579 'widget' -- gtk.Widget - The widget sending the event.
581 'profile' -- dictionary - The AP profile to which to connect.
583 'data' -- tuple - list of arbitrary arguments (not used)
589 store
, selected_iter
= self
.plist
.get_selection().get_selected()
590 if selected_iter
is None:
592 essid
= self
.pstore
.get_value(selected_iter
, 0)
593 bssid
= self
.pstore
.get_value(selected_iter
, 1)
594 known
= store
.get_value(selected_iter
, 3)
596 dlg
= gtk
.MessageDialog(self
,
597 gtk
.DIALOG_DESTROY_WITH_PARENT | gtk
.DIALOG_MODAL
,
598 gtk
.MESSAGE_QUESTION
, gtk
.BUTTONS_YES_NO
,
599 _('This network does not have a profile configured.\n\n'
600 'Would you like to create one now?'))
604 if res
== gtk
.RESPONSE_NO
:
606 profile
= get_new_profile()
607 profile
['essid'] = essid
608 profile
['bssid'] = bssid
609 if not self
.create_new_profile(widget
, profile
, data
):
612 # Check for roaming profile.
613 ap_name
= make_section_name(essid
, '')
614 profile
= self
.config
.get_profile(ap_name
)
616 # Check for normal profile.
617 ap_name
= make_section_name(essid
, bssid
)
618 profile
= self
.config
.get_profile(ap_name
)
620 # No configured profile
622 profile
['bssid'] = self
.access_points
[ap_name
]['bssid']
623 profile
['channel'] = self
.access_points
[ap_name
]['channel']
624 self
.msg_pipe
.send(Message('CONNECT', profile
))
626 def disconnect_profile(self
, widget
=None, data
=None):
627 """ Respond to a request to disconnect by sending a message to
628 ConnectionManager. :data:`widget` is the widget sending the
629 signal and :data:`data` is a list of arbitrary arguments, both
632 self
.msg_pipe
.send(Message('DISCONNECT', ''))
634 self
.status_window
.update_message(_('Canceling connection...'))
635 if sys
.modules
.has_key('gtk'):
636 while gtk
.events_pending():
637 gtk
.main_iteration(False)
639 def profile_order_updater(self
, model
, path
, iter, auto_profile_order
):
642 if model
.get_value(iter, 3) is True:
643 essid
= self
.pstore
.get_value(iter, 0)
644 bssid
= self
.pstore
.get_value(iter, 1)
645 if bssid
== _(' Multiple APs'):
647 apname
= make_section_name(essid
, bssid
)
648 auto_profile_order
.append(apname
)
650 def update_auto_profile_order(self
, widget
=None, data
=None, data2
=None):
651 """ Update the config file auto profile order from the on-screen
652 order. :data:`widget` is the widget sending the signal and
653 :data:`data` and :data:`data2` is a list of arbitrary arguments,
656 # recreate the auto_profile_order
657 auto_profile_order
= []
658 self
.pstore
.foreach(self
.profile_order_updater
, auto_profile_order
)
659 self
.msg_pipe
.send(Message('PROFILE-ORDER-UPDATE', auto_profile_order
))
662 # Make so we can be imported
663 if __name__
== '__main__':