"""Simple textbox editing widget with Emacs-like keybindings."""
def rectangle(win
, uly
, ulx
, lry
, lrx
):
"""Draw a rectangle with corners at the provided upper-left
and lower-right coordinates.
win
.vline(uly
+1, ulx
, curses
.ACS_VLINE
, lry
- uly
- 1)
win
.hline(uly
, ulx
+1, curses
.ACS_HLINE
, lrx
- ulx
- 1)
win
.hline(lry
, ulx
+1, curses
.ACS_HLINE
, lrx
- ulx
- 1)
win
.vline(uly
+1, lrx
, curses
.ACS_VLINE
, lry
- uly
- 1)
win
.addch(uly
, ulx
, curses
.ACS_ULCORNER
)
win
.addch(uly
, lrx
, curses
.ACS_URCORNER
)
win
.addch(lry
, lrx
, curses
.ACS_LRCORNER
)
win
.addch(lry
, ulx
, curses
.ACS_LLCORNER
)
"""Editing widget using the interior of a window object.
Supports the following Emacs-like key bindings:
Ctrl-A Go to left edge of window.
Ctrl-B Cursor left, wrapping to previous line if appropriate.
Ctrl-D Delete character under cursor.
Ctrl-E Go to right edge (stripspaces off) or end of line (stripspaces on).
Ctrl-F Cursor right, wrapping to next line when appropriate.
Ctrl-G Terminate, returning the window contents.
Ctrl-H Delete character backward.
Ctrl-J Terminate if the window is 1 line, otherwise insert newline.
Ctrl-K If line is blank, delete it, otherwise clear to end of line.
Ctrl-N Cursor down; move down one line.
Ctrl-O Insert a blank line at cursor location.
Ctrl-P Cursor up; move up one line.
Move operations do nothing if the cursor is at an edge where the movement
is not possible. The following synonyms are supported where possible:
KEY_LEFT = Ctrl-B, KEY_RIGHT = Ctrl-F, KEY_UP = Ctrl-P, KEY_DOWN = Ctrl-N
(self
.maxy
, self
.maxx
) = win
.getmaxyx()
self
.maxy
= self
.maxy
- 1
self
.maxx
= self
.maxx
- 1
def _end_of_line(self
, y
):
"Go to the location of the first blank on the given line."
if ascii
.ascii(self
.win
.inch(y
, last
)) != ascii
.SP
:
last
= min(self
.maxx
, last
+1)
def do_command(self
, ch
):
"Process a single editing command."
(y
, x
) = self
.win
.getyx()
if y
< self
.maxy
or x
< self
.maxx
:
# The try-catch ignores the error we trigger from some curses
# versions by trying to write into the lowest-rightmost spot
elif ch
== ascii
.SOH
: # ^a
elif ch
in (ascii
.STX
,curses
.KEY_LEFT
, ascii
.BS
,curses
.KEY_BACKSPACE
):
self
.win
.move(y
-1, self
._end
_of
_line
(y
-1))
self
.win
.move(y
-1, self
.maxx
)
if ch
in (ascii
.BS
, curses
.KEY_BACKSPACE
):
elif ch
== ascii
.EOT
: # ^d
elif ch
== ascii
.ENQ
: # ^e
self
.win
.move(y
, self
._end
_of
_line
(y
))
self
.win
.move(y
, self
.maxx
)
elif ch
in (ascii
.ACK
, curses
.KEY_RIGHT
): # ^f
elif ch
== ascii
.BEL
: # ^g
elif ch
== ascii
.NL
: # ^j
elif ch
== ascii
.VT
: # ^k
if x
== 0 and self
._end
_of
_line
(y
) == 0:
# first undo the effect of self._end_of_line
elif ch
== ascii
.FF
: # ^l
elif ch
in (ascii
.SO
, curses
.KEY_DOWN
): # ^n
if x
> self
._end
_of
_line
(y
+1):
self
.win
.move(y
+1, self
._end
_of
_line
(y
+1))
elif ch
== ascii
.SI
: # ^o
elif ch
in (ascii
.DLE
, curses
.KEY_UP
): # ^p
if x
> self
._end
_of
_line
(y
-1):
self
.win
.move(y
-1, self
._end
_of
_line
(y
-1))
"Collect and return the contents of the window."
for y
in range(self
.maxy
+1):
stop
= self
._end
_of
_line
(y
)
if stop
== 0 and self
.stripspaces
:
for x
in range(self
.maxx
+1):
if self
.stripspaces
and x
== stop
:
result
= result
+ chr(ascii
.ascii(self
.win
.inch(y
, x
)))
def edit(self
, validate
=None):
"Edit in the widget window and collect the results."
if not self
.do_command(ch
):
if __name__
== '__main__':
def test_editbox(stdscr
):
stdscr
.addstr(uly
-2, ulx
, "Use Ctrl-G to end editing.")
win
= curses
.newwin(nlines
, ncols
, uly
, ulx
)
rectangle(stdscr
, uly
-1, ulx
-1, uly
+ nlines
, ulx
+ ncols
)
return Textbox(win
).edit()
str = curses
.wrapper(test_editbox
)
print 'Contents of text box:', repr(str)