]> git.ipfire.org Git - thirdparty/newt.git/blob - snack.py
added help support and testing
[thirdparty/newt.git] / snack.py
1 # snack.py: maps C extension module _snack to proper python types in module
2 # snack.
3 # The first section is a very literal mapping.
4 # The second section contains convenience classes that amalgamate
5 # the literal classes and make them more object-oriented.
6
7 import _snack
8 import types
9 import string
10
11 snackArgs = {}
12 snackArgs['append'] = -1
13
14 FLAG_DISABLED = _snack.FLAG_DISABLED
15 FLAGS_SET = _snack.FLAGS_SET
16 FLAGS_RESET = _snack.FLAGS_RESET
17 FLAGS_TOGGLE = _snack.FLAGS_TOGGLE
18
19 class Widget:
20 def setCallback(self, obj, data = None):
21 if data:
22 self.w.setCallback(obj, data)
23 else:
24 self.w.setCallback(obj)
25
26 class Button(Widget):
27
28 def __init__(self, text):
29 self.w = _snack.button(text)
30
31 class Checkbox(Widget):
32
33 def value(self):
34 return self.w.checkboxValue
35
36 def selected(self):
37 return self.w.checkboxValue != 0
38
39 def setFlags (self, flag, sense):
40 return self.w.checkboxSetFlags(flag, sense)
41
42 def __init__(self, text, isOn = 0):
43 self.w = _snack.checkbox(text, isOn)
44
45 class SingleRadioButton(Widget):
46
47 def selected(self):
48 return self.w.key == self.w.radioValue;
49
50 def __init__(self, text, group, isOn = 0):
51 if group:
52 self.w = _snack.radiobutton(text, group.w, isOn)
53 else:
54 self.w = _snack.radiobutton(text, None, isOn)
55
56 class Listbox(Widget):
57
58 def append(self, text, item):
59 key = self.w.listboxAddItem(text)
60 self.key2item[key] = item
61 self.item2key[item] = key
62
63 def insert(self, text, item, before):
64 if (not before):
65 key = self.w.listboxInsertItem(text, 0)
66 else:
67 key = self.w.listboxInsertItem(text, self.item2key[before])
68 self.key2item[key] = item
69 self.item2key[item] = key
70
71 def delete(self, item):
72 self.w.listboxDeleteItem(self.item2key[item])
73 del self.key2item[self.item2key[item]]
74 del self.item2key[item]
75
76 def replace(self, text, item):
77 key = self.w.listboxInsertItem(text, self.item2key[item])
78 self.w.listboxDeleteItem(self.item2key[item])
79 del self.key2item[self.item2key[item]]
80 self.item2key[item] = key
81 self.key2item[key] = item
82
83 def current(self):
84 return self.key2item[self.w.listboxGetCurrent()]
85
86 def setCurrent(self, item):
87 self.w.listboxSetCurrent(self.item2key[item])
88
89 def __init__(self, height, scroll = 0, returnExit = 0, width = 0):
90 self.w = _snack.listbox(height, scroll, returnExit)
91 self.key2item = {}
92 self.item2key = {}
93 if (width):
94 self.w.listboxSetWidth(width)
95
96 class Textbox(Widget):
97
98 def setText(self, text):
99 self.w.textboxText(text)
100
101 def __init__(self, width, height, text, scroll = 0, wrap = 0):
102 self.w = _snack.textbox(width, height, text, scroll, wrap)
103
104 class TextboxReflowed(Textbox):
105
106 def __init__(self, width, text, flexDown = 5, flexUp = 10, maxHeight = -1):
107 (newtext, width, height) = reflow(text, width, flexDown, flexUp)
108 if maxHeight != -1 and height > maxHeight:
109 Textbox.__init__(self, width, maxHeight, newtext, 1)
110 else:
111 Textbox.__init__(self, width, height, newtext, 0)
112
113 class Label(Widget):
114
115 def setText(self, text):
116 self.w.labelText(text)
117
118 def __init__(self, text):
119 self.w = _snack.label(text)
120
121 class Scale(Widget):
122
123 def set(self, amount):
124 self.w.scaleSet(amount)
125
126 def __init__(self, width, total):
127 self.w = _snack.scale(width, total)
128
129 class Entry(Widget):
130
131 def value(self):
132 return self.w.entryValue
133
134 def set(self, text):
135 return self.w.entrySetValue(text)
136
137 def setFlags (self, flag, sense):
138 return self.w.entrySetFlags(flag, sense)
139
140 def __init__(self, width, text = "", hidden = 0, scroll = 1,
141 returnExit = 0):
142 self.w = _snack.entry(width, text, hidden, scroll, returnExit)
143
144
145 # Form uses hotkeys
146 hotkeys = { "F1" : _snack.KEY_F1, "F2" : _snack.KEY_F2, "F3" : _snack.KEY_F3,
147 "F4" : _snack.KEY_F4, "F5" : _snack.KEY_F5, "F6" : _snack.KEY_F6,
148 "F7" : _snack.KEY_F7, "F8" : _snack.KEY_F8, "F9" : _snack.KEY_F9,
149 "F10" : _snack.KEY_F10, "F11" : _snack.KEY_F11,
150 "F12" : _snack.KEY_F12 }
151
152 for n in hotkeys.keys():
153 hotkeys[hotkeys[n]] = n
154
155 class Form:
156
157 def addHotKey(self, keyname):
158 self.w.addhotkey(hotkeys[keyname])
159
160 def add(self, widget):
161 if widget.__dict__.has_key('hotkeys'):
162 for key in widget.hotkeys.keys():
163 self.addHotKey(key)
164
165 if widget.__dict__.has_key('gridmembers'):
166 for w in widget.gridmembers:
167 self.add(w)
168 elif widget.__dict__.has_key('w'):
169 self.trans[widget.w.key] = widget
170 return self.w.add(widget.w)
171 return None
172
173 def run(self):
174 (what, which) = self.w.run()
175 if (what == _snack.FORM_EXIT_WIDGET):
176 return self.trans[which]
177 elif (what == _snack.FORM_EXIT_TIMER):
178 return "TIMER"
179
180 return hotkeys[which]
181
182 def draw(self):
183 self.w.draw()
184 return None
185
186 def __init__(self, helpArg = None):
187 self.trans = {}
188 self.w = _snack.form(helpArg)
189 # we do the reference count for the helpArg in python! gross
190 self.helpArg = helpArg
191
192 def setCurrent (self, co):
193 self.w.setcurrent (co.w)
194
195 def setTimer (self, timer):
196 self.w.settimer (timer)
197
198 class Grid:
199
200 def place(self, x, y):
201 return self.g.place(x, y)
202
203 def setField(self, what, col, row, padding = (0, 0, 0, 0),
204 anchorLeft = 0, anchorTop = 0, anchorRight = 0,
205 anchorBottom = 0, growx = 0, growy = 0):
206 self.gridmembers.append(what)
207 anchorFlags = 0
208 if (anchorLeft):
209 anchorFlags = _snack.ANCHOR_LEFT
210 elif (anchorRight):
211 anchorFlags = _snack.ANCHOR_RIGHT
212
213 if (anchorTop):
214 anchorFlags = anchorFlags | _snack.ANCHOR_TOP
215 elif (anchorBottom):
216 anchorFlags = anchorFlags | _snack.ANCHOR_BOTTOM
217
218 gridFlags = 0
219 if (growx):
220 gridFlags = _snack.GRID_GROWX
221 if (growy):
222 gridFlags = gridFlags | _snack.GRID_GROWY
223
224 if (what.__dict__.has_key('g')):
225 return self.g.setfield(col, row, what.g, padding, anchorFlags,
226 gridFlags)
227 else:
228 return self.g.setfield(col, row, what.w, padding, anchorFlags)
229
230 def __init__(self, *args):
231 self.g = apply(_snack.grid, args)
232 self.gridmembers = []
233
234 class SnackScreen:
235
236 def __init__(self):
237 _snack.init()
238 (self.width, self.height) = _snack.size()
239 self.pushHelpLine(None)
240
241 def finish(self):
242 return _snack.finish()
243
244 def resume(self):
245 _snack.resume()
246
247 def suspend(self):
248 _snack.suspend()
249
250 def doHelpCallback(self, arg):
251 self.helpCb(self, arg)
252
253 def helpCallback(self, cb):
254 self.helpCb = cb
255 return _snack.helpcallback(self.doHelpCallback)
256
257 def suspendCallback(self, cb, data = None):
258 if data:
259 return _snack.suspendcallback(cb, data)
260 return _snack.suspendcallback(cb)
261
262 def openWindow(self, left, top, width, height, title):
263 return _snack.openwindow(left, top, width, height, title)
264
265 def pushHelpLine(self, text):
266 if (not text):
267 return _snack.pushhelpline("*default*")
268 else:
269 return _snack.pushhelpline(text)
270
271 def popHelpLine(self):
272 return _snack.pophelpline()
273
274 def drawRootText(self, left, top, text):
275 return _snack.drawroottext(left, top, text)
276
277 def centeredWindow(self, width, height, title):
278 return _snack.centeredwindow(width, height, title)
279
280 def gridWrappedWindow(self, grid, title, x = None, y = None):
281 if x and y:
282 return _snack.gridwrappedwindow(grid.g, title, x, y)
283
284 return _snack.gridwrappedwindow(grid.g, title)
285
286 def popWindow(self):
287 return _snack.popwindow()
288
289 def refresh(self):
290 return _snack.refresh()
291
292 # returns a tuple of the wrapped text, the actual width, and the actual height
293 def reflow(text, width, flexDown = 5, flexUp = 5):
294 return _snack.reflow(text, width, flexDown, flexUp)
295
296 # combo widgets
297
298 class RadioGroup(Widget):
299
300 def __init__(self):
301 self.prev = None
302 self.buttonlist = []
303
304 def add(self, title, value, default = None):
305 if not self.prev and default == None:
306 # If the first element is not explicitly set to
307 # not be the default, make it be the default
308 default = 1
309 b = SingleRadioButton(title, self.prev, default)
310 self.prev = b
311 self.buttonlist.append((b, value))
312 return b
313
314 def getSelection(self):
315 for (b, value) in self.buttonlist:
316 if b.selected(): return value
317 return None
318
319
320 class RadioBar(Grid):
321
322 def __init__(self, screen, buttonlist):
323 self.list = []
324 self.item = 0
325 self.group = RadioGroup()
326 Grid.__init__(self, 1, len(buttonlist))
327 for (title, value, default) in buttonlist:
328 b = self.group.add(title, value, default)
329 self.list.append(b, value)
330 self.setField(b, 0, self.item, anchorLeft = 1)
331 self.item = self.item + 1
332
333 def getSelection(self):
334 return self.group.getSelection()
335
336
337 # you normally want to pack a ButtonBar with growx = 1
338
339 class ButtonBar(Grid):
340
341 def __init__(self, screen, buttonlist):
342 self.list = []
343 self.hotkeys = {}
344 self.item = 0
345 Grid.__init__(self, len(buttonlist), 1)
346 for blist in buttonlist:
347 if (type(blist) == types.StringType):
348 title = blist
349 value = string.lower(blist)
350 elif len(blist) == 2:
351 (title, value) = blist
352 else:
353 (title, value, hotkey) = blist
354 self.hotkeys[hotkey] = value
355
356 b = Button(title)
357 self.list.append(b, value)
358 self.setField(b, self.item, 0, (1, 0, 1, 0))
359 self.item = self.item + 1
360
361 def buttonPressed(self, result):
362 """Takes the widget returned by Form.run and looks to see
363 if it was one of the widgets in the ButtonBar."""
364
365 if self.hotkeys.has_key(result):
366 return self.hotkeys[result]
367
368 for (button, value) in self.list:
369 if result == button:
370 return value
371 return None
372
373
374 class GridFormHelp(Grid):
375
376 def __init__(self, screen, title, help, *args):
377 self.screen = screen
378 self.title = title
379 self.form = Form(help)
380 self.childList = []
381 self.form_created = 0
382 args = list(args)
383 args[:0] = [self]
384 apply(Grid.__init__, tuple(args))
385
386 def add(self, widget, col, row, padding = (0, 0, 0, 0),
387 anchorLeft = 0, anchorTop = 0, anchorRight = 0,
388 anchorBottom = 0, growx = 0, growy = 0):
389 self.setField(widget, col, row, padding, anchorLeft,
390 anchorTop, anchorRight, anchorBottom,
391 growx, growy);
392 self.childList.append(widget)
393
394 def runOnce(self, x = None, y = None):
395 result = self.run(x, y)
396 self.screen.popWindow()
397 return result
398
399 def addHotKey(self, keyname):
400 self.form.addHotKey(keyname)
401
402 def setTimer(self, keyname):
403 self.form.setTimer(keyname)
404
405 def create(self, x = None, y = None):
406 if not self.form_created:
407 self.place(1,1)
408 for child in self.childList:
409 self.form.add(child)
410 self.screen.gridWrappedWindow(self, self.title, x, y)
411 self.form_created = 1
412
413 def run(self, x = None, y = None):
414 self.create(x, y)
415 return self.form.run()
416
417 def draw(self):
418 self.create()
419 return self.form.draw()
420
421 def runPopup(self):
422 self.create()
423 self.screen.gridWrappedWindow(self, self.title)
424 result = self.form.run()
425 self.screen.popWindow()
426 return result
427
428 def setCurrent (self, co):
429 self.form.setCurrent (co)
430
431 class GridForm(GridFormHelp):
432
433 def __init__(self, screen, title, *args):
434 myargs = (self, screen, title, None) + args
435 apply(GridFormHelp.__init__, myargs)
436
437 class CheckboxTree(Widget):
438 def append(self, text, item = None, selected = 0):
439 self.addItem(text, (snackArgs['append'], ), item, selected)
440
441 def addItem(self, text, path, item = None, selected = 0):
442 if (not item):
443 item = text
444 key = self.w.checkboxtreeAddItem(text, path, selected)
445 self.key2item[key] = item
446 self.item2key[item] = key
447
448 def getCurrent(self):
449 curr = self.w.checkboxtreeGetCurrent()
450 return self.key2item[curr]
451
452 def __init__(self, height, scroll = 0):
453 self.w = _snack.checkboxtree(height, scroll)
454 self.key2item = {}
455 self.item2key = {}
456
457 def getSelection(self):
458 selection = []
459 list = self.w.checkboxtreeGetSelection()
460 for key in list:
461 selection.append(self.key2item[key])
462 return selection
463
464 def setEntry(self, item, text):
465 self.w.checkboxtreeSetEntry(self.item2key[item], text)
466
467 def setEntryValue(self, item, selected = 1):
468 self.w.checkboxtreeSetEntryValue(self.item2key[item], selected)
469
470 def getEntryValue(self, item):
471 return self.w.checkboxtreeGetEntryValue(self.item2key[item])
472
473 def ListboxChoiceWindow(screen, title, text, items,
474 buttons = ('Ok', 'Cancel'),
475 width = 40, scroll = 0, height = -1, default = None,
476 help = None):
477 if (height == -1): height = len(items)
478
479 bb = ButtonBar(screen, buttons)
480 t = TextboxReflowed(width, text)
481 l = Listbox(height, scroll = scroll, returnExit = 1)
482 count = 0
483 for item in items:
484 if (type(item) == types.TupleType):
485 (text, key) = item
486 else:
487 text = item
488 key = count
489
490 if (default == count):
491 default = key
492 elif (default == item):
493 default = key
494
495 l.append(text, key)
496 count = count + 1
497
498 if (default != None):
499 l.setCurrent (default)
500
501 g = GridFormHelp(screen, title, help, 1, 3)
502 g.add(t, 0, 0)
503 g.add(l, 0, 1, padding = (0, 1, 0, 1))
504 g.add(bb, 0, 2, growx = 1)
505
506 rc = g.runOnce()
507
508 return (bb.buttonPressed(rc), l.current())
509
510 def ButtonChoiceWindow(screen, title, text,
511 buttons = [ 'Ok', 'Cancel' ],
512 width = 40, x = None, y = None, help = None):
513 bb = ButtonBar(screen, buttons)
514 t = TextboxReflowed(width, text, maxHeight = screen.height - 12)
515
516 g = GridFormHelp(screen, title, help, 1, 2)
517 g.add(t, 0, 0, padding = (0, 0, 0, 1))
518 g.add(bb, 0, 1, growx = 1)
519 return bb.buttonPressed(g.runOnce(x, y))
520
521 def EntryWindow(screen, title, text, prompts, allowCancel = 1, width = 40,
522 entryWidth = 20, buttons = [ 'Ok', 'Cancel' ], help = None):
523 bb = ButtonBar(screen, buttons);
524 t = TextboxReflowed(width, text)
525
526 count = 0
527 for n in prompts:
528 count = count + 1
529
530 sg = Grid(2, count)
531
532 count = 0
533 entryList = []
534 for n in prompts:
535 if (type(n) == types.TupleType):
536 (n, e) = n
537 else:
538 e = Entry(entryWidth)
539
540 sg.setField(Label(n), 0, count, padding = (0, 0, 1, 0), anchorLeft = 1)
541 sg.setField(e, 1, count, anchorLeft = 1)
542 count = count + 1
543 entryList.append(e)
544
545 g = GridFormHelp(screen, title, help, 1, 3)
546
547 g.add(t, 0, 0, padding = (0, 0, 0, 1))
548 g.add(sg, 0, 1, padding = (0, 0, 0, 1))
549 g.add(bb, 0, 2, growx = 1)
550
551 result = g.runOnce()
552
553 entryValues = []
554 count = 0
555 for n in prompts:
556 entryValues.append(entryList[count].value())
557 count = count + 1
558
559 return (bb.buttonPressed(result), tuple(entryValues))