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 <seankrobinson@gmail.com>
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 the Free Software
30 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
41 from wifiradar
.config
import make_section_name
42 import wifiradar
.connections
as connections
43 import wifiradar
.misc
as misc
44 from wifiradar
.pubsub
import Message
46 from . import profile
as profile_ed
47 from . import transients
50 logger
= logging
.getLogger(__name__
)
53 # Create a bunch of icons from files in the package.
54 known_profile_icon
= gtk
.gdk
.pixbuf_new_from_file("pixmaps/known_profile.png")
55 unknown_profile_icon
= gtk
.gdk
.pixbuf_new_from_file("pixmaps/unknown_profile.png")
56 signal_none_pb
= gtk
.gdk
.pixbuf_new_from_file("pixmaps/signal_none.xpm")
57 signal_low_pb
= gtk
.gdk
.pixbuf_new_from_file("pixmaps/signal_low.xpm")
58 signal_barely_pb
= gtk
.gdk
.pixbuf_new_from_file("pixmaps/signal_barely.xpm")
59 signal_ok_pb
= gtk
.gdk
.pixbuf_new_from_file("pixmaps/signal_ok.xpm")
60 signal_best_pb
= gtk
.gdk
.pixbuf_new_from_file("pixmaps/signal_best.xpm")
62 def pixbuf_from_known(known
):
63 """ Return a :class:`gtk.gdk.Pixbuf` icon to represent :data:`known`.
64 Any true :data:`known` value returns the icon showing previous
68 return known_profile_icon
69 return unknown_profile_icon
71 def pixbuf_from_signal(signal
):
72 """ Return a :class:`gtk.gdk.Pixbuf` icon to indicate the :data:`signal`
73 level. :data:`signal` is as reported by iwlist (may be arbitrary
74 scale in 0-100 or -X dBm)
77 # Shift signal up by 80 to convert dBm scale to arbitrary scale.
86 return signal_barely_pb
94 def __init__(self
, msg_pipe
):
95 """ Create a new RadarWindow wanting to communicate through
96 :data:`msg_pipe`, a :class:`multiprocessing.Connection`.
98 self
.msg_pipe
= msg_pipe
100 gtk
.gdk
.threads_init()
102 self
.icon
= gtk
.gdk
.pixbuf_new_from_file("pixmaps/wifi-radar.png")
104 self
.window
= gtk
.Dialog('WiFi Radar', None, gtk
.DIALOG_MODAL
)
105 self
.window
.set_icon(self
.icon
)
106 self
.window
.set_border_width(10)
107 self
.window
.set_size_request(550, 300)
108 self
.window
.set_title("WiFi Radar")
109 self
.window
.connect('delete_event', self
.delete_event
)
110 # let's create all our widgets
111 self
.current_network
= gtk
.Label()
112 self
.current_network
.set_property('justify', gtk
.JUSTIFY_CENTER
)
113 self
.current_network
.show()
114 self
.close_button
= gtk
.Button("Close", gtk
.STOCK_CLOSE
)
115 self
.close_button
.show()
116 self
.close_button
.connect('clicked', self
.delete_event
, None)
117 self
.about_button
= gtk
.Button("About", gtk
.STOCK_ABOUT
)
118 self
.about_button
.show()
119 self
.about_button
.connect('clicked', self
.show_about_info
, None)
120 self
.preferences_button
= gtk
.Button("Preferences", gtk
.STOCK_PREFERENCES
)
121 self
.preferences_button
.show()
122 self
.preferences_button
.connect('clicked', self
.edit_preferences
, None)
123 # essid bssid known_icon known available wep_icon signal_level mode protocol channel
124 self
.pstore
= gtk
.ListStore(str, str, gtk
.gdk
.Pixbuf
, bool, bool, str, gtk
.gdk
.Pixbuf
, str, str, str)
125 self
.plist
= gtk
.TreeView(self
.pstore
)
126 # The icons column, known and encryption
127 self
.pix_cell
= gtk
.CellRendererPixbuf()
128 self
.wep_cell
= gtk
.CellRendererPixbuf()
129 self
.icons_cell
= gtk
.CellRendererText()
130 self
.icons_col
= gtk
.TreeViewColumn()
131 self
.icons_col
.pack_start(self
.pix_cell
, False)
132 self
.icons_col
.pack_start(self
.wep_cell
, False)
133 self
.icons_col
.add_attribute(self
.pix_cell
, 'pixbuf', 2)
134 self
.icons_col
.add_attribute(self
.wep_cell
, 'stock-id', 5)
135 self
.plist
.append_column(self
.icons_col
)
137 self
.ap_cell
= gtk
.CellRendererText()
138 self
.ap_col
= gtk
.TreeViewColumn("Access Point")
139 self
.ap_col
.pack_start(self
.ap_cell
, True)
140 self
.ap_col
.set_cell_data_func(self
.ap_cell
, self
._set
_ap
_col
_value
)
141 self
.plist
.append_column(self
.ap_col
)
143 self
.sig_cell
= gtk
.CellRendererPixbuf()
144 self
.signal_col
= gtk
.TreeViewColumn("Signal")
145 self
.signal_col
.pack_start(self
.sig_cell
, True)
146 self
.signal_col
.add_attribute(self
.sig_cell
, 'pixbuf', 6)
147 self
.plist
.append_column(self
.signal_col
)
149 self
.mode_cell
= gtk
.CellRendererText()
150 self
.mode_col
= gtk
.TreeViewColumn("Mode")
151 self
.mode_col
.pack_start(self
.mode_cell
, True)
152 self
.mode_col
.add_attribute(self
.mode_cell
, 'text', 7)
153 self
.plist
.append_column(self
.mode_col
)
154 # The protocol column
155 self
.prot_cell
= gtk
.CellRendererText()
156 self
.protocol_col
= gtk
.TreeViewColumn("802.11")
157 self
.protocol_col
.pack_start(self
.prot_cell
, True)
158 self
.protocol_col
.add_attribute(self
.prot_cell
, 'text', 8)
159 self
.plist
.append_column(self
.protocol_col
)
161 self
.channel_cell
= gtk
.CellRendererText()
162 self
.channel_col
= gtk
.TreeViewColumn("Channel")
163 self
.channel_col
.pack_start(self
.channel_cell
, True)
164 self
.channel_col
.add_attribute(self
.channel_cell
, 'text', 9)
165 self
.plist
.append_column(self
.channel_col
)
167 self
.plist
.set_reorderable(True)
168 # detect d-n-d of AP in round-about way, since rows-reordered does not work as advertised
169 self
.pstore
.connect('row-deleted', self
.update_auto_profile_order
)
170 # enable/disable buttons based on the selected network
171 self
.selected_network
= self
.plist
.get_selection()
172 self
.selected_network
.connect('changed', self
.on_network_selection
, None)
173 # the list scroll bar
174 sb
= gtk
.VScrollbar(self
.plist
.get_vadjustment())
178 self
.new_button
= gtk
.Button("_New")
179 self
.new_button
.connect('clicked', self
.create_new_profile
, misc
.get_new_profile())
180 self
.new_button
.show()
181 # Add Configure button
182 self
.edit_button
= gtk
.Button("C_onfigure")
183 self
.edit_button
.connect('clicked', self
.request_profile_edit
, None)
184 self
.edit_button
.show()
185 self
.edit_button
.set_sensitive(False)
187 self
.delete_button
= gtk
.Button("_Delete")
188 self
.delete_button
.connect('clicked', self
.delete_profile_with_check
, None)
189 self
.delete_button
.show()
190 self
.delete_button
.set_sensitive(False)
192 self
.connect_button
= gtk
.Button("Co_nnect")
193 self
.connect_button
.connect('clicked', self
.connect_profile
, None)
194 # Add Disconnect button
195 self
.disconnect_button
= gtk
.Button("D_isconnect")
196 self
.disconnect_button
.connect('clicked', self
.disconnect_profile
, None)
197 # lets add our widgets
198 rows
= gtk
.VBox(False, 3)
199 net_list
= gtk
.HBox(False, 0)
200 listcols
= gtk
.HBox(False, 0)
201 prows
= gtk
.VBox(False, 0)
204 net_list
.pack_start(self
.plist
, True, True, 0)
205 net_list
.pack_start(sb
, False, False, 0)
207 rows
.pack_start(net_list
, True, True, 0)
208 rows
.pack_start(self
.current_network
, False, True, 0)
210 listcols
.pack_start(rows
, True, True, 0)
211 listcols
.pack_start(prows
, False, False, 5)
213 prows
.pack_start(self
.new_button
, False, False, 2)
214 prows
.pack_start(self
.edit_button
, False, False, 2)
215 prows
.pack_start(self
.delete_button
, False, False, 2)
216 prows
.pack_end(self
.connect_button
, False, False, 2)
217 prows
.pack_end(self
.disconnect_button
, False, False, 2)
219 self
.window
.action_area
.pack_start(self
.about_button
)
220 self
.window
.action_area
.pack_start(self
.preferences_button
)
221 self
.window
.action_area
.pack_start(self
.close_button
)
226 self
.window
.vbox
.add(listcols
)
227 self
.window
.vbox
.set_spacing(3)
228 self
.window
.show_all()
230 # Now, immediately hide these two. The proper one will be
231 # displayed later, based on interface state. -BEF-
232 self
.disconnect_button
.hide()
233 self
.connect_button
.hide()
234 self
.connect_button
.set_sensitive(False)
236 # set up status window for later use
237 self
.status_window
= transients
.StatusWindow(self
)
238 self
.status_window
.cancel_button
.connect('clicked', self
.disconnect_profile
, "cancel")
241 # Check for incoming messages every 25 ms, a.k.a. 40 Hz.
242 glib
.timeout_add(25, self
.run
)
247 """ Watch for incoming messages.
249 if self
.msg_pipe
.poll():
251 msg
= self
.msg_pipe
.recv()
252 except (EOFError, IOError) as e
:
253 # This is bad, really bad.
254 logger
.critical('read on closed ' +
255 'Pipe ({}), failing...'.format(rfd
))
256 raise misc
.PipeError(e
)
258 self
._check
_message
(msg
)
261 def _check_message(self
, msg
):
262 """ Process incoming messages.
264 if msg
.topic
== 'EXIT':
266 elif msg
.topic
== 'CONFIG-UPDATE':
267 # Replace configuration manager with the one in msg.details.
268 self
.config
= msg
.details
269 elif msg
.topic
== 'PROFILE-EDIT':
271 self
.edit_profile(msg
.details
)
272 elif msg
.topic
== 'PROFILE-UPDATE':
274 self
.update_plist_items(msg
.details
)
275 elif msg
.topic
== 'PROFILE-UNLIST':
277 self
.remove_profile(msg
.details
)
278 elif msg
.topic
== 'PROFILE-MOVE':
279 profile
, new_position
= msg
.details
281 if profile
['roaming']:
282 old_position
= self
.get_row_by_ap(profile
['essid'])
284 old_position
= self
.get_row_by_ap(profile
['essid'],
286 self
.pstore
.move_before(old_position
, self
.pstore
[new_position
].iter)
287 elif msg
.topic
== 'PREFS-EDIT':
289 self
.edit_preferences()
291 logger
.warning('unrecognized Message: "{}"'.format(msg
))
293 def destroy(self
, widget
=None):
294 """ Quit the Gtk event loop. :data:`widget` is the widget
295 sending the signal, but it is ignored.
297 if self
.status_window
:
298 self
.status_window
.destroy()
301 def delete_event(self
, widget
=None, data
=None):
302 """ Shutdown the application. :data:`widget` is the widget sending
303 the signal and :data:`data` is a list of arbitrary arguments,
304 both are ignored. Always returns False to not propigate the
305 signal which called :func:`delete_event`.
307 self
._running
= False
308 self
.msg_pipe
.send(Message('EXIT', ''))
309 self
.msg_pipe
.close()
311 # process GTK events so that window hides more quickly
312 if sys
.modules
.has_key("gtk"):
313 while gtk
.events_pending():
314 gtk
.main_iteration(False)
318 def update_network_info(self
, profile
=None, ip
=None):
319 """ Update the current ip and essid shown to the user.
321 if (profile
is None) and (ip
is None):
322 self
.current_network
.set_text("Not Connected.")
324 self
.current_network
.set_text('Connected to {}\nIP Address {}'.format(profile
, ip
))
326 def update_connect_buttons(self
, connected
=False):
327 """ Set the state of connect/disconnect buttons to reflect the
328 current connected state.
331 self
.connect_button
.hide()
332 self
.disconnect_button
.show()
334 self
.disconnect_button
.hide()
335 self
.connect_button
.show()
337 def update_plist_items(self
, profile
):
338 """ Updates the display of :data:`profile`.
340 if profile
['roaming']:
341 prow_iter
= self
.get_row_by_ap(profile
['essid'])
343 prow_iter
= self
.get_row_by_ap(profile
['essid'], profile
['bssid'])
345 if prow_iter
is None:
346 # the AP is not in the list of APs on the screen
347 self
.add_profile(profile
)
349 # the AP is in the list of APs on the screen
351 if profile
['encrypted']:
352 wep
= gtk
.STOCK_DIALOG_AUTHENTICATION
353 # Update the Gtk objects.
354 self
.pstore
.set_value(prow_iter
, 2, pixbuf_from_known(profile
['known']))
355 self
.pstore
.set_value(prow_iter
, 3, profile
['known'])
356 self
.pstore
.set_value(prow_iter
, 4, profile
['available'])
357 self
.pstore
.set_value(prow_iter
, 5, wep
)
358 self
.pstore
.set_value(prow_iter
, 6, pixbuf_from_signal(profile
['signal']))
359 self
.pstore
.set_value(prow_iter
, 7, profile
['mode'])
360 self
.pstore
.set_value(prow_iter
, 8, profile
['protocol'])
361 self
.pstore
.set_value(prow_iter
, 9, profile
['channel'])
363 def _set_ap_col_value(self
, column
, cell
, model
, iter):
364 """ Set the text attribute of :data:`column` to the first two
365 :data:`model` values joined by a newline. This is for
366 displaying the :data:`essid` and :data:`bssid` in a single
369 essid
= model
.get_value(iter, 0)
370 bssid
= model
.get_value(iter, 1)
371 cell
.set_property('text', '\n'.join([essid
, bssid
]))
373 def get_row_by_ap(self
, essid
, bssid
=' Multiple APs'):
374 """ Returns a :class:`gtk.TreeIter` for the row which holds
375 :data:`essid` and :data:`bssid`.
377 :data:`bssid` is optional. If not given, :func:`get_row_by_ap`
378 will try to match a roaming profile with the given :data:`essid`.
380 If no match is found, it returns None.
382 for row
in self
.pstore
:
383 if (row
[0] == essid
) and (row
[1] == bssid
):
387 def on_network_selection(self
, widget
=None, data
=None):
388 """ Enable/disable buttons based on the selected network.
389 :data:`widget` is the widget sending the signal and :data:`data`
390 is a list of arbitrary arguments, both are ignored.
392 store
, selected_iter
= self
.selected_network
.get_selected()
393 if selected_iter
is None:
394 # No row is selected, disable all buttons except New.
395 # This occurs after a drag-and-drop.
396 self
.edit_button
.set_sensitive(False)
397 self
.delete_button
.set_sensitive(False)
398 self
.connect_button
.set_sensitive(False)
400 # One row is selected, so enable or disable buttons.
401 self
.connect_button
.set_sensitive(True)
402 if store
.get_value(selected_iter
, 3):
404 self
.edit_button
.set_sensitive(True)
405 self
.delete_button
.set_sensitive(True)
408 self
.edit_button
.set_sensitive(True)
409 self
.delete_button
.set_sensitive(False)
411 def show_about_info(self
, widget
=None, data
=None):
412 """ Handle the life-cycle of the About dialog. :data:`widget` is
413 the widget sending the signal and :data:`data` is a list of
414 arbitrary arguments, both are ignored.
416 about
= transients
.AboutDialog()
420 def edit_preferences(self
, widget
=None, data
=None):
421 """ Handle the life-cycle of the preferences dialog. :data:`widget`
422 is the widget sending the signal and :data:`data` is a list of
423 arbitrary arguments, both are ignored.
425 # get raw strings from config file
426 self
.config
.raw
= True
427 prefs_editor
= prefs
.PreferencesEditor(self
, self
.config
)
428 response
= prefs_editor
.run()
429 if response
== int(gtk
.RESPONSE_ACCEPT
):
431 prefs_editor
.destroy()
432 # get cooked strings from config file
433 self
.config
.raw
= False
435 def add_profile(self
, profile
):
436 """ Add :data:`profile` to the list of APs shown to the user.
438 if profile
['roaming']:
439 profile
['bssid'] = ' Multiple APs'
442 if profile
['encrypted']:
443 wep
= gtk
.STOCK_DIALOG_AUTHENTICATION
445 self
.pstore
.append([profile
['essid'], profile
['bssid'],
446 known_profile_icon
, profile
['known'], profile
['available'],
447 wep
, signal_none_pb
, profile
['mode'], profile
['protocol'],
450 def remove_profile(self
, profile
):
451 """ Remove :data:`profile` from the list of APs shown to the user.
453 if profile
['roaming']:
454 prow_iter
= self
.get_row_by_ap(profile
['essid'])
456 prow_iter
= self
.get_row_by_ap(profile
['essid'], profile
['bssid'])
457 if prow_iter
is not None:
458 self
.pstore
.remove(prow_iter
)
460 def create_new_profile(self
, widget
=None, profile
=None, data
=None):
461 """ Respond to a user request to create a new AP profile.
462 :data:`widget` is the widget sending the signal. :data:profile`
463 is an AP profile to use as the basis for the new profile. It
464 is likely empty or mostly empty. :data:`data` is a list of
465 arbitrary arguments. :data:`widget` and "data"`data` are both
468 The order of parameters is important. Because when this method
469 is called from a signal handler, :data:`widget` is always the
472 profile_editor
= profile_ed
.ProfileEditor(self
, profile
)
474 profile
= profile_editor
.run()
476 self
.msg_pipe
.send(Message('ERROR', 'Cannot save empty ESSID'))
479 self
.msg_pipe
.send(Message('PROFILE-UPDATE', profile
))
481 profile_editor
.destroy()
483 def request_profile_edit(self
, widget
=None, data
=None):
484 """ Respond to a request to edit an AP profile. Edit the selected
485 AP profile, if it is known. Otherwise, create a new profile
486 with the selected ESSID and BSSID. :data:`widget` is the widget
487 sending the signal and :data:`data` is a list of arbitrary
488 arguments, both are ignored.
490 store
, selected_iter
= self
.plist
.get_selection().get_selected()
491 if selected_iter
is None:
495 essid
= self
.pstore
.get_value(selected_iter
, 0)
496 bssid
= self
.pstore
.get_value(selected_iter
, 1)
498 if bssid
== ' Multiple APs':
499 # AP list says this is a roaming profile
500 apname
= make_section_name(essid
, '')
502 # AP list says this is NOT a roaming profile
503 apname
= make_section_name(essid
, bssid
)
504 self
.msg_pipe
.send(Message('PROFILE-EDIT-REQUEST', apname
))
506 def edit_profile(self
, profile
):
507 """ Allow the user to edit :data:`profile`.
509 profile_editor
= profile_ed
.ProfileEditor(self
, profile
)
511 # Try editing the profile.
512 edited_profile
= profile_editor
.run()
514 self
.msg_pipe
.send(Message('ERROR', 'Cannot save empty ESSID'))
516 if edited_profile
is not None:
517 # A profile was returned by the editor.
518 self
.msg_pipe
.send(Message('SCAN-STOP', ''))
519 # Replace old profile.
520 self
.msg_pipe
.send(Message('PROFILE-REPLACE',
521 (edited_profile
, profile
)))
522 self
.msg_pipe
.send(Message('SCAN-START', ''))
523 # Request saving the configuration file after the update.
524 self
.msg_pipe
.send(Message('CONFIG-SAVE', ''))
526 # Always remove profile editor window from screen
527 profile_editor
.destroy()
529 def delete_profile(self
, apname
):
530 """ Delete the profile associated with :data:`apname` (i.e. make
531 the profile unknown).
533 self
.msg_pipe
.send(Message('PROFILE-REMOVE', apname
))
535 def delete_profile_with_check(self
, widget
=None, data
=None):
536 """ Respond to a request to delete an AP profile (i.e. make the
537 profile unknown) Check with the user first. :data:`widget`
538 is the widget sending the signal and :data:`data` is a list of
539 arbitrary arguments, both are ignored.
541 store
, selected_iter
= self
.plist
.get_selection().get_selected()
542 if selected_iter
is None:
545 essid
= self
.pstore
.get_value(selected_iter
, 0)
546 bssid
= self
.pstore
.get_value(selected_iter
, 1)
547 if bssid
== ' Multiple APs':
548 apname
= make_section_name(essid
, '')
550 apname
= make_section_name(essid
, bssid
)
551 profile
= self
.config
.get_profile(apname
)
552 if profile
['roaming']:
553 dlg
= gtk
.MessageDialog(self
.window
, gtk
.DIALOG_DESTROY_WITH_PARENT | gtk
.DIALOG_MODAL
, gtk
.MESSAGE_QUESTION
, gtk
.BUTTONS_YES_NO
, "Are you sure you want to delete the %s profile?" % (essid
, ))
555 dlg
= gtk
.MessageDialog(self
.window
, gtk
.DIALOG_DESTROY_WITH_PARENT | gtk
.DIALOG_MODAL
, gtk
.MESSAGE_QUESTION
, gtk
.BUTTONS_YES_NO
, "Are you sure you want to delete the %s (%s) profile?" % (essid
, bssid
))
556 known
= store
.get_value(selected_iter
, 3)
561 if res
== gtk
.RESPONSE_NO
:
563 self
.delete_profile(apname
)
565 def connect_profile(self
, widget
, profile
, data
=None):
566 """ Respond to a request to connect to an AP.
570 'widget' -- gtk.Widget - The widget sending the event.
572 'profile' -- dictionary - The AP profile to which to connect.
574 'data' -- tuple - list of arbitrary arguments (not used)
580 store
, selected_iter
= self
.plist
.get_selection().get_selected()
581 if selected_iter
is None:
583 essid
= self
.pstore
.get_value(selected_iter
, 0)
584 bssid
= self
.pstore
.get_value(selected_iter
, 1)
585 known
= store
.get_value(selected_iter
, 3)
587 dlg
= gtk
.MessageDialog(self
.window
, gtk
.DIALOG_DESTROY_WITH_PARENT | gtk
.DIALOG_MODAL
, gtk
.MESSAGE_QUESTION
, gtk
.BUTTONS_YES_NO
, "This network does not have a profile configured.\n\nWould you like to create one now?")
591 if res
== gtk
.RESPONSE_NO
:
593 profile
= misc
.get_new_profile()
594 profile
['essid'] = essid
595 profile
['bssid'] = bssid
596 if not self
.create_new_profile(widget
, profile
, data
):
599 # Check for roaming profile.
600 ap_name
= make_section_name(essid
, '')
601 profile
= self
.config
.get_profile(ap_name
)
603 # Check for normal profile.
604 ap_name
= make_section_name(essid
, bssid
)
605 profile
= self
.config
.get_profile(ap_name
)
607 # No configured profile
609 profile
['bssid'] = self
.access_points
[ap_name
]['bssid']
610 profile
['channel'] = self
.access_points
[ap_name
]['channel']
611 self
.msg_pipe
.send(Message('CONNECT', profile
))
613 def disconnect_profile(self
, widget
=None, data
=None):
614 """ Respond to a request to disconnect by sending a message to
615 ConnectionManager. :data:`widget` is the widget sending the
616 signal and :data:`data` is a list of arbitrary arguments, both
619 self
.msg_pipe
.send(Message('DISCONNECT', ''))
621 self
.status_window
.update_message("Canceling connection...")
622 if sys
.modules
.has_key("gtk"):
623 while gtk
.events_pending():
624 gtk
.main_iteration(False)
626 def profile_order_updater(self
, model
, path
, iter, auto_profile_order
):
629 if model
.get_value(iter, 3) is True:
630 essid
= self
.pstore
.get_value(iter, 0)
631 bssid
= self
.pstore
.get_value(iter, 1)
632 if bssid
== ' Multiple APs':
634 apname
= make_section_name(essid
, bssid
)
635 auto_profile_order
.append(apname
)
637 def update_auto_profile_order(self
, widget
=None, data
=None, data2
=None):
638 """ Update the config file auto profile order from the on-screen
639 order. :data:`widget` is the widget sending the signal and
640 :data:`data` and :data:`data2` is a list of arbitrary arguments,
643 # recreate the auto_profile_order
644 auto_profile_order
= []
645 self
.pstore
.foreach(self
.profile_order_updater
, auto_profile_order
)
646 self
.msg_pipe
.send(Message('PROFILE-ORDER-UPDATE', auto_profile_order
))
647 # Request saving the configuration file after the update.
648 self
.msg_pipe
.send(Message('CONFIG-SAVE', ''))
651 # Make so we can be imported
652 if __name__
== "__main__":