def create_textbox(self, color_code, default_value=None, validate=None):
        is_valid = False
        data = None
        while(not is_valid):
            self._window.hline(self.y + 1, self.x, "_", 16)
            self._window.refresh()

            new_win = curses.newwin(self.h, self.w,
                                    self.y + self.header_height, self.x)
            text = Textbox(new_win, insert_mode=False)

            if default_value:
                for i in default_value:
                    text.do_command(ord(i))

            data = text.edit()
            if not validate:
                is_valid = True
            elif getattr(Validation, validate)(data.strip()):
                is_valid = True
            else:
                col_code_attr = ColorCode().get_color_pair(3)
                self._obj.on_attr(col_code_attr)
                self._window.addstr(self.header_height - 1,
                                    3,
                                    f"Error: Invalid {validate}"
                                    f" {data.strip()} Please re-enter")
                self._obj.off_attr(col_code_attr)
                self._window.refresh()
        return data
Example #2
0
class MiniWindow:
    def __init__(self):
        self.__mini_window = curses.newwin(1, curses.COLS, curses.LINES - 1, 0)
        self.__mini_window.keypad(True)
        self.__mini_window_tb = Textbox(self.__mini_window)

    def display_message_in_mini_buffer(self, msg):
        self.__mini_window.erase()
        self.__mini_window.addstr(msg)
        self.__mini_window.refresh()

    def get_file_name(self):
        self.display_message_in_mini_buffer('file to save in: ')

        filename = ""
        while True:
            mini_buffer_ch = self.__mini_window.getch()
            if mini_buffer_ch == 24:
                self.__mini_window.erase()
                self.__mini_window.refresh()
                return None

            elif mini_buffer_ch == 10:
                return filename

            else:
                self.__mini_window_tb.do_command(mini_buffer_ch)
                filename += chr(mini_buffer_ch)
Example #3
0
def edited_text(scr,
                text,
                y,
                x,
                w=50,
                prompt="Edit the text then Ctrl-G to exit"):
    """
    Provides the editing capability:
    Returns the edited (or not) version of text.
    Editing window begins at (y,x) of the <scr>een,
    consists of 3 rows and is w+2 characters wide.
    Text to be edited appears on line y+1 beginning in column x+1
    within a 'bordered' window with room for w characters or as many
    as are in text, which ever is the greater.
    The <prompt> overwrites the box border in bold.
    """
    #   scr.
    #   scr.refresh()
    if l := len(text) > w: w = l
    # create the text box with border around the outside
    tb_border = cur.newwin(3, w + 2, y, x)
    tb_border.box()
    # place promt on line above the box
    tb_border.refresh()
    scr.addstr(y, x + 2, prompt, cur.A_BOLD)
    scr.refresh()
    tb_body = cur.newwin(1, w, y + 1, x + 1)
    tb = Textbox(tb_body)
    for ch in text:  # insert starting text
        tb.do_command(ch)
    tb.edit()  # start the editor running, Ctrl-G ends
    s2 = tb.gather()  # fetch the contents
    scr.clear()  # clear the screen
    #   return s2
    return s2.strip()
Example #4
0
    def __init__(self, win, insert_mode=False, text=''):
        Textbox.__init__(self, win, insert_mode)

        for chr in text:
            if chr == '\n':
                Textbox.do_command(self, curses.ascii.NL)
            else:
                Textbox.do_command(self, chr)
Example #5
0
def edit_box():
    global box, NUM_LINES, NUM_COLS, TOP, LEFT, chosen_option, edit_box_message
    editwin = curses.newwin(NUM_LINES, NUM_COLS - 1, TOP,
                            LEFT + len(chosen_option) + 2)
    box = Textbox(editwin)
    for char in edit_box_message:
        box.do_command(char)
    box.edit(navigator)
Example #6
0
 def do_command(self, ch):
     if ch == 127:  # backspace
         Textbox.do_command(self, curses.KEY_BACKSPACE)
         self.win.refresh()
         return 1
     if ch == 27:
         Textbox.gather(self)
         return -1
     return Textbox.do_command(self, ch)
Example #7
0
def new_game_get_seed(win):
    # Ask for a random seed to start the game

    win.clear()
    win.addstr(0, 0, SEED_STRING)

    # create a text box for the user to type in
    editwin = curses.newpad(1, 32)
    rectangle(win, 5, 0, 1 + 5 + 1, 1 + 32 + 1)

    win_height = curses.LINES - 2
    win_width = curses.COLS - 4
    win.refresh(0, 0, 1, 2, win_height, win_width)

    box = Textbox(editwin)

    while True:
        # wait for user input
        win.refresh(0, 0, 1, 2, win_height, win_width)
        editwin.refresh(0, 0, 7, 4, 7 + 1, 4 + 32)
        c = editwin.getch()

        # handle special cases:
        if c == 10:  # [enter]
            break
        elif c == curses.KEY_RESIZE:  # window resizing

            stdscr.clear()
            stdscr.border('|', '|', '-', '-', '+', '+', '+', '+')
            stdscr.refresh()
            rectangle(win, 5, 0, 1 + 5 + 1, 1 + 32 + 1)

            h, w = stdscr.getmaxyx()
            assert h > 2 and w > 10
            win_height = h - 2
            win_width = w - 4

            win.resize(100, win_width - 1)
            win.refresh(0, 0, 1, 2, win_height, win_width)

        # draw the user input in the text box
        box.do_command(c)

    # Get resulting contents
    message = box.gather()

    win.clear()

    if message:
        logger.info(f"Seed by user: {message}")
        return message
    else:
        seed = random.randint(1, sys.maxsize)
        logger.info(f"Seed generated: {seed}")
        return seed
Example #8
0
    def do_command(self, ch):
        if ch == curses.KEY_RESIZE:
            raise errors.CursesScreenResizedError()
        if ch == 10:  # Enter
            return 0
        if ch == 127:  # BackSpace
            return 8

        return Textbox.do_command(self, ch)
Example #9
0
 def do_command(self, ch):
     if ch == curses.KEY_RESIZE:
         resizeWindows()
         for i in range(8):  # delete 8 input window lines
             Textbox.do_command(self, 1)
             Textbox.do_command(self, 11)
             Textbox.do_command(self, 16)
         return Textbox.do_command(self, 7)
     if ch == 10:  # Enter
         return 0
     if ch == 127:  # Backspace
         return 8
     return Textbox.do_command(self, ch)
Example #10
0
 def do_command(self, ch):
     if ch == curses.KEY_RESIZE:
         resizeWindows()
         for i in range(8): # delete 8 input window lines
             Textbox.do_command(self, 1)
             Textbox.do_command(self, 11)
             Textbox.do_command(self, 16)
         return Textbox.do_command(self, 7)
     if ch == 10: # Enter
         return 0
     if ch == 127: # Backspace
         return 8
     return Textbox.do_command(self, ch)
Example #11
0
def main4(screen):
    curses.curs_set(False)
    cursor_pos = 0

    screen.addstr(1, 1, "Enter a text or something yo")

    height, width, y, x = 1, 20, 4, 1

    editwin = curses.newwin(height, width, y, x)

    curses.curs_set(True)
    rectangle(screen, y - 1, x - 1, height + y, width + x)
    screen.refresh()

    box = Textbox(editwin)

    for c in 'test input':
        box.do_command(c)

    box.edit(enter_is_terminate)

    message = box.gather()

    #screen.refresh()
    #screen.move(y,x)

    editwin = curses.newwin(height, width, y, x)

    curses.curs_set(True)
    rectangle(screen, y - 1, x - 1, height + y, width + x)
    screen.refresh()
    box = Textbox(editwin)

    for c in message:
        box.do_command(c)

    box.edit(enter_is_terminate)

    curses.curs_set(False)
Example #12
0
    def do_command(self, ch: int) -> int:
        """
        Change the default textbox behavior of up, and down.
    Ā Ā Ā Ā """

        if ch == curses.KEY_UP:
            return 1

        elif ch == curses.KEY_DOWN:
            return 1

        else:
            return Textbox.do_command(self, ch)
class EditField(ScrollWindow):
    '''EditFields represent editable text areas on the screen

    At any time, the text of the object can be accessed by
    referencing an EditField's 'text' parameter (edit_field.text).
    Note that this returns a list of character codes. To get a string,
    see EditField.get_text()

    '''

    ASTERISK_CHAR = ord('*')
    LARROW_CHAR = ord('<')

    CMD_DONE_EDIT = ord(ctrl('g'))
    CMD_MV_BOL = ord(ctrl('a'))  # Move to beginning of line
    CMD_MV_EOL = ord(ctrl('e'))  # Move to end of line
    CMD_CLR_LINE = ord(ctrl('k'))

    def __init__(self, area, window=None, text="", masked=False,
                 color_theme=None, color=None, highlight_color=None,
                 validate=None, on_exit=None, error_win=None,
                 numeric_pad=None, **kwargs):
        '''See InnerWindow.__init__

        In general, specifying a specific color_theme is unnecessary, unless
        this instance requires a theme other than that of the rest of the
        program

        window (required): A parent InnerWindow

        area (required): Describes the area within the parent window to be
                         used.
                         area.lines must be 1, as only single line EditFields
                         are currently supported.

        text (optional): The text in this field, before it is edited by
        the user. Defaults to empty string.

        masked (optional): If True, then this field will display bullets when
        the user types into it, instead of echo'ing the text

        color_theme (optional): The color_theme for this edit field. By default
        the parent window's theme is used. color_theme.edit_field is used
        as the background color; color_theme.highlight_edit is the background
        when this EditField is active.

        validate (optional) - A function for validation on each keystroke.
        The function passed in should take as parameter a string, and if
        invalid, it should raise a UIMessage with an indicator of why. This
        function, if present, will be called after each keystroke.
        IMPORTANT: If no UIMessage is raised, the string is assumed to be
        valid

        Additional keyword arguments can be passed to this function by
        modifying this EditField's validate_kwargs dictionary

        on_exit (optional) - A function for final validation. This is called
        when this EditField loses focus. Like validate, it should accept a
        string argument and raise a UIMessage if the string is not valid.

        Additional keyword arguments can be passed to this function by
        modifying this EditField's on_exit_kwargs dictionary

        error_win (optional) - If given, error_win.display_error() is called
        whenever validate or on_exit raise a UIMessage (the UIMessage's
        explanation string is passed as parameter). Additionally,
        error_win.clear_err() is called when those functions return
        successfully.

        numeric_pad (optional) - A single character to pad self.text with.
        If given, when this EditField handles input, it works in numeric mode.
        In numeric mode:
            * When editing begins, numeric_pad is stripped from the current
              text in the field. Text is shifted to the left to compensate
            * When done editing, the text is right justified, and padded with
              the value of numeric_pad. In general, either a space or zero will
              be used as the value of numeric_pad
            * IMPORTANT: The function hooks for 'validate' and 'on_exit' are
              called using the value of self.get_text PRIOR to padding.

        '''
        self._modified = False
        self.numeric_pad = numeric_pad
        self.right_justify = False
        self.on_exit_kwargs = {}
        self.validate_kwargs = {}
        self.validate = validate
        self.on_exit = on_exit
        self.error_win = error_win
        if color_theme is None:
            color_theme = window.color_theme
        if color is None:
            color = color_theme.edit_field
        if highlight_color is None:
            highlight_color = color_theme.highlight_edit

        if area.lines != 1:
            raise ValueError("area.lines must be 1")
        super(EditField, self).__init__(area, window=window,
                                        color_theme=color_theme, color=color,
                                        highlight_color=highlight_color,
                                        **kwargs)
        self.masked = masked
        self.masked_char = EditField.ASTERISK_CHAR
        self.textbox = Textbox(self.window)
        self.textbox.stripspaces = True
        self.input_key = None
        self.text = None
        self.key_dict[curses.KEY_ENTER] = consume_action
        self.key_dict[curses.KEY_LEFT] = no_action
        self.key_dict[curses.KEY_RIGHT] = no_action

        # Set use_horiz_scroll_bar to False to let edit_field handle
        # the drawing of the horiz scroll arrow, since it is inline with
        # the text. Do this before calling set_text, which does a screen
        # update and use_horiz_scroll_bar needs to be False by then.
        self.use_horiz_scroll_bar = False
        self._set_text(text)
        self.clear_on_enter = False

    @property
    def modified(self):
        '''Returns True if the text in this field has been modified
        since the field was created'''
        return self._modified

    def set_text(self, text):
        '''Set the text of this EditField to text. Processes each
        character individually; thus emulating a user typing in the text.
        This means that each substring of text that start at text[0] must
        be valid in the context of any validation function this EditField
        has.

        If numeric_pad is set, pad text with it if field isn't blank.
        '''
        self._modified = True
        self._set_text(text)

    def _do_pad(self, text):
        '''Apply the numeric padding to text'''
        if self.numeric_pad is not None:
            width = self.area.columns - 1
            if text:
                text = text.lstrip(self.numeric_pad)
                text = rjust_columns(text, width, self.numeric_pad)
        return text

    def _set_text(self, text, do_pad=True):
        '''Used internally to bypass the the public
        interface's numeric_pad functionality'''
        if do_pad:
            text = self._do_pad(text)
        if text is None:
            text = u""
        self._reset_text()
        terminalui.LOGGER.log(LOG_LEVEL_INPUT,
                    "_set_text textwidth=%s, columns=%s, text=%s",
                    textwidth(text), self.area.columns, text)
        length_diff = textwidth(text) - self.area.columns
        if (length_diff >= 0):
            self.scroll(scroll_to_column=length_diff)
        for idx, char in enumerate(text):
            if length_diff > 0 and idx == length_diff:
                self.textbox.do_command(EditField.LARROW_CHAR)
            elif self.masked:
                self.textbox.do_command(self.masked_char)
            else:
                self.textbox.do_command(ord(char))
            self.text.append(char)
        self.no_ut_refresh()

    def handle_input(self, input_key):
        '''
        For each keystroke, determine if it's a special character (and needs
        to end editing), printable character, or backspace.

        For special characters, send the return the done-editing code (CTRL-G),
        and store the special character for processing by parent window
        objects.

        If printable, append it to self.text, and try to validate. If
        validation fails, reject the character.

        '''
        input_key = self.translate_input(input_key)
        if self.is_special_char(input_key):
            self.input_key = input_key
            return EditField.CMD_DONE_EDIT
        else:
            self.input_key = None
        if isprint(input_key) or (ismeta(input_key) and
                                  input_key < curses.KEY_MIN):
            # isprint: ASCII characters
            # ismeta and < curses.KEY_MIN: Remaining UTF-8 characters
            # > curses.KEY_MIN: Special key such as down arrow, backspace, etc.
            self.text.append(unichr(input_key))
            if not self.is_valid():
                if len(self.text) > 0:
                    self.text.pop()
                return None
            self._modified = True
            if self.masked:
                input_key = self.masked_char
        elif input_key == curses.KEY_BACKSPACE:
            if len(self.text) > 0:
                del_char = self.text.pop()
                self._modified = True
                del_width = charwidth(del_char)
                if textwidth(self.get_text()) >= self.area.columns:
                    self.scroll(columns=-del_width)
            self.is_valid()
            # Run self.is_valid here so that any functional side effects can
            # occur, but don't check the return value (removing a character
            # from a valid string should never be invalid, and even if it were,
            # it would not make sense to add the deleted character back in)

        return input_key

    def edit_loop(self):
        '''Loop for handling characters in an edit field.
        Called by EditField.process().

        '''
        input_key = None
        while input_key != EditField.CMD_DONE_EDIT:
            input_key = self.handle_input(self.getch())
            self._set_text(self.get_text())
            curses.doupdate()

    def process(self, input_key):
        '''Process a keystroke. For an EditField, this means preparing the
        textpad for processing and passing the input_key in.

        Try to enable the blinking cursor (if it was disabled) before
        editing begins, so the user can see where they're typing. Once
        finished, restore the cursor state to its previous condition.

        After editing, return self.input_key, which will either be None
        (indicating all keystrokes were processed by the Textbox) or
        a special character (such as F2) which caused EditField.handle_input
        to stop processing text through the Textbox.

        '''
        try:
            curses.curs_set(2)
        except curses.error:
            terminalui.LOGGER.debug("Got curses.error when enabling cursor")

        if input_key is not None and not self.is_special_char(input_key):
            # Put input_key back on stack so that textbox.edit can read it
            curses.ungetch(input_key)
            if self.numeric_pad is not None:
                self._set_text(self.get_text().lstrip(self.numeric_pad),
                               do_pad=False)

            if self.clear_on_enter:
                self.clear_text()
            else:
                # Move to end of previous input.
                self.textbox.do_command(EditField.CMD_MV_EOL)
            self.edit_loop()
            return_key = self.input_key
            if self.numeric_pad is not None:
                self._set_text(self.get_text())
        else:
            return_key = input_key
        terminalui.LOGGER.debug("Returning: %s", return_key)
        return return_key

    def get_text(self):
        '''Join the array of characters as a unicode string'''
        return u"".join(self.text)

    def is_special_char(self, input_key):
        '''Check to see if this is a keystroke that should break us out of
        adding input to the Textbox and return control to the parent window

        '''
        if (input_key in range(curses.KEY_F0, curses.KEY_F10) or
            input_key in self.key_dict):
            return True
        else:
            return False

    def is_valid(self):
        '''Check to see if the text we have is valid or not.
        First check the length to make sure it fits in the space alloted.
        If it doesn't, flag an error indicating the maximum supported
        length for the field.

        Then, if this EditField has a validate function, call it (passing
        in any validate_kwargs we have).

        If validate raises an exception, display the error (if we have a
        handle to an ErrorWindow) and return False.

        '''
        win_size_x = self.window.getmaxyx()[1]
        if len(self.get_text().lstrip(self.numeric_pad)) >= win_size_x:
            if self.error_win is not None:
                self.error_win.display_err(_("Max supported field length: %s")
                    % (win_size_x - 1))
            return False
        elif callable(self.validate):
            try:
                self.validate(self, **self.validate_kwargs)
            except UIMessage as error_str:
                if self.error_win is not None:
                    self.error_win.display_err(unicode(error_str))
                return False
        if self.error_win is not None and self.error_win.visible:
            self.error_win.clear_err()
        return True

    def run_on_exit(self):
        '''Fire the on_exit function, if there is one. If an error occurs,
        and this EditField has an error_win, display it there.

        '''
        if callable(self.on_exit):
            try:
                self.on_exit(self, **self.on_exit_kwargs)
                if self.error_win is not None and self.error_win.visible:
                    self.error_win.clear_err()
                return True
            except UIMessage as error_str:
                if self.error_win is not None:
                    self.error_win.display_err(unicode(error_str))
                return False
        return True

    def make_active(self):
        '''Enable the cursor when activating this field'''
        super(EditField, self).make_active()
        try:
            curses.curs_set(2)
        except curses.error:
            terminalui.LOGGER.debug("Got curses.error when enabling cursor")

    def make_inactive(self):
        '''Fire the on_exit function before leaving the field and making
        it inactive.

        '''
        self.run_on_exit()
        try:
            curses.curs_set(0)
        except curses.error:
            terminalui.LOGGER.debug("Got curses.error when reverting cursor")
        super(EditField, self).make_inactive()

    def _reset_text(self):
        '''Resets the text area, either to permanently remove the text,
        or as part of the process of redrawing with additional text
        (during self._set_text())

        '''
        # Move cursor to left side of window
        self.textbox.do_command(EditField.CMD_MV_BOL)
        # Clear from cursor to end of line
        self.textbox.do_command(EditField.CMD_CLR_LINE)
        self.text = []
        self.no_ut_refresh()

    def clear_text(self):
        '''Issue the commands to textbox to clear itself, reset self.text
        to an empty array, and reset horizontal scrolling.

        '''
        self._modified = True
        self.scroll(scroll_to_column=0)
        self._reset_text()

    def get_cursor_loc(self):
        '''Cursor should be positioned at the end of the entered text'''
        win_loc = self.window.getbegyx()
        x_loc = win_loc[1]
        if not self.clear_on_enter:
            x_loc += min(textwidth(self.get_text()), self.area.columns)
        return (win_loc[0], x_loc)
Example #14
0
class cursesWindow():
    __shared_state = {}

    def __init__(self, pad=None):
        self.__dict__ = self.__shared_state

        if not pad:
            pad = cursesPad()
        self.pad = pad

        self.screen = ''
        self.window = ''
        self.textbox = ''
        self.size = os.popen('stty size', 'r').read().split()

    def initialize(self):
        self.initializeWindow()
        self.initializeColorScheme()

    def initializeWindow(self):
        self.screen = curses.initscr()
        curses.start_color()
        curses.use_default_colors()
        curses.noecho()
        curses.cbreak()
        self.screen.keypad(1)
        self.setCursorVisibility(0)
        self.window = curses.newwin(int(self.size[0]), int(self.size[1]), 0, 0)
        signal.signal(signal.SIGWINCH, self.windowSizeChangeEventHandler)

    def initializeColorScheme(self):
        curses.init_pair(2, curses.COLOR_RED, -1)
        curses.init_pair(3, curses.COLOR_GREEN, -1)
        curses.init_pair(4, curses.COLOR_YELLOW, -1)
        curses.init_pair(5, curses.COLOR_BLUE, -1)
        curses.init_pair(6, curses.COLOR_MAGENTA, -1)
        curses.init_pair(7, curses.COLOR_CYAN, -1)
        curses.init_pair(8, curses.COLOR_WHITE, -1)

    def windowSizeChangeEventHandler(self, n, frame):
        self.size = os.popen('stty size', 'r').read().split()

    def setCursorVisibility(self, visible):
        try:
            curses.curs_set(visible)
        except:
            pass

    def getUserInput(self):
        return self.screen.getch()

    def close(self):
        self.setCursorVisibility(1)
        curses.nocbreak()
        self.screen.keypad(0)
        curses.echo()
        curses.endwin()

    def getInput(self, edit_window):
        self.setCursorVisibility(1)
        value = curses.textpad.Textbox(edit_window,
                                       insert_mode=True).edit().strip()
        self.setCursorVisibility(0)
        return value

    def wait(self, delay):
        self.screen.timeout(delay)

    def refresh(self):
        try:
            self.screen.noutrefresh()
        except:
            pass

    def update(self):
        curses.doupdate()

    def clear(self):
        self.screen.clear()
        self.pad.clear()

    # Drawing
    def addStr(self, string, color=''):
        try:
            if not color:
                self.screen.addstr(string)
            else:
                self.screen.addstr(string, curses.color_pair(color))
        except:
            pass

    def addString(self, posX, posY, string, color=''):
        try:
            if not color:
                self.screen.addstr(posY, posX, string)
            else:
                self.screen.addstr(posY, posX, string,
                                   curses.color_pair(color))
        except:
            pass

    def draw(self, screen):
        if not screen:
            return

        if len(screen[0]) == 4:
            for (y, x, line, color) in screen:
                self.addString(y, x, line, color)
        elif len(screen[0]) == 2:
            for (line, color) in screen:
                self.addStr(line, color)

    def editTextOnScreen(self, line_index, line_contents):
        editwin = self.window.subwin(1, int(self.size[1]) - 33, line_index, 7)
        editwin.addstr(0, 0, ' ' * (int(self.size[1]) - 34))
        editwin.addstr(0, 0, line_contents)
        return self.editTextField(editwin)

    def editTextField(self, editwin):
        self.setCursorVisibility(1)
        self.textbox = Textbox(editwin, insert_mode=True)
        newname = self.textbox.edit(self.deleteKeyPressHandler).strip()
        self.textbox = ''
        self.setCursorVisibility(0)
        return newname

    def deleteKeyPressHandler(self, keyPressed):
        if keyPressed in (330, 263, 127):  # Delete
            self.textbox.do_command(curses.KEY_BACKSPACE)
        else:
            return keyPressed

        return
Example #15
0
 def do_command(self, ch):
     if ch == 10:  # Enter
         return 0
     if ch == 127:  # Enter
         return 8
     return Textbox.do_command(self, ch)
 def do_command(self, ch):
     if ch == 10:  # Enter
         return 0
     if ch == 127:  # Enter
         return 8
     return Textbox.do_command(self, ch)
Example #17
0
class NewTextbox:
    height, width = 2, 20
    y, x = 4, 1
    text = ""
    _add_row = False
    _screen = None
    _curses_window = None
    _textbox = None

    def __init__(self, height, width, y, x, text="", screen=None):
        self.height, self.width, self.y, self.x = height, width, y, x
        self.text = text
        self._screen = screen
        #self._curses_window = curses.newwin(height, width, y, x)
        #self._textbox = Textbox(self._curses_window)
        self.redraw()

    def draw_rectangle(self):
        if self._screen is None:
            return
        rectangle(self._screen, self.y - 1, self.x - 1, self.height + self.y,
                  self.width + self.x)
        self._screen.refresh()

    def redraw(self):
        self._curses_window = curses.newwin(self.height, self.width, self.y,
                                            self.x)
        self._textbox = Textbox(self._curses_window)
        self._textbox.stripspaces = False

    def edit(self, text=""):
        if self._screen is None:
            #print("screen needs to be set")
            return

        exit = False
        i = 0

        while exit == False:

            self.redraw()
            for c in self.text:
                self._textbox.do_command(c)
            #if self._add_row == True:
            #	self._textbox.do_command(chr(16))
            #	self._add_row = False
            self.draw_rectangle()
            self._textbox.edit(self.enter_is_terminate)
            self.text = self._textbox.gather()
            if self._add_row == True:
                self.add_row()

            i = i + 1
            if i == 3: exit = True

    def enter_is_terminate(self, c):
        if c == 10:
            self.add_row = True
            self.height = self.height + 1
            return 7
        return c

    def add_row(self):
        self.height = self.height + 1
Example #18
0
class EditField(ScrollWindow):
    '''EditFields represent editable text areas on the screen

    At any time, the text of the object can be accessed by
    referencing an EditField's 'text' parameter (edit_field.text).
    Note that this returns a list of character codes. To get a string,
    see EditField.get_text()

    '''

    ASTERISK_CHAR = ord('*')
    LARROW_CHAR = ord('<')

    CMD_DONE_EDIT = ord(ctrl('g'))
    CMD_MV_BOL = ord(ctrl('a'))  # Move to beginning of line
    CMD_MV_EOL = ord(ctrl('e'))  # Move to end of line
    CMD_CLR_LINE = ord(ctrl('k'))

    def __init__(self,
                 area,
                 window=None,
                 text="",
                 masked=False,
                 color_theme=None,
                 color=None,
                 highlight_color=None,
                 validate=None,
                 on_exit=None,
                 error_win=None,
                 numeric_pad=None,
                 **kwargs):
        '''See InnerWindow.__init__

        In general, specifying a specific color_theme is unnecessary, unless
        this instance requires a theme other than that of the rest of the
        program

        window (required): A parent InnerWindow

        area (required): Describes the area within the parent window to be
                         used.
                         area.lines must be 1, as only single line EditFields
                         are currently supported.

        text (optional): The text in this field, before it is edited by
        the user. Defaults to empty string.

        masked (optional): If True, then this field will display bullets when
        the user types into it, instead of echo'ing the text

        color_theme (optional): The color_theme for this edit field. By default
        the parent window's theme is used. color_theme.edit_field is used
        as the background color; color_theme.highlight_edit is the background
        when this EditField is active.

        validate (optional) - A function for validation on each keystroke.
        The function passed in should take as parameter a string, and if
        invalid, it should raise a UIMessage with an indicator of why. This
        function, if present, will be called after each keystroke.
        IMPORTANT: If no UIMessage is raised, the string is assumed to be
        valid

        Additional keyword arguments can be passed to this function by
        modifying this EditField's validate_kwargs dictionary

        on_exit (optional) - A function for final validation. This is called
        when this EditField loses focus. Like validate, it should accept a
        string argument and raise a UIMessage if the string is not valid.

        Additional keyword arguments can be passed to this function by
        modifying this EditField's on_exit_kwargs dictionary

        error_win (optional) - If given, error_win.display_error() is called
        whenever validate or on_exit raise a UIMessage (the UIMessage's
        explanation string is passed as parameter). Additionally,
        error_win.clear_err() is called when those functions return
        successfully.

        numeric_pad (optional) - A single character to pad self.text with.
        If given, when this EditField handles input, it works in numeric mode.
        In numeric mode:
            * When editing begins, numeric_pad is stripped from the current
              text in the field. Text is shifted to the left to compensate
            * When done editing, the text is right justified, and padded with
              the value of numeric_pad. In general, either a space or zero will
              be used as the value of numeric_pad
            * IMPORTANT: The function hooks for 'validate' and 'on_exit' are
              called using the value of self.get_text PRIOR to padding.

        '''
        self._modified = False
        self.numeric_pad = numeric_pad
        self.right_justify = False
        self.on_exit_kwargs = {}
        self.validate_kwargs = {}
        self.validate = validate
        self.on_exit = on_exit
        self.error_win = error_win
        if color_theme is None:
            color_theme = window.color_theme
        if color is None:
            color = color_theme.edit_field
        if highlight_color is None:
            highlight_color = color_theme.highlight_edit

        if area.lines != 1:
            raise ValueError("area.lines must be 1")
        super(EditField, self).__init__(area,
                                        window=window,
                                        color_theme=color_theme,
                                        color=color,
                                        highlight_color=highlight_color,
                                        **kwargs)
        self.masked = masked
        self.masked_char = EditField.ASTERISK_CHAR
        self.textbox = Textbox(self.window)
        self.textbox.stripspaces = True
        self.input_key = None
        self.text = None
        self.key_dict[curses.KEY_ENTER] = consume_action
        self.key_dict[curses.KEY_LEFT] = no_action
        self.key_dict[curses.KEY_RIGHT] = no_action

        # Set use_horiz_scroll_bar to False to let edit_field handle
        # the drawing of the horiz scroll arrow, since it is inline with
        # the text. Do this before calling set_text, which does a screen
        # update and use_horiz_scroll_bar needs to be False by then.
        self.use_horiz_scroll_bar = False
        self._set_text(text)
        self.clear_on_enter = False

    @property
    def modified(self):
        '''Returns True if the text in this field has been modified
        since the field was created'''
        return self._modified

    def set_text(self, text):
        '''Set the text of this EditField to text. Processes each
        character individually; thus emulating a user typing in the text.
        This means that each substring of text that start at text[0] must
        be valid in the context of any validation function this EditField
        has.

        If numeric_pad is set, pad text with it if field isn't blank.
        '''
        self._modified = True
        self._set_text(text)

    def _do_pad(self, text):
        '''Apply the numeric padding to text'''
        if self.numeric_pad is not None:
            width = self.area.columns - 1
            if text:
                text = text.lstrip(self.numeric_pad)
                text = rjust_columns(text, width, self.numeric_pad)
        return text

    def _set_text(self, text, do_pad=True):
        '''Used internally to bypass the the public
        interface's numeric_pad functionality'''
        if do_pad:
            text = self._do_pad(text)
        if text is None:
            text = u""
        self._reset_text()
        terminalui.LOGGER.log(LOG_LEVEL_INPUT,
                              "_set_text textwidth=%s, columns=%s, text=%s",
                              textwidth(text), self.area.columns, text)
        length_diff = textwidth(text) - self.area.columns
        if (length_diff >= 0):
            self.scroll(scroll_to_column=length_diff)
        for idx, char in enumerate(text):
            if length_diff > 0 and idx == length_diff:
                self.textbox.do_command(EditField.LARROW_CHAR)
            elif self.masked:
                self.textbox.do_command(self.masked_char)
            else:
                self.textbox.do_command(ord(char))
            self.text.append(char)
        self.no_ut_refresh()

    def handle_input(self, input_key):
        '''
        For each keystroke, determine if it's a special character (and needs
        to end editing), printable character, or backspace.

        For special characters, send the return the done-editing code (CTRL-G),
        and store the special character for processing by parent window
        objects.

        If printable, append it to self.text, and try to validate. If
        validation fails, reject the character.

        '''
        input_key = self.translate_input(input_key)
        if self.is_special_char(input_key):
            self.input_key = input_key
            return EditField.CMD_DONE_EDIT
        else:
            self.input_key = None
        if isprint(input_key) or (ismeta(input_key)
                                  and input_key < curses.KEY_MIN):
            # isprint: ASCII characters
            # ismeta and < curses.KEY_MIN: Remaining UTF-8 characters
            # > curses.KEY_MIN: Special key such as down arrow, backspace, etc.
            self.text.append(unichr(input_key))
            if not self.is_valid():
                if len(self.text) > 0:
                    self.text.pop()
                return None
            self._modified = True
            if self.masked:
                input_key = self.masked_char
        elif input_key == curses.KEY_BACKSPACE:
            if len(self.text) > 0:
                del_char = self.text.pop()
                self._modified = True
                del_width = charwidth(del_char)
                if textwidth(self.get_text()) >= self.area.columns:
                    self.scroll(columns=-del_width)
            self.is_valid()
            # Run self.is_valid here so that any functional side effects can
            # occur, but don't check the return value (removing a character
            # from a valid string should never be invalid, and even if it were,
            # it would not make sense to add the deleted character back in)

        return input_key

    def edit_loop(self):
        '''Loop for handling characters in an edit field.
        Called by EditField.process().

        '''
        input_key = None
        while input_key != EditField.CMD_DONE_EDIT:
            input_key = self.handle_input(self.getch())
            self._set_text(self.get_text())
            curses.doupdate()

    def process(self, input_key):
        '''Process a keystroke. For an EditField, this means preparing the
        textpad for processing and passing the input_key in.

        Try to enable the blinking cursor (if it was disabled) before
        editing begins, so the user can see where they're typing. Once
        finished, restore the cursor state to its previous condition.

        After editing, return self.input_key, which will either be None
        (indicating all keystrokes were processed by the Textbox) or
        a special character (such as F2) which caused EditField.handle_input
        to stop processing text through the Textbox.

        '''
        try:
            curses.curs_set(2)
        except curses.error:
            terminalui.LOGGER.debug("Got curses.error when enabling cursor")

        if input_key is not None and not self.is_special_char(input_key):
            # Put input_key back on stack so that textbox.edit can read it
            curses.ungetch(input_key)
            if self.numeric_pad is not None:
                self._set_text(self.get_text().lstrip(self.numeric_pad),
                               do_pad=False)

            if self.clear_on_enter:
                self.clear_text()
            else:
                # Move to end of previous input.
                self.textbox.do_command(EditField.CMD_MV_EOL)
            self.edit_loop()
            return_key = self.input_key
            if self.numeric_pad is not None:
                self._set_text(self.get_text())
        else:
            return_key = input_key
        terminalui.LOGGER.debug("Returning: %s", return_key)
        return return_key

    def get_text(self):
        '''Join the array of characters as a unicode string'''
        return u"".join(self.text)

    def is_special_char(self, input_key):
        '''Check to see if this is a keystroke that should break us out of
        adding input to the Textbox and return control to the parent window

        '''
        if (input_key in range(curses.KEY_F0, curses.KEY_F10)
                or input_key in self.key_dict):
            return True
        else:
            return False

    def is_valid(self):
        '''Check to see if the text we have is valid or not.
        First check the length to make sure it fits in the space alloted.
        If it doesn't, flag an error indicating the maximum supported
        length for the field.

        Then, if this EditField has a validate function, call it (passing
        in any validate_kwargs we have).

        If validate raises an exception, display the error (if we have a
        handle to an ErrorWindow) and return False.

        '''
        win_size_x = self.window.getmaxyx()[1]
        if len(self.get_text().lstrip(self.numeric_pad)) >= win_size_x:
            if self.error_win is not None:
                self.error_win.display_err(
                    _("Max supported field length: %s") % (win_size_x - 1))
            return False
        elif callable(self.validate):
            try:
                self.validate(self, **self.validate_kwargs)
            except UIMessage as error_str:
                if self.error_win is not None:
                    self.error_win.display_err(unicode(error_str))
                return False
        if self.error_win is not None and self.error_win.visible:
            self.error_win.clear_err()
        return True

    def run_on_exit(self):
        '''Fire the on_exit function, if there is one. If an error occurs,
        and this EditField has an error_win, display it there.

        '''
        if callable(self.on_exit):
            try:
                self.on_exit(self, **self.on_exit_kwargs)
                if self.error_win is not None and self.error_win.visible:
                    self.error_win.clear_err()
                return True
            except UIMessage as error_str:
                if self.error_win is not None:
                    self.error_win.display_err(unicode(error_str))
                return False
        return True

    def make_active(self):
        '''Enable the cursor when activating this field'''
        super(EditField, self).make_active()
        try:
            curses.curs_set(2)
        except curses.error:
            terminalui.LOGGER.debug("Got curses.error when enabling cursor")

    def make_inactive(self):
        '''Fire the on_exit function before leaving the field and making
        it inactive.

        '''
        self.run_on_exit()
        try:
            curses.curs_set(0)
        except curses.error:
            terminalui.LOGGER.debug("Got curses.error when reverting cursor")
        super(EditField, self).make_inactive()

    def _reset_text(self):
        '''Resets the text area, either to permanently remove the text,
        or as part of the process of redrawing with additional text
        (during self._set_text())

        '''
        # Move cursor to left side of window
        self.textbox.do_command(EditField.CMD_MV_BOL)
        # Clear from cursor to end of line
        self.textbox.do_command(EditField.CMD_CLR_LINE)
        self.text = []
        self.no_ut_refresh()

    def clear_text(self):
        '''Issue the commands to textbox to clear itself, reset self.text
        to an empty array, and reset horizontal scrolling.

        '''
        self._modified = True
        self.scroll(scroll_to_column=0)
        self._reset_text()

    def get_cursor_loc(self):
        '''Cursor should be positioned at the end of the entered text'''
        win_loc = self.window.getbegyx()
        x_loc = win_loc[1]
        if not self.clear_on_enter:
            x_loc += min(textwidth(self.get_text()), self.area.columns)
        return (win_loc[0], x_loc)
Example #19
0
 def do_delete_line(self):
     y, x = self._win.getyx()
     self._win.move(y, 0)
     Textbox.do_command(self, 11)
     del TextSave.text[y]
     log("delete")
Example #20
0
    def inter_loop(self):
        start_y, start_x = self.scrs['text'].getyx()

        pad = Textbox(self.scrs['text'])
        pad.stripspaces = True

        msg = ['Welcome to CBTM Client.','List of accepted commands(separate '+\
        'the commands and the arguments with ","):']
        for k, v in self.commands.items():
            msg.append('%s -- %s' % (k, v))
        data = ''
        while True:
            self.show_response(msg)
            msg = []
            #Get user input
            while True:
                ch = self.scrs['text'].getch()

                if ch in self.pr:
                    self.scrs['text'].echochar(ch)
                else:
                    if ch == ord('\n'):
                        data = self.get_text(pad)
                        break
                    elif ch == 14:
                        continue
                    elif ch == 16:
                        self.show_text(data)
                        self.scrs['text'].move(1, 1 + len(data))
                    elif ch == 263:
                        tmp = self.get_text(pad)[:-1]
                        self.show_text(tmp)
                        self.scrs['text'].move(1, 1 + len(tmp))
                    else:
                        # self.show_text(str(ch))
                        pad.do_command(ch)
                        y, x = self.scrs['text'].getyx()
                        if x < start_x:
                            self.text_reset()

            #Separate the text entered by the user on commas
            args = data.split(',')
            try:
                #Get the first argument (Code for command)
                code = args.pop(0)
            except IndexError:
                code = -1

            #Interprets the command
            try:
                command = self.commands[code]
            except KeyError:
                msg.append('Code not recognized: ' + code)
                msg.append('Try:')
                for k, v in self.commands.items():
                    msg.append('%s -- %s' % (k, v))
                continue

            if command == 'quit':
                break
            elif command == 'set_port':
                #Execute set port
                self.port = int(args[0])
                msg.append('Port for CBTM communication set to %d' % self.port)
                continue
            elif command == 'set_host':
                #Execute set host
                self.host = str(args[0])
                msg.append('CBTM host set to %s' % self.host)
                continue
            elif code in ['rl', 'us', 'su', 'sdu', 'rbu']:
                # Checks for number of arguments
                if len(args) != 1:
                    msg.append('The requested command needs one argument.')
                    continue
            elif code in ['ru']:
                # Checks for number of arguments
                if len(args) != 4:
                    msg.append('The requested command needs 4 arguments.')
                    continue

            msg.append('Message from CBTM:')
            try:
                # Send command to CBTM Server
                rp = self.send(self.structure(command, args))
                msg.extend(rp.split('\n'))
            except socket.error:
                msg = [
                    'No server active on port %d' % self.port,
                ]
Example #21
0
class InputWindow(InnerWindow):
    '''InputWindow represent editable text areas on the screen
    
    At any time, the text of the object can be accessed by
    referencing an InputWindow's 'text' parameter (edit_field.text).
    Note that this returns a list of character codes.
    '''


    def __init__(self, area, window = None, text = "", masked = False, 
                 color_theme = None, at_index = None):
        '''Unlike InnerWindow, this constructor WILL NOT TAKE
        curses.window objects for the window parameter - they must
        be InnerWindows.
        
        In general, specifying a specific color_theme is unnecessary, unless
        this instance requires a theme other than that of the rest of the
        program 
        
        window (required): A parent InnerWindow
        
        area (required): Describes the area within the parent window to be used.
        area.lines is overridden to 1.
        
        text (optional): The text in this field, before it is edited by
        the user. Defaults to empty string.
        
        masked (optional): If True, then this field will display bullets when
        the user types into it, instead of echo'ing the text
        
        color_theme (optional): The color_theme for this edit field. By default
        the parent window's theme is used. color_theme.edit_field is used
        as the background color; color_theme.highlight_edit is the background
        when this InputWindow is active.
        '''
        if color_theme is None:
            color_theme = window.color_theme
        color = color_theme.edit_field
        highlight_color = color_theme.highlight_edit
        
        area.lines = 1
        super(InputWindow, self).__init__(area, window, color_theme, 
                color, highlight_color, at_index)
        self.masked = masked
        self.masked_char = ord('*')
        self.old_cursor_state = 0

        self.textbox = Textbox(self.window)
        self.textbox.stripspaces = True
        self.set_text(text)

        self.text_buff = []
    
    def set_text(self, text):
        self.text = []
        for char in text:
            self.textbox.do_command(self.handle_input(ord(char)))
        self.no_ut_refresh()
    
    def set_repo(self, text):
        self.text = []
        self.textbox.do_command(ord(ctrl('a')))
        self.textbox.do_command(ord(ctrl('k')))
        for char in text:
            self.textbox.do_command(self.handle_input(ord(char)))
        self.no_ut_refresh()

    def handle_input(self, input):
        input = self.translate_input(input)
        if input in range(curses.KEY_F0, curses.KEY_F10) or \
                input == curses.KEY_UP or input == curses.KEY_DOWN:
            logging.debug("Got special key, breaking")
            self.input = input
            return ord(ctrl('g'))
        else:
            self.input = None
        if input is not None and isprint(input):
            self.text_buff.append(chr(input))
            self.text.append(input)
            if not self.is_valid():
                if len(self.text) > 0:
                    self.text.pop()
                    length = len(self.text_buff)
                    temp_buff = self.text_buff[:length-49]
                    self.set_repo(self.text_buff[length-49:])
                    length = len(self.text_buff)
                    self.text_buff = temp_buff + self.text_buff[length-49:]
            if self.masked:
                input = self.masked_char
        elif input == curses.KEY_BACKSPACE or input == ord(ctrl('H')):
            if len(self.text) > 0:
                self.text.pop()
                self.text_buff.pop()
      
        return input
    
    def process(self, input):
        try:
            self.old_cursor_state = curses.curs_set(2)
        except curses.error:
            logging.debug("Got curses.error when enabling cursor")
        
        # Put input back on stack so that textbox.edit can read it
        try:
            curses.ungetch(input)
        except TypeError:
            pass

        self.textbox.do_command(ord(ctrl('e')))
        self.textbox.edit(self.handle_input)
        
        try:
            self.old_cursor_state = curses.curs_set(self.old_cursor_state)
        except curses.error:
            logging.debug("Got curses.error when reverting cursor")
        logging.debug("Returning: " + str(self.input))
        return self.input
    
    def is_special_char(self, input_key):
        '''Check to see if this is a keystroke that should break us out of
        adding input to the Textbox and return control to the parent window
        
        '''
        if (input_key in range(curses.KEY_F0, curses.KEY_F10) or
            input_key in self.key_dict):
            return True
        else:
            return False
        
    def is_valid(self):
        win_size_x = self.window.getmaxyx()[1]
        return len(self.text) < win_size_x
Example #22
0
class cursesWindow():
    __shared_state = {}

    def __init__(self, pad = None):
        self.__dict__ = self.__shared_state

        if not pad:
            pad = cursesPad()
        self.pad = pad

        self.screen = ''
        self.window = ''
        self.textbox = ''
        self.size = os.popen('stty size', 'r').read().split()

    def initialize(self):
        self.initializeWindow()
        self.initializeColorScheme()

    def initializeWindow(self):
        self.screen = curses.initscr()
        curses.start_color()
        curses.use_default_colors()
        curses.noecho()
        curses.cbreak()
        self.screen.keypad(1)
        self.setCursorVisibility(0)
        self.window = curses.newwin(int(self.size[0]), int(self.size[1]), 0, 0)
        signal.signal(signal.SIGWINCH, self.windowSizeChangeEventHandler)

    def initializeColorScheme(self):
        curses.init_pair(2, curses.COLOR_RED, -1)
        curses.init_pair(3, curses.COLOR_GREEN, -1)
        curses.init_pair(4, curses.COLOR_YELLOW, -1)
        curses.init_pair(5, curses.COLOR_BLUE, -1)
        curses.init_pair(6, curses.COLOR_MAGENTA, -1)
        curses.init_pair(7, curses.COLOR_CYAN, -1)
        curses.init_pair(8, curses.COLOR_WHITE, -1)

    def windowSizeChangeEventHandler(self, n, frame):
        self.size = os.popen('stty size', 'r').read().split()

    def setCursorVisibility(self, visible):
        try:
            curses.curs_set(visible)
        except:
            pass

    def getUserInput(self):
        return self.screen.getch()

    def close(self):
        self.setCursorVisibility(1)
        curses.nocbreak()
        self.screen.keypad(0)
        curses.echo()
        curses.endwin()

    def getInput(self, edit_window):
        self.setCursorVisibility(1)
        value = curses.textpad.Textbox(edit_window, insert_mode = True).edit().strip()
        self.setCursorVisibility(0)
        return value

    def wait(self, delay):
        self.screen.timeout(delay)

    def refresh(self):
        try:
            self.screen.noutrefresh()
        except:
            pass

    def update(self):
        curses.doupdate()

    def clear(self):
        self.screen.clear()
        self.pad.clear()

    # Drawing
    def addStr(self, string, color = ''):
        try:
            if not color:
                self.screen.addstr(string)
            else:
                self.screen.addstr(string, curses.color_pair(color))
        except:
            pass

    def addString(self, posX, posY, string, color = ''):
        try:
            if not color:
                self.screen.addstr(posY, posX, string)
            else:
                self.screen.addstr(posY, posX, string, curses.color_pair(color))
        except:
            pass

    def draw(self, screen):
        if not screen:
            return

        if len(screen[0]) == 4:
            for (y, x, line, color) in screen:
                self.addString(y, x, line, color)
        elif len(screen[0]) == 2:
            for (line, color) in screen:
                self.addStr(line, color)

    def editTextOnScreen(self, line_index, line_contents):
        editwin = self.window.subwin(1, int(self.size[1]) - 33, line_index, 7)
        editwin.addstr(0, 0, ' ' * (int(self.size[1]) - 34))
        editwin.addstr(0, 0, line_contents)
        return self.editTextField(editwin)

    def editTextField(self, editwin):
        self.setCursorVisibility(1)
        self.textbox = Textbox(editwin, insert_mode = True)
        newname = self.textbox.edit(self.deleteKeyPressHandler).strip()
        self.textbox = ''
        self.setCursorVisibility(0)
        return newname

    def deleteKeyPressHandler(self, keyPressed):
        if keyPressed in (330, 263, 127):  # Delete
            self.textbox.do_command(curses.KEY_BACKSPACE)
        else:
            return keyPressed

        return