diff --git a/curses/README b/curses/README index 3e5df9d..ecd25c3 100644 --- a/curses/README +++ b/curses/README @@ -14,15 +14,15 @@ All features that I plan on implementing (that are not mentioned above) are listed the TODO file in this same directory. If you want any other features, ask me. I try to be on the #wicd Freenode IRC channel most of the time. -Controls: +Controls (most of these should be viewable in wicd-curses itself): F5 : refresh wireless networks -F8 or Q or q: quit +F8 Q q : quit D : disconnect from all active networks ESC : if connecting to a network, stop doing so -ENTER : Attempt connection to selected network +ENTER C : Attempt connection to selected network P : Display preferences dialog -C : Display network configuration for selected network +right arrow : Display network configuration for selected network A : Display "About" dialog I : Raise the "Scan for hidden networks" dialog H or h or ? : Raise help dialog @@ -33,7 +33,7 @@ O : Raise ad-hoc network dialog IN DIALOGS (Meta usually is "Alt"): ESC or Q: Quit dialog without saving information (if present) -Meta+Left/Right: Change tabs Left/Right (if tabs present) +Meta+[/]: Change tabs Left/Right (if tabs present) Meta+Enter : Quit dialog and save information FAQ (WIP): diff --git a/curses/curses_misc.py b/curses/curses_misc.py index e5079c7..f2d1ba0 100644 --- a/curses/curses_misc.py +++ b/curses/curses_misc.py @@ -49,7 +49,12 @@ class SelText(urwid.Text): """Don't handle any keys.""" return key -# This class is annoying. ^_^ +# ListBox that can't be selected. +class NSelListBox(urwid.ListBox): + def selectable(self): + return False + +# This class is annoying. :/ class DynWrap(urwid.AttrWrap): """ Makes an object have mutable selectivity. Attributes will change like @@ -136,7 +141,8 @@ class MaskingEdit(urwid.Edit): Render edit widget and return canvas. Include cursor when in focus. """ - # If we aren't masking anything ATM, then act like an Edit. No problems. + # If we aren't masking anything ATM, then act like an Edit. + # No problems. if self.mask_mode == "off" or (self.mask_mode == 'no_focus' and focus == True): canv = self.__super.render((maxcol,),focus) # The cache messes this thing up, because I am totally changing what @@ -165,9 +171,10 @@ class TabColumns(urwid.WidgetWrap): attr = normal attributes attrsel = attribute when active """ - def __init__(self,tab_str,tab_wid,title,bottom_part,attr=('body','focus'), + # FIXME Make the bottom_part optional + def __init__(self,tab_str,tab_wid,title,bottom_part=None,attr=('body','focus'), attrsel='tab active', attrtitle='header'): - self.bottom_part = bottom_part + #self.bottom_part = bottom_part #title_wid = urwid.Text((attrtitle,title),align='right') column_list = [] for w in tab_str: @@ -189,7 +196,7 @@ class TabColumns(urwid.WidgetWrap): self.pile = urwid.Pile([ ('fixed',1,urwid.Filler(self.columns,'top')), urwid.Filler(lbox,'top',height=('relative',99)), - ('fixed',1,urwid.Filler(self.bottom_part,'bottom')) + #('fixed',1,urwid.Filler(self.bottom_part,'bottom')) ]) if not firstrun: self.frame.set_body(self.pile) @@ -199,12 +206,13 @@ class TabColumns(urwid.WidgetWrap): return True def keypress(self,size,key): - self._w.keypress(size,key) - if key == "meta left" or key == "meta right": + if key == "meta [" or key == "meta ]": self._w.get_body().set_focus(0) - self.keypress(size,key[5:]) + newK = 'left' if key[-1] == '[' else 'right' + self.keypress(size,newK) self._w.get_body().set_focus(1) else: + key = self._w.keypress(size,key) wid = self.pile.get_focus().get_body() if wid == self.columns: # lw = self.listbox.body @@ -213,8 +221,20 @@ class TabColumns(urwid.WidgetWrap): self.columns.get_focus().set_attr('tab active') self.active_tab = self.columns.get_focus() self.gen_pile(self.tab_map[self.active_tab]) - return key + + return key # self.listbox.body = lw + def mouse_event(self,size,event,button,x,y,focus): + wid = self.pile.get_focus().get_body() + if wid == self.columns: + self.active_tab.set_attr('body') + + self._w.mouse_event(size,event,button,x,y,focus) + if wid == self.columns: + self.active_tab.set_attr('body') + self.columns.get_focus().set_attr('tab active') + self.active_tab = self.columns.get_focus() + self.gen_pile(self.tab_map[self.active_tab]) ### Combo box code begins here @@ -340,7 +360,6 @@ class ComboBox(urwid.WidgetWrap): def build_combobox(self,parent,ui,row): str,trash = self.label.get_text() - self.cbox = DynWrap(SelText([self.list[self.focus]+' vvv']), attrs=self.attrs,focus_attr=self.focus_attr) if str != '': @@ -438,7 +457,8 @@ class Dialog2(urwid.WidgetWrap): def run(self,ui,parent): ui.set_mouse_tracking() size = ui.get_cols_rows() - overlay = urwid.Overlay(urwid.LineBox(self.view), parent, 'center', self.width, + overlay = urwid.Overlay(urwid.LineBox(self.view), + parent, 'center', self.width, 'middle', self.height) try: while True: @@ -512,3 +532,98 @@ class InputDialog(Dialog2): def on_exit(self, exitcode): return exitcode, self.edit.get_edit_text() + + +class ClickCols(urwid.WidgetWrap): + def __init__(self,items,callback=None,args=None): + cols = urwid.Columns(items) + self.__super.__init__(cols) + self.callback = callback + self.args = args + def mouse_event(self,size,event,button,x,y,focus): + if event == "mouse press": + # The keypress dealie in wicd-curses.py expects a list of keystrokes + self.callback([self.args]) + +# htop-style menu menu-bar on the bottom of the screen +class OptCols(urwid.WidgetWrap): + # tuples = [(key,desc)], on_event gets passed a key + # attrs = (attr_key,attr_desc) + # mentions of 'left' and right will be converted to <- and -> respectively + def __init__(self,tuples,handler,attrs=('body','infobar'),debug=False): + # Find the longest string. Keys for this bar should be no greater than + # 2 characters long (e.g., -> for left) + #maxlen = 6 + #for i in tuples: + # newmax = len(i[0])+len(i[1]) + # if newmax > maxlen: + # maxlen = newmax + + # Construct the texts + textList = [] + i = 0 + # callbacks map the text contents to its assigned callback. + self.callbacks = [] + for cmd in tuples: + splitcmd = cmd[0].split() + key = '' + for part in splitcmd: + if part == 'ctrl': + key+='C^' + elif part == 'meta': + key+='M^' + else: + if part == 'left': + key += '<-' + elif part == 'right': + key += '->' + elif part == 'esc': + key += 'ESC' + else: + key += part + + #theText = urwid.Text([(attrs[0],cmd[0]),(attrs[1],cmd[1])]) + if debug: + callback = self.debugClick + args = cmd[1] + else: + callback = handler + args = cmd[0] + #self.callbacks.append(cmd[2]) + col = ClickCols([ + ('fixed',len(key)+1,urwid.Text((attrs[0],key+':')) ), + urwid.AttrWrap(urwid.Text(cmd[1]),attrs[1])], + callback,args) + #if i != len(tuples)-1: + # textList.append(('fixed',maxlen,col)) + #else: # The last one + textList.append(col) + i+=1 + if debug: + self.debug = urwid.Text("DEBUG_MODE") + textList.append(('fixed',10,self.debug)) + + cols = urwid.Columns(textList) + self.__super.__init__(cols) + def debugClick(self,args): + self.debug.set_text(args) + + def mouse_event(self,size,event,button,x,y,focus): + # Widgets are evenly long (as of current), so... + return self._w.mouse_event(size,event,button,x,y,focus) + """ + if self.debug: + if x > size[0]-10: + return + widsize = (size[0]-10)/len(self.callbacks) + else: + widsize = size[0]/len(self.callbacks) + widnum = x/widsize + if self.debug: + text = str(widnum) + if self.callbacks[widnum] == None: + text += " None" + self.debug.set_text(text) + elif self.callbacks[widnum] != None: + self.callbacks[widnum]() + """ diff --git a/curses/netentry_curses.py b/curses/netentry_curses.py index 0beed28..4e6bf29 100644 --- a/curses/netentry_curses.py +++ b/curses/netentry_curses.py @@ -82,13 +82,6 @@ class AdvancedSettingsDialog(urwid.WidgetWrap): _blank = urwid.Text('') - # Buttons. These need to be added to the list in superclasses. - self.OK_PRESSED= False - self.CANCEL_PRESSED = False - self.ok_button = urwid.AttrWrap(urwid.Button('OK',self.ok_callback),'body','focus') - self.cancel_button = urwid.AttrWrap(urwid.Button('Cancel',self.cancel_callback),'body','focus') - self.button_cols = urwid.Columns([self.ok_button,self.cancel_button]) - walker = urwid.SimpleListWalker([self.static_ip_cb, self.ip_edit, self.netmask_edit, @@ -103,16 +96,9 @@ class AdvancedSettingsDialog(urwid.WidgetWrap): self._listbox = urwid.ListBox(walker) #self._frame = urwid.Frame(self._listbox) - self._frame = urwid.Frame(self._listbox,footer=self.button_cols) + self._frame = urwid.Frame(self._listbox) self.__super.__init__(self._frame) - - # Button callbacks - def ok_callback(self,button_object,user_data=None): - self.OK_PRESSED = True - def cancel_callback(self,button_object,user_data=None): - self.CANCEL_PRESSED = True - def static_ip_set_state(self,checkb,new_state,user_data=None): for w in [ self.ip_edit,self.netmask_edit,self.gateway_edit ]: w.set_sensitive(new_state) @@ -164,54 +150,9 @@ class AdvancedSettingsDialog(urwid.WidgetWrap): self.set_net_prop("dns1", '') self.set_net_prop("dns2", '') self.set_net_prop("dns3", '') - - def prerun(self,ui,dim,display): + # Prevent comboboxes from dying. + def ready_widgets(self,ui,body): pass - def run(self,ui,dim,display): - self.ui = ui - self.parent = display - width,height = ui.get_cols_rows() - - self.overlay = urwid.Overlay(self, display, ('fixed left', 0),width - , ('fixed top',1), height-3) - self.prerun(ui,dim,display) - #self.ready_comboboxes(ui,overlay) - - keys = True - while True: - if keys: - ui.draw_screen(dim, self.overlay.render(dim, True)) - keys = ui.get_input() - - for k in keys: - #Send key to underlying widget: - if urwid.is_mouse_event(k): - event, button, col, row = k - self.overlay.mouse_event( dim, - event, button, col, row, - focus=True) - else: - k = self.overlay.keypress(dim, k) - if k in ('up','page up'): - self._w.set_focus('body') - # Until I figure out a better way to do this, then this will - # have to do. - self._w.body.get_focus()[0].get_focus()._invalidate() - #self._w.body.keypress(dim,'down') - elif k in ('down','page down'): - self._w.set_focus('footer') - - if "window resize" in keys: - dim = ui.get_cols_rows() - if "esc" in keys or 'Q' in keys: - return False - if "meta enter" in keys or self.OK_PRESSED: - self.OK_PRESSED = False - if self.save_settings(): - return True - if self.CANCEL_PRESSED: - return False - class WiredSettingsDialog(AdvancedSettingsDialog): def __init__(self,name): @@ -270,7 +211,7 @@ class WirelessSettingsDialog(AdvancedSettingsDialog): def __init__(self,networkID): global wireless, daemon AdvancedSettingsDialog.__init__(self) - self.networkID = networkID + self.networkid = networkID global_settings_t = language['global_settings'] encryption_t = language['use_encryption'] autoconnect_t = language['automatic_connect'] @@ -301,7 +242,7 @@ class WirelessSettingsDialog(AdvancedSettingsDialog): def set_values(self): """ Set the various network settings to the right values. """ - networkID = self.networkID + networkID = self.networkid self.ip_edit.set_edit_text(self.format_entry(networkID,"ip")) self.netmask_edit.set_edit_text(self.format_entry(networkID,"netmask")) self.gateway_edit.set_edit_text(self.format_entry(networkID,"gateway")) @@ -327,17 +268,13 @@ class WirelessSettingsDialog(AdvancedSettingsDialog): # Throw the encryption stuff into a list list = [] + activeID = -1 # Set the menu to this item when we are done for x, enc_type in enumerate(self.encrypt_types): - list.append(enc_type[0]) + list.append(enc_type['name']) + if enc_type['type'] == wireless.GetWirelessProperty(networkID, "enctype"): + activeID = x self.encryption_combo.set_list(list) - self.change_encrypt_method() - activeID = -1 # Set the menu to this item when we are done - user_enctype = wireless.GetWirelessProperty(networkID, "enctype") - for x, enc_type in enumerate(self.encrypt_types): - if enc_type[1] == user_enctype: - activeID = x - self.encryption_combo.set_focus(activeID) if activeID != -1: self.encryption_chkbox.set_state(True,do_callback=False) @@ -346,9 +283,12 @@ class WirelessSettingsDialog(AdvancedSettingsDialog): else: self.encryption_combo.set_focus(0) + self.change_encrypt_method() + + def set_net_prop(self, option, value): """ Sets the given option to the given value for this network. """ - wireless.SetWirelessProperty(self.networkID, option, value) + wireless.SetWirelessProperty(self.networkid, option, value) def format_entry(self, networkid, label): """ Helper method for fetching/formatting wireless properties. """ @@ -359,25 +299,32 @@ class WirelessSettingsDialog(AdvancedSettingsDialog): # Check encryption info if self.encryption_chkbox.get_state(): #print "setting encryption info..." - encryption_info = self.encryption_info - encrypt_methods = misc.LoadEncryptionMethods() + encrypt_info = self.encryption_info + encrypt_methods = self.encrypt_types self.set_net_prop("enctype", - encrypt_methods[self.encryption_combo.get_focus()[1] ][1]) - for x in encryption_info: - if encryption_info[x].get_edit_text() == "": - error(self.ui, self.overlay,language['encrypt_info_missing']) + encrypt_methods[self.encryption_combo.get_focus()[1] ]['type']) + # Make sure all required fields are filled in. + for entry_info in encrypt_info.itervalues(): + if entry_info[0].get_edit_text() == "" \ + and entry_info[1] == 'required': + error(self.ui, self.overlay,"%s (%s)" \ + % (language['encrypt_info_missing'], + entry_info[0].get_captionabel() ) + ) return False - self.set_net_prop(x, noneToString(encryption_info[x]. + + for entry_key, entry_info in encrypt_info.iteritems(): + self.set_net_prop(entry_key, noneToString(entry_info[0]. get_edit_text())) elif not self.encryption_chkbox.get_state() and \ - wireless.GetWirelessProperty(self.networkID, "encryption"): + wireless.GetWirelessProperty(self.networkid, "encryption"): + # Encrypt checkbox is off, but the network needs it. error(self.ui, self.overlay, language['enable_encryption']) return False else: - #print 'encryption is ' + str(wireless.GetWirelessProperty(self.networkID, - # "encryption")) - #print "no encryption specified..." self.set_net_prop("enctype", "None") + for entry in encrypt_info.iterkeys(): + self.set_net_prop(entry[0].entry, "") AdvancedSettingsDialog.save_settings(self) # Save the autoconnect setting. This is not where it originally was @@ -388,9 +335,9 @@ class WirelessSettingsDialog(AdvancedSettingsDialog): self.set_net_prop('use_settings_globally', True) else: self.set_net_prop('use_settings_globally', False) - wireless.RemoveGlobalEssidEntry(self.networkID) + wireless.RemoveGlobalEssidEntry(self.networkid) - wireless.SaveWirelessNetworkProfile(self.networkID) + wireless.SaveWirelessNetworkProfile(self.networkid) return True # More or less ripped from netentry.py @@ -405,30 +352,35 @@ class WirelessSettingsDialog(AdvancedSettingsDialog): # If nothing is selected, select the first entry. if ID == -1: - self.encryption_combo.set_active(0) + self.encryption_combo.set_focus(0) ID = 0 - opts = methods[ID][2] theList = [] - for x in opts: - edit = None - if language.has_key(opts[x][0]): - edit = MaskingEdit(('editcp',language[opts[x][0].lower().replace(' ','_')]+': '),mask_mode='no_focus') - else: - edit = MaskingEdit(('editcp',opts[x][0].replace('_',' ')+': '),mask_mode='no_focus') - theList.append(edit) - # Add the data to any array, so that the information - # can be easily accessed by giving the name of the wanted - # data. - self.encryption_info[opts[x][1]] = edit + for type_ in ['required', 'optional']: + fields = methods[ID][type_] + for field in fields: + if language.has_key(field[1]): + edit = MaskingEdit(('editcp',language[field[1].lower().replace(' ','_')]+': ')) + else: + edit = MaskingEdit(('editcp',field[1].replace('_',' ')+': ')) + edit.set_mask_mode('no_focus') + theList.append(edit) + # Add the data to any array, so that the information + # can be easily accessed by giving the name of the wanted + # data. + self.encryption_info[field[0]] = [edit, type_] - edit.set_edit_text(noneToBlankString( - wireless.GetWirelessProperty(self.networkID, opts[x][1]))) + edit.set_edit_text(noneToBlankString( + wireless.GetWirelessProperty(self.networkid, field[0]))) + #FIXME: This causes the entire pile to light up upon use. + # Make this into a listbox? self.pile_encrypt = DynWrap(urwid.Pile(theList),attrs=('editbx','editnfc')) self._w.body.body.insert(self._w.body.body.__len__(),self.pile_encrypt) #self._w.body.body.append(self.pile_encrypt) - def prerun(self,ui,dim,display): - self.encryption_combo.build_combobox(self.overlay,ui,14) + def ready_widgets(self,ui,body): + self.ui = ui + self.body = body + self.encryption_combo.build_combobox(body,ui,14) self.change_encrypt_method() diff --git a/curses/prefs_curses.py b/curses/prefs_curses.py index 20d45d2..b220597 100644 --- a/curses/prefs_curses.py +++ b/curses/prefs_curses.py @@ -68,8 +68,8 @@ class PrefsDialog(urwid.WidgetWrap): global_dns_cat_t = ('header',language['global_dns_servers']) global_dns_t = ('editcp',language['use_global_dns']) - dns_dom_t = ('editcp',' DNS Domain: ') - search_dom_t = ('editcp',' Search domain:') + dns_dom_t = ('editcp',' '+language['dns_domain']+': ') + search_dom_t = ('editcp',' '+language['search_domain']+':') dns1_t = ('editcp',' DNS server 1: ') dns2_t = ('editcp',' DNS server 2: ') dns3_t = ('editcp',' DNS server 3: ') @@ -237,27 +237,10 @@ class PrefsDialog(urwid.WidgetWrap): self.header2 : advancedLB} #self.load_settings() - # Now for the buttons: - ok_t = 'OK' - cancel_t = 'Cancel' - - ok_button = urwid.AttrWrap(urwid.Button('OK',self.ok_callback),'body','focus') - cancel_button = urwid.AttrWrap(urwid.Button('Cancel',self.cancel_callback),'body','focus') - # Variables set by the buttons' callback functions - self.CANCEL_PRESSED = False - self.OK_PRESSED = False - - - self.button_cols = urwid.Columns([ok_button,cancel_button], - dividechars=1) - - self.tabs = TabColumns(headerList,lbList,language['preferences'],self.button_cols) + self.tabs = TabColumns(headerList,lbList,language['preferences']) self.__super.__init__(self.tabs) def load_settings(self): - # Reset the buttons - self.CANCEL_PRESSED = False - self.OK_PRESSED = False ### General Settings # ComboBox does not like dbus.Strings as text markups. My fault. :/ @@ -322,7 +305,7 @@ class PrefsDialog(urwid.WidgetWrap): self.debug_mode_checkb.set_state(daemon.GetDebugMode()) self.use_dbm_checkb.set_state(daemon.GetSignalDisplayType()) - def save_results(self): + def save_settings(self): """ Pushes the selected settings to the daemon. This exact order is found in prefs.py""" daemon.SetUseGlobalDNS(self.global_dns_checkb.get_state()) @@ -378,45 +361,6 @@ class PrefsDialog(urwid.WidgetWrap): for w in self.dns1,self.dns2,self.dns3,self.dns_dom,self.search_dom: w.set_sensitive(new_state) - # Button callbacks - def ok_callback(self,button_object,user_data=None): - self.OK_PRESSED = True - def cancel_callback(self,button_object,user_data=None): - self.CANCEL_PRESSED = True - - def ready_comboboxes(self,ui,body): + def ready_widgets(self,ui,body): self.wpa_cbox.build_combobox(body,ui,4) self.backend_cbox.build_combobox(body,ui,8) - - # Put the widget into an overlay, and run! - def run(self,ui, dim, display): - width,height = ui.get_cols_rows() - self.load_settings() - - overlay = urwid.Overlay(self.tabs, display, ('fixed left', 0),width - , ('fixed top',1), height-3) - self.ready_comboboxes(ui,overlay) - - keys = True - while True: - if keys: - ui.draw_screen(dim, overlay.render(dim, True)) - keys = ui.get_input() - - if "window resize" in keys: - dim = ui.get_cols_rows() - if "esc" in keys or 'Q' in keys: - return False - for k in keys: - #Send key to underlying widget: - overlay.keypress(dim, k) - if urwid.is_mouse_event(k): - event, button, col, row = k - overlay.mouse_event( dim, - event, button, col, row, - focus=True) - # Check if buttons are pressed. - if self.CANCEL_PRESSED: - return False - if self.OK_PRESSED or 'meta enter' in keys: - return True diff --git a/curses/wicd-curses.py b/curses/wicd-curses.py index 9586d3b..7d01658 100755 --- a/curses/wicd-curses.py +++ b/curses/wicd-curses.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +# -* coding: utf-8 -*- """ wicd-curses. (curses/urwid-based) console interface to wicd @@ -50,14 +51,14 @@ from wicd import dbusmanager # Internal Python stuff import sys -from time import sleep +from time import sleep, strftime, ctime # Curses UIs for other stuff -from curses_misc import SelText,DynEdit,DynIntEdit,ComboBox,Dialog2,TextDialog,InputDialog,error +from curses_misc import * from prefs_curses import PrefsDialog import netentry_curses -from netentry_curses import WirelessSettingsDialog, WiredSettingsDialog +from netentry_curses import WirelessSettingsDialog, WiredSettingsDialog,AdvancedSettingsDialog from optparse import OptionParser @@ -100,8 +101,8 @@ class wrap_exceptions: raise except : # Quit the loop - if 'loop' in locals(): - loop.quit() + #if 'loop' in locals(): + loop.quit() # Zap the screen ui.stop() # Print out standard notification: @@ -157,44 +158,11 @@ def check_for_wireless(iwconfig, wireless_ip, set_status): return True -# Self explanitory, and not used until I can get some list sort function -# working... -# Also defunct. -# Current list header is STR,ESSID,ENCRYPT,BSSID,TYPE,CHANNEL -#def gen_list_header(): -# return '%3s %4s %s %19s %s ' % ('NUM','STR','BSSID','CHANNEL','ESSID') - # Generate the list of networks. # Mostly borrowed/stolen from wpa_cli, since I had no clue what all of those # DBUS interfaces do. ^_^ # Whatever calls this must be exception-wrapped if it is run if the UI is up def gen_network_list(): - # Pick which strength measure to use based on what the daemon says - if daemon.GetSignalDisplayType() == 0: - strenstr = 'quality' - gap = 3 - else: - strenstr = 'strength' - gap = 5 - - id = 0 - wiredL = [] - #is_active = wireless.GetWirelessIP('') == None and wired.GetWiredIP('') != None - # This one makes a list of strings to put in a combo box. - #for profile in wired.GetWiredProfileList(): - #theString = '%4s %25s' % (id, profile) - #### THIS IS wired.blah() in experimental - #print config.GetLastUsedWiredNetwork() - # Tag if no wireless IP present, and wired one is - #if is_active: - # theString = '>'+theString[1:] - - #wiredL.append(urwid.AttrWrap(SelText(theString),'connected', - # 'connected focus')) - #else: - #wiredL.append(urwid.AttrWrap(SelText(theString),'body','focus')) - #wiredL.append(theString) - #id+=1 wiredL = wired.GetWiredProfileList() wlessL = [] # This one makes a list of NetLabels @@ -227,24 +195,62 @@ def about_dialog(body): about = TextDialog(theText,16,55,header=('header','About Wicd')) about.run(ui,body) +# Modeled after htop's help def help_dialog(body): - theText = [ + textT = urwid.Text(('header','Wicd-curses help'),'right') + textSH = urwid.Text(['This is ',('blue','wicd-curses-'+CURSES_REVNO),' using wicd ',unicode(daemon.Hello()),'\n']) + + textH = urwid.Text([ "For more detailed help, consult the wicd-curses(8) man page.\n", -"\n", "All controls are case sensitive\n", -('bold','H')," or ",('bold','h'),' or ',('bold','?')," Display this help dialog\n", -('bold','enter')," Connect to selected network\n", -('bold','D')," Disconnect from all networks\n", -('bold','ESC')," Stop a network connection in progress\n", -('bold','F5')," or ", ('bold','R')," Refresh network list\n", -('bold','P')," Prefrences dialog\n", -('bold','I')," Scan for hidden networks\n", -('bold','S')," Select scripts\n", -('bold','O')," Set up Ad-hoc network\n", -('bold','C')," Configure Selected Network\n", -('bold','A')," Display 'about' dialog\n" - ] - help = TextDialog(theText,18,62,header=('header',"Wicd-Curses Help")) - help.run(ui,body) +('bold','->'),' and ',('bold','<-')," are the right and left arrows respectively.\n"]) + + text1 = urwid.Text([ +('bold',' H h ?'),": Display this help dialog\n", +('bold','enter C'),": Connect to selected network\n", +('bold',' D'),": Disconnect from all networks\n", +('bold',' ESC'),": Stop a network connection in progress\n", +('bold',' F5 R'),": Refresh network list\n", +('bold',' P'),": Prefrences dialog\n", + ]) + text2 = urwid.Text([ +('bold',' I'),": Scan for hidden networks\n", +('bold',' S'),": Select scripts\n", +('bold',' O'),": Set up Ad-hoc network\n", +('bold',' ->'),": Configure selected network\n", +('bold',' A'),": Display 'about' dialog\n", +('bold',' Q'),": Quit wicd-curses\n", + ]) + textF = urwid.Text('Press any key to return.') + + # textJ = urwid.Text(('important','Nobody expects the Spanish Inquisition!')) + + blank = urwid.Text('') + # Pile containing a text and columns? + cols = urwid.Columns([text1,text2]) + pile = urwid.Pile([textH,cols]) + fill = urwid.Filler(pile) + frame = urwid.Frame(fill,header=urwid.Pile([textT,textSH]),footer=textF) + dim = ui.get_cols_rows() + while True: + ui.draw_screen(dim, frame.render(dim, True)) + + keys = ui.get_input() + # Don't stop because someone let go of the mouse on the frame + mouse_release = False + for k in keys: + if urwid.is_mouse_event(k) and k[0] == "mouse release": + mouse_release = True + break + if mouse_release : + continue + if 'window resize' in keys: + dim = ui.get_cols_rows() + elif keys: + break + #elif keys != '': + # break + #help = TextDialog(theText,18,62,header=('header',"Wicd-Curses Help")) + #help.run(ui,body) def run_configscript(parent,netname,nettype): configfile = wpath.etc+netname+'-settings.conf' @@ -293,6 +299,15 @@ Once there, you can adjust (or add) the "beforescript", "afterscript", and "disc main() """ +def gen_list_header(): + if daemon.GetSignalDisplayType() == 0: + # Allocate 25 cols for the ESSID name + essidgap = 25 + else: + # Need 3 more to accomodate dBm strings + essidgap = 28 + return 'C %s %*s %9s %17s %6s %s' % ('STR ',essidgap,'ESSID','ENCRYPT','BSSID','MODE','CHNL') + ######################################## ##### URWID SUPPORT CLASSES ######################################## @@ -300,13 +315,14 @@ Once there, you can adjust (or add) the "beforescript", "afterscript", and "disc # Wireless network label class NetLabel(urwid.WidgetWrap): def __init__(self, id, is_active): - # Pick which strength measure to use based on what the daemon says + # Pick which strength measure to use based on what the daemon says + # gap allocates more space to the first module if daemon.GetSignalDisplayType() == 0: strenstr = 'quality' - gap = 3 + gap = 4 # Allow for 100% else: strenstr = 'strength' - gap = 5 + gap = 7 # -XX dbm = 7 self.id = id # All of that network property stuff self.stren = daemon.FormatSignalForPrinting( @@ -316,7 +332,7 @@ class NetLabel(urwid.WidgetWrap): self.encrypt = wireless.GetWirelessProperty(id,'encryption_method') if wireless.GetWirelessProperty(id, 'encryption') else language['unsecured'] self.mode = wireless.GetWirelessProperty(id, 'mode') # Master, Ad-Hoc self.channel = wireless.GetWirelessProperty(id, 'channel') - theString = ' %*s %25s %9s %17s %6s: %s' % (gap, + theString = ' %-*s %25s %9s %17s %6s %4s' % (gap, self.stren,self.essid,self.encrypt,self.bssid,self.mode,self.channel) if is_active: theString = '>'+theString[1:] @@ -481,6 +497,7 @@ class AdHocDialog(Dialog2): self.key_edit.get_edit_text()) return exitcode, data + ######################################## ##### APPLICATION INTERFACE CLASS ######################################## @@ -488,6 +505,7 @@ class AdHocDialog(Dialog2): class appGUI(): """The UI itself, all glory belongs to it!""" def __init__(self): + global loop self.size = ui.get_cols_rows() # Happy screen saying that you can't do anything because we're scanning # for networks. :-) @@ -503,7 +521,8 @@ class appGUI(): header = urwid.AttrWrap(urwid.Text(self.TITLE,align='right'), 'header') self.wiredH=urwid.Filler(urwid.Text("Wired Network(s)")) - self.wlessH=urwid.Filler(urwid.Text("Wireless Network(s)")) + self.list_header=urwid.AttrWrap(urwid.Text(gen_list_header()),'listbar') + self.wlessH=NSelListBox([urwid.Text("Wireless Network(s)"),self.list_header]) #if wireless.GetNumberOfNetworks() == 0: # wireless.Scan() @@ -511,7 +530,9 @@ class appGUI(): # These are empty to make sure that things go my way. wiredL,wlessL = [],[]# = gen_network_list() + self.frame = None + self.diag = None self.wiredCB = urwid.Filler(WiredComboBox(wiredL)) self.wlessLB = urwid.ListBox(wlessL) @@ -525,17 +546,36 @@ class appGUI(): # spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam,spam, # spam,spam,spam,spam] ] #self.spamLB = urwid.ListBox(spamL) + # Keymappings proposed by nanotube in #wicd + keys = [ + ('H' ,'Help' ,None), + ('right','Config',None), + #(' ',' ',None), + ('C' ,'Connect',None), + ('D' ,'Disconn',None), + ('R' ,'Refresh',None), + ('P' ,'Prefs',None), + ('I' ,'Hidden',None), + ('Q' ,'Quit',loop.quit) + ] - self.footer1 = urwid.AttrWrap(urwid.Text("Something important will eventually go here."),'body') - self.footer2 = urwid.AttrWrap(urwid.Text("If you are seeing this, then something has gone wrong!"),'important') - self.footerList = urwid.ListBox([self.footer1,self.footer2]) + self.primaryCols = OptCols(keys,self.handle_keys) + #self.time_label = urwid.Text(strftime('%H:%M:%S')) + self.time_label = \ + urwid.AttrWrap(urwid.Text(strftime('%H:%M:%S')), 'timebar') + self.status_label = urwid.AttrWrap(urwid.Text('blah'),'important') + self.footer2 = urwid.Columns([self.status_label,('fixed', 8, self.time_label)]) + self.footerList = urwid.Pile([self.primaryCols,self.footer2]) # Pop takes a number! #walker.pop(1) self.frame = urwid.Frame(self.thePile, header=header, - footer=urwid.BoxAdapter(self.footerList,2)) + footer=self.footerList) self.wiredCB.get_body().build_combobox(self.frame,ui,3) + # Init the other columns used in the program + self.init_other_optcols() + self.frame.set_body(self.thePile) # Booleans gallore! self.prev_state = False @@ -548,6 +588,18 @@ class appGUI(): #self.dialog = PrefOverlay(self.frame,self.size) + def init_other_optcols(self): + # The "tabbed" preferences dialog + self.prefCols = OptCols( [('meta enter','OK'), + ('esc','Cancel'), + ('meta [','Tab Left',), + ('meta ]','Tab Right')],self.handle_keys + ) + self.confCols = OptCols( [ + ('meta enter','OK'), + ('esc','Cancel') + ],self.handle_keys) + # Does what it says it does def lock_screen(self): self.frame.set_body(self.screen_locker) @@ -586,14 +638,19 @@ class appGUI(): where = self.thePile.get_focus().get_focus()[1] #where = self.wlessLB.get_focus()[1] self.focusloc = (wlessorwired,where) + # Be clunky until I get to a later stage of development. # Update the list of networks. Usually called by DBus. - # TODO: Preserve current focus when updating the list. @wrap_exceptions() def update_netlist(self,state=None, x=None, force_check=False,firstrun=False): - # Run focus-collecting code if we are not running this for the first time + # Don't even try to do this if we are running a dialog + if self.diag: + return + # Run focus-collecting code if we are not running this for the first + # time if not firstrun: self.update_focusloc() + self.list_header.set_text(gen_list_header()) """ Updates the overall network list.""" if not state: state, x = daemon.GetConnectionStatus() @@ -615,7 +672,7 @@ class appGUI(): #if firstrun: self.thePile = urwid.Pile([('fixed',1,self.wiredH), ('fixed',1,self.wiredCB), - ('fixed',1,self.wlessH), + ('fixed',2,self.wlessH), self.wlessLB] ) if not firstrun: self.frame.body = self.thePile @@ -630,7 +687,7 @@ class appGUI(): else: self.thePile.set_focus(self.wiredCB) else: - self.thePile = urwid.Pile([('fixed',1,self.wlessH),self.wlessLB] ) + self.thePile = urwid.Pile([('fixed',2,self.wlessH),self.wlessLB] ) if not firstrun: self.frame.body = self.thePile #if self.focusloc[0] == self.wlessLB: @@ -688,9 +745,9 @@ class appGUI(): self.update_ui() return True - # Cheap little indicator stating that we are actually connecting twirl = ['|','/','-','\\'] + tcount = 0 # Counter for said indicator def set_status(self,text,from_idle=False): # Set the status text, usually called by the update_status method # from_idle : a check to see if we are being called directly from the @@ -701,6 +758,7 @@ class appGUI(): if from_idle and not self.connecting: #self.update_netlist() self.update_status() + self.tcount = 0 #self.update_ui() return False toAppend = '' @@ -708,26 +766,22 @@ class appGUI(): # the wheel. if from_idle and self.connecting: # This is probably the wrong way to do this, but it works for now. - toAppend=self.twirl[self.incr % 4] - self.footer2 = urwid.AttrWrap(urwid.Text(text+' '+toAppend),'important') - self.frame.set_footer(urwid.BoxAdapter( - urwid.ListBox([self.footer1,self.footer2]),2)) + toAppend=self.twirl[self.tcount % 4] + self.tcount+=1 + #self.footer2 = urwid.Columns([ + # urwid.AttrWrap(urwid.Text(text+' '+toAppend),'important'), + # ('fixed',8,urwid.Text(str(self.time),align='right'))]) + self.status_label.set_text(text+' '+toAppend) + #self.frame.set_footer(urwid.BoxAdapter( + # urwid.ListBox([self.footer1,self.footer2]),2)) return True # Make sure the screen is still working by providing a pretty counter. # Not necessary in the end, but I will be using footer1 for stuff in # the long run, so I might as well put something there. - incr = 0 - @wrap_exceptions() - def idle_incr(self): - theText = " " - if self.connecting: - theText += "-- "+language['connecting']+' -- '+language["esc_to_cancel"] - else: - theText += "-- Press H or ? for Help" - quit_note = ' -- '+language["press_to_quit"] - self.footer1 = urwid.Text(str(self.incr) + theText+quit_note,wrap='clip') - self.incr+=1 + #@wrap_exceptions() + def update_time(self): + self.time_label.set_text(strftime('%H:%M:%S')) return True # Yeah, I'm copying code. Anything wrong with that? @@ -743,91 +797,104 @@ class appGUI(): def dbus_scan_started(self): self.lock_screen() - # Redraw the screen - @wrap_exceptions() - def update_ui(self): - #self.update_status() - canvas = self.frame.render( (self.size),True ) - ### GRRRRRRRRRRRRRRRRRRRRR ->^^^^ - # It looks like if I wanted to get the statusbar to update itself - # continuously, I would have to use overlay the canvasses and redirect - # the input. I'll try to get that working at a later time, if people - # want that "feature". - #canvaso = urwid.CanvasOverlay(self.dialog.render( (80,20),True),canvas,0,1) - ui.draw_screen((self.size),canvas) - keys = ui.get_input() + def restore_primary(self): + self.frame.set_body(self.thePile) + self.diag = None + self.frame.set_footer(urwid.Pile([self.primaryCols,self.footer2])) + self.update_ui() - # Handle keystrokes - if "f8" in keys or 'Q' in keys or 'q' in keys: - loop.quit() - #return False - if "f5" in keys or 'R' in keys: - self.lock_screen() - wireless.Scan(True) - if "D" in keys: - # Disconnect from all networks. - daemon.Disconnect() - self.update_netlist() - # Guess what! I actually need to put this here, else I'll have tons of - # references to self.frame lying around. ^_^ - if "enter" in keys: - focus = self.frame.body.get_focus() - if focus == self.wiredCB: - self.special = focus - self.connect("wired",0) - else: - # wless list only other option - wid,pos = self.thePile.get_focus().get_focus() - self.connect("wireless",pos) + def handle_keys(self,keys): + if not self.diag: + # Handle keystrokes + if "f8" in keys or 'Q' in keys or 'q' in keys: + loop.quit() + #return False + if "f5" in keys or 'R' in keys: + self.lock_screen() + wireless.Scan(True) + if "D" in keys: + # Disconnect from all networks. + daemon.Disconnect() + self.update_netlist() + if 'right' in keys: + focus = self.thePile.get_focus() + self.frame.set_footer(urwid.Pile([self.confCols,self.footer2])) + if focus == self.wiredCB: + self.diag = WiredSettingsDialog(self.wiredCB.get_body().get_selected_profile()) + self.frame.set_body(self.diag) + else: + # wireless list only other option + wid,pos = self.thePile.get_focus().get_focus() + self.diag = WirelessSettingsDialog(pos) + self.diag.ready_widgets(ui,self.frame) + self.frame.set_body(self.diag) + # Guess what! I actually need to put this here, else I'll have + # tons of references to self.frame lying around. ^_^ + if "enter" in keys: + #pass + focus = self.frame.body.get_focus() + if focus == self.wiredCB: + self.special = focus + self.connect("wired",0) + else: + # wless list only other option + wid,pos = self.thePile.get_focus().get_focus() + self.connect("wireless",pos) - if "esc" in keys: - # Force disconnect here if connection in progress - if self.connecting: - daemon.CancelConnect() - # Prevents automatic reconnecting if that option is enabled - daemon.SetForcedDisconnect(True) - if "P" in keys: - if not self.pref: - self.pref = PrefsDialog(self.frame,(0,1),ui, - dbusmanager.get_dbus_ifaces()) - if self.pref.run(ui,self.size,self.frame): - self.pref.save_results() - self.update_ui() - if "A" in keys: - about_dialog(self.frame) - if "C" in keys: - focus = self.thePile.get_focus() - if focus == self.wiredCB: - WiredSettingsDialog(self.wiredCB.get_body(). - get_selected_profile()).run(ui,self.size,self.frame) - else: - # wireless list only other option - wid,pos = self.thePile.get_focus().get_focus() - WirelessSettingsDialog(pos).run(ui,self.size,self.frame) - #self.netentry = NetEntryBase(dbusmanager.get_dbus_ifaces()) - #self.netentry.run(ui,self.size,self.frame) - if "I" in keys: - self.raise_hidden_network_dialog() - if "H" in keys or 'h' in keys or '?' in keys: - help_dialog(self.frame) - if "S" in keys: - focus = self.thePile.get_focus() - if focus == self.wiredCB: - nettype = 'wired' - netname = self.wiredCB.get_body().get_selected_profile() - else: - nettype = 'wireless' - netname = str(self.wlessLB.get_focus()[1]) - run_configscript(self.frame,netname,nettype) - if "O" in keys: - exitcode,data = AdHocDialog().run(ui,self.frame) - #data = (essid,ip,channel,use_ics,use_encrypt,key_edit) - if exitcode == 1: - wireless.CreateAdHocNetwork(data[0], - data[2], - data[1], "WEP", - data[5], - data[4], False) + if "esc" in keys: + # Force disconnect here if connection in progress + if self.connecting: + daemon.CancelConnect() + # Prevents automatic reconnecting if that option is enabled + daemon.SetForcedDisconnect(True) + if "P" in keys: + if not self.pref: + self.pref = PrefsDialog(self.frame,(0,1),ui, + dbusmanager.get_dbus_ifaces()) + self.pref.load_settings() + self.pref.ready_widgets(ui,self.frame) + self.frame.set_footer(urwid.Pile([self.prefCols,self.footer2])) + self.diag = self.pref + self.frame.set_body(self.diag) + # Halt here, keypress gets passed to the dialog otherwise + return True + if "A" in keys: + about_dialog(self.frame) + if "C" in keys: + # Same as "enter" for now + focus = self.frame.body.get_focus() + if focus == self.wiredCB: + self.special = focus + self.connect("wired",0) + else: + # wless list only other option + wid,pos = self.thePile.get_focus().get_focus() + self.connect("wireless",pos) + if "I" in keys: + self.raise_hidden_network_dialog() + if "H" in keys or 'h' in keys or '?' in keys: + # FIXME I shouldn't need this, OptCols messes up this one + # particular button + if not self.diag: + help_dialog(self.frame) + if "S" in keys: + focus = self.thePile.get_focus() + if focus == self.wiredCB: + nettype = 'wired' + netname = self.wiredCB.get_body().get_selected_profile() + else: + nettype = 'wireless' + netname = str(self.wlessLB.get_focus()[1]) + run_configscript(self.frame,netname,nettype) + if "O" in keys: + exitcode,data = AdHocDialog().run(ui,self.frame) + #data = (essid,ip,channel,use_ics,use_encrypt,key_edit) + if exitcode == 1: + wireless.CreateAdHocNetwork(data[0], + data[2], + data[1], "WEP", + data[5], + data[4], False) for k in keys: if urwid.is_mouse_event(k): @@ -835,10 +902,35 @@ class appGUI(): self.frame.mouse_event( self.size, event, button, col, row, focus=True) + continue + if self.diag: + if k == 'esc': + self.restore_primary() + break + if k == 'meta enter': + self.diag.save_settings() + self.restore_primary() + break + self.frame.keypress(self.size,k) if k == "window resize": self.size = ui.get_cols_rows() continue - self.frame.keypress( self.size, k ) + + # Redraw the screen + @wrap_exceptions() + def update_ui(self): + #self.update_status() + canvas = self.frame.render( (self.size),True ) + ### GRRRRRRRRRRRRRRRRRRRRR ->^^^^ + # It looks like if I want to get the statusbar to update itself + # continuously, I would have to use overlay the canvasses and redirect + # the input. I'll try to get that working at a later time, if people + # want that "feature". + #canvaso = urwid.CanvasOverlay(self.dialog.render( (80,20),True),canvas,0,1) + ui.draw_screen((self.size),canvas) + keys = ui.get_input() + self.handle_keys(keys) + return True def connect(self, nettype, networkid, networkentry=None): @@ -859,7 +951,6 @@ class appGUI(): def main(): global ui - # We are _not_ python. misc.RenameProcess('wicd-curses') @@ -876,34 +967,37 @@ def main(): # Default Color scheme. # Other potential color schemes can be found at: # http://excess.org/urwid/wiki/RecommendedPalette - # Note: the current palette below is optimized for the linux console. - # For example, this looks particularly bad on a default-colored XTerm. - # NB: To find current terminal background use variable COLORFGBG + + # Thanks to nanotube on #wicd for helping with this ui.register_palette([ ('body','default','default'), ('focus','dark magenta','light gray'), ('header','light blue','default'), ('important','light red','default'), ('connected','dark green','default'), - ('connected focus','default','dark green'), + ('connected focus','black','dark green'), ('editcp', 'default', 'default', 'standout'), ('editbx', 'light gray', 'dark blue'), ('editfc', 'white','dark blue', 'bold'), - ('editnfc','dark gray','default'), + ('editnfc','brown','default','bold'), ('tab active','dark green','light gray'), + ('infobar','light gray','dark blue'), + ('timebar','dark gray','default'), + ('listbar','dark gray','default'), # Simple colors around text ('green','dark green','default'), - ('blue','dark blue','default'), + ('blue','light blue','default'), ('red','dark red','default'), ('bold','white','black','bold')]) - # This is a wrapper around a function that calls another a function that is a - # wrapper around a infinite loop. Fun. + # This is a wrapper around a function that calls another a function that + # is a wrapper around a infinite loop. Fun. urwid.set_encoding('utf8') ui.run_wrapper(run) def run(): global loop - + loop = gobject.MainLoop() + ui.set_mouse_tracking() app = appGUI() @@ -915,12 +1009,11 @@ def run(): # I've left this commented out many times. bus.add_signal_receiver(app.update_netlist, 'StatusChanged', 'org.wicd.daemon') - loop = gobject.MainLoop() # Update what the interface looks like as an idle function gobject.idle_add(app.update_ui) # Update the connection status on the bottom every 1.5 s. - gobject.timeout_add(1500,app.update_status) - gobject.idle_add(app.idle_incr) + gobject.timeout_add(2000,app.update_status) + gobject.timeout_add(1000,app.update_time) # DEFUNCT: Terminate the loop if the UI is terminated. #gobject.idle_add(app.stop_loop) loop.run() @@ -962,4 +1055,4 @@ if __name__ == '__main__': main() # Make sure that the terminal does not try to overwrite the last line of # the program, so that everything looks pretty. - print "" + #print "" diff --git a/encryption/templates/eap b/encryption/templates/eap index a8f71b8..c61fafa 100644 --- a/encryption/templates/eap +++ b/encryption/templates/eap @@ -1,7 +1,8 @@ name = EAP-FAST author = Adam Blackburn version = 2 -require username *Username password *Password pac_file *Path_To_PAC_File +require username *Username password *Password +optional pac_file *Path_To_PAC_File ----- ctrl_interface=/var/run/wpa_supplicant network={ diff --git a/encryption/templates/eap-tls b/encryption/templates/eap-tls index 59f1023..ea6e9fe 100644 --- a/encryption/templates/eap-tls +++ b/encryption/templates/eap-tls @@ -1,7 +1,9 @@ name = EAP-TLS author = Dan O'Reilly version = 1 -require identity *Identity ca_cert *Path_to_CA_Cert client_cert *Path_to_Client_Cert private_key *Private_Key private_key_passwd *Private_Key_Password +require identity *Identity private_key *Private_Key private_key_passwd *Private_Key_Password +optional ca_cert *Path_to_CA_Cert client_cert *Path_to_Client_Cert + ----- ctrl_interface=/var/run/wpa_supplicant network={ diff --git a/encryption/templates/peap-tkip b/encryption/templates/peap-tkip index df141ae..94a9812 100644 --- a/encryption/templates/peap-tkip +++ b/encryption/templates/peap-tkip @@ -1,7 +1,8 @@ -name = PEAP with TKIP +name = PEAP with TKIP/MSCHAPV2 author = Fralaltro version = 1 -require identity *Identity password *Password ca_cert *Path_to_CA_Cert +require identity *Identity password *Password +optional ca_cert *Path_to_CA_Cert ----- ctrl_interface=/var/run/wpa_supplicant network={ diff --git a/encryption/templates/wep-hex b/encryption/templates/wep-hex index e7d147f..6d331bc 100644 --- a/encryption/templates/wep-hex +++ b/encryption/templates/wep-hex @@ -1,4 +1,4 @@ -name = WEP (Hex) +name = WEP (Hex [0-9/A-F]) author = Adam Blackburn version = 1 require key *Key diff --git a/in/man=wicd-curses.8.in b/in/man=wicd-curses.8.in index 99cfd68..603b057 100644 --- a/in/man=wicd-curses.8.in +++ b/in/man=wicd-curses.8.in @@ -52,7 +52,7 @@ Raise the "About wicd-curses" dialog .\".PP .\"The following is a work in progress and might not be fully functional as of yet. .TP -.BR C +.BR "C " or "enter" Bring up network configuration controller for the selected network .TP .BR delete diff --git a/setup.py b/setup.py index 4b39c74..e9eeeed 100755 --- a/setup.py +++ b/setup.py @@ -28,7 +28,7 @@ import subprocess VERSION_NUM = '1.6.0a1' # REVISION_NUM is automatically updated REVISION_NUM = 'unknown' -CURSES_REVNO = 'r279' +CURSES_REVNO = 'uimod' try: if not os.path.exists('vcsinfo.py'): diff --git a/wicd/gui.py b/wicd/gui.py index 4be5bfb..6832337 100644 --- a/wicd/gui.py +++ b/wicd/gui.py @@ -632,9 +632,12 @@ class appGui(object): # Make sure no entries are left blank if entry.chkbox_encryption.get_active(): encryption_info = entry.encryption_info - for x in encryption_info: - if encryption_info[x].get_text() == "": - error(self.window, language['encrypt_info_missing']) + for entry_info in encryption_info.itervalues(): + if entry_info[0].entry.get_text() == "" and \ + entry_info[1] == 'required': + error(self, "%s (%s)" % (language['encrypt_info_missing'], + entry_info[0].label.get_label()) + ) return False # Make sure the checkbox is checked when it should be elif not entry.chkbox_encryption.get_active() and \ diff --git a/wicd/misc.py b/wicd/misc.py index 3e6eba3..0c6dbaa 100644 --- a/wicd/misc.py +++ b/wicd/misc.py @@ -285,10 +285,6 @@ def LoadEncryptionMethods(): loaded, the template must be listed in the "active" file. """ - def parse_ent(line, key): - return line.replace(key, "").replace("=", "").strip() - - encryptionTypes = [] try: enctypes = open(wpath.encryption + "active","r").readlines() except IOError, e: @@ -296,49 +292,73 @@ def LoadEncryptionMethods(): raise IOError(e) # Parse each encryption method - for x in enctypes: - x = x.strip() - try: - f = open(wpath.encryption + x, "r") - except IOError: - print 'Failed to load encryption type ' + str(x) - continue - line = f.readlines() - f.close() - - cur_type = {} - cur_type[0] = parse_ent(line[0], "name") - cur_type[1] = x - cur_type[2] = {} - - # Find the line containing our required fields. - i = 1 - try: - while not line[i].startswith("require"): - i += 1 - except IndexError: - print "Bad encryption template: Couldn't find 'require' line" - requiredFields = parse_ent(line[i], "require") - requiredFields = requiredFields.split(" ") - - # Get the required fields. - index = -1 - for current in requiredFields: - # The pretty names will start with an * so we can - # separate them with that. - if current[0] == "*": - # Make underscores spaces - # and remove the * - cur_type[2][index][0] = current.replace("_", " ").lstrip("*") - else: - # Add to the list of things that are required. - index = len(cur_type[2]) - cur_type[2][index] = {} - cur_type[2][index][1] = current - # Add the current type to the dict of encryption types. - encryptionTypes.append(cur_type) + encryptionTypes = [] + for enctype in enctypes: + parsed_template = _parse_enc_template(enctype.strip()) + if parsed_template: + encryptionTypes.append(parsed_template) return encryptionTypes +def __parse_field_ent(fields, field_type='require'): + fields = fields.split(" ") + ret = [] + # We need an even number of entries in the line for it to be valid. + if (len(fields) % 2) != 0: + return None + else: + for val, disp_val in grouper(2, fields, fillvalue=None): + if val.startswith("*") or not disp_val.startswith("*"): + return None + ret.append([val, disp_val[1:]]) + return ret + +def _parse_enc_template(enctype): + """ Parse an encryption template. """ + def parse_ent(line, key): + return line.replace(key, "").replace("=", "").strip() + + try: + f = open(os.path.join(wpath.encryption, enctype), "r") + except IOError: + print "Failed to open template file %s" % enctype + return None + + cur_type = {} + cur_type["type"] = enctype + cur_type["fields"] = [] + cur_type['optional'] = [] + cur_type['required'] = [] + cur_type['name'] = "" + for index, line in enumerate(f): + if line.startswith("name") and not cur_type["name"]: + cur_type["name"] = parse_ent(line, "name") + elif line.startswith("require"): + cur_type["required"] = __parse_field_ent(parse_ent(line, "require")) + if not cur_type["required"]: + # An error occured parsing the require line. + print "Invalid 'required' line found in template %s" % enctype + continue + elif line.startswith("optional"): + cur_type["optional"] = __parse_field_ent(parse_ent(line, + "optional"), + field_type="optional") + if not cur_type["optional"]: + # An error occured parsing the optional line. + print "Invalid 'optional' line found in template %s" % enctype + continue + elif line.startswith("----"): + # We're done. + break + f.close() + if not cur_type["required"]: + print "Failed to find a 'require' line in template %s" % enctype + return None + if not cur_type["name"]: + print "Failed to find a 'name' line in template %s" % enctype + return None + else: + return cur_type + def noneToString(text): """ Convert None, "None", or "" to string type "None" diff --git a/wicd/netentry.py b/wicd/netentry.py index 58e0be9..a6e1919 100644 --- a/wicd/netentry.py +++ b/wicd/netentry.py @@ -308,8 +308,8 @@ class WirelessSettingsDialog(AdvancedSettingsDialog): # Build the encryption menu activeID = -1 # Set the menu to this item when we are done for x, enc_type in enumerate(self.encrypt_types): - self.combo_encryption.append_text(enc_type[0]) - if enc_type[1] == wireless.GetWirelessProperty(networkID, "enctype"): + self.combo_encryption.append_text(enc_type['name']) + if enc_type['type'] == wireless.GetWirelessProperty(networkID, "enctype"): activeID = x self.combo_encryption.set_active(activeID) if activeID != -1: @@ -384,7 +384,7 @@ class WirelessSettingsDialog(AdvancedSettingsDialog): activeID = -1 # Set the menu to this item when we are done user_enctype = wireless.GetWirelessProperty(networkID, "enctype") for x, enc_type in enumerate(self.encrypt_types): - if enc_type[1] == user_enctype: + if enc_type['type'] == user_enctype: activeID = x self.combo_encryption.set_active(activeID) @@ -400,24 +400,32 @@ class WirelessSettingsDialog(AdvancedSettingsDialog): # Check encryption info if self.chkbox_encryption.get_active(): print "setting encryption info..." - encryption_info = self.encryption_info - encrypt_methods = misc.LoadEncryptionMethods() + encrypt_info = self.encryption_info + encrypt_methods = self.encrypt_types self.set_net_prop("enctype", - encrypt_methods[self.combo_encryption.get_active()][1]) - for x in encryption_info: - if encryption_info[x].get_text() == "": - error(self, language['encrypt_info_missing']) + encrypt_methods[self.combo_encryption.get_active()]['type']) + # Make sure all required fields are filled in. + for entry_info in encrypt_info.itervalues(): + if entry_info[0].entry.get_text() == "" and \ + entry_info[1] == 'required': + error(self, "%s (%s)" % (language['encrypt_info_missing'], + entry_info[0].label.get_label()) + ) return False - self.set_net_prop(x, noneToString(encryption_info[x].get_text())) + # Now save all the entries. + for entry_key, entry_info in encrypt_info.iteritems(): + self.set_net_prop(entry_key, + noneToString(entry_info[0].entry.get_text())) elif not self.chkbox_encryption.get_active() and \ wireless.GetWirelessProperty(networkid, "encryption"): + # Encrypt checkbox is off, but the network needs it. error(self, language['enable_encryption']) return False else: print "no encryption specified..." self.set_net_prop("enctype", "None") - for x in self.encryption_info: - self.set_net_prop(x, "") + for entry in encrypt_info.iterkeys(): + self.set_net_prop(entry[0].entry, "") AdvancedSettingsDialog.save_settings(self) if self.chkbox_global_settings.get_active(): @@ -444,7 +452,7 @@ class WirelessSettingsDialog(AdvancedSettingsDialog): for z in self.vbox_encrypt_info: z.destroy() # Remove stuff in there already ID = self.combo_encryption.get_active() - methods = misc.LoadEncryptionMethods() + methods = self.encrypt_types self.encryption_info = {} # If nothing is selected, select the first entry. @@ -452,22 +460,22 @@ class WirelessSettingsDialog(AdvancedSettingsDialog): self.combo_encryption.set_active(0) ID = 0 - opts = methods[ID][2] - for x in opts: - box = None - if language.has_key(opts[x][0]): - box = LabelEntry(language[opts[x][0].lower().replace(' ','_')]) - else: - box = LabelEntry(opts[x][0].replace('_',' ')) - box.set_auto_hidden(True) - self.vbox_encrypt_info.pack_start(box) - # Add the data to any array, so that the information - # can be easily accessed by giving the name of the wanted - # data. - self.encryption_info[opts[x][1]] = box.entry + for type_ in ['required', 'optional']: + fields = methods[ID][type_] + for field in fields: + if language.has_key(field[1]): + box = LabelEntry(language[field[1].lower().replace(' ','_')]) + else: + box = LabelEntry(field[1].replace('_',' ')) + box.set_auto_hidden(True) + self.vbox_encrypt_info.pack_start(box) + # Add the data to a dict, so that the information + # can be easily accessed by giving the name of the wanted + # data. + self.encryption_info[field[0]] = [box, type_] - box.entry.set_text(noneToBlankString( - wireless.GetWirelessProperty(self.networkID, opts[x][1]))) + box.entry.set_text(noneToBlankString( + wireless.GetWirelessProperty(self.networkID, field[0]))) self.vbox_encrypt_info.show_all() @@ -740,7 +748,7 @@ class WirelessNetworkEntry(NetworkEntry): self.set_encryption(wireless.GetWirelessProperty(networkID, 'encryption'), wireless.GetWirelessProperty(networkID, - 'encryption_method')) + 'encryption_method')) self.set_channel(wireless.GetWirelessProperty(networkID, 'channel')) self.name_label.set_use_markup(True) self.name_label.set_label("%s %s %s %s" % (self._escape(self.essid),