def calc_pos(text, layout, pref_col, row): """ Calculate the closest linear position to pref_col and row given a layout structure. """ if row < 0 or row >= len(layout): raise Exception("calculate_pos: out of layout row range") pos = calc_line_pos(text, layout[row], pref_col) if pos is not None: return pos rows_above = list(xrange(row - 1, -1, -1)) rows_below = list(xrange(row + 1, len(layout))) while rows_above and rows_below: if rows_above: r = rows_above.pop(0) pos = calc_line_pos(text, layout[r], pref_col) if pos is not None: return pos if rows_below: r = rows_below.pop(0) pos = calc_line_pos(text, layout[r], pref_col) if pos is not None: return pos return 0
def erase(self, start, end): """ Erase a region of the terminal. The 'start' tuple (x, y) defines the starting position of the erase, while end (x, y) the last position. For example if the terminal size is 4x3, start=(1, 1) and end=(1, 2) would erase the following region: .... .XXX XX.. """ sx, sy = self.constrain_coords(*start) ex, ey = self.constrain_coords(*end) # within a single row if sy == ey: for x in xrange(sx, ex + 1): self.term[sy][x] = self.empty_char() return # spans multiple rows y = sy while y <= ey: if y == sy: for x in xrange(sx, self.width): self.term[y][x] = self.empty_char() elif y == ey: for x in xrange(ex + 1): self.term[y][x] = self.empty_char() else: self.blank_line(y) y += 1
def erase(self, start, end): """ Erase a region of the terminal. The 'start' tuple (x, y) defines the starting position of the erase, while end (x, y) the last position. For example if the terminal size is 4x3, start=(1, 1) and end=(1, 2) would erase the following region: .... .XXX XX.. """ sx, sy = self.constrain_coords(*start) ex, ey = self.constrain_coords(*end) # within a single row if sy == ey: for x in xrange(sx, ex + 1): self.term[sy][x] = self.empty_char() return # spans multiple rows y = sy while y <= ey: if y == sy: for x in xrange(sx, self.width): self.term[y][x] = self.empty_char() elif y == ey: for x in xrange(ex + 1): self.term[y][x] = self.empty_char() else: self.blank_line(y) y += 1
def calc_pos( text, layout, pref_col, row ): """ Calculate the closest linear position to pref_col and row given a layout structure. """ if row < 0 or row >= len(layout): raise Exception("calculate_pos: out of layout row range") pos = calc_line_pos( text, layout[row], pref_col ) if pos is not None: return pos rows_above = list(xrange(row-1,-1,-1)) rows_below = list(xrange(row+1,len(layout))) while rows_above and rows_below: if rows_above: r = rows_above.pop(0) pos = calc_line_pos(text, layout[r], pref_col) if pos is not None: return pos if rows_below: r = rows_below.pop(0) pos = calc_line_pos(text, layout[r], pref_col) if pos is not None: return pos return 0
def _adjust_focus_on_contents_modified(self, slc, new_items=()): """ Default behaviour is to move the focus to the item following any removed items, unless that item was simply replaced. Failing that choose the last item in the list. returns focus position for after change is applied """ num_new_items = len(new_items) start, stop, step = indices = slc.indices(len(self)) num_removed = len(list(xrange(*indices))) focus = self._validate_contents_modified(indices, new_items) if focus is not None: return focus focus = self._focus if step == 1: if start + num_new_items <= focus < stop: focus = stop # adjust for added/removed items if stop <= focus: focus += num_new_items - (stop - start) else: if not num_new_items: # extended slice being removed if focus in xrange(start, stop, step): focus += 1 # adjust for removed items focus -= len(list(xrange(start, min(focus, stop), step))) return min(focus, len(self) + num_new_items - num_removed -1)
def _adjust_focus_on_contents_modified(self, slc, new_items=()): """ Default behaviour is to move the focus to the item following any removed items, unless that item was simply replaced. Failing that choose the last item in the list. returns focus position for after change is applied """ num_new_items = len(new_items) start, stop, step = indices = slc.indices(len(self)) num_removed = len(list(xrange(*indices))) focus = self._validate_contents_modified(indices, new_items) if focus is not None: return focus focus = self._focus if step == 1: if start + num_new_items <= focus < stop: focus = stop # adjust for added/removed items if stop <= focus: focus += num_new_items - (stop - start) else: if not num_new_items: # extended slice being removed if focus in xrange(start, stop, step): focus += 1 # adjust for removed items focus -= len(list(xrange(start, min(focus, stop), step))) return min(focus, len(self) + num_new_items - num_removed - 1)
def reverse_video(self, undo=False): """ Reverse video/scanmode (DECSCNM) by swapping fg and bg colors. """ for y in xrange(self.height): for x in xrange(self.width): char = self.term[y][x] attrs = self.reverse_attrspec(char[0], undo=undo) self.term[y][x] = (attrs, ) + char[1:]
def reverse_video(self, undo=False): """ Reverse video/scanmode (DECSCNM) by swapping fg and bg colors. """ for y in xrange(self.height): for x in xrange(self.width): char = self.term[y][x] attrs = self.reverse_attrspec(char[0], undo=undo) self.term[y][x] = (attrs,) + char[1:]
def resize(self, width, height): """ Resize the terminal to the given width and height. """ x, y = self.term_cursor if width > self.width: # grow for y in xrange(self.height): self.term[y] += [self.empty_char()] * (width - self.width) elif width < self.width: # shrink for y in xrange(self.height): self.term[y] = self.term[y][:width] self.width = width if height > self.height: # grow for y in xrange(self.height, height): try: last_line = self.scrollback_buffer.pop() except IndexError: # nothing in scrollback buffer, append an empty line self.term.append(self.empty_line()) self.scrollregion_end += 1 continue # adjust x axis of scrollback buffer to the current width if len(last_line) < self.width: last_line += [self.empty_char()] * \ (self.width - len(last_line)) else: last_line = last_line[:self.width] y += 1 self.term.insert(0, last_line) elif height < self.height: # shrink for y in xrange(height, self.height): self.scrollback_buffer.append(self.term.pop(0)) self.height = height self.reset_scroll() x, y = self.constrain_coords(x, y) self.set_term_cursor(x, y) # extend tabs self.init_tabstops(extend=True)
def resize(self, width, height): """ Resize the terminal to the given width and height. """ x, y = self.term_cursor if width > self.width: # grow for y in xrange(self.height): self.term[y] += [self.empty_char()] * (width - self.width) elif width < self.width: # shrink for y in xrange(self.height): self.term[y] = self.term[y][:width] self.width = width if height > self.height: # grow for y in xrange(self.height, height): try: last_line = self.scrollback_buffer.pop() except IndexError: # nothing in scrollback buffer, append an empty line self.term.append(self.empty_line()) self.scrollregion_end += 1 continue # adjust x axis of scrollback buffer to the current width if len(last_line) < self.width: last_line += [self.empty_char()] * \ (self.width - len(last_line)) else: last_line = last_line[:self.width] y += 1 self.term.insert(0, last_line) elif height < self.height: # shrink for y in xrange(height, self.height): self.scrollback_buffer.append(self.term.pop(0)) self.height = height self.reset_scroll() x, y = self.constrain_coords(x, y) self.set_term_cursor(x, y) # extend tabs self.init_tabstops(extend=True)
def parse_csi(self, char): """ Parse ECMA-48 CSI (Control Sequence Introducer) sequences. """ qmark = self.escbuf.startswith(B('?')) escbuf = [] for arg in self.escbuf[qmark and 1 or 0:].split(B(';')): try: num = int(arg) except ValueError: num = None escbuf.append(num) if CSI_COMMANDS[char] is not None: if CSI_COMMANDS[char][0] == 'alias': csi_cmd = CSI_COMMANDS[(CSI_COMMANDS[char][1])] else: csi_cmd = CSI_COMMANDS[char] number_of_args, default_value, cmd = csi_cmd while len(escbuf) < number_of_args: escbuf.append(default_value) for i in xrange(len(escbuf)): if escbuf[i] is None or escbuf[i] == 0: escbuf[i] = default_value try: cmd(self, escbuf, qmark) except ValueError: # ignore commands that don't match the # unpacked tuples in CSI_COMMANDS. pass
def parse_csi(self, char): """ Parse ECMA-48 CSI (Control Sequence Introducer) sequences. """ qmark = self.escbuf.startswith(B('?')) escbuf = [] for arg in self.escbuf[qmark and 1 or 0:].split(B(';')): try: num = int(arg) except ValueError: num = None escbuf.append(num) if CSI_COMMANDS[char] is not None: if CSI_COMMANDS[char][0] == 'alias': csi_cmd = CSI_COMMANDS[(CSI_COMMANDS[char][1])] else: csi_cmd = CSI_COMMANDS[char] number_of_args, default_value, cmd = csi_cmd while len(escbuf) < number_of_args: escbuf.append(default_value) for i in xrange(len(escbuf)): if escbuf[i] is None or escbuf[i] == 0: escbuf[i] = default_value try: cmd(self, escbuf, qmark) except ValueError: # ignore commands that don't match the # unpacked tuples in CSI_COMMANDS. pass
def _setup_colour_pairs(self): """ Initialize all 63 color pairs based on the term: bg * 8 + 7 - fg So to get a color, we just need to use that term and get the right color pair number. """ if not self.has_color: return for fg in xrange(8): for bg in xrange(8): # leave out white on black if fg == curses.COLOR_WHITE and \ bg == curses.COLOR_BLACK: continue curses.init_pair(bg * 8 + 7 - fg, fg, bg)
def _setup_colour_pairs(self): """ Initialize all 63 color pairs based on the term: bg * 8 + 7 - fg So to get a color, we just need to use that term and get the right color pair number. """ if not self.has_color: return for fg in xrange(8): for bg in xrange(8): # leave out white on black if fg == curses.COLOR_WHITE and \ bg == curses.COLOR_BLACK: continue curses.init_pair(bg * 8 + 7 - fg, fg, bg)
def clear(self, cursor=None): """ Clears the whole terminal screen and resets the cursor position to (0, 0) or to the coordinates given by 'cursor'. """ self.term = [self.empty_line() for x in xrange(self.height)] if cursor is None: self.set_term_cursor(0, 0) else: self.set_term_cursor(*cursor)
def clear(self, cursor=None): """ Clears the whole terminal screen and resets the cursor position to (0, 0) or to the coordinates given by 'cursor'. """ self.term = [self.empty_line() for x in xrange(self.height)] if cursor is None: self.set_term_cursor(0, 0) else: self.set_term_cursor(*cursor)
def set_tabstop(self, x=None, remove=False, clear=False): if clear: for tab in xrange(len(self.tabstops)): self.tabstops[tab] = 0 return if x is None: x = self.term_cursor[0] div, mod = divmod(x, 8) if remove: self.tabstops[div] &= ~(1 << mod) else: self.tabstops[div] |= (1 << mod)
def set_tabstop(self, x=None, remove=False, clear=False): if clear: for tab in xrange(len(self.tabstops)): self.tabstops[tab] = 0 return if x is None: x = self.term_cursor[0] div, mod = divmod(x, 8) if remove: self.tabstops[div] &= ~(1 << mod) else: self.tabstops[div] |= (1 << mod)
def decaln(self): """ DEC screen alignment test: Fill screen with E's. """ for row in xrange(self.height): self.term[row] = self.empty_line('E')
def decaln(self): """ DEC screen alignment test: Fill screen with E's. """ for row in xrange(self.height): self.term[row] = self.empty_line('E')
from __future__ import division, print_function import os import sys try: import termios except ImportError: pass # windows from urwid.util import StoppingContext, int_scale from urwid import signals from urwid.compat import B, bytes3, xrange, with_metaclass # for replacing unprintable bytes with '?' UNPRINTABLE_TRANS_TABLE = B("?") * 32 + bytes3(list(xrange(32, 256))) # signals sent by BaseScreen UPDATE_PALETTE_ENTRY = "update palette entry" INPUT_DESCRIPTORS_CHANGED = "input descriptors changed" # AttrSpec internal values _BASIC_START = 0 # first index of basic color aliases _CUBE_START = 16 # first index of color cube _CUBE_SIZE_256 = 6 # one side of the color cube _GRAY_SIZE_256 = 24 _GRAY_START_256 = _CUBE_SIZE_256**3 + _CUBE_START _CUBE_WHITE_256 = _GRAY_START_256 - 1 _CUBE_SIZE_88 = 4 _GRAY_SIZE_88 = 8 _GRAY_START_88 = _CUBE_SIZE_88**3 + _CUBE_START
from __future__ import division, print_function import os import sys try: import termios except ImportError: pass # windows from urwid.util import StoppingContext, int_scale from urwid import signals from urwid.compat import B, bytes3, xrange, with_metaclass # for replacing unprintable bytes with '?' UNPRINTABLE_TRANS_TABLE = B("?") * 32 + bytes3(list(xrange(32,256))) # signals sent by BaseScreen UPDATE_PALETTE_ENTRY = "update palette entry" INPUT_DESCRIPTORS_CHANGED = "input descriptors changed" # AttrSpec internal values _BASIC_START = 0 # first index of basic color aliases _CUBE_START = 16 # first index of color cube _CUBE_SIZE_256 = 6 # one side of the color cube _GRAY_SIZE_256 = 24 _GRAY_START_256 = _CUBE_SIZE_256 ** 3 + _CUBE_START _CUBE_WHITE_256 = _GRAY_START_256 -1 _CUBE_SIZE_88 = 4