Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / devtools / v9 / lib / python2.4 / curses / textpad.py
CommitLineData
920dae64
AT
1"""Simple textbox editing widget with Emacs-like keybindings."""
2
3import curses, ascii
4
5def rectangle(win, uly, ulx, lry, lrx):
6 """Draw a rectangle with corners at the provided upper-left
7 and lower-right coordinates.
8 """
9 win.vline(uly+1, ulx, curses.ACS_VLINE, lry - uly - 1)
10 win.hline(uly, ulx+1, curses.ACS_HLINE, lrx - ulx - 1)
11 win.hline(lry, ulx+1, curses.ACS_HLINE, lrx - ulx - 1)
12 win.vline(uly+1, lrx, curses.ACS_VLINE, lry - uly - 1)
13 win.addch(uly, ulx, curses.ACS_ULCORNER)
14 win.addch(uly, lrx, curses.ACS_URCORNER)
15 win.addch(lry, lrx, curses.ACS_LRCORNER)
16 win.addch(lry, ulx, curses.ACS_LLCORNER)
17
18class Textbox:
19 """Editing widget using the interior of a window object.
20 Supports the following Emacs-like key bindings:
21
22 Ctrl-A Go to left edge of window.
23 Ctrl-B Cursor left, wrapping to previous line if appropriate.
24 Ctrl-D Delete character under cursor.
25 Ctrl-E Go to right edge (stripspaces off) or end of line (stripspaces on).
26 Ctrl-F Cursor right, wrapping to next line when appropriate.
27 Ctrl-G Terminate, returning the window contents.
28 Ctrl-H Delete character backward.
29 Ctrl-J Terminate if the window is 1 line, otherwise insert newline.
30 Ctrl-K If line is blank, delete it, otherwise clear to end of line.
31 Ctrl-L Refresh screen.
32 Ctrl-N Cursor down; move down one line.
33 Ctrl-O Insert a blank line at cursor location.
34 Ctrl-P Cursor up; move up one line.
35
36 Move operations do nothing if the cursor is at an edge where the movement
37 is not possible. The following synonyms are supported where possible:
38
39 KEY_LEFT = Ctrl-B, KEY_RIGHT = Ctrl-F, KEY_UP = Ctrl-P, KEY_DOWN = Ctrl-N
40 KEY_BACKSPACE = Ctrl-h
41 """
42 def __init__(self, win):
43 self.win = win
44 (self.maxy, self.maxx) = win.getmaxyx()
45 self.maxy = self.maxy - 1
46 self.maxx = self.maxx - 1
47 self.stripspaces = 1
48 self.lastcmd = None
49 win.keypad(1)
50
51 def _end_of_line(self, y):
52 "Go to the location of the first blank on the given line."
53 last = self.maxx
54 while 1:
55 if ascii.ascii(self.win.inch(y, last)) != ascii.SP:
56 last = min(self.maxx, last+1)
57 break
58 elif last == 0:
59 break
60 last = last - 1
61 return last
62
63 def do_command(self, ch):
64 "Process a single editing command."
65 (y, x) = self.win.getyx()
66 self.lastcmd = ch
67 if ascii.isprint(ch):
68 if y < self.maxy or x < self.maxx:
69 # The try-catch ignores the error we trigger from some curses
70 # versions by trying to write into the lowest-rightmost spot
71 # in the window.
72 try:
73 self.win.addch(ch)
74 except curses.error:
75 pass
76 elif ch == ascii.SOH: # ^a
77 self.win.move(y, 0)
78 elif ch in (ascii.STX,curses.KEY_LEFT, ascii.BS,curses.KEY_BACKSPACE):
79 if x > 0:
80 self.win.move(y, x-1)
81 elif y == 0:
82 pass
83 elif self.stripspaces:
84 self.win.move(y-1, self._end_of_line(y-1))
85 else:
86 self.win.move(y-1, self.maxx)
87 if ch in (ascii.BS, curses.KEY_BACKSPACE):
88 self.win.delch()
89 elif ch == ascii.EOT: # ^d
90 self.win.delch()
91 elif ch == ascii.ENQ: # ^e
92 if self.stripspaces:
93 self.win.move(y, self._end_of_line(y))
94 else:
95 self.win.move(y, self.maxx)
96 elif ch in (ascii.ACK, curses.KEY_RIGHT): # ^f
97 if x < self.maxx:
98 self.win.move(y, x+1)
99 elif y == self.maxy:
100 pass
101 else:
102 self.win.move(y+1, 0)
103 elif ch == ascii.BEL: # ^g
104 return 0
105 elif ch == ascii.NL: # ^j
106 if self.maxy == 0:
107 return 0
108 elif y < self.maxy:
109 self.win.move(y+1, 0)
110 elif ch == ascii.VT: # ^k
111 if x == 0 and self._end_of_line(y) == 0:
112 self.win.deleteln()
113 else:
114 # first undo the effect of self._end_of_line
115 self.win.move(y, x)
116 self.win.clrtoeol()
117 elif ch == ascii.FF: # ^l
118 self.win.refresh()
119 elif ch in (ascii.SO, curses.KEY_DOWN): # ^n
120 if y < self.maxy:
121 self.win.move(y+1, x)
122 if x > self._end_of_line(y+1):
123 self.win.move(y+1, self._end_of_line(y+1))
124 elif ch == ascii.SI: # ^o
125 self.win.insertln()
126 elif ch in (ascii.DLE, curses.KEY_UP): # ^p
127 if y > 0:
128 self.win.move(y-1, x)
129 if x > self._end_of_line(y-1):
130 self.win.move(y-1, self._end_of_line(y-1))
131 return 1
132
133 def gather(self):
134 "Collect and return the contents of the window."
135 result = ""
136 for y in range(self.maxy+1):
137 self.win.move(y, 0)
138 stop = self._end_of_line(y)
139 if stop == 0 and self.stripspaces:
140 continue
141 for x in range(self.maxx+1):
142 if self.stripspaces and x == stop:
143 break
144 result = result + chr(ascii.ascii(self.win.inch(y, x)))
145 if self.maxy > 0:
146 result = result + "\n"
147 return result
148
149 def edit(self, validate=None):
150 "Edit in the widget window and collect the results."
151 while 1:
152 ch = self.win.getch()
153 if validate:
154 ch = validate(ch)
155 if not ch:
156 continue
157 if not self.do_command(ch):
158 break
159 self.win.refresh()
160 return self.gather()
161
162if __name__ == '__main__':
163 def test_editbox(stdscr):
164 ncols, nlines = 9, 4
165 uly, ulx = 15, 20
166 stdscr.addstr(uly-2, ulx, "Use Ctrl-G to end editing.")
167 win = curses.newwin(nlines, ncols, uly, ulx)
168 rectangle(stdscr, uly-1, ulx-1, uly + nlines, ulx + ncols)
169 stdscr.refresh()
170 return Textbox(win).edit()
171
172 str = curses.wrapper(test_editbox)
173 print 'Contents of text box:', repr(str)