python-2.5.2/win32/Lib/curses/textpad.py
changeset 0 ae805ac0140d
equal deleted inserted replaced
-1:000000000000 0:ae805ac0140d
       
     1 """Simple textbox editing widget with Emacs-like keybindings."""
       
     2 
       
     3 import curses, ascii
       
     4 
       
     5 def 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 
       
    18 class 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 
       
   162 if __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)