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
36 import ConfigParser
as configparser
53 from time
import sleep
55 import wifiradar
.connections
as connections
56 import wifiradar
.misc
as misc
58 from . import profile
as profile_ed
59 from . import transients
62 logger
= logging
.getLogger(__name__
)
65 # Create a bunch of icons from files in the package.
66 known_profile_icon
= gtk
.gdk
.pixbuf_new_from_file("pixmaps/known_profile.png")
67 unknown_profile_icon
= gtk
.gdk
.pixbuf_new_from_file("pixmaps/unknown_profile.png")
68 signal_none_pb
= gtk
.gdk
.pixbuf_new_from_file("pixmaps/signal_none.xpm")
69 signal_low_pb
= gtk
.gdk
.pixbuf_new_from_file("pixmaps/signal_low.xpm")
70 signal_barely_pb
= gtk
.gdk
.pixbuf_new_from_file("pixmaps/signal_barely.xpm")
71 signal_ok_pb
= gtk
.gdk
.pixbuf_new_from_file("pixmaps/signal_ok.xpm")
72 signal_best_pb
= gtk
.gdk
.pixbuf_new_from_file("pixmaps/signal_best.xpm")
74 def pixbuf_from_known(known
):
75 """ Return a :class:`gtk.gdk.Pixbuf` icon to represent :data:`known`.
76 Any true :data:`known` value returns the icon showing previous
80 return known_profile_icon
81 return unknown_profile_icon
83 def pixbuf_from_signal(signal
):
84 """ Return a :class:`gtk.gdk.Pixbuf` icon to indicate the :data:`signal`
85 level. :data:`signal` is as reported by iwlist (may be arbitrary
86 scale in 0-100 or -X dBm)
89 # Shift signal up by 80 to convert dBm scale to arbitrary scale.
98 return signal_barely_pb
102 return signal_best_pb
106 """ Center for most of the rest of the operations.
108 def __init__(self
, config
, apQueue
, commQueue
, exit_event
):
109 """ Create a new RadarWindow.
113 'config' -- ConfigFile - The config file in which to store/read settings.
115 'apQueue' -- Queue - The Queue from which AP profiles are read.
117 'commQueue' -- Queue - The Queue to which to send commands to the scanning thread.
124 self
.apQueue
= apQueue
125 self
.commandQueue
= commQueue
126 self
.access_points
= {}
127 self
.exit_event
= exit_event
128 self
.connection
= None
130 self
.icon
= gtk
.gdk
.pixbuf_new_from_file("pixmaps/wifi-radar.png")
132 self
.window
= gtk
.Dialog('WiFi Radar', None, gtk
.DIALOG_MODAL
)
133 self
.window
.set_icon(self
.icon
)
134 self
.window
.set_border_width(10)
135 self
.window
.set_size_request(550, 300)
136 self
.window
.set_title("WiFi Radar")
137 self
.window
.connect('delete_event', self
.delete_event
)
138 self
.window
.connect('destroy', self
.destroy
)
139 # let's create all our widgets
140 self
.current_network
= gtk
.Label()
141 self
.current_network
.set_property('justify', gtk
.JUSTIFY_CENTER
)
142 self
.current_network
.show()
143 self
.close_button
= gtk
.Button("Close", gtk
.STOCK_CLOSE
)
144 self
.close_button
.show()
145 self
.close_button
.connect('clicked', self
.delete_event
, None)
146 self
.about_button
= gtk
.Button("About", gtk
.STOCK_ABOUT
)
147 self
.about_button
.show()
148 self
.about_button
.connect('clicked', self
.show_about_info
, None)
149 self
.preferences_button
= gtk
.Button("Preferences", gtk
.STOCK_PREFERENCES
)
150 self
.preferences_button
.show()
151 self
.preferences_button
.connect('clicked', self
.edit_preferences
, None)
152 # essid + bssid known_icon known available wep_icon signal_level mode protocol channel
153 self
.pstore
= gtk
.ListStore(str, gtk
.gdk
.Pixbuf
, bool, bool, str, gtk
.gdk
.Pixbuf
, str, str, str)
154 self
.plist
= gtk
.TreeView(self
.pstore
)
155 # The icons column, known and encryption
156 self
.pix_cell
= gtk
.CellRendererPixbuf()
157 self
.wep_cell
= gtk
.CellRendererPixbuf()
158 self
.icons_cell
= gtk
.CellRendererText()
159 self
.icons_col
= gtk
.TreeViewColumn()
160 self
.icons_col
.pack_start(self
.pix_cell
, False)
161 self
.icons_col
.pack_start(self
.wep_cell
, False)
162 self
.icons_col
.add_attribute(self
.pix_cell
, 'pixbuf', 1)
163 self
.icons_col
.add_attribute(self
.wep_cell
, 'stock-id', 4)
164 self
.plist
.append_column(self
.icons_col
)
166 self
.ap_cell
= gtk
.CellRendererText()
167 self
.ap_col
= gtk
.TreeViewColumn("Access Point")
168 self
.ap_col
.pack_start(self
.ap_cell
, True)
169 self
.ap_col
.add_attribute(self
.ap_cell
, 'text', 0)
170 self
.plist
.append_column(self
.ap_col
)
172 self
.sig_cell
= gtk
.CellRendererPixbuf()
173 self
.signal_col
= gtk
.TreeViewColumn("Signal")
174 self
.signal_col
.pack_start(self
.sig_cell
, True)
175 self
.signal_col
.add_attribute(self
.sig_cell
, 'pixbuf', 5)
176 self
.plist
.append_column(self
.signal_col
)
178 self
.mode_cell
= gtk
.CellRendererText()
179 self
.mode_col
= gtk
.TreeViewColumn("Mode")
180 self
.mode_col
.pack_start(self
.mode_cell
, True)
181 self
.mode_col
.add_attribute(self
.mode_cell
, 'text', 6)
182 self
.plist
.append_column(self
.mode_col
)
183 # The protocol column
184 self
.prot_cell
= gtk
.CellRendererText()
185 self
.protocol_col
= gtk
.TreeViewColumn("802.11")
186 self
.protocol_col
.pack_start(self
.prot_cell
, True)
187 self
.protocol_col
.add_attribute(self
.prot_cell
, 'text', 7)
188 self
.plist
.append_column(self
.protocol_col
)
190 self
.channel_cell
= gtk
.CellRendererText()
191 self
.channel_col
= gtk
.TreeViewColumn("Channel")
192 self
.channel_col
.pack_start(self
.channel_cell
, True)
193 self
.channel_col
.add_attribute(self
.channel_cell
, 'text', 8)
194 self
.plist
.append_column(self
.channel_col
)
196 self
.plist
.set_reorderable(True)
197 # detect d-n-d of AP in round-about way, since rows-reordered does not work as advertised
198 self
.pstore
.connect('row-deleted', self
.update_auto_profile_order
)
199 # enable/disable buttons based on the selected network
200 self
.selected_network
= self
.plist
.get_selection()
201 self
.selected_network
.connect('changed', self
.on_network_selection
, None)
202 # the list scroll bar
203 sb
= gtk
.VScrollbar(self
.plist
.get_vadjustment())
207 self
.new_button
= gtk
.Button("_New")
208 self
.new_button
.connect('clicked', self
.create_new_profile
, misc
.get_new_profile(), None)
209 self
.new_button
.show()
210 # Add Configure button
211 self
.edit_button
= gtk
.Button("C_onfigure")
212 self
.edit_button
.connect('clicked', self
.edit_profile
, None)
213 self
.edit_button
.show()
214 self
.edit_button
.set_sensitive(False)
216 self
.delete_button
= gtk
.Button("_Delete")
217 self
.delete_button
.connect('clicked', self
.delete_profile_with_check
, None)
218 self
.delete_button
.show()
219 self
.delete_button
.set_sensitive(False)
221 self
.connect_button
= gtk
.Button("Co_nnect")
222 self
.connect_button
.connect('clicked', self
.connect_profile
, None)
223 # Add Disconnect button
224 self
.disconnect_button
= gtk
.Button("D_isconnect")
225 self
.disconnect_button
.connect('clicked', self
.disconnect_profile
, None)
226 # lets add our widgets
227 rows
= gtk
.VBox(False, 3)
228 net_list
= gtk
.HBox(False, 0)
229 listcols
= gtk
.HBox(False, 0)
230 prows
= gtk
.VBox(False, 0)
233 net_list
.pack_start(self
.plist
, True, True, 0)
234 net_list
.pack_start(sb
, False, False, 0)
236 rows
.pack_start(net_list
, True, True, 0)
237 rows
.pack_start(self
.current_network
, False, True, 0)
239 listcols
.pack_start(rows
, True, True, 0)
240 listcols
.pack_start(prows
, False, False, 5)
242 prows
.pack_start(self
.new_button
, False, False, 2)
243 prows
.pack_start(self
.edit_button
, False, False, 2)
244 prows
.pack_start(self
.delete_button
, False, False, 2)
245 prows
.pack_end(self
.connect_button
, False, False, 2)
246 prows
.pack_end(self
.disconnect_button
, False, False, 2)
248 self
.window
.action_area
.pack_start(self
.about_button
)
249 self
.window
.action_area
.pack_start(self
.preferences_button
)
250 self
.window
.action_area
.pack_start(self
.close_button
)
255 self
.window
.vbox
.add(listcols
)
256 self
.window
.vbox
.set_spacing(3)
257 self
.window
.show_all()
259 # Now, immediately hide these two. The proper one will be
260 # displayed later, based on interface state. -BEF-
261 self
.disconnect_button
.hide()
262 self
.connect_button
.hide()
263 self
.connect_button
.set_sensitive(False)
265 # set up connection manager for later use
266 self
.connection
= connections
.ConnectionManager(self
.commandQueue
)
267 self
.connection
.set_config(self
.config
)
268 # set up status window for later use
269 self
.status_window
= transients
.StatusWindow(self
)
270 self
.status_window
.cancel_button
.connect('clicked', self
.disconnect_profile
, "cancel")
272 # Add our known profiles in order
273 for profile_name
in self
.config
.auto_profile_order
:
274 profile_name
= profile_name
.strip()
275 self
.access_points
[profile_name
] = self
.config
.get_profile(profile_name
)
277 if self
.access_points
[profile_name
]['encrypted']:
278 wep
= gtk
.STOCK_DIALOG_AUTHENTICATION
279 if self
.access_points
[profile_name
]['roaming']:
280 ap_name
= self
.access_points
[profile_name
]['essid'] + "\n" + ' Multiple APs'
282 ap_name
= self
.access_points
[profile_name
]['essid'] + "\n" + self
.access_points
[profile_name
]['bssid']
283 self
.pstore
.append([ap_name
, self
.known_profile_icon
, self
.access_points
[profile_name
]['known'], self
.access_points
[profile_name
]['available'], wep
, self
.signal_none_pb
, self
.access_points
[profile_name
]['mode'], self
.access_points
[profile_name
]['protocol'], self
.access_points
[profile_name
]['channel'] ])
284 # This is the first run (or, at least, no config file was present), so pop up the preferences window
286 new_file
= self
.config
.get_opt_as_bool('DEFAULT', 'new_file')
288 self
.config
.remove_option('DEFAULT', 'new_file')
289 self
.edit_preferences(self
.preferences_button
)
290 except configparser
.NoOptionError
:
294 """ Begin running RadarWindow in Gtk event loop.
306 def destroy(self
, widget
=None):
307 """ Quit application.
311 'widget' -- gtk.Widget - The widget sending the event.
317 if self
.status_window
:
318 self
.status_window
.destroy()
321 def delete_event(self
, widget
, data
=None):
322 """ Kill scanning thread, update profile order for config file, and ask to be destroyed.
326 'widget' -- gtk.Widget - The widget sending the event.
328 'data' -- tuple - list of arbitrary arguments (not used)
332 boolean -- always return False (i.e. do not propigate the signal which called)
335 # process GTK events so that window hides more quickly
336 if sys
.modules
.has_key("gtk"):
337 while gtk
.events_pending():
338 gtk
.main_iteration(False)
339 # Let other threads know it is time to exit
340 self
.exit_event
.set()
341 # Wait for all other threads to exit before continuing
342 while threading
.activeCount() > 1:
347 def update_network_info(self
, profile
=None, ip
=None):
348 """ Update the current ip and essid shown to the user.
350 if (profile
is None) and (ip
is None):
351 self
.current_network
.set_text("Not Connected.")
353 self
.current_network
.set_text('Connected to {}\nIP Address {}'.format(profile
, ip
))
355 def update_connect_buttons(self
, connected
=False):
356 """ Set the state of connect/disconnect buttons to reflect the
357 current connected state.
360 self
.connect_button
.hide()
361 self
.disconnect_button
.show()
363 self
.disconnect_button
.hide()
364 self
.connect_button
.show()
366 def update_plist_items(self
, ap
):
367 """ Updates the on-screen profiles list.
371 'ap' -- dictionary -- The AP found by scanning_thread.
377 # Check for roaming profile.
378 ap_name
= misc
.make_section_name(ap
['essid'], '')
379 profile
= self
.config
.get_profile(ap_name
)
380 prow_iter
= self
.get_row_by_ap(ap
['essid'], ' Multiple APs')
381 ap_display
= ap
['essid'] + "\n" + ' Multiple APs'
383 # Check for normal profile.
384 ap_name
= misc
.make_section_name(ap
['essid'], ap
['bssid'])
385 profile
= self
.config
.get_profile(ap_name
)
386 prow_iter
= self
.get_row_by_ap(ap
['essid'], ap
['bssid'])
387 ap_display
= ap
['essid'] + "\n" + ap
['bssid']
389 if prow_iter
is not None:
390 # the AP is in the list of APs on the screen
391 # Set the 'known' values; False is default, overridden to True by self.access_points
392 ap
['known'] = self
.access_points
[ap_name
]['known']
393 self
.pstore
.set_value(prow_iter
, 1, self
.pixbuf_from_known(ap
['known']))
394 self
.pstore
.set_value(prow_iter
, 2, ap
['known'])
395 self
.pstore
.set_value(prow_iter
, 3, ap
['available'])
397 wep
= gtk
.STOCK_DIALOG_AUTHENTICATION
398 self
.pstore
.set_value(prow_iter
, 4, wep
)
399 self
.pstore
.set_value(prow_iter
, 5, self
.pixbuf_from_signal(self
.access_points
[ap_name
]['signal']))
400 self
.pstore
.set_value(prow_iter
, 6, ap
['mode'])
401 self
.pstore
.set_value(prow_iter
, 7, ap
['protocol'])
402 self
.pstore
.set_value(prow_iter
, 8, self
.access_points
[ap_name
]['channel'])
404 # the AP is not in the list of APs on the screen
405 self
.pstore
.append([ap_display
, self
.pixbuf_from_known(ap
['known']), ap
['known'], ap
['available'], wep
, self
.pixbuf_from_signal(ap
['signal']), ap
['mode'], ap
['protocol'], ap
['channel']])
406 #print "update_plist_items: new ap", ap[ 'essid' ], ap['bssid']
408 def update_ap_list(self
, ap
):
409 """ Updates the record-keeping profiles list.
413 'ap' -- dictionary -- The AP found by scanning_thread.
419 # Check for roaming profile.
420 ap_name
= misc
.make_section_name(ap
['essid'], '')
421 profile
= self
.config
.get_profile(ap_name
)
423 # Check for normal profile.
424 ap_name
= misc
.make_section_name(ap
['essid'], ap
['bssid'])
425 profile
= self
.config
.get_profile(ap_name
)
427 # No profile, so make a new one.
428 profile
= misc
.get_new_profile()
429 if self
.access_points
.has_key(ap_name
):
430 # This AP has been configured and should be updated
431 self
.access_points
[ap_name
]['known'] = profile
['known']
432 self
.access_points
[ap_name
]['available'] = ap
['available']
433 self
.access_points
[ap_name
]['encrypted'] = ap
['encrypted']
434 self
.access_points
[ap_name
]['mode'] = ap
['mode']
435 self
.access_points
[ap_name
]['protocol'] = ap
['protocol']
436 if self
.access_points
[ap_name
]['roaming']:
438 if self
.access_points
[ap_name
]['bssid'] == ap
['bssid']:
439 # Same AP for this roaming profile
440 self
.access_points
[ap_name
]['signal'] = ap
['signal']
443 if int(self
.access_points
[ap_name
]['signal']) < int(ap
['signal']):
444 # Stronger signal with this AP, so promote it to preferred
445 self
.access_points
[ap_name
]['signal'] = ap
['signal']
446 self
.access_points
[ap_name
]['channel'] = ap
['channel']
447 self
.access_points
[ap_name
]['bssid'] = ap
['bssid']
450 self
.access_points
[ap_name
]['signal'] = ap
['signal']
451 self
.access_points
[ap_name
]['channel'] = ap
['channel']
453 # Not seen before, begin tracking it.
454 self
.access_points
[ap_name
] = profile
456 def update_window(self
):
457 """ Updates the main user interface.
465 boolean -- always return True
468 self
.update_network_info()
469 self
.update_connect_buttons()
471 # Get APs scanned by iwlist
473 ap
= self
.apQueue
.get_nowait()
477 self
.update_ap_list(ap
)
478 self
.update_plist_items(ap
)
491 def get_row_by_ap(self
, essid
, bssid
):
492 """ Return row which holds specified ESSID and BSSID.
496 'essid' -- string - ESSID to match
498 'bssid' -- string - BSSID to match
502 gtk.TreeIter or None -- pointer to the row containing the match or None for no match found
504 if bssid
== "roaming":
505 for row
in self
.pstore
:
506 if ( row
[0][:row
[0].find("\n")] == essid
):
507 #print "roaming match:", row.iter, essid, bssid
510 for row
in self
.pstore
:
511 if ( row
[0] == essid
+ "\n" + bssid
):
512 #print "normal match:", row.iter, essid, bssid
516 def on_network_selection(self
, widget
, data
=None):
517 """ Enable/disable buttons based on the selected network.
521 'widget' -- gtk.Widget - The widget sending the event.
523 'data' -- tuple - list of arbitrary arguments (not used)
529 (store
, selected_iter
) = self
.selected_network
.get_selected()
530 #print self.access_points[(misc.make_section_name(store.get_value(selected_iter, 0), store.get_value(selected_iter, 1) ))]
531 # if no networks are selected, disable all buttons except New
532 # (this occurs after a drag-and-drop)
533 if selected_iter
is None:
534 self
.edit_button
.set_sensitive(False)
535 self
.delete_button
.set_sensitive(False)
536 self
.connect_button
.set_sensitive(False)
538 # enable/disable buttons
539 self
.connect_button
.set_sensitive(True)
540 if ( store
.get_value(selected_iter
, 2) == True ): # is selected network known?
541 self
.edit_button
.set_sensitive(True)
542 self
.delete_button
.set_sensitive(True)
544 self
.edit_button
.set_sensitive(True)
545 self
.delete_button
.set_sensitive(False)
547 def show_about_info(self
, widget
, data
=None):
548 """ Init and run the about dialog
552 'widget' -- gtk.Widget - The widget sending the event.
554 'data' -- tuple - list of arbitrary arguments (not used)
560 about
= transients
.AboutDialog()
564 def edit_preferences(self
, widget
, data
=None):
565 """ Init and run the preferences dialog
569 'widget' -- gtk.Widget - The widget sending the event.
571 'data' -- tuple - list of arbitrary arguments (not used)
577 # get raw strings from config file
578 self
.config
.raw
= True
579 prefs_editor
= prefs
.PreferencesEditor(self
, self
.config
)
580 response
= prefs_editor
.run()
581 if response
== int(gtk
.RESPONSE_ACCEPT
):
583 prefs_editor
.destroy()
584 # get cooked strings from config file
585 self
.config
.raw
= False
587 def create_new_profile(self
, widget
, profile
, data
=None):
588 """ Respond to a request to create a new AP profile
592 'widget' -- gtk.Widget - The widget sending the event.
594 'profile' -- dictionary - The AP profile to use as basis for new profile.
596 'data' -- tuple - list of arbitrary arguments (not used)
600 boolean -- True if a profile was created and False if profile creation was canceled.
602 profile_editor
= profile_ed
.ProfileEditor(self
, profile
)
604 profile
= profile_editor
.run()
606 error_dlg
= transients
.ErrorDialog(profile_editor
.dialog
, "Cannot save empty ESSID")
610 profile_editor
.destroy()
612 (store
, selected_iter
) = self
.plist
.get_selection().get_selected()
613 if selected_iter
is not None:
614 store
.remove(selected_iter
)
615 if profile
['roaming']:
616 apname
= misc
.make_section_name(profile
['essid'], '')
618 apname
= misc
.make_section_name(profile
['essid'], profile
['bssid'])
619 # Check that the ap does not exist already
620 if apname
in self
.config
.profiles():
621 error_dlg
= transients
.ErrorDialog(self
.window
, "A profile for %s already exists" % (apname
))
624 self
.access_points
[ apname
] = profile
625 self
.config
.set_section(apname
, profile
)
626 # if it is not in the auto_profile_order add it
627 if apname
not in self
.config
.auto_profile_order
:
628 self
.config
.auto_profile_order
.insert(0, apname
)
631 if profile
['encrypted']: wep
= gtk
.STOCK_DIALOG_AUTHENTICATION
635 if e
.errno
== errno
.ENOENT
:
636 error_dlg
= transients
.ErrorDialog(self
.window
, "Could not save configuration file:\n%s\n\n%s" % (self
.config
.filename
, e
.strerror
))
640 # Add AP to the list displayed to user
641 if profile
['roaming']:
642 ap_name
= profile
['essid'] + "\n" + ' Multiple APs'
644 prow_iter
= self
.get_row_by_ap(profile
['essid'], 'roaming')
646 self
.pstore
.remove(prow_iter
)
650 ap_name
= profile
['essid'] + "\n" + profile
['bssid']
651 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']])
654 # Did not create new profile
657 def edit_profile(self
, widget
, data
=None):
658 """ Respond to a request to edit an AP profile. Edit selected AP profile if it
659 is known. Otherwise, create a new profile with the selected ESSID and BSSID.
663 'widget' -- gtk.Widget - The widget sending the event.
665 'data' -- tuple - list of arbitrary arguments (not used)
671 (store
, selected_iter
) = self
.plist
.get_selection().get_selected()
672 if not selected_iter
:
675 (essid
, bssid
) = str(self
.pstore
.get_value(selected_iter
, 0)).split("\n")
676 if bssid
== ' Multiple APs':
677 # AP list says this is a roaming profile
678 apname
= misc
.make_section_name(essid
, '')
680 # AP list says this is NOT a roaming profile
681 apname
= misc
.make_section_name(essid
, bssid
)
682 profile
= self
.config
.get_profile(apname
)
684 # A profile was found in the config file
685 profile
['bssid'] = self
.access_points
[apname
]['bssid']
686 profile_editor
= profile_ed
.ProfileEditor(self
, profile
)
688 # try editing the profile
689 edited_profile
= profile_editor
.run()
691 error_dlg
= transients
.ErrorDialog(profile_editor
.dialog
, "Cannot save empty ESSID")
695 # Always remove profile editor window from screen
696 profile_editor
.destroy()
698 # A profile was returned by the editor
701 if edited_profile
['essid'] != profile
['essid'] or edited_profile
['bssid'] != profile
['bssid'] or edited_profile
['roaming'] != profile
['roaming']:
702 # ESSID, BSSID, or roaming was changed in profile editor
704 self
.commandQueue
.put('pause')
705 self
.commandQueue
.join()
708 if profile
['roaming']:
709 # The old profile was a roaming profile
710 old_ap
= misc
.make_section_name(profile
['essid'], '')
712 # The old profile was NOT a roaming profile
713 old_ap
= misc
.make_section_name(profile
['essid'], profile
['bssid'])
714 # Find where old profile was in auto order
715 old_index
= self
.config
.auto_profile_order
.index(old_ap
)
716 # Remove old profile and get its place in AP list
717 row
= self
.delete_profile(selected_iter
, old_ap
)
718 self
.config
.remove_section(old_ap
)
720 # Add AP to the list displayed to user
721 self
.apQueue
.put_nowait(edited_profile
)
722 self
.commandQueue
.put('scan')
725 if edited_profile
['roaming']:
726 # New profile is a roaming profile
727 apname
= misc
.make_section_name(edited_profile
['essid'], '')
728 ap_display
= edited_profile
['essid'] + "\n" + ' Multiple APs'
729 # Remove all other profiles that match the new profile ESSID
731 prow_iter
= self
.get_row_by_ap(edited_profile
['essid'], 'roaming')
733 self
.pstore
.remove(prow_iter
)
737 # New profile is NOT a roaming profile
738 apname
= misc
.make_section_name(edited_profile
['essid'], edited_profile
['bssid'])
739 ap_display
= edited_profile
['essid'] + "\n" + edited_profile
['bssid']
740 # Insert the new profile in the same position as the one being replaced
741 if old_index
is not None:
742 # Old profile was in auto order list
743 self
.config
.auto_profile_order
.insert(old_index
, apname
)
744 if (( row
is not None) and (self
.pstore
.iter_is_valid(row
)) ):
745 self
.pstore
.insert_before(row
, [ap_display
, None, None, None, None, None, None, None, None])
746 self
.access_points
[apname
] = edited_profile
747 self
.config
.set_section(apname
, edited_profile
)
749 # Save updated profile to config file
752 if e
.errno
== errno
.ENOENT
:
753 error_dlg
= transients
.ErrorDialog(self
.window
, "Could not save configuration file:\n%s\n\n%s" % (self
.config
.filename
, e
.strerror
))
758 # The AP does not already have a profile
759 profile
= misc
.get_new_profile()
760 (profile
['essid'], profile
['bssid']) = store
.get_value(selected_iter
, 0).split("\n")
761 self
.create_new_profile(widget
, profile
, data
)
763 def delete_profile(self
, selected_iter
, apname
):
764 """ Delete an AP profile (i.e. make profile unknown)
768 'selected_iter' -- gtk.TreeIter - The selected row.
770 'apname' -- string - The configuration file section to remove
774 gtk.TreeIter -- the iter for the row removed from the gtk.ListStore
777 del self
.access_points
[apname
]
778 self
.config
.remove_section(apname
)
780 if apname
in self
.config
.auto_profile_order
:
781 self
.config
.auto_profile_order
.remove(apname
)
782 self
.pstore
.remove(selected_iter
)
783 # Let's save our current state
784 self
.update_auto_profile_order()
788 if e
.errno
== errno
.ENOENT
:
789 error_dlg
= transients
.ErrorDialog(self
.window
, "Could not save configuration file:\n%s\n\n%s" % (self
.config
.filename
, e
.strerror
))
795 def delete_profile_with_check(self
, widget
, data
=None):
796 """ Respond to a request to delete an AP profile (i.e. make profile unknown)
797 Check with user first.
801 'widget' -- gtk.Widget - The widget sending the event.
803 'data' -- tuple - list of arbitrary arguments (not used)
809 (store
, selected_iter
) = self
.plist
.get_selection().get_selected()
810 if not selected_iter
: return
811 (essid
, bssid
) = store
.get_value(selected_iter
, 0).split("\n")
812 if bssid
== ' Multiple APs':
813 apname
= misc
.make_section_name(essid
, '')
815 apname
= misc
.make_section_name(essid
, bssid
)
816 profile
= self
.config
.get_profile(apname
)
817 if profile
['roaming']:
818 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
, ))
820 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
))
821 known
= store
.get_value(selected_iter
, 1)
826 if res
== gtk
.RESPONSE_NO
:
828 self
.delete_profile(selected_iter
, apname
)
830 def connect_profile(self
, widget
, profile
, data
=None):
831 """ Respond to a request to connect to an AP.
835 'widget' -- gtk.Widget - The widget sending the event.
837 'profile' -- dictionary - The AP profile to which to connect.
839 'data' -- tuple - list of arbitrary arguments (not used)
845 (store
, selected_iter
) = self
.plist
.get_selection().get_selected()
846 if not selected_iter
: return
847 (essid
, bssid
) = store
.get_value(selected_iter
, 0).split("\n")
848 known
= store
.get_value(selected_iter
, 2)
850 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?")
854 if res
== gtk
.RESPONSE_NO
:
856 profile
= misc
.get_new_profile()
857 profile
['essid'] = essid
858 profile
['bssid'] = bssid
859 if not self
.create_new_profile(widget
, profile
, data
):
862 # Check for roaming profile.
863 ap_name
= misc
.make_section_name(essid
, '')
864 profile
= self
.config
.get_profile(ap_name
)
866 # Check for normal profile.
867 ap_name
= misc
.make_section_name(essid
, bssid
)
868 profile
= self
.config
.get_profile(ap_name
)
870 # No configured profile
872 profile
['bssid'] = self
.access_points
[ap_name
]['bssid']
873 profile
['channel'] = self
.access_points
[ap_name
]['channel']
874 self
.connection
.connect_to_network(profile
, self
.status_window
)
876 def disconnect_profile(self
, widget
, data
=None):
877 """ Respond to a request to disconnect by calling ConnectionManager disconnect_interface().
881 'widget' -- gtk.Widget - The widget sending the event.
883 'data' -- tuple - list of arbitrary arguments (not used)
890 self
.status_window
.update_message("Canceling connection...")
891 if sys
.modules
.has_key("gtk"):
892 while gtk
.events_pending():
893 gtk
.main_iteration(False)
895 self
.connection
.disconnect_interface()
897 def profile_order_updater(self
, model
, path
, iter, auto_profile_order
):
900 if model
.get_value(iter, 2) is True:
901 (essid
, bssid
) = model
.get_value(iter, 0).split("\n")
902 if bssid
== ' Multiple APs':
904 apname
= misc
.make_section_name(essid
, bssid
)
905 auto_profile_order
.append(apname
)
907 def update_auto_profile_order(self
, widget
=None, data
=None, data2
=None):
908 """ Update the config file auto profile order from the on-screen order
912 'widget' -- gtk.Widget - The widget sending the event.
914 'data' -- tuple - list of arbitrary arguments (not used)
916 'data2' -- tuple - list of arbitrary arguments (not used)
922 # recreate the auto_profile_order
923 auto_profile_order
= []
924 self
.pstore
.foreach(self
.profile_order_updater
, auto_profile_order
)
925 self
.config
.auto_profile_order
= auto_profile_order
929 if e
.errno
== errno
.ENOENT
:
930 error_dlg
= transients
.ErrorDialog(self
.window
, "Could not save configuration file:\n%s\n\n%s" % (self
.config
.filename
, e
.strerror
))
936 # Make so we can be imported
937 if __name__
== "__main__":