Add tab completion for userid fields
[public/pyceo-broken.git] / ceo / urwid / widgets.py
1 import urwid, ldap
2 from ceo.urwid.window import raise_back, push_window
3 import ceo.ldapi as ldapi
4
5 uwldap_uri = "ldap://uwldap.uwaterloo.ca/"
6 uwldap_base = "dc=uwaterloo,dc=ca"
7 csclub_uri = "ldap://ldap1.csclub.uwaterloo.ca/ ldap://ldap2.csclub.uwaterloo.ca"
8 csclub_base = "dc=csclub,dc=uwaterloo,dc=ca"
9
10 def menu_items(items):
11     return [ urwid.AttrWrap( ButtonText( cb, data, txt ), 'menu', 'selected') for (txt, cb, data) in items ]
12
13 def push_wizard(name, pages, dimensions=(50, 10)):
14     state = {}
15     wiz = Wizard()
16     for page in pages:
17         if type(page) != tuple:
18             page = (page, )
19         wiz.add_panel( page[0](state, *page[1:]) )
20     push_window( urwid.Filler( urwid.Padding(
21         urwid.LineBox(wiz), 'center', dimensions[0]),
22         'middle', dimensions[1] ), name )
23
24 class ButtonText(urwid.Text):
25     def __init__(self, callback, data, *args, **kwargs):
26         self.callback = callback
27         self.data = data
28         urwid.Text.__init__(self, *args, **kwargs)
29     def selectable(self):
30         return True
31     def keypress(self, size, key):
32         if key == 'enter' and self.callback:
33             self.callback(self.data)
34         else:
35             return key
36
37 class SingleEdit(urwid.Edit):
38     def keypress(self, size, key):
39         if key == 'enter':
40             return urwid.Edit.keypress(self, size, 'down')
41         else:
42             return urwid.Edit.keypress(self, size, key)
43
44 class SingleIntEdit(urwid.IntEdit):
45     def keypress(self, size, key):
46         if key == 'enter':
47             return urwid.Edit.keypress(self, size, 'down')
48         else:
49             return urwid.Edit.keypress(self, size, key)
50
51 class WordEdit(SingleEdit):
52     def valid_char(self, ch):
53         return urwid.Edit.valid_char(self, ch) and ch != ' '
54
55 class LdapWordEdit(WordEdit):
56     ldap = None
57     tabbing = False
58     def __init__(self, uri, base, attr, *args):
59         try:
60             self.ldap = ldap.initialize(uri)
61             self.ldap.simple_bind_s("", "")
62         except ldap.LDAPError:
63             return WordEdit.__init__(self, *args)
64         self.base = base
65         self.attr = ldapi.escape(attr)
66         return WordEdit.__init__(self, *args)
67     def keypress(self, size, key):
68         if key == 'tab' and self.ldap != None:
69             if self.tabbing:
70                 self.index = (self.index + 1) % len(self.choices)
71                 text = self.choices[self.index]
72                 self.set_edit_text(text)
73                 self.set_edit_pos(len(text))
74             else:
75                 try:
76                     search = ldapi.escape(self.get_edit_text())
77                     matches = self.ldap.search_s(self.base,
78                         ldap.SCOPE_SUBTREE, '(%s=%s*)' % (self.attr, search))
79                     self.choices = []
80                     for match in matches:
81                         (_, attrs) = match
82                         self.choices += attrs['uid']
83                     if len(self.choices) > 0:
84                         self.index = 0
85                         self.tabbing = True
86                         text = self.choices[self.index]
87                         self.set_edit_text(text)
88                         self.set_edit_pos(len(text))
89                 except ldap.LDAPError, e:
90                     pass
91         else:
92             self.tabbing = False
93             return WordEdit.keypress(self, size, key)
94
95 class LdapFilterWordEdit(LdapWordEdit):
96     def __init__(self, uri, base, attr, map, *args):
97         LdapWordEdit.__init__(self, uri, base, attr, *args)
98         self.map = map
99     def keypress(self, size, key):
100         if self.ldap != None:
101             if key == 'enter' or key == 'down' or key == 'up':
102                 search = ldapi.escape(self.get_edit_text())
103                 try:
104                     matches = self.ldap.search_s(self.base,
105                         ldap.SCOPE_SUBTREE, '(%s=%s)' % (self.attr, search))
106                     if len(matches) > 0:
107                         (_, attrs) = matches[0]
108                         for (k, v) in self.map.items():
109                             if attrs.has_key(k) and len(attrs[k]) > 0:
110                                 v.set_edit_text(attrs[k][0])
111                 except ldap.LDAPError:
112                     pass
113         return LdapWordEdit.keypress(self, size, key)
114
115 class PassEdit(SingleEdit):
116     def get_text(self):
117         text = urwid.Edit.get_text(self)
118         return (self.caption + " " * len(self.get_edit_text()), text[1])
119
120 class Wizard(urwid.WidgetWrap):
121     def __init__(self):
122         self.selected = None
123         self.panels = []
124
125         self.panelwrap = urwid.WidgetWrap( urwid.SolidFill() )
126         self.back = urwid.Button("Back", self.back)
127         self.next = urwid.Button("Next", self.next)
128         self.buttons = urwid.Columns( [ self.back, self.next ], dividechars=3, focus_column=1 )
129         pad = urwid.Padding( self.buttons, ('fixed right', 2), 19 )
130         self.pile = urwid.Pile( [self.panelwrap, ('flow', pad)], 0 )
131         urwid.WidgetWrap.__init__(self, self.pile)
132
133     def add_panel(self, panel):
134         self.panels.append( panel )
135         if len(self.panels) == 1:
136             self.select(0)
137
138     def select(self, panelno, set_focus=True):
139         if 0 <= panelno < len(self.panels):
140             self.selected = panelno
141             self.panelwrap.set_w( self.panels[panelno] )
142             self.panels[panelno].activate()
143
144             if set_focus:
145                 if self.panels[panelno].focusable():
146                     self.pile.set_focus( 0 )
147                 else:
148                     self.pile.set_focus( 1 )
149
150     def next(self, *args, **kwargs):
151         if self.panels[self.selected].check():
152             self.select( self.selected )
153             return
154         self.select(self.selected + 1)
155
156     def back(self, *args, **kwargs):
157         if self.selected == 0:
158             raise_back()
159         self.select(self.selected - 1, False)
160
161 class WizardPanel(urwid.WidgetWrap):
162     def __init__(self, state):
163         self.state = state
164         self.init_widgets()
165         self.box = urwid.ListBox( urwid.SimpleListWalker( self.widgets ) )
166         urwid.WidgetWrap.__init__( self, self.box )
167     def init_widgets(self):
168         self.widgets = []
169     def focus_widget(self, widget):
170         self.box.set_focus( self.widgets.index( widget ) )
171     def focusable(self):
172         return True
173     def check(self):
174         return
175     def activate(self):
176         return