source: python/vendor/Python-2.6.5/Lib/curses/textpad.py

Last change on this file was 2, checked in by Yuri Dario, 15 years ago

Initial import for vendor code.

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