Apply SIGINT restoration to new module layout
[wifi-radar.git] / wifiradar / gui / g2 / __init__.py
blob126308256e31b8c8330c033131602c0bb1a30dab
1 #!/usr/bin/python
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
37 import errno
38 import logging
39 import signal
40 import sys
42 import glib
43 import gtk
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
49 from . import prefs
50 from . import profile as profile_ed
51 from . import transients
53 # create a logger
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
69 familiarity.
70 """
71 if known:
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)
79 """
80 signal = int(signal)
81 # Shift signal up by 80 to convert dBm scale to arbitrary scale.
82 if signal < 0:
83 signal = signal + 80
84 # Find an icon...
85 if signal < 3:
86 return signal_none_pb
87 elif signal < 12:
88 return signal_low_pb
89 elif signal < 20:
90 return signal_barely_pb
91 elif signal < 35:
92 return signal_ok_pb
93 elif signal >= 35:
94 return signal_best_pb
96 def start(ui_pipe):
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)
106 ui.run()
107 with gtk.gdk.lock:
108 gtk.main()
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)
151 # The AP column
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)
157 # The signal column
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)
163 # The mode column
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)
175 # The channel column
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)
181 # DnD Ordering
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)
191 self.plist.show()
192 # Add New button
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)
201 # Add Delete button
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)
206 # Add Connect button
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)
217 # lets start packing
218 # the network list
219 net_list.pack_start(sb, True, True, 0)
220 sb.add(self.plist)
221 # the rows level
222 rows.pack_start(net_list , True, True, 0)
223 rows.pack_start(self.current_network, False, True, 0)
224 # the list columns
225 listcols.pack_start(rows, True, True, 0)
226 listcols.pack_start(prows, False, False, 5)
227 # the list buttons
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)
238 rows.show()
239 prows.show()
240 listcols.show()
241 self.vbox.add(listcols)
242 self.vbox.set_spacing(3)
243 self.show_all()
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')
255 self._running = True
256 # Check for incoming messages every 25 ms, a.k.a. 40 Hz.
257 glib.timeout_add(25, self.run)
259 def run(self):
260 """ Watch for incoming messages.
262 if self.msg_pipe.poll():
263 try:
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))
269 raise PipeError(e)
270 else:
271 self._check_message(msg)
272 # Update the UI before returning.
273 self.update_network_info()
274 self.update_connect_buttons()
275 return self._running
277 def _check_message(self, msg):
278 """ Process incoming messages.
280 if msg.topic == 'EXIT':
281 self.delete_event()
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':
286 with gtk.gdk.lock:
287 self.edit_profile(msg.details)
288 elif msg.topic == 'PROFILE-UPDATE':
289 with gtk.gdk.lock:
290 self.update_profile(msg.details)
291 elif msg.topic == 'PROFILE-UNLIST':
292 with gtk.gdk.lock:
293 self.delete_profile(msg.details)
294 elif msg.topic == 'PROFILE-MOVE':
295 new_position, profile = msg.details
296 with gtk.gdk.lock:
297 if profile['roaming']:
298 old_position = self.get_row_by_ap(profile['essid'])
299 else:
300 old_position = self.get_row_by_ap(profile['essid'],
301 profile['bssid'])
302 self.pstore.move_before(old_position, self.pstore[new_position].iter)
303 elif msg.topic == 'PREFS-EDIT':
304 with gtk.gdk.lock:
305 self.edit_preferences(msg.details)
306 elif msg.topic == 'ERROR':
307 with gtk.gdk.lock:
308 transients.ErrorDialog(self, msg.details)
309 else:
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()
318 gtk.main_quit()
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()
329 self.hide()
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)
334 self.destroy()
335 return 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.'))
342 else:
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.
350 if connected:
351 self.connect_button.hide()
352 self.disconnect_button.show()
353 else:
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
361 cell column.
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):
378 return row.iter
379 return None
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)
393 else:
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):
397 # Known profile.
398 self.edit_button.set_sensitive(True)
399 self.delete_button.set_sensitive(True)
400 else:
401 # Unknown profile.
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()
411 about.run()
412 about.destroy()
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'])
435 else:
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)
441 else:
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')
451 wep = None
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'],
458 profile['channel']])
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`.
464 wep = None
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
483 ignored.
485 The order of parameters is important. Because when this method
486 is called from a signal handler, :data:`widget` is always the
487 first argument.
489 if profile is None:
490 profile = get_new_profile()
492 profile_editor = profile_ed.ProfileEditor(self, profile)
493 try:
494 edited_profile = profile_editor.run()
495 except ValueError:
496 self.msg_pipe.send(Message('ERROR', _('Cannot save empty ESSID')))
497 else:
498 if profile:
499 self.msg_pipe.send(Message('PROFILE-EDITED', (edited_profile, profile)))
500 finally:
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
514 bssid = ''
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
544 bssid = ''
545 profile_name = essid
546 else:
547 profile_name = '{ESSID} ({BSSID})'.format(ESSID=essid,
548 BSSID=bssid)
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()
557 dialog.destroy()
558 del dialog
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'])
569 else:
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.
577 Parameters:
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)
585 Returns:
587 nothing
589 store, selected_iter = self.plist.get_selection().get_selected()
590 if selected_iter is None:
591 return
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)
595 if not known:
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?'))
601 res = dlg.run()
602 dlg.destroy()
603 del dlg
604 if res == gtk.RESPONSE_NO:
605 return
606 profile = get_new_profile()
607 profile['essid'] = essid
608 profile['bssid'] = bssid
609 if not self.create_new_profile(widget, profile, data):
610 return
611 else:
612 # Check for roaming profile.
613 ap_name = make_section_name(essid, '')
614 profile = self.config.get_profile(ap_name)
615 if not profile:
616 # Check for normal profile.
617 ap_name = make_section_name(essid, bssid)
618 profile = self.config.get_profile(ap_name)
619 if not profile:
620 # No configured profile
621 return
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
630 are ignored.
632 self.msg_pipe.send(Message('DISCONNECT', ''))
633 if data == 'cancel':
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'):
646 bssid = ''
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,
654 all are ignored.
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__':
664 pass