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