diff --git a/curses/README b/curses/README index d443845..bf4aebd 100644 --- a/curses/README +++ b/curses/README @@ -2,30 +2,33 @@ This is a curses-based client for wicd. It is designed to imitate wicd-client as much as can be done with a console-based interface. It is written using the Urwid (http://excess.org/urwid) toolkit, and thus requires it. -That's all there is to it, really. It installs unless you disable when you -call setup.py +That's all there is to it, really. It installs unless you disable it when you +call setup.py. Right now, it lists current available networks, and whether you are connected -to anything or not, all of which is updated in real time. It will actually -connect you to networks now. Global preferences are now configurable from the -console. Per-network settings is a work in progress. +to anything or not, all of which is updated in real time. Other features +include the ability to connect to networks, global preferences controls, and +per-network settings for wireless networks. + +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: F5 : refresh wireless networks F8 or Q: quit -D : disconnect from active network +D : disconnect from all active networks ESC : if connecting to a network, stop doing so ENTER : Attempt connection to selected network P : Display preferences dialog -C : Display network configuration for selected network +C : Display network configuration for selected network (only works for + wireless at the moment) A : Display "About" dialog -IN DIALOGS: +IN DIALOGS (Meta usually is "Alt"): ESC or Q: Quit dialog without saving information (if present) -Meta+Left/Right: Change tabs Left/Right +Meta+Left/Right: Change tabs Left/Right (if tabs present) Meta+Enter : Quit dialog and save information - - ~NaCl diff --git a/curses/TODO b/curses/TODO index 1c80fde..56eb346 100644 --- a/curses/TODO +++ b/curses/TODO @@ -1,17 +1,10 @@ Things to do (in no particular order): -* Make a network config dialog for both wireless and wired interfaces -* Make an about dialog +* Make a network config dialog for wired interfaces * Implement a keyhandler function for the overall frame * Make keystrokes customizable * Make color schemes customizable -* Integrate this with the my local copy of the experimental branch -* Give some indication of activity during the connection process -* Make a man page for the UI. - -Oh, and most importantly: - -* Tell people how they can quit the app in the app (F8 or Q, until I do all of - that stuff) :-) - -Anything else? That's all I can think of now. +* Implement a "scan for hidden networks" dialog +* Implement a "make an ad-hoc network" dialog +* Implement a "help" dialog +* Perform a mass code cleanup diff --git a/curses/curses_misc.py b/curses/curses_misc.py index 06bfb77..dfbfd84 100644 --- a/curses/curses_misc.py +++ b/curses/curses_misc.py @@ -261,7 +261,7 @@ class ComboBox(urwid.WidgetWrap): #def get_size(self): - def __init__(self,label='',list=[],attrs=('body','editnfc'),focus_attr='focus',use_enter=True,show_first=0): + def __init__(self,label='',list=[],attrs=('body','editnfc'),focus_attr='focus',use_enter=True,focus=0,callback=None,user_args=None): """ label : bit of text that preceeds the combobox. If it is "", then ignore it @@ -269,13 +269,16 @@ class ComboBox(urwid.WidgetWrap): body : parent widget ui : the screen row : where this object is to be found onscreen - show_first: index of the element in the list to pick first + focus : index of the element in the list to pick first + callback : function that takes (combobox,sel_index,user_args=None) + user_args : user_args in the callback """ self.label = urwid.Text(label) self.attrs = attrs self.focus_attr = focus_attr self.list = list + str,trash = self.label.get_text() self.overlay = None @@ -290,26 +293,27 @@ class ComboBox(urwid.WidgetWrap): # We need this to pick our keypresses self.use_enter = use_enter - - # Set the focus at the beginning to 0 - self.show_first = show_first - + # The Focus + self.focus = focus + # The callback and friends + self.callback = callback + self.user_args = user_args def set_list(self,list): self.list = list - def set_show_first(self,show_first): - self.show_first = show_first + def set_focus(self,index): + self.focus = index def build_combobox(self,body,ui,row): str,trash = self.label.get_text() - self.cbox = DynWrap(SelText([self.list[self.show_first]+' vvv']),attrs=self.attrs,focus_attr=self.focus_attr) + self.cbox = DynWrap(SelText([self.list[self.focus]+' vvv']),attrs=self.attrs,focus_attr=self.focus_attr) if str != '': w = urwid.Columns([('fixed',len(str),self.label),self.cbox],dividechars=1) - self.overlay = self.ComboSpace(self.list,body,ui,self.show_first, + self.overlay = self.ComboSpace(self.list,body,ui,self.focus, pos=(len(str)+1,row)) else: w = urwid.Columns([self.cbox]) - self.overlay = self.ComboSpace(self.list,body,ui,self.show_first, + self.overlay = self.ComboSpace(self.list,body,ui,self.focus, pos=(0,row)) self.set_w(w) @@ -328,16 +332,18 @@ class ComboBox(urwid.WidgetWrap): retval = self.overlay.show(self.ui,self.body) if retval != None: self.cbox.set_w(SelText(retval+' vvv')) + if self.callback != None: + self.callback(self,self.overlay._listbox.get_focus()[1],self.user_args) return self._w.keypress(size,key) - # Most obvious thing ever. :-) def selectable(self): return self.cbox.selectable() - # Return the index of the selected element - def get_selected(self): - wid,pos = self.overlay._listbox.get_focus() - return (wid,pos) + def get_focus(self): + if self.overlay: + return self.overlay._listbox.get_focus() + else: + return None,self.focus def get_sensitive(self): return self.cbox.get_sensitive() diff --git a/curses/netentry_curses.py b/curses/netentry_curses.py index 71f0d18..79f3703 100644 --- a/curses/netentry_curses.py +++ b/curses/netentry_curses.py @@ -31,13 +31,13 @@ def error(ui,parent,message): # /\ # /!!\ # /____\ - dialog = Dialog(message,[OK],('body','body','focus'),40,6) + dialog = Dialog([('important','ERROR: '),message],['OK'],('body','body','focus'),40,6,parent) keys = True dim = ui.get_cols_rows() while True: if keys: - ui.draw_screen(dim, about.render(dim, True)) + ui.draw_screen(dim, dialog.render(dim, True)) keys = ui.get_input() if "window resize" in keys: @@ -61,10 +61,13 @@ def dbus_init(dbus_ifaces): wired = dbus_ifaces['wired'] wireless = dbus_ifaces['wireless'] -# Both the wired and the wireless NetEntries some of the same fields. -# This will be used to produce the individual network settings -class NetEntryBase(urwid.WidgetWrap): +# Both the wired and the wireless settings preferences dialogs use some of the +# same fields. +# This will be used to produce the individual network settings dialogs way far below +class AdvancedSettingsDialog(urwid.WidgetWrap): def __init__(self): + self.ui=None + static_ip_t = language['use_static_ip'] ip_t = ('editcp',language['ip']+': ') netmask_t = ('editcp',language['netmask']+':') @@ -78,7 +81,7 @@ class NetEntryBase(urwid.WidgetWrap): dns2_t = ('editcp',language['dns']+ ' ' + language['2']+':'+' '*8) dns3_t = ('editcp',language['dns']+ ' ' + language['3']+':'+' '*8) - cancel_t = 'cancel' + cancel_t = 'Cancel' ok_t = 'OK' self.static_ip_cb = urwid.CheckBox(static_ip_t, @@ -102,6 +105,12 @@ class NetEntryBase(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, @@ -113,11 +122,20 @@ class NetEntryBase(urwid.WidgetWrap): self.dns1,self.dns2,self.dns3 ]) + + self._listbox = urwid.ListBox(walker) #self._frame = urwid.Frame(self._listbox) 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) @@ -139,7 +157,7 @@ class NetEntryBase(urwid.WidgetWrap): # Code totally yanked from netentry.py def save_settings(self): """ Save settings common to wired and wireless settings dialogs. """ - if self.chkbox_static_ip.get_active(): + if self.static_ip_cb.get_state(): self.set_net_prop("ip", noneToString(self.ip_edit.get_edit_text())) self.set_net_prop("netmask", noneToString(self.netmask_edit.get_edit_text())) self.set_net_prop("gateway", noneToString(self.gateway_edit.get_edit_text())) @@ -148,17 +166,17 @@ class NetEntryBase(urwid.WidgetWrap): self.set_net_prop("netmask", '') self.set_net_prop("gateway", '') - if self.chkbox_static_dns.get_active() and \ - not self.chkbox_global_dns.get_active(): + if self.static_dns_cb.get_state() and \ + not self.global_dns_cb.get_state(): self.set_net_prop('use_static_dns', True) self.set_net_prop('use_global_dns', False) - self.set_net_prop('dns_domain', noneToString(self.txt_domain.get_text())) - self.set_net_prop("search_domain", noneToString(self.txt_search_dom.get_text())) - self.set_net_prop("dns1", noneToString(self.dns_1.get_text())) - self.set_net_prop("dns2", noneToString(self.dns_2.get_text())) - self.set_net_prop("dns3", noneToString(self.dns_3.get_text())) - elif self.chkbox_static_dns.get_active() and \ - self.chkbox_global_dns.get_active(): + self.set_net_prop('dns_domain', noneToString(self.dns_dom_edit.get_text())) + self.set_net_prop("search_domain", noneToString(self.search_dom_edit.get_text())) + self.set_net_prop("dns1", noneToString(self.dns1.get_text())) + self.set_net_prop("dns2", noneToString(self.dns2.get_text())) + self.set_net_prop("dns3", noneToString(self.dns3.get_text())) + elif self.static_dns_cb.get_state() and \ + self.global_dns_cb.get_state(): self.set_net_prop('use_static_dns', True) self.set_net_prop('use_global_dns', True) else: @@ -198,24 +216,35 @@ class NetEntryBase(urwid.WidgetWrap): ######################################## -class WirelessNetEntry(NetEntryBase): +class WirelessSettingsDialog(AdvancedSettingsDialog): def __init__(self,networkID): - NetEntryBase.__init__(self) + global wireless, daemon + AdvancedSettingsDialog.__init__(self) self.networkID = networkID global_settings_t = language['global_settings'] encryption_t = language['use_encryption'] self.global_settings_chkbox = urwid.CheckBox(global_settings_t) self.encryption_chkbox = urwid.CheckBox(encryption_t,on_state_change=self.encryption_toggle) - self.encryption_combo = ComboBox() + self.encryption_combo = ComboBox(callback=self.combo_on_change) + self.pile_encrypt = None + # _w is a Frame, _w.body is a ListBox, _w.body.body is the ListWalker :-) self._w.body.body.append(self.global_settings_chkbox) self._w.body.body.append(self.encryption_chkbox) self._w.body.body.append(self.encryption_combo) + self._w.body.body.append(self.button_cols) self.encrypt_types = misc.LoadEncryptionMethods() self.set_values() + + # Set the frame title so that people will always know what we're dealing with. + self._w.header = urwid.Text(('header',">Configuring preferences for wireless network \"%s\"" % wireless.GetWirelessProperty(networkID,'essid')),align='right' ) def encryption_toggle(self,chkbox,new_state,user_data=None): self.encryption_combo.set_sensitive(new_state) + self.pile_encrypt.set_sensitive(new_state) + + def combo_on_change(self,combobox,new_index,user_data=None): + self.change_encrypt_method() def set_values(self): """ Set the various network settings to the right values. """ @@ -238,9 +267,9 @@ class WirelessNetEntry(NetEntryBase): #self.reset_static_checkboxes() self.encryption_chkbox.set_state(bool(wireless.GetWirelessProperty(networkID, - 'encryption'))) - self.global_settings_chkbox.set_state(bool(wireless.GetWirelessProperty(networkID, - 'use_settings_globally'))) + 'encryption')),do_callback=False) + self.global_settings_chkbox.set_state(bool(wireless.GetWirelessProperty(networkID + ,'use_settings_globally'))) activeID = -1 # Set the menu to this item when we are done user_enctype = wireless.GetWirelessProperty(networkID, "enctype") @@ -248,14 +277,19 @@ class WirelessNetEntry(NetEntryBase): if enc_type[1] == user_enctype: activeID = x - #self.combo_encryption.set_active(activeID) - #if activeID != -1: - # self.chkbox_encryption.set_active(True) - # self.combo_encryption.set_sensitive(True) - # self.vbox_encrypt_info.set_sensitive(True) - #else: - # self.combo_encryption.set_active(0) - #self.change_encrypt_method() + self.encryption_combo.set_focus(activeID) + if activeID != -1: + self.encryption_chkbox.set_state(True,do_callback=False) + self.encryption_combo.set_sensitive(True) + #self.lbox_encrypt_info.set_sensitive(True) + else: + self.encryption_combo.set_focus(0) + # Throw the encryption stuff into a list + list = [] + for x, enc_type in enumerate(self.encrypt_types): + list.append(enc_type[0]) + self.encryption_combo.set_list(list) + self.change_encrypt_method() def set_net_prop(self, option, value): """ Sets the given option to the given value for this network. """ @@ -265,15 +299,85 @@ class WirelessNetEntry(NetEntryBase): """ Helper method for fetching/formatting wireless properties. """ return noneToBlankString(wireless.GetWirelessProperty(networkid, label)) + # Ripped from netentry.py + def save_settings(self, networkid): + # Check encryption info + if self.encryption_chkbox.get_state(): + #print "setting encryption info..." + encryption_info = self.encryption_info + encrypt_methods = misc.LoadEncryptionMethods() + 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, language['encrypt_info_missing']) + return False + self.set_net_prop(x, noneToString(encryption_info[x]. + get_edit_text())) + elif not self.encryption_chkbox.get_state() and \ + wireless.GetWirelessProperty(networkid, "encryption"): + error(self.ui, self, language['enable_encryption']) + return False + else: + #print 'encryption is ' + str(wireless.GetWirelessProperty(networkid, + # "encryption")) + #print "no encryption specified..." + self.set_net_prop("enctype", "None") + AdvancedSettingsDialog.save_settings(self) + + if self.global_settings_chkbox.get_state(): + self.set_net_prop('use_settings_globally', True) + else: + self.set_net_prop('use_settings_globally', False) + wireless.RemoveGlobalEssidEntry(networkid) + + wireless.SaveWirelessNetworkProfile(networkid) + return True + + # More or less ripped from netentry.py + def change_encrypt_method(self): + #self.lbox_encrypt = urwid.ListBox() + wid,ID = self.encryption_combo.get_focus() + methods = misc.LoadEncryptionMethods() + self.encryption_info = {} + + if self._w.body.body.__contains__(self.pile_encrypt): + self._w.body.body.pop(self._w.body.body.__len__()-2) + + # If nothing is selected, select the first entry. + if ID == -1: + self.encryption_combo.set_active(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='on_focus') + else: + edit = MaskingEdit(('editcp',opts[x][0].replace('_',' ')+': '),mask_mode='on_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 + + edit.set_edit_text(noneToBlankString( + wireless.GetWirelessProperty(self.networkID, opts[x][1]))) + + self.pile_encrypt = DynWrap(urwid.Pile(theList),attrs=('editbx','editnfc')) + self._w.body.body.insert(self._w.body.body.__len__()-1,self.pile_encrypt) + #self._w.body.body.append(self.pile_encrypt) + def run(self,ui,dim,display): + self.ui = ui width,height = ui.get_cols_rows() - list = [] - for x, enc_type in enumerate(self.encrypt_types): - list.append(enc_type[0]) - self.encryption_combo.set_list(list) overlay = urwid.Overlay(self, display, ('fixed left', 0),width , ('fixed top',1), height-3) self.encryption_combo.build_combobox(overlay,ui,14) + #self.change_encrypt_method() + #self._w.body.body.append(self.pile_encrypt) keys = True while True: @@ -281,15 +385,15 @@ class WirelessNetEntry(NetEntryBase): ui.draw_screen(dim, overlay.render(dim, True)) keys = ui.get_input() + for k in keys: + #Send key to underlying widget: + overlay.keypress(dim, k) 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) - # Check if buttons are pressed. - #if self.CANCEL_PRESSED: - # return False - #if self.OK_PRESSED or 'meta enter' in keys: - # return True + if "meta enter" in keys or self.OK_PRESSED: + if self.save_settings(self.networkID): + return True + if self.CANCEL_PRESSED: + return False diff --git a/curses/prefs_curses.py b/curses/prefs_curses.py index 4f19361..c771798 100755 --- a/curses/prefs_curses.py +++ b/curses/prefs_curses.py @@ -109,6 +109,7 @@ class PrefsDialog(urwid.WidgetWrap): backend_cat_t = ('header',language['backend']) backend_t = language['backend']+':' backend_list = ['spam','double spam','triple spam','quadruple spam'] + backend_warn_t = ('important','Changes to the backend (probably) requires a daemon restart') debug_cat_t = ('header','Debugging') debug_mode_t = language['use_debug_mode'] @@ -207,6 +208,7 @@ class PrefsDialog(urwid.WidgetWrap): self.backend_cat = urwid.Text(backend_cat_t) self.backend_cbox = ComboBox(backend_t) + self.backend_warn = urwid.Text(backend_warn_t) self.debug_cat = urwid.Text(debug_cat_t) self.debug_mode_checkb = urwid.CheckBox(debug_mode_t) @@ -218,7 +220,7 @@ class PrefsDialog(urwid.WidgetWrap): advancedLB = urwid.ListBox([self.wpa_cat, self.wpa_cbox,self.wpa_warn,_blank, self.backend_cat, - self.backend_cbox,_blank, + self.backend_cbox,self.backend_warn,_blank, self.debug_cat, self.debug_mode_checkb, _blank, self.wless_cat, @@ -315,7 +317,7 @@ class PrefsDialog(urwid.WidgetWrap): # Pick where to begin first: def_driver = daemon.GetWPADriver() try: - self.wpa_cbox.set_show_first(self.wpadrivers.index(def_driver)) + self.wpa_cbox.set_focus(self.wpadrivers.index(def_driver)) except ValueError: pass # It defaults to 0 anyway @@ -326,9 +328,9 @@ class PrefsDialog(urwid.WidgetWrap): self.backend_cbox.set_list(self.thebackends) cur_backend = daemon.GetSavedBackend() try: - self.backend_cbox.set_show_first(self.thebackends.index(cur_backend)) + self.backend_cbox.set_focus(self.thebackends.index(cur_backend)) except ValueError: - self.backend_cbox.set_show_first(0) + self.backend_cbox.set_focus(0) # Two last checkboxes self.debug_mode_checkb.set_state(daemon.GetDebugMode()) diff --git a/curses/wicd-curses.py b/curses/wicd-curses.py index fc483aa..cedecc2 100644 --- a/curses/wicd-curses.py +++ b/curses/wicd-curses.py @@ -56,7 +56,7 @@ from time import sleep from curses_misc import SelText,ComboBox,Dialog from prefs_curses import PrefsDialog import netentry_curses -from netentry_curses import WirelessNetEntry +from netentry_curses import WirelessSettingsDialog language = misc.get_language_list_gui() @@ -303,7 +303,7 @@ class WiredComboBox(ComboBox): wiredL.append(theString) id+=1 self.__super.__init__(list=wiredL,use_enter=False) - self.set_show_first(theList.index(wired.GetDefaultWiredProfile())) + self.set_focus(theList.index(wired.GetDefaultWiredProfile())) def keypress(self,size,key): self.__super.keypress(size,key) @@ -503,8 +503,9 @@ class appGUI(): def idle_incr(self): theText = "" if self.connecting: - theText = "-- Connecting -- Press ESC to cancel" - self.footer1 = urwid.Text(str(self.incr) + ' '+theText) + theText = "-- Connecting -- Press ESC to cancel " + quit_note = "-- Press F8 or Q to quit." + self.footer1 = urwid.Text(str(self.incr) + ' '+theText+quit_note) self.incr+=1 return True @@ -585,12 +586,10 @@ class appGUI(): focus = self.thePile.get_focus() if focus == self.wiredCB: pass - #self.connect("wired",0) else: - # wless list only other option + # wireless list only other option wid,pos = self.thePile.get_focus().get_focus() - WirelessNetEntry(pos).run(ui,self.size,self.frame) - #self.connect("wireless",pos) + WirelessSettingsDialog(pos).run(ui,self.size,self.frame) #self.netentry = NetEntryBase(dbusmanager.get_dbus_ifaces()) #self.netentry.run(ui,self.size,self.frame) diff --git a/in/man=wicd-curses.8.in b/in/man=wicd-curses.8.in index 670b37d..5913828 100644 --- a/in/man=wicd-curses.8.in +++ b/in/man=wicd-curses.8.in @@ -39,11 +39,19 @@ The following is a work in progress and might not be fully functional as of yet. .BR C Bring up network configuration controller for the selected network .PP -The following is not implemented yet: +The following are not implemented yet: .TP .BR S Bring up the script selector for the selected network (requires superuser privileges) - +.TP +.BR I +Bring up hidden network scanning dialog +.TP +.BR R +Bring up script selector "dialog." +.TP +.BR H +Bring up a rather simplistic help dialog. Of course, it mentions this man page first. :-) .SH "FILES" These are not used yet. .TP