Add add_profile method
[wifi-radar.git] / wifiradar / gui / g2 / __init__.py
blob012ead2bf3c5a2f4ab16e6c101d34fe7ff28020f
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 <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
34 try:
35 # Py2
36 import ConfigParser as configparser
37 except ImportError:
38 # Py3
39 import configparser
41 try:
42 # Py2
43 import Queue as queue
44 except ImportError:
45 # Py3
46 import queue
48 import errno
49 import gtk
50 import logging
51 import sys
52 import threading
53 from time import sleep
55 import wifiradar.connections as connections
56 import wifiradar.misc as misc
57 from . import prefs
58 from . import profile as profile_ed
59 from . import transients
61 # create a logger
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
77 familiarity.
78 """
79 if known:
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)
87 """
88 signal = int(signal)
89 # Shift signal up by 80 to convert dBm scale to arbitrary scale.
90 if signal < 0:
91 signal = signal + 80
92 # Find an icon...
93 if signal < 3:
94 return signal_none_pb
95 elif signal < 12:
96 return signal_low_pb
97 elif signal < 20:
98 return signal_barely_pb
99 elif signal < 35:
100 return signal_ok_pb
101 elif signal >= 35:
102 return signal_best_pb
105 class RadarWindow:
106 """ Center for most of the rest of the operations.
108 def __init__(self, config, apQueue, commQueue, exit_event):
109 """ Create a new RadarWindow.
111 Parameters:
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.
119 Returns:
121 RadarWindow instance
123 self.config = config
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)
165 # The AP column
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)
171 # The signal column
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)
177 # The mode column
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)
189 # The channel column
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)
195 # DnD Ordering
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())
204 sb.show()
205 self.plist.show()
206 # Add New button
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)
215 # Add Delete button
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)
220 # Add Connect button
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)
231 # lets start packing
232 # the network list
233 net_list.pack_start(self.plist, True, True, 0)
234 net_list.pack_start(sb, False, False, 0)
235 # the rows level
236 rows.pack_start(net_list , True, True, 0)
237 rows.pack_start(self.current_network, False, True, 0)
238 # the list columns
239 listcols.pack_start(rows, True, True, 0)
240 listcols.pack_start(prows, False, False, 5)
241 # the list buttons
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)
252 rows.show()
253 prows.show()
254 listcols.show()
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)
276 wep = None
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'
281 else:
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
285 try:
286 new_file = self.config.get_opt_as_bool('DEFAULT', 'new_file')
287 if (new == True):
288 self.config.remove_option('DEFAULT', 'new_file')
289 self.edit_preferences(self.preferences_button)
290 except configparser.NoOptionError:
291 pass
293 def main(self):
294 """ Begin running RadarWindow in Gtk event loop.
296 Parameters:
298 nothing
300 Returns:
302 nothing
304 gtk.main()
306 def destroy(self, widget=None):
307 """ Quit application.
309 Parameters:
311 'widget' -- gtk.Widget - The widget sending the event.
313 Returns:
315 nothing
317 if self.status_window:
318 self.status_window.destroy()
319 gtk.main_quit()
321 def delete_event(self, widget, data=None):
322 """ Kill scanning thread, update profile order for config file, and ask to be destroyed.
324 Parameters:
326 'widget' -- gtk.Widget - The widget sending the event.
328 'data' -- tuple - list of arbitrary arguments (not used)
330 Returns:
332 boolean -- always return False (i.e. do not propigate the signal which called)
334 self.window.hide()
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:
343 sleep(0.25)
344 self.destroy()
345 return False
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.")
352 else:
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.
359 if connected:
360 self.connect_button.hide()
361 self.disconnect_button.show()
362 else:
363 self.disconnect_button.hide()
364 self.connect_button.show()
366 def update_plist_items(self, profile):
367 """ Updates the display of :data:`profile`.
369 if profile['roaming']:
370 prow_iter = self.get_row_by_ap(profile['essid'])
371 else:
372 prow_iter = self.get_row_by_ap(profile['essid'], profile['bssid'])
374 if prow_iter is None:
375 # the AP is not in the list of APs on the screen
376 self.add_profile(profile)
377 else:
378 # the AP is in the list of APs on the screen
379 wep = None
380 if profile['encrypted']:
381 wep = gtk.STOCK_DIALOG_AUTHENTICATION
382 # Update the Gtk objects.
383 self.pstore.set_value(prow_iter, 1, pixbuf_from_known(profile['known']))
384 self.pstore.set_value(prow_iter, 2, profile['known'])
385 self.pstore.set_value(prow_iter, 3, profile['available'])
386 self.pstore.set_value(prow_iter, 4, wep)
387 self.pstore.set_value(prow_iter, 5, pixbuf_from_signal(profile['signal']))
388 self.pstore.set_value(prow_iter, 6, profile['mode'])
389 self.pstore.set_value(prow_iter, 7, profile['protocol'])
390 self.pstore.set_value(prow_iter, 8, profile['channel'])
392 def get_row_by_ap(self, essid, bssid=' Multiple APs'):
393 """ Returns a :class:`gtk.TreeIter` for the row which holds
394 :data:`essid` and :data:`bssid`.
396 :data:`bssid` is optional. If not given, :func:`get_row_by_ap`
397 will try to match a roaming profile with the given :data:`essid`.
399 If no match is found, it returns None.
401 for row in self.pstore:
402 if (row[0] == essid) and (row[1] == bssid):
403 return row.iter
404 return None
406 def on_network_selection(self, widget, data=None):
407 """ Enable/disable buttons based on the selected network.
409 Parameters:
411 'widget' -- gtk.Widget - The widget sending the event.
413 'data' -- tuple - list of arbitrary arguments (not used)
415 Returns:
417 nothing
419 (store, selected_iter) = self.selected_network.get_selected()
420 #print self.access_points[(misc.make_section_name(store.get_value(selected_iter, 0), store.get_value(selected_iter, 1) ))]
421 # if no networks are selected, disable all buttons except New
422 # (this occurs after a drag-and-drop)
423 if selected_iter is None:
424 self.edit_button.set_sensitive(False)
425 self.delete_button.set_sensitive(False)
426 self.connect_button.set_sensitive(False)
427 return
428 # enable/disable buttons
429 self.connect_button.set_sensitive(True)
430 if ( store.get_value(selected_iter, 2) == True ): # is selected network known?
431 self.edit_button.set_sensitive(True)
432 self.delete_button.set_sensitive(True)
433 else:
434 self.edit_button.set_sensitive(True)
435 self.delete_button.set_sensitive(False)
437 def show_about_info(self, widget, data=None):
438 """ Init and run the about dialog
440 Parameters:
442 'widget' -- gtk.Widget - The widget sending the event.
444 'data' -- tuple - list of arbitrary arguments (not used)
446 Returns:
448 nothing
450 about = transients.AboutDialog()
451 about.run()
452 about.destroy()
454 def edit_preferences(self, widget, data=None):
455 """ Init and run the preferences dialog
457 Parameters:
459 'widget' -- gtk.Widget - The widget sending the event.
461 'data' -- tuple - list of arbitrary arguments (not used)
463 Returns:
465 nothing
467 # get raw strings from config file
468 self.config.raw = True
469 prefs_editor = prefs.PreferencesEditor(self, self.config)
470 response = prefs_editor.run()
471 if response == int(gtk.RESPONSE_ACCEPT):
472 prefs_editor.save()
473 prefs_editor.destroy()
474 # get cooked strings from config file
475 self.config.raw = False
477 def add_profile(self, profile):
478 """ Add :data:`profile` to the list of APs shown to the user.
480 if profile['roaming']:
481 profile['bssid'] = ' Multiple APs'
483 wep = None
484 if profile['encrypted']:
485 wep = gtk.STOCK_DIALOG_AUTHENTICATION
487 self.pstore.append([profile['essid'], profile['bssid'],
488 self.known_profile_icon, profile['known'], profile['available'],
489 wep, self.signal_none_pb, profile['mode'], profile['protocol'],
490 profile['channel']])
492 def create_new_profile(self, widget, profile, data=None):
493 """ Respond to a request to create a new AP profile
495 Parameters:
497 'widget' -- gtk.Widget - The widget sending the event.
499 'profile' -- dictionary - The AP profile to use as basis for new profile.
501 'data' -- tuple - list of arbitrary arguments (not used)
503 Returns:
505 boolean -- True if a profile was created and False if profile creation was canceled.
507 profile_editor = profile_ed.ProfileEditor(self, profile)
508 try:
509 profile = profile_editor.run()
510 except ValueError:
511 error_dlg = transients.ErrorDialog(profile_editor.dialog, "Cannot save empty ESSID")
512 del error_dlg
513 return False
514 finally:
515 profile_editor.destroy()
516 if profile:
517 (store, selected_iter) = self.plist.get_selection().get_selected()
518 if selected_iter is not None:
519 store.remove(selected_iter)
520 if profile['roaming']:
521 apname = misc.make_section_name(profile['essid'], '')
522 else:
523 apname = misc.make_section_name(profile['essid'], profile['bssid'])
524 # Check that the ap does not exist already
525 if apname in self.config.profiles():
526 error_dlg = transients.ErrorDialog(self.window, "A profile for %s already exists" % (apname))
527 del error_dlg
528 # try again
529 self.access_points[ apname ] = profile
530 self.config.set_section(apname, profile)
531 # if it is not in the auto_profile_order add it
532 if apname not in self.config.auto_profile_order:
533 self.config.auto_profile_order.insert(0, apname)
534 # add to the store
535 wep = None
536 if profile['encrypted']: wep = gtk.STOCK_DIALOG_AUTHENTICATION
537 try:
538 self.config.write()
539 except IOError as e:
540 if e.errno == errno.ENOENT:
541 error_dlg = transients.ErrorDialog(self.window, "Could not save configuration file:\n%s\n\n%s" % (self.config.filename, e.strerror))
542 del error_dlg
543 else:
544 raise e
545 # Add AP to the list displayed to user
546 if profile['roaming']:
547 ap_name = profile['essid'] + "\n" + ' Multiple APs'
548 while True:
549 prow_iter = self.get_row_by_ap(profile['essid'])
550 if prow_iter:
551 self.pstore.remove(prow_iter)
552 else:
553 break
554 else:
555 ap_name = profile['essid'] + "\n" + profile['bssid']
556 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']])
557 return True
558 else:
559 # Did not create new profile
560 return False
562 def edit_profile(self, widget, data=None):
563 """ Respond to a request to edit an AP profile. Edit selected AP profile if it
564 is known. Otherwise, create a new profile with the selected ESSID and BSSID.
566 Parameters:
568 'widget' -- gtk.Widget - The widget sending the event.
570 'data' -- tuple - list of arbitrary arguments (not used)
572 Returns:
574 nothing
576 (store, selected_iter) = self.plist.get_selection().get_selected()
577 if not selected_iter:
578 # No AP is selected
579 return
580 (essid, bssid) = str(self.pstore.get_value(selected_iter, 0)).split("\n")
581 if bssid == ' Multiple APs':
582 # AP list says this is a roaming profile
583 apname = misc.make_section_name(essid, '')
584 else:
585 # AP list says this is NOT a roaming profile
586 apname = misc.make_section_name(essid, bssid)
587 profile = self.config.get_profile(apname)
588 if profile:
589 # A profile was found in the config file
590 profile['bssid'] = self.access_points[apname]['bssid']
591 profile_editor = profile_ed.ProfileEditor(self, profile)
592 try:
593 # try editing the profile
594 edited_profile = profile_editor.run()
595 except ValueError:
596 error_dlg = transients.ErrorDialog(profile_editor.dialog, "Cannot save empty ESSID")
597 del error_dlg
598 return False
599 finally:
600 # Always remove profile editor window from screen
601 profile_editor.destroy()
602 if edited_profile:
603 # A profile was returned by the editor
604 old_index = None
605 row = None
606 if edited_profile['essid'] != profile['essid'] or edited_profile['bssid'] != profile['bssid'] or edited_profile['roaming'] != profile['roaming']:
607 # ESSID, BSSID, or roaming was changed in profile editor
608 try:
609 self.commandQueue.put('pause')
610 self.commandQueue.join()
611 except queue.Full:
612 pass
613 if profile['roaming']:
614 # The old profile was a roaming profile
615 old_ap = misc.make_section_name(profile['essid'], '')
616 else:
617 # The old profile was NOT a roaming profile
618 old_ap = misc.make_section_name(profile['essid'], profile['bssid'])
619 # Find where old profile was in auto order
620 old_index = self.config.auto_profile_order.index(old_ap)
621 # Remove old profile and get its place in AP list
622 row = self.delete_profile(selected_iter, old_ap)
623 self.config.remove_section(old_ap)
624 try:
625 # Add AP to the list displayed to user
626 self.apQueue.put_nowait(edited_profile)
627 self.commandQueue.put('scan')
628 except queue.Full:
629 pass
630 if edited_profile['roaming']:
631 # New profile is a roaming profile
632 apname = misc.make_section_name(edited_profile['essid'], '')
633 ap_display = edited_profile['essid'] + "\n" + ' Multiple APs'
634 # Remove all other profiles that match the new profile ESSID
635 while True:
636 prow_iter = self.get_row_by_ap(edited_profile['essid'])
637 if prow_iter:
638 self.pstore.remove(prow_iter)
639 else:
640 break
641 else:
642 # New profile is NOT a roaming profile
643 apname = misc.make_section_name(edited_profile['essid'], edited_profile['bssid'])
644 ap_display = edited_profile['essid'] + "\n" + edited_profile['bssid']
645 # Insert the new profile in the same position as the one being replaced
646 if old_index is not None:
647 # Old profile was in auto order list
648 self.config.auto_profile_order.insert(old_index, apname)
649 if (( row is not None) and (self.pstore.iter_is_valid(row)) ):
650 self.pstore.insert_before(row, [ap_display, None, None, None, None, None, None, None, None])
651 self.access_points[apname] = edited_profile
652 self.config.set_section(apname, edited_profile)
653 try:
654 # Save updated profile to config file
655 self.config.write()
656 except IOError as e:
657 if e.errno == errno.ENOENT:
658 error_dlg = transients.ErrorDialog(self.window, "Could not save configuration file:\n%s\n\n%s" % (self.config.filename, e.strerror))
659 del error_dlg
660 else:
661 raise e
662 else:
663 # The AP does not already have a profile
664 profile = misc.get_new_profile()
665 (profile['essid'], profile['bssid']) = store.get_value(selected_iter, 0).split("\n")
666 self.create_new_profile(widget, profile, data)
668 def delete_profile(self, selected_iter, apname):
669 """ Delete an AP profile (i.e. make profile unknown)
671 Parameters:
673 'selected_iter' -- gtk.TreeIter - The selected row.
675 'apname' -- string - The configuration file section to remove
677 Returns:
679 gtk.TreeIter -- the iter for the row removed from the gtk.ListStore
681 # Remove it
682 del self.access_points[apname]
683 self.config.remove_section(apname)
684 logger.info(apname)
685 if apname in self.config.auto_profile_order:
686 self.config.auto_profile_order.remove(apname)
687 self.pstore.remove(selected_iter)
688 # Let's save our current state
689 self.update_auto_profile_order()
690 try:
691 self.config.write()
692 except IOError as e:
693 if e.errno == errno.ENOENT:
694 error_dlg = transients.ErrorDialog(self.window, "Could not save configuration file:\n%s\n\n%s" % (self.config.filename, e.strerror))
695 del error_dlg
696 else:
697 raise e
698 return selected_iter
700 def delete_profile_with_check(self, widget, data=None):
701 """ Respond to a request to delete an AP profile (i.e. make profile unknown)
702 Check with user first.
704 Parameters:
706 'widget' -- gtk.Widget - The widget sending the event.
708 'data' -- tuple - list of arbitrary arguments (not used)
710 Returns:
712 nothing
714 (store, selected_iter) = self.plist.get_selection().get_selected()
715 if not selected_iter: return
716 (essid, bssid) = store.get_value(selected_iter, 0).split("\n")
717 if bssid == ' Multiple APs':
718 apname = misc.make_section_name(essid, '')
719 else:
720 apname = misc.make_section_name(essid, bssid)
721 profile = self.config.get_profile(apname)
722 if profile['roaming']:
723 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, ))
724 else:
725 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))
726 known = store.get_value(selected_iter, 1)
727 if not known: return
728 res = dlg.run()
729 dlg.destroy()
730 del dlg
731 if res == gtk.RESPONSE_NO:
732 return
733 self.delete_profile(selected_iter, apname)
735 def connect_profile(self, widget, profile, data=None):
736 """ Respond to a request to connect to an AP.
738 Parameters:
740 'widget' -- gtk.Widget - The widget sending the event.
742 'profile' -- dictionary - The AP profile to which to connect.
744 'data' -- tuple - list of arbitrary arguments (not used)
746 Returns:
748 nothing
750 (store, selected_iter) = self.plist.get_selection().get_selected()
751 if not selected_iter: return
752 (essid, bssid) = store.get_value(selected_iter, 0).split("\n")
753 known = store.get_value(selected_iter, 2)
754 if not known:
755 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?")
756 res = dlg.run()
757 dlg.destroy()
758 del dlg
759 if res == gtk.RESPONSE_NO:
760 return
761 profile = misc.get_new_profile()
762 profile['essid'] = essid
763 profile['bssid'] = bssid
764 if not self.create_new_profile(widget, profile, data):
765 return
766 else:
767 # Check for roaming profile.
768 ap_name = misc.make_section_name(essid, '')
769 profile = self.config.get_profile(ap_name)
770 if not profile:
771 # Check for normal profile.
772 ap_name = misc.make_section_name(essid, bssid)
773 profile = self.config.get_profile(ap_name)
774 if not profile:
775 # No configured profile
776 return
777 profile['bssid'] = self.access_points[ap_name]['bssid']
778 profile['channel'] = self.access_points[ap_name]['channel']
779 self.connection.connect_to_network(profile, self.status_window)
781 def disconnect_profile(self, widget, data=None):
782 """ Respond to a request to disconnect by calling ConnectionManager disconnect_interface().
784 Parameters:
786 'widget' -- gtk.Widget - The widget sending the event.
788 'data' -- tuple - list of arbitrary arguments (not used)
790 Returns:
792 nothing
794 if data == "cancel":
795 self.status_window.update_message("Canceling connection...")
796 if sys.modules.has_key("gtk"):
797 while gtk.events_pending():
798 gtk.main_iteration(False)
799 sleep(1)
800 self.connection.disconnect_interface()
802 def profile_order_updater(self, model, path, iter, auto_profile_order):
805 if model.get_value(iter, 2) is True:
806 (essid, bssid) = model.get_value(iter, 0).split("\n")
807 if bssid == ' Multiple APs':
808 bssid = ''
809 apname = misc.make_section_name(essid, bssid)
810 auto_profile_order.append(apname)
812 def update_auto_profile_order(self, widget=None, data=None, data2=None):
813 """ Update the config file auto profile order from the on-screen order
815 Parameters:
817 'widget' -- gtk.Widget - The widget sending the event.
819 'data' -- tuple - list of arbitrary arguments (not used)
821 'data2' -- tuple - list of arbitrary arguments (not used)
823 Returns:
825 nothing
827 # recreate the auto_profile_order
828 auto_profile_order = []
829 self.pstore.foreach(self.profile_order_updater, auto_profile_order)
830 self.config.auto_profile_order = auto_profile_order
831 try:
832 self.config.write()
833 except IOError as e:
834 if e.errno == errno.ENOENT:
835 error_dlg = transients.ErrorDialog(self.window, "Could not save configuration file:\n%s\n\n%s" % (self.config.filename, e.strerror))
836 del error_dlg
837 else:
838 raise e
841 # Make so we can be imported
842 if __name__ == "__main__":
843 pass