Example #1
0
    def __init__(self, text=None, attr=None, cs=None, 
        cursor=None, maxcol=None, check_width=True):
        """
        text -- list of strings, one for each line
        attr -- list of run length encoded attributes for text
        cs -- list of run length encoded character set for text
        cursor -- (x,y) of cursor or None
        maxcol -- screen columns taken by this canvas
        check_width -- check and fix width of all lines in text
        """
        Canvas.__init__(self)
        if text == None: 
            text = []

        if check_width:
            widths = []
            for t in text:
                if type(t) != type(""):
                    raise CanvasError("Canvas text must be plain strings encoded in the screen's encoding", `text`)
                widths.append( calc_width( t, 0, len(t)) )
        else:
            assert type(maxcol) == type(0)
            widths = [maxcol] * len(text)

        if maxcol is None:
            if widths:
                # find maxcol ourselves
                maxcol = max(widths)
            else:
                maxcol = 0

        if attr == None: 
            attr = [[] for x in range(len(text))]
        if cs == None:
            cs = [[] for x in range(len(text))]
        
        # pad text and attr to maxcol
        for i in range(len(text)):
            w = widths[i]
            if w > maxcol: 
                raise CanvasError("Canvas text is wider than the maxcol specified \n%s\n%s\n%s"%(`maxcol`,`widths`,`text`))
            if w < maxcol:
                text[i] = text[i] + " "*(maxcol-w)
            a_gap = len(text[i]) - rle_len( attr[i] )
            if a_gap < 0:
                raise CanvasError("Attribute extends beyond text \n%s\n%s" % (`text[i]`,`attr[i]`) )
            if a_gap:
                rle_append_modify( attr[i], (None, a_gap))
            
            cs_gap = len(text[i]) - rle_len( cs[i] )
            if cs_gap < 0:
                raise CanvasError("Character Set extends beyond text \n%s\n%s" % (`text[i]`,`cs[i]`) )
            if cs_gap:
                rle_append_modify( cs[i], (None, cs_gap))
            
        self._attr = attr
        self._cs = cs
        self.cursor = cursor
        self._text = text
        self._maxcol = maxcol
Example #2
0
def calc_coords( text, layout, pos, clamp=1 ):
    """
    Calculate the coordinates closest to position pos in text with layout.
    
    text -- raw string or unicode string
    layout -- layout structure applied to text
    pos -- integer position into text
    clamp -- ignored right now
    """
    closest = None
    y = 0
    for line_layout in layout:
        x = 0
        for seg in line_layout:
            s = LayoutSegment(seg)
            if s.offs is None:
                x += s.sc
                continue
            if s.offs == pos:
                return x,y
            if s.end is not None and s.offs<=pos and s.end>pos:
                x += calc_width( text, s.offs, pos )
                return x,y
            distance = abs(s.offs - pos)
            if s.end is not None and s.end<pos:
                distance = pos - (s.end-1)
            if closest is None or distance < closest[0]:
                closest = distance, (x,y)
            x += s.sc
        y += 1
    
    if closest:
        return closest[1]
    return 0,0
Example #3
0
 def _last_row(self, row):
     """On the last row we need to slide the bottom right character
     into place. Calculate the new line, attr and an insert sequence
     to do that.
     
     eg. last row:
     XXXXXXXXXXXXXXXXXXXXYZ
     
     Y will be drawn after Z, shifting Z into position.
     """
     
     new_row = row[:-1]
     z_attr, z_cs, last_text = row[-1]
     last_cols = util.calc_width(last_text, 0, len(last_text))
     last_offs, z_col = util.calc_text_pos(last_text, 0, 
         len(last_text), last_cols-1)
     if last_offs == 0:
         z_text = last_text
         del new_row[-1]
         # we need another segment
         y_attr, y_cs, nlast_text = row[-2]
         nlast_cols = util.calc_width(nlast_text, 0, 
             len(nlast_text))
         z_col += nlast_cols
         nlast_offs, y_col = util.calc_text_pos(nlast_text, 0,
             len(nlast_text), nlast_cols-1)
         y_text = nlast_text[nlast_offs:]
         if nlast_offs:
             new_row.append((y_attr, y_cs, 
                 nlast_text[:nlast_offs]))
     else:
         z_text = last_text[last_offs:]
         y_attr, y_cs = z_attr, z_cs
         nlast_cols = util.calc_width(last_text, 0,
             last_offs)
         nlast_offs, y_col = util.calc_text_pos(last_text, 0,
             last_offs, nlast_cols-1)
         y_text = last_text[nlast_offs:last_offs]
         if nlast_offs:
             new_row.append((y_attr, y_cs,
                 last_text[:nlast_offs]))
     
     new_row.append((z_attr, z_cs, z_text))
     return new_row, z_col-y_col, (y_attr, y_cs, y_text)
Example #4
0
    def pack(self, size=None, focus=False):
        """
        Return the number of screen columns and rows required for
        this Text widget to be displayed without wrapping or 
        clipping, as a single element tuple.

        size -- None for unlimited screen columns or (maxcol,) to
                specify a maximum column size

        >>> Text("important things").pack()
        (16, 1)
        >>> Text("important things").pack((15,))
        (9, 2)
        >>> Text("important things").pack((8,))
        (8, 2)
        """
        text, attr = self.get_text()
        
        if size is not None:
            (maxcol,) = size
            if not hasattr(self.layout, "pack"):
                return size
            trans = self.get_line_translation( maxcol, (text,attr))
            cols = self.layout.pack( maxcol, trans )
            return (cols, len(trans))
    
        i = 0
        cols = 0
        while i < len(text):
            j = text.find('\n', i)
            if j == -1:
                j = len(text)
            c = calc_width(text, i, j)
            if c>cols:
                cols = c
            i = j+1
        return (cols, text.count('\n') + 1)
Example #5
0
                if y in old_line_numbers:
                    old_line = y
                else:
                    old_line = old_line_numbers[0]
                send( "<%d\n"%old_line )
                continue
            
            col = 0
            for (a, cs, run) in row:
                run = run.translate(_trans_table)
                if a is None:
                    fg,bg,mono = "black", "light gray", None
                else:
                    fg,bg,mono = self.palette[a]
                if y == cy and col <= cx:
                    run_width = util.calc_width(run, 0, 
                        len(run))
                    if col+run_width > cx:
                        l.append(code_span(run, fg, bg,
                            cx-col))
                    else:
                        l.append(code_span(run, fg, bg))
                    col += run_width
                else:
                    l.append(code_span(run, fg, bg))

            send("".join(l)+"\n")
        self.last_screen = new_screen
        self.last_screen_width = cols
        
        if self.update_method == "polling":
            sys.stdout.write("".join(sendq))
Example #6
0
    def calculate_text_segments( self, text, width, wrap ):
        """
        Calculate the segments of text to display given width screen 
        columns to display them.  
        
        text - text to display
        width - number of available screen columns
        wrap - wrapping mode used
        
        Returns a layout structure without aligmnent applied.
        """
        b = []
        p = 0
        if wrap == 'clip':
            # no wrapping to calculate, so it's easy.
            while p<=len(text):
                n_cr = text.find("\n", p)
                if n_cr == -1: 
                    n_cr = len(text)
                sc = calc_width(text, p, n_cr)
                l = [(0,n_cr)]
                if p!=n_cr:
                    l = [(sc, p, n_cr)] + l
                b.append(l)
                p = n_cr+1
            return b

        
        while p<=len(text):
            # look for next eligible line break
            n_cr = text.find("\n", p)
            if n_cr == -1: 
                n_cr = len(text)
            sc = calc_width(text, p, n_cr)
            if sc == 0:
                # removed character hint
                b.append([(0,n_cr)])
                p = n_cr+1
                continue
            if sc <= width:
                # this segment fits
                b.append([(sc,p,n_cr),
                    # removed character hint
                    (0,n_cr)])
                
                p = n_cr+1
                continue
            pos, sc = calc_text_pos( text, p, n_cr, width )
            # FIXME: handle pathological width=1 double-byte case
            if wrap == 'any':
                b.append([(sc,p,pos)])
                p = pos
                continue
            assert wrap == 'space'
            if text[pos] == " ":
                # perfect space wrap
                b.append([(sc,p,pos),
                    # removed character hint
                    (0,pos)])
                p = pos+1
                continue
            if is_wide_char(text, pos):
                # perfect next wide
                b.append([(sc,p,pos)])
                p = pos
                continue
            prev = pos    
            while prev > p:
                prev = move_prev_char(text, p, prev)
                if text[prev] == " ":
                    sc = calc_width(text,p,prev)
                    l = [(0,prev)]
                    if p!=prev:
                        l = [(sc,p,prev)] + l
                    b.append(l)
                    p = prev+1 
                    break
                if is_wide_char(text,prev):
                    # wrap after wide char
                    next = move_next_char(text, prev, pos)
                    sc = calc_width(text,p,next)
                    b.append([(sc,p,next)])
                    p = next
                    break
            else:
                # unwrap previous line space if possible to
                # fit more text (we're breaking a word anyway)
                if b and (len(b[-1]) == 2 or ( len(b[-1])==1 
                        and len(b[-1][0])==2 )):
                    # look for removed space above
                    if len(b[-1]) == 1:
                        [(h_sc, h_off)] = b[-1]
                        p_sc = 0
                        p_off = p_end = h_off
                    else:
                        [(p_sc, p_off, p_end),
                               (h_sc, h_off)] = b[-1]
                    if (p_sc < width and h_sc==0 and
                        text[h_off] == " "):
                        # combine with previous line
                        del b[-1]
                        p = p_off
                        pos, sc = calc_text_pos( 
                            text, p, n_cr, width )
                        b.append([(sc,p,pos)])
                        # check for trailing " " or "\n"
                        p = pos
                        if p < len(text) and (
                            text[p] in (" ","\n")):
                            # removed character hint
                            b[-1].append((0,p))
                            p += 1
                        continue
                        
                        
                # force any char wrap
                b.append([(sc,p,pos)])
                p = pos
        return b
Example #7
0
class HtmlGenerator:
    # class variables
    fragments = []
    sizes = []
    keys = []
    started = True

    def __init__(self):
        self.palette = {}
        self.has_color = True

    def register_palette(self, l):
        """Register a list of palette entries.

		l -- list of (name, foreground, background) or
		     (name, same_as_other_name) palette entries.

		calls self.register_palette_entry for each item in l
		"""

        for item in l:
            if len(item) in (3, 4):
                self.register_palette_entry(*item)
                continue
            assert len(item) == 2, "Invalid register_palette usage"
            name, like_name = item
            if not self.palette.has_key(like_name):
                raise Exception("palette entry '%s' doesn't exist" % like_name)
            self.palette[name] = self.palette[like_name]

    def register_palette_entry(self, name, foreground, background, mono=None):
        """Register a single palette entry.

		name -- new entry/attribute name
		foreground -- foreground colour
		background -- background colour
		mono -- monochrome terminal attribute

		See curses_display.register_palette_entry for more info.
		"""
        if foreground == "default":
            foreground = "black"
        if background == "default":
            background = "light gray"
        self.palette[name] = (foreground, background, mono)

    def set_mouse_tracking(self):
        """Not yet implemented"""
        pass

    def start(self):
        pass

    def stop(self):
        pass

    def run_wrapper(self, fn):
        """Call fn."""
        return fn()

    def draw_screen(self, (cols, rows), r):
        """Create an html fragment from the render object. 
		Append it to HtmlGenerator.fragments list.
		"""
        # collect output in l
        l = []

        assert r.rows() == rows

        if r.cursor is not None:
            cx, cy = r.cursor
        else:
            cx = cy = None

        y = -1
        for row in r.content():
            y += 1
            col = 0

            for a, cs, run in row:
                run = run.translate(_trans_table)
                if a is None:
                    fg, bg, mono = "black", "light gray", None
                else:
                    fg, bg, mono = self.palette[a]
                if y == cy and col <= cx:
                    run_width = util.calc_width(run, 0, len(run))
                    if col + run_width > cx:
                        l.append(html_span(run, fg, bg, cx - col))
                    else:
                        l.append(html_span(run, fg, bg))
                    col += run_width
                else:
                    l.append(html_span(run, fg, bg))

            l.append("\n")

        # add the fragment to the list
        self.fragments.append("<pre>%s</pre>" % "".join(l))
Example #8
0
class HtmlGenerator(BaseScreen):
    # class variables
    fragments = []
    sizes = []
    keys = []
    started = True

    def __init__(self):
        super(HtmlGenerator, self).__init__()
        self.colors = 16
        self.bright_is_bold = False  # ignored
        self.has_underline = True  # ignored
        self.register_palette_entry(None, _default_foreground,
                                    _default_background)

    def set_terminal_properties(self,
                                colors=None,
                                bright_is_bold=None,
                                has_underline=None):

        if colors is None:
            colors = self.colors
        if bright_is_bold is None:
            bright_is_bold = self.bright_is_bold
        if has_underline is None:
            has_unerline = self.has_underline

        self.colors = colors
        self.bright_is_bold = bright_is_bold
        self.has_underline = has_underline

    def set_mouse_tracking(self):
        """Not yet implemented"""
        pass

    def start(self):
        pass

    def stop(self):
        pass

    def set_input_timeouts(self, *args):
        pass

    def reset_default_terminal_palette(self, *args):
        pass

    def run_wrapper(self, fn):
        """Call fn."""
        return fn()

    def draw_screen(self, (cols, rows), r):
        """Create an html fragment from the render object. 
        Append it to HtmlGenerator.fragments list.
        """
        # collect output in l
        l = []

        assert r.rows() == rows

        if r.cursor is not None:
            cx, cy = r.cursor
        else:
            cx = cy = None

        y = -1
        for row in r.content():
            y += 1
            col = 0

            for a, cs, run in row:
                run = run.translate(_trans_table)
                if isinstance(a, AttrSpec):
                    aspec = a
                else:
                    aspec = self._palette[a][{
                        1: 1,
                        16: 0,
                        88: 2,
                        256: 3
                    }[self.colors]]

                if y == cy and col <= cx:
                    run_width = util.calc_width(run, 0, len(run))
                    if col + run_width > cx:
                        l.append(html_span(run, aspec, cx - col))
                    else:
                        l.append(html_span(run, aspec))
                    col += run_width
                else:
                    l.append(html_span(run, aspec))

            l.append("\n")

        # add the fragment to the list
        self.fragments.append("<pre>%s</pre>" % "".join(l))