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 import wifiradar
.connections
as connections
42 import wifiradar
.misc
as misc
43 from wifiradar
.pubsub
import Message
45 from . import profile
as profile_ed
46 from . import transients
49 logger
= logging
.getLogger(__name__
)
52 # Create a bunch of icons from files in the package.
53 known_profile_icon
= gtk
.gdk
.pixbuf_new_from_file("pixmaps/known_profile.png")
54 unknown_profile_icon
= gtk
.gdk
.pixbuf_new_from_file("pixmaps/unknown_profile.png")
55 signal_none_pb
= gtk
.gdk
.pixbuf_new_from_file("pixmaps/signal_none.xpm")
56 signal_low_pb
= gtk
.gdk
.pixbuf_new_from_file("pixmaps/signal_low.xpm")
57 signal_barely_pb
= gtk
.gdk
.pixbuf_new_from_file("pixmaps/signal_barely.xpm")
58 signal_ok_pb
= gtk
.gdk
.pixbuf_new_from_file("pixmaps/signal_ok.xpm")
59 signal_best_pb
= gtk
.gdk
.pixbuf_new_from_file("pixmaps/signal_best.xpm")
61 def pixbuf_from_known(known
):
62 """ Return a :class:`gtk.gdk.Pixbuf` icon to represent :data:`known`.
63 Any true :data:`known` value returns the icon showing previous
67 return known_profile_icon
68 return unknown_profile_icon
70 def pixbuf_from_signal(signal
):
71 """ Return a :class:`gtk.gdk.Pixbuf` icon to indicate the :data:`signal`
72 level. :data:`signal` is as reported by iwlist (may be arbitrary
73 scale in 0-100 or -X dBm)
76 # Shift signal up by 80 to convert dBm scale to arbitrary scale.
85 return signal_barely_pb
93 def __init__(self
, msg_pipe
):
94 """ Create a new RadarWindow wanting to communicate through
95 :data:`msg_pipe`, a :class:`multiprocessing.Connection`.
97 self
.access_points
= {}
98 self
.connection
= None
99 self
.msg_pipe
= msg_pipe
101 gtk
.gdk
.threads_init()
103 self
.icon
= gtk
.gdk
.pixbuf_new_from_file("pixmaps/wifi-radar.png")
105 self
.window
= gtk
.Dialog('WiFi Radar', None, gtk
.DIALOG_MODAL
)
106 self
.window
.set_icon(self
.icon
)
107 self
.window
.set_border_width(10)
108 self
.window
.set_size_request(550, 300)
109 self
.window
.set_title("WiFi Radar")
110 self
.window
.connect('delete_event', self
.delete_event
)
111 self
.window
.connect('destroy', self
.destroy
)
112 # let's create all our widgets
113 self
.current_network
= gtk
.Label()
114 self
.current_network
.set_property('justify', gtk
.JUSTIFY_CENTER
)
115 self
.current_network
.show()
116 self
.close_button
= gtk
.Button("Close", gtk
.STOCK_CLOSE
)
117 self
.close_button
.show()
118 self
.close_button
.connect('clicked', self
.delete_event
, None)
119 self
.about_button
= gtk
.Button("About", gtk
.STOCK_ABOUT
)
120 self
.about_button
.show()
121 self
.about_button
.connect('clicked', self
.show_about_info
, None)
122 self
.preferences_button
= gtk
.Button("Preferences", gtk
.STOCK_PREFERENCES
)
123 self
.preferences_button
.show()
124 self
.preferences_button
.connect('clicked', self
.edit_preferences
, None)
125 # essid bssid known_icon known available wep_icon signal_level mode protocol channel
126 self
.pstore
= gtk
.ListStore(str, str, gtk
.gdk
.Pixbuf
, bool, bool, str, gtk
.gdk
.Pixbuf
, str, str, str)
127 self
.plist
= gtk
.TreeView(self
.pstore
)
128 # The icons column, known and encryption
129 self
.pix_cell
= gtk
.CellRendererPixbuf()
130 self
.wep_cell
= gtk
.CellRendererPixbuf()
131 self
.icons_cell
= gtk
.CellRendererText()
132 self
.icons_col
= gtk
.TreeViewColumn()
133 self
.icons_col
.pack_start(self
.pix_cell
, False)
134 self
.icons_col
.pack_start(self
.wep_cell
, False)
135 self
.icons_col
.add_attribute(self
.pix_cell
, 'pixbuf', 2)
136 self
.icons_col
.add_attribute(self
.wep_cell
, 'stock-id', 5)
137 self
.plist
.append_column(self
.icons_col
)
139 self
.ap_cell
= gtk
.CellRendererText()
140 self
.ap_col
= gtk
.TreeViewColumn("Access Point")
141 self
.ap_col
.pack_start(self
.ap_cell
, True)
142 self
.ap_col
.set_cell_data_func(self
.ap_cell
, self
._set
_ap
_col
_value
)
143 self
.plist
.append_column(self
.ap_col
)
145 self
.sig_cell
= gtk
.CellRendererPixbuf()
146 self
.signal_col
= gtk
.TreeViewColumn("Signal")
147 self
.signal_col
.pack_start(self
.sig_cell
, True)
148 self
.signal_col
.add_attribute(self
.sig_cell
, 'pixbuf', 6)
149 self
.plist
.append_column(self
.signal_col
)
151 self
.mode_cell
= gtk
.CellRendererText()
152 self
.mode_col
= gtk
.TreeViewColumn("Mode")
153 self
.mode_col
.pack_start(self
.mode_cell
, True)
154 self
.mode_col
.add_attribute(self
.mode_cell
, 'text', 7)
155 self
.plist
.append_column(self
.mode_col
)
156 # The protocol column
157 self
.prot_cell
= gtk
.CellRendererText()
158 self
.protocol_col
= gtk
.TreeViewColumn("802.11")
159 self
.protocol_col
.pack_start(self
.prot_cell
, True)
160 self
.protocol_col
.add_attribute(self
.prot_cell
, 'text', 8)
161 self
.plist
.append_column(self
.protocol_col
)
163 self
.channel_cell
= gtk
.CellRendererText()
164 self
.channel_col
= gtk
.TreeViewColumn("Channel")
165 self
.channel_col
.pack_start(self
.channel_cell
, True)
166 self
.channel_col
.add_attribute(self
.channel_cell
, 'text', 9)
167 self
.plist
.append_column(self
.channel_col
)
169 self
.plist
.set_reorderable(True)
170 # detect d-n-d of AP in round-about way, since rows-reordered does not work as advertised
171 self
.pstore
.connect('row-deleted', self
.update_auto_profile_order
)
172 # enable/disable buttons based on the selected network
173 self
.selected_network
= self
.plist
.get_selection()
174 self
.selected_network
.connect('changed', self
.on_network_selection
, None)
175 # the list scroll bar
176 sb
= gtk
.VScrollbar(self
.plist
.get_vadjustment())
180 self
.new_button
= gtk
.Button("_New")
181 self
.new_button
.connect('clicked', self
.create_new_profile
, misc
.get_new_profile(), None)
182 self
.new_button
.show()
183 # Add Configure button
184 self
.edit_button
= gtk
.Button("C_onfigure")
185 self
.edit_button
.connect('clicked', self
.edit_profile
, None)
186 self
.edit_button
.show()
187 self
.edit_button
.set_sensitive(False)
189 self
.delete_button
= gtk
.Button("_Delete")
190 self
.delete_button
.connect('clicked', self
.delete_profile_with_check
, None)
191 self
.delete_button
.show()
192 self
.delete_button
.set_sensitive(False)
194 self
.connect_button
= gtk
.Button("Co_nnect")
195 self
.connect_button
.connect('clicked', self
.connect_profile
, None)
196 # Add Disconnect button
197 self
.disconnect_button
= gtk
.Button("D_isconnect")
198 self
.disconnect_button
.connect('clicked', self
.disconnect_profile
, None)
199 # lets add our widgets
200 rows
= gtk
.VBox(False, 3)
201 net_list
= gtk
.HBox(False, 0)
202 listcols
= gtk
.HBox(False, 0)
203 prows
= gtk
.VBox(False, 0)
206 net_list
.pack_start(self
.plist
, True, True, 0)
207 net_list
.pack_start(sb
, False, False, 0)
209 rows
.pack_start(net_list
, True, True, 0)
210 rows
.pack_start(self
.current_network
, False, True, 0)
212 listcols
.pack_start(rows
, True, True, 0)
213 listcols
.pack_start(prows
, False, False, 5)
215 prows
.pack_start(self
.new_button
, False, False, 2)
216 prows
.pack_start(self
.edit_button
, False, False, 2)
217 prows
.pack_start(self
.delete_button
, False, False, 2)
218 prows
.pack_end(self
.connect_button
, False, False, 2)
219 prows
.pack_end(self
.disconnect_button
, False, False, 2)
221 self
.window
.action_area
.pack_start(self
.about_button
)
222 self
.window
.action_area
.pack_start(self
.preferences_button
)
223 self
.window
.action_area
.pack_start(self
.close_button
)
228 self
.window
.vbox
.add(listcols
)
229 self
.window
.vbox
.set_spacing(3)
230 self
.window
.show_all()
232 # Now, immediately hide these two. The proper one will be
233 # displayed later, based on interface state. -BEF-
234 self
.disconnect_button
.hide()
235 self
.connect_button
.hide()
236 self
.connect_button
.set_sensitive(False)
238 # set up connection manager for later use
239 self
.connection
= connections
.ConnectionManager(self
.commandQueue
)
240 self
.connection
.set_config(self
.config
)
241 # set up status window for later use
242 self
.status_window
= transients
.StatusWindow(self
)
243 self
.status_window
.cancel_button
.connect('clicked', self
.disconnect_profile
, "cancel")
246 # Check for incoming messages every 25 ms, a.k.a. 40 Hz.
247 glib
.timeout_add(25, self
.run
)
248 # This is the first run (or, at least, no config file was present), so pop up the preferences window
250 new_file
= self
.config
.get_opt_as_bool('DEFAULT', 'new_file')
252 self
.config
.remove_option('DEFAULT', 'new_file')
253 self
.edit_preferences(self
.preferences_button
)
254 except configparser
.NoOptionError
:
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 ' +
268 'Pipe ({}), failing...'.format(rfd
))
269 raise misc
.PipeError(e
)
271 self
._check
_message
(msg
)
274 def _check_message(self
, msg
):
275 """ Process incoming messages.
277 if msg
.topic
== 'EXIT':
279 elif msg
.topic
== 'CONFIG-UPDATE':
280 # Replace configuration manager with the one in msg.details.
281 self
.config
= msg
.details
282 elif msg
.topic
== 'PROFILE-UPDATE':
283 self
.update_plist_items(msg
.details
)
285 logger
.warning('unrecognized Message: "{}"'.format(msg
))
287 def destroy(self
, widget
=None):
288 """ Quit the Gtk event loop. :data:`widget` is the widget
289 sending the signal, but it is ignored.
291 if self
.status_window
:
292 self
.status_window
.destroy()
295 def delete_event(self
, widget
=None, data
=None):
296 """ Shutdown the application. :data:`widget` is the widget sending
297 the signal and :data:`data` is a list of arbitrary arguments,
298 both are ignored. Always returns False to not propigate the
299 signal which called :func:`delete_event`.
301 self
._running
= False
302 self
.msg_pipe
.send(Message('EXIT', ''))
303 self
.msg_pipe
.close()
305 # process GTK events so that window hides more quickly
306 if sys
.modules
.has_key("gtk"):
307 while gtk
.events_pending():
308 gtk
.main_iteration(False)
312 def update_network_info(self
, profile
=None, ip
=None):
313 """ Update the current ip and essid shown to the user.
315 if (profile
is None) and (ip
is None):
316 self
.current_network
.set_text("Not Connected.")
318 self
.current_network
.set_text('Connected to {}\nIP Address {}'.format(profile
, ip
))
320 def update_connect_buttons(self
, connected
=False):
321 """ Set the state of connect/disconnect buttons to reflect the
322 current connected state.
325 self
.connect_button
.hide()
326 self
.disconnect_button
.show()
328 self
.disconnect_button
.hide()
329 self
.connect_button
.show()
331 def update_plist_items(self
, profile
):
332 """ Updates the display of :data:`profile`.
334 if profile
['roaming']:
335 prow_iter
= self
.get_row_by_ap(profile
['essid'])
337 prow_iter
= self
.get_row_by_ap(profile
['essid'], profile
['bssid'])
339 if prow_iter
is None:
340 # the AP is not in the list of APs on the screen
341 self
.add_profile(profile
)
343 # the AP is in the list of APs on the screen
345 if profile
['encrypted']:
346 wep
= gtk
.STOCK_DIALOG_AUTHENTICATION
347 # Update the Gtk objects.
348 self
.pstore
.set_value(prow_iter
, 2, pixbuf_from_known(profile
['known']))
349 self
.pstore
.set_value(prow_iter
, 3, profile
['known'])
350 self
.pstore
.set_value(prow_iter
, 4, profile
['available'])
351 self
.pstore
.set_value(prow_iter
, 5, wep
)
352 self
.pstore
.set_value(prow_iter
, 6, pixbuf_from_signal(profile
['signal']))
353 self
.pstore
.set_value(prow_iter
, 7, profile
['mode'])
354 self
.pstore
.set_value(prow_iter
, 8, profile
['protocol'])
355 self
.pstore
.set_value(prow_iter
, 9, profile
['channel'])
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
, 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
, data
=None):
406 """ Init and run the about dialog
410 'widget' -- gtk.Widget - The widget sending the event.
412 'data' -- tuple - list of arbitrary arguments (not used)
418 about
= transients
.AboutDialog()
422 def edit_preferences(self
, widget
, data
=None):
423 """ Init and run the preferences dialog
427 'widget' -- gtk.Widget - The widget sending the event.
429 'data' -- tuple - list of arbitrary arguments (not used)
435 # get raw strings from config file
436 self
.config
.raw
= True
437 prefs_editor
= prefs
.PreferencesEditor(self
, self
.config
)
438 response
= prefs_editor
.run()
439 if response
== int(gtk
.RESPONSE_ACCEPT
):
441 prefs_editor
.destroy()
442 # get cooked strings from config file
443 self
.config
.raw
= False
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 remove_profile(self
, profile
):
461 """ Remove :data:`profile` from the list of APs shown to the user.
463 if profile
['roaming']:
464 prow_iter
= self
.get_row_by_ap(profile
['essid'])
466 prow_iter
= self
.get_row_by_ap(profile
['essid'], profile
['bssid'])
467 if prow_iter
is not None:
468 self
.pstore
.remove(prow_iter
)
470 def create_new_profile(self
, widget
, profile
, data
=None):
471 """ Respond to a request to create a new AP profile
475 'widget' -- gtk.Widget - The widget sending the event.
477 'profile' -- dictionary - The AP profile to use as basis for new profile.
479 'data' -- tuple - list of arbitrary arguments (not used)
483 boolean -- True if a profile was created and False if profile creation was canceled.
485 profile_editor
= profile_ed
.ProfileEditor(self
, profile
)
487 profile
= profile_editor
.run()
489 error_dlg
= transients
.ErrorDialog(profile_editor
.dialog
, "Cannot save empty ESSID")
493 profile_editor
.destroy()
495 store
, selected_iter
= self
.plist
.get_selection().get_selected()
496 if selected_iter
is not None:
497 store
.remove(selected_iter
)
498 if profile
['roaming']:
499 apname
= misc
.make_section_name(profile
['essid'], '')
501 apname
= misc
.make_section_name(profile
['essid'], profile
['bssid'])
502 # Check that the ap does not exist already
503 if apname
in self
.config
.profiles():
504 error_dlg
= transients
.ErrorDialog(self
.window
, "A profile for %s already exists" % (apname
))
507 self
.access_points
[ apname
] = profile
508 self
.config
.set_section(apname
, profile
)
509 # if it is not in the auto_profile_order add it
510 if apname
not in self
.config
.auto_profile_order
:
511 self
.config
.auto_profile_order
.insert(0, apname
)
514 if profile
['encrypted']: wep
= gtk
.STOCK_DIALOG_AUTHENTICATION
518 if e
.errno
== errno
.ENOENT
:
519 error_dlg
= transients
.ErrorDialog(self
.window
, "Could not save configuration file:\n%s\n\n%s" % (self
.config
.filename
, e
.strerror
))
523 # Add AP to the list displayed to user
524 if profile
['roaming']:
525 ap_name
= profile
['essid'] + "\n" + ' Multiple APs'
527 prow_iter
= self
.get_row_by_ap(profile
['essid'])
529 self
.pstore
.remove(prow_iter
)
533 ap_name
= profile
['essid'] + "\n" + profile
['bssid']
534 self
.pstore
.prepend([ap_name
, self
.pixbuf_from_known(profile
['known']), profile
['known'], profile
['available'], wep
, self
.pixbuf_from_signal(profile
['signal']), profile
['mode'], profile
['protocol'], profile
['channel']])
537 # Did not create new profile
540 def edit_profile(self
, widget
, data
=None):
541 """ Respond to a request to edit an AP profile. Edit selected AP profile if it
542 is known. Otherwise, create a new profile with the selected ESSID and BSSID.
546 'widget' -- gtk.Widget - The widget sending the event.
548 'data' -- tuple - list of arbitrary arguments (not used)
554 store
, selected_iter
= self
.plist
.get_selection().get_selected()
555 if selected_iter
is None:
559 essid
= self
.pstore
.get_value(selected_iter
, 0)
560 bssid
= self
.pstore
.get_value(selected_iter
, 1)
562 if bssid
== ' Multiple APs':
563 # AP list says this is a roaming profile
564 apname
= misc
.make_section_name(essid
, '')
566 # AP list says this is NOT a roaming profile
567 apname
= misc
.make_section_name(essid
, bssid
)
568 profile
= self
.config
.get_profile(apname
)
570 # A profile was found in the config file
571 profile
['bssid'] = self
.access_points
[apname
]['bssid']
572 profile_editor
= profile_ed
.ProfileEditor(self
, profile
)
574 # try editing the profile
575 edited_profile
= profile_editor
.run()
577 error_dlg
= transients
.ErrorDialog(profile_editor
.dialog
, "Cannot save empty ESSID")
581 # Always remove profile editor window from screen
582 profile_editor
.destroy()
584 # A profile was returned by the editor
587 if edited_profile
['essid'] != profile
['essid'] or edited_profile
['bssid'] != profile
['bssid'] or edited_profile
['roaming'] != profile
['roaming']:
588 # ESSID, BSSID, or roaming was changed in profile editor
590 self
.commandQueue
.put('pause')
591 self
.commandQueue
.join()
594 if profile
['roaming']:
595 # The old profile was a roaming profile
596 old_ap
= misc
.make_section_name(profile
['essid'], '')
598 # The old profile was NOT a roaming profile
599 old_ap
= misc
.make_section_name(profile
['essid'], profile
['bssid'])
600 # Find where old profile was in auto order
601 old_index
= self
.config
.auto_profile_order
.index(old_ap
)
602 # Remove old profile and get its place in AP list
603 row
= self
.delete_profile(selected_iter
, old_ap
)
604 self
.config
.remove_section(old_ap
)
606 # Add AP to the list displayed to user
607 self
.apQueue
.put_nowait(edited_profile
)
608 self
.commandQueue
.put('scan')
611 if edited_profile
['roaming']:
612 # New profile is a roaming profile
613 apname
= misc
.make_section_name(edited_profile
['essid'], '')
614 ap_display
= edited_profile
['essid'] + "\n" + ' Multiple APs'
615 # Remove all other profiles that match the new profile ESSID
617 prow_iter
= self
.get_row_by_ap(edited_profile
['essid'])
619 self
.pstore
.remove(prow_iter
)
623 # New profile is NOT a roaming profile
624 apname
= misc
.make_section_name(edited_profile
['essid'], edited_profile
['bssid'])
625 ap_display
= edited_profile
['essid'] + "\n" + edited_profile
['bssid']
626 # Insert the new profile in the same position as the one being replaced
627 if old_index
is not None:
628 # Old profile was in auto order list
629 self
.config
.auto_profile_order
.insert(old_index
, apname
)
630 if (( row
is not None) and (self
.pstore
.iter_is_valid(row
)) ):
631 self
.pstore
.insert_before(row
, [ap_display
, None, None, None, None, None, None, None, None])
632 self
.access_points
[apname
] = edited_profile
633 self
.config
.set_section(apname
, edited_profile
)
635 # Save updated profile to config file
638 if e
.errno
== errno
.ENOENT
:
639 error_dlg
= transients
.ErrorDialog(self
.window
, "Could not save configuration file:\n%s\n\n%s" % (self
.config
.filename
, e
.strerror
))
644 # The AP does not already have a profile
645 profile
= misc
.get_new_profile()
646 profile
['essid'] = self
.pstore
.get_value(selected_iter
, 0)
647 profile
['bssid'] = self
.pstore
.get_value(selected_iter
, 1)
648 self
.create_new_profile(widget
, profile
, data
)
650 def delete_profile(self
, selected_iter
, apname
):
651 """ Delete an AP profile (i.e. make profile unknown)
655 'selected_iter' -- gtk.TreeIter - The selected row.
657 'apname' -- string - The configuration file section to remove
661 gtk.TreeIter -- the iter for the row removed from the gtk.ListStore
664 del self
.access_points
[apname
]
665 self
.config
.remove_section(apname
)
667 if apname
in self
.config
.auto_profile_order
:
668 self
.config
.auto_profile_order
.remove(apname
)
669 self
.pstore
.remove(selected_iter
)
670 # Let's save our current state
671 self
.update_auto_profile_order()
675 if e
.errno
== errno
.ENOENT
:
676 error_dlg
= transients
.ErrorDialog(self
.window
, "Could not save configuration file:\n%s\n\n%s" % (self
.config
.filename
, e
.strerror
))
682 def delete_profile_with_check(self
, widget
, data
=None):
683 """ Respond to a request to delete an AP profile (i.e. make profile unknown)
684 Check with user first.
688 'widget' -- gtk.Widget - The widget sending the event.
690 'data' -- tuple - list of arbitrary arguments (not used)
696 store
, selected_iter
= self
.plist
.get_selection().get_selected()
697 if selected_iter
is None:
700 essid
= self
.pstore
.get_value(selected_iter
, 0)
701 bssid
= self
.pstore
.get_value(selected_iter
, 1)
702 if bssid
== ' Multiple APs':
703 apname
= misc
.make_section_name(essid
, '')
705 apname
= misc
.make_section_name(essid
, bssid
)
706 profile
= self
.config
.get_profile(apname
)
707 if profile
['roaming']:
708 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
, ))
710 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
))
711 known
= store
.get_value(selected_iter
, 3)
716 if res
== gtk
.RESPONSE_NO
:
718 self
.delete_profile(selected_iter
, apname
)
720 def connect_profile(self
, widget
, profile
, data
=None):
721 """ Respond to a request to connect to an AP.
725 'widget' -- gtk.Widget - The widget sending the event.
727 'profile' -- dictionary - The AP profile to which to connect.
729 'data' -- tuple - list of arbitrary arguments (not used)
735 store
, selected_iter
= self
.plist
.get_selection().get_selected()
736 if selected_iter
is None:
738 essid
= self
.pstore
.get_value(selected_iter
, 0)
739 bssid
= self
.pstore
.get_value(selected_iter
, 1)
740 known
= store
.get_value(selected_iter
, 3)
742 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?")
746 if res
== gtk
.RESPONSE_NO
:
748 profile
= misc
.get_new_profile()
749 profile
['essid'] = essid
750 profile
['bssid'] = bssid
751 if not self
.create_new_profile(widget
, profile
, data
):
754 # Check for roaming profile.
755 ap_name
= misc
.make_section_name(essid
, '')
756 profile
= self
.config
.get_profile(ap_name
)
758 # Check for normal profile.
759 ap_name
= misc
.make_section_name(essid
, bssid
)
760 profile
= self
.config
.get_profile(ap_name
)
762 # No configured profile
764 profile
['bssid'] = self
.access_points
[ap_name
]['bssid']
765 profile
['channel'] = self
.access_points
[ap_name
]['channel']
766 self
.connection
.connect_to_network(profile
, self
.status_window
)
768 def disconnect_profile(self
, widget
, data
=None):
769 """ Respond to a request to disconnect by calling ConnectionManager disconnect_interface().
773 'widget' -- gtk.Widget - The widget sending the event.
775 'data' -- tuple - list of arbitrary arguments (not used)
782 self
.status_window
.update_message("Canceling connection...")
783 if sys
.modules
.has_key("gtk"):
784 while gtk
.events_pending():
785 gtk
.main_iteration(False)
786 self
.connection
.disconnect_interface()
788 def profile_order_updater(self
, model
, path
, iter, auto_profile_order
):
791 if model
.get_value(iter, 3) is True:
792 essid
= self
.pstore
.get_value(selected_iter
, 0)
793 bssid
= self
.pstore
.get_value(selected_iter
, 1)
794 if bssid
== ' Multiple APs':
796 apname
= misc
.make_section_name(essid
, bssid
)
797 auto_profile_order
.append(apname
)
799 def update_auto_profile_order(self
, widget
=None, data
=None, data2
=None):
800 """ Update the config file auto profile order from the on-screen order
804 'widget' -- gtk.Widget - The widget sending the event.
806 'data' -- tuple - list of arbitrary arguments (not used)
808 'data2' -- tuple - list of arbitrary arguments (not used)
814 # recreate the auto_profile_order
815 auto_profile_order
= []
816 self
.pstore
.foreach(self
.profile_order_updater
, auto_profile_order
)
817 self
.config
.auto_profile_order
= auto_profile_order
821 if e
.errno
== errno
.ENOENT
:
822 error_dlg
= transients
.ErrorDialog(self
.window
, "Could not save configuration file:\n%s\n\n%s" % (self
.config
.filename
, e
.strerror
))
828 # Make so we can be imported
829 if __name__
== "__main__":