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