diff --git a/curses/curses_misc.py b/curses/curses_misc.py index 78f0945..06bfb77 100644 --- a/curses/curses_misc.py +++ b/curses/curses_misc.py @@ -5,7 +5,7 @@ wicd-curses. """ -# Copyright (C) 2008-9 Andrew Psaltis +# Copyright (C) 2008-2009 Andrew Psaltis # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -53,7 +53,7 @@ class DynWrap(urwid.AttrWrap): """ def __init__(self,w,sensitive=True,attrs=('editbx','editnfc'),focus_attr='editfc'): - self.attrs=attrs + self._attrs=attrs self._sensitive = sensitive cur_attr = attrs[0] if sensitive else attrs[1] @@ -63,22 +63,78 @@ class DynWrap(urwid.AttrWrap): def get_sensitive(self): return self._sensitive def set_sensitive(self,state): - if state : - self.set_attr(self.attrs[0]) + if state: + self.set_attr(self._attrs[0]) else: - self.set_attr(self.attrs[1]) + self.set_attr(self._attrs[1]) self._sensitive = state property(get_sensitive,set_sensitive) def get_attrs(self): return self._attrs def set_attrs(self,attrs): - self.attrs = attrs + self._attrs = attrs property(get_attrs,set_attrs) def selectable(self): return self._sensitive +class MaskingEditException(Exception): + pass + +# Password-style edit +class MaskingEdit(urwid.Edit): + """ + mask_mode = one of: + "always" : everything is a '*' all of the time + "on_focus" : everything is a '*' only when not in focus + "off" : everything is always unmasked + mask_char = the single character that masks all other characters in the field + """ + def __init__(self, caption = "", edit_text = "", multiline = False, + align = 'left', wrap = 'space', allow_tab = False, + edit_pos = None, layout=None, mask_mode="masked",mask_char='*'): + self.mask_mode = mask_mode + if len(mask_char) > 1: + raise MaskingEditException('Masks of more than one character are not supported!') + self.mask_char = mask_char + self.__super.__init__(caption,edit_text,multiline,align,wrap,allow_tab,edit_pos,layout) + + def get_mask_mode(self): + return self.mask_mode + def set_mask_mode(self,mode): + self.mask_mode = mode + + def get_masked_text(self): + return self.mask_char*len(self.get_edit_text()) + + def render(self,(maxcol,), focus=False): + """ + 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 self.mask_mode == "off" or (self.mask_mode == 'on_focus' and focus == True): + canv = self.__super.render((maxcol,),focus) + # The cache messes this thing up, because I am totally changing what is + # displayed. + self._invalidate() + return canv + # Else, we have a slight mess to deal with... + + self._shift_view_to_cursor = not not focus # force bool + + text, attr = self.get_text() + text = text[:len(self.caption)]+self.get_masked_text() + trans = self.get_line_translation( maxcol, (text,attr) ) + canv = urwid.canvas.apply_text_layout(text, attr, trans, maxcol) + + if focus: + canv = urwid.CompositeCanvas(canv) + canv.cursor = self.get_cursor_coords((maxcol,)) + + return canv + # Tabbed interface, mostly for use in the Preferences Dialog class TabColumns(urwid.WidgetWrap): """ @@ -146,6 +202,9 @@ class ComboBoxException(Exception): # A "combo box" of SelTexts # I based this off of the code found here: # http://excess.org/urwid/browser/contrib/trunk/rbreu_menus.py +# This is a hack. It isn't without quirks, but it more or less works. +# We need to wait for changes in urwid's Canvas controls before we can actually +# make a real ComboBox. class ComboBox(urwid.WidgetWrap): """A ComboBox of text objects""" class ComboSpace(urwid.WidgetWrap): @@ -202,7 +261,7 @@ class ComboBox(urwid.WidgetWrap): #def get_size(self): - def __init__(self,label='',list=[],attr=('body','focus'),use_enter=True,show_first=0): + def __init__(self,label='',list=[],attrs=('body','editnfc'),focus_attr='focus',use_enter=True,show_first=0): """ label : bit of text that preceeds the combobox. If it is "", then ignore it @@ -214,13 +273,14 @@ class ComboBox(urwid.WidgetWrap): """ self.label = urwid.Text(label) - self.attr = attr + self.attrs = attrs + self.focus_attr = focus_attr self.list = list str,trash = self.label.get_text() self.overlay = None - - self.cbox = urwid.AttrWrap(SelText(' vvv'),attr[0],attr[1]) + #w,sensitive=True,attrs=('editbx','editnfc'),focus_attr='editfc') + self.cbox = DynWrap(SelText(' vvv'),attrs=attrs,focus_attr=focus_attr) # Unicode will kill me sooner or later. ^_^ if label != '': w = urwid.Columns([('fixed',len(str),self.label),self.cbox],dividechars=1) @@ -242,8 +302,7 @@ class ComboBox(urwid.WidgetWrap): def build_combobox(self,body,ui,row): str,trash = self.label.get_text() - self.cbox = urwid.AttrWrap(SelText([self.list[self.show_first]+' vvv']), - self.attr[0],self.attr[1]) + self.cbox = DynWrap(SelText([self.list[self.show_first]+' 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, @@ -273,13 +332,17 @@ class ComboBox(urwid.WidgetWrap): # Most obvious thing ever. :-) def selectable(self): - return True + 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_sensitive(self): + return self.cbox.get_sensitive() + def set_sensitive(self,state): + self.cbox.set_sensitive(state) # Almost completely ripped from rbreu_filechooser.py: # http://excess.org/urwid/browser/contrib/trunk/rbreu_menus.py diff --git a/curses/netentry_curses.py b/curses/netentry_curses.py index 2eb4d8f..71f0d18 100644 --- a/curses/netentry_curses.py +++ b/curses/netentry_curses.py @@ -1,4 +1,8 @@ #!/usr/bin/env python +""" + netentry_curses -- everyone's favorite networks settings dialogs... in text + form! +""" # Copyright (C) 2009 Andrew Psaltis @@ -18,14 +22,49 @@ # MA 02110-1301, USA. import urwid -from curses_misc import Dialog,DynWrap +from curses_misc import Dialog,DynWrap,MaskingEdit,ComboBox import wicd.misc as misc +from wicd.misc import noneToString, stringToNone, noneToBlankString, to_bool + +def error(ui,parent,message): + """Shows an error dialog (or something that resembles one)""" + # /\ + # /!!\ + # /____\ + dialog = Dialog(message,[OK],('body','body','focus'),40,6) + + keys = True + dim = ui.get_cols_rows() + while True: + if keys: + ui.draw_screen(dim, about.render(dim, True)) + + keys = ui.get_input() + if "window resize" in keys: + dim = ui.get_cols_rows() + if "esc" in keys: + return False + for k in keys: + dialog.keypress(dim, k) + if dialog.b_pressed == 'OK': + return False language = misc.get_language_list_gui() + +daemon = None +wired = None +wireless = None +# Call this first! +def dbus_init(dbus_ifaces): + global daemon,wired,wireless + daemon = dbus_ifaces['daemon'] + 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): - def __init__(self,dbus): + def __init__(self): static_ip_t = language['use_static_ip'] ip_t = ('editcp',language['ip']+': ') netmask_t = ('editcp',language['netmask']+':') @@ -49,12 +88,12 @@ class NetEntryBase(urwid.WidgetWrap): self.gateway_edit=DynWrap(urwid.Edit(gateway_t),False) - self.use_static_dns_cb = urwid.CheckBox(use_static_dns_t, + self.static_dns_cb = urwid.CheckBox(use_static_dns_t, on_state_change=self.dns_toggle) - self.use_global_dns_cb = DynWrap(urwid.CheckBox(use_global_dns_t, + self.global_dns_cb = DynWrap(urwid.CheckBox(use_global_dns_t, on_state_change=self.dns_toggle),False,('body','editnfc'),None) - checkb_cols = urwid.Columns([self.use_static_dns_cb, - self.use_global_dns_cb]) + checkb_cols = urwid.Columns([self.static_dns_cb, + self.global_dns_cb]) self.dns_dom_edit = DynWrap(urwid.Edit(dns_dom_t) ,False) self.search_dom_edit = DynWrap(urwid.Edit(search_dom_t),False) self.dns1 = DynWrap(urwid.Edit(dns1_t) ,False) @@ -63,39 +102,73 @@ class NetEntryBase(urwid.WidgetWrap): _blank = urwid.Text('') - self._listbox = urwid.ListBox([self.static_ip_cb, - self.ip_edit, - self.netmask_edit, - _blank, - checkb_cols, - self.dns_dom_edit,self.search_dom_edit, - self.dns1,self.dns2,self.dns3 - ]) + + walker = urwid.SimpleListWalker([self.static_ip_cb, + self.ip_edit, + self.netmask_edit, + self.gateway_edit, + _blank, + checkb_cols, + self.dns_dom_edit,self.search_dom_edit, + self.dns1,self.dns2,self.dns3 + ]) + self._listbox = urwid.ListBox(walker) #self._frame = urwid.Frame(self._listbox) - self.__super.__init__(self._listbox) + self._frame = urwid.Frame(self._listbox) + self.__super.__init__(self._frame) 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) def dns_toggle(self,checkb,new_state,user_data=None): - if checkb == self.use_static_dns_cb: + if checkb == self.static_dns_cb: for w in [ self.dns_dom_edit,self.search_dom_edit, self.dns1,self.dns2,self.dns3 ]: w.set_sensitive(new_state) if not new_state: - self.use_global_dns_cb.set_state(False,do_callback=False) - self.use_global_dns_cb.set_sensitive(new_state) + self.global_dns_cb.set_state(False,do_callback=False) + self.global_dns_cb.set_sensitive(new_state) # use_global_dns_cb is DynWrapped - if checkb == self.use_global_dns_cb.get_w(): + if checkb == self.global_dns_cb.get_w(): for w in [ self.dns_dom_edit,self.search_dom_edit, self.dns1,self.dns2,self.dns3 ]: w.set_sensitive(not new_state) - # We need a network ID for this, and I am not sure how to get it yet. - # TODO: Implement this - #def load_settings(self): + # 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(): + 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())) + else: + self.set_net_prop("ip", '') + 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(): + 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('use_static_dns', True) + self.set_net_prop('use_global_dns', True) + else: + self.set_net_prop('use_static_dns', False) + self.set_net_prop('use_global_dns', False) + self.set_net_prop('dns_domain', '') + self.set_net_prop("search_domain", '') + self.set_net_prop("dns1", '') + self.set_net_prop("dns2", '') + self.set_net_prop("dns3", '') def run(self,ui,dim,display): width,height = ui.get_cols_rows() @@ -122,3 +195,101 @@ class NetEntryBase(urwid.WidgetWrap): # return False #if self.OK_PRESSED or 'meta enter' in keys: # return True + +######################################## + +class WirelessNetEntry(NetEntryBase): + def __init__(self,networkID): + NetEntryBase.__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._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.encrypt_types = misc.LoadEncryptionMethods() + self.set_values() + + def encryption_toggle(self,chkbox,new_state,user_data=None): + self.encryption_combo.set_sensitive(new_state) + + def set_values(self): + """ Set the various network settings to the right values. """ + 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")) + + self.global_dns_cb.set_state(bool(wireless.GetWirelessProperty(networkID, + 'use_global_dns'))) + self.static_dns_cb.set_state(bool(wireless.GetWirelessProperty(networkID, + 'use_static_dns'))) + + self.dns1.set_edit_text(self.format_entry(networkID, "dns1")) + self.dns2.set_edit_text(self.format_entry(networkID, "dns2")) + self.dns3.set_edit_text(self.format_entry(networkID, "dns3")) + self.dns_dom_edit.set_edit_text(self.format_entry(networkID, "dns_domain")) + self.search_dom_edit.set_edit_text(self.format_entry(networkID, "search_domain")) + + + #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'))) + + 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.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() + + def set_net_prop(self, option, value): + """ Sets the given option to the given value for this network. """ + wireless.SetWirelessProperty(self.networkID, option, value) + + def format_entry(self, networkid, label): + """ Helper method for fetching/formatting wireless properties. """ + return noneToBlankString(wireless.GetWirelessProperty(networkid, label)) + + def run(self,ui,dim,display): + 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) + + 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) + # 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/prefs_curses.py b/curses/prefs_curses.py index 8ba6461..4f19361 100755 --- a/curses/prefs_curses.py +++ b/curses/prefs_curses.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -# Copyright (C) 2008-9 Andrew Psaltis +# Copyright (C) 2008-2009 Andrew Psaltis # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/curses/wicd-curses.py b/curses/wicd-curses.py index 7133dd0..fc483aa 100644 --- a/curses/wicd-curses.py +++ b/curses/wicd-curses.py @@ -1,13 +1,13 @@ #!/usr/bin/env python -""" wicd-curses -- a (curses-based) console interface to wicd +""" wicd-curses. (curses/urwid-based) console interface to wicd Provides the a console UI for wicd, so that people with broken X servers can -at least get a network connection. Or those who don't like using X. :-) +at least get a network connection. Or those who don't like using X. ;-) """ -# Copyright (C) 2008-9 Andrew Psaltis +# Copyright (C) 2008-2009 Andrew Psaltis # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -55,10 +55,10 @@ from time import sleep # Curses UIs for other stuff from curses_misc import SelText,ComboBox,Dialog from prefs_curses import PrefsDialog -from netentry_curses import NetEntryBase +import netentry_curses +from netentry_curses import WirelessNetEntry language = misc.get_language_list_gui() -# Whew. Now on to more interesting stuff: ######################################## ##### SUPPORT CLASSES @@ -582,8 +582,17 @@ class appGUI(): self.frame.keypress( self.size, k ) if "C" in keys: - self.netentry = NetEntryBase(dbusmanager.get_dbus_ifaces()) - self.netentry.run(ui,self.size,self.frame) + focus = self.thePile.get_focus() + if focus == self.wiredCB: + pass + #self.connect("wired",0) + else: + # wless list only other option + wid,pos = self.thePile.get_focus().get_focus() + WirelessNetEntry(pos).run(ui,self.size,self.frame) + #self.connect("wireless",pos) + #self.netentry = NetEntryBase(dbusmanager.get_dbus_ifaces()) + #self.netentry.run(ui,self.size,self.frame) if " " in keys: focus = self.thePile.get_focus() @@ -678,7 +687,7 @@ def setup_dbus(force=True): dbusmanager.connect_to_dbus() except DBusException: # I may need to be a little more verbose here. - # Suggestions as to what should go here + # Suggestions as to what should go here, please? print "Can't connect to the daemon. Are you sure it is running?" print "Please check the wicd log for error messages." #raise @@ -690,6 +699,8 @@ def setup_dbus(force=True): wired = dbus_ifaces['wired'] DBUS_AVAIL = True + + netentry_curses.dbus_init(dbus_ifaces) return True setup_dbus()