Example #1
0
 def __init__(self, l=1, c=1):
     # Screen image
     self.lines = l
     self.columns = c
     self._image = [[DCA for _ in xrange(c)] for _ in xrange(l + 1)]
     self._line_wrapped = [False for _ in xrange(l + 1)]
     # History buffer
     self.hist_cursor = 0
     self._hist = HistoryScrollBuffer(1000)
     # Cursor location
     self._cu_x = 0
     self._cu_y = 0
     # Cursor color and rendition info
     self._cu_fg = 0
     self._cu_bg = 0
     self._cu_re = 0
     # Margins top / bottom
     self._margin_t = 0
     self._margin_b = 0
     # States
     self._curr_mode = [None, None, None, None, None, None]
     self._save_mode = [None, None, None, None, None, None]
     self.__tabStops = None
     # Effective color and rendition
     self._eff_fg = 0
     self._eff_bg = 0
     self._eff_re = 0
     # Save cursor location
     self.__saCuX = 0
     self.__saCuY = 0
     # save cursor color and rendition info
     self.__saCuFg = 0
     self.__saCuBg = 0
     self.__saCuRe = 0
     # Selection
     self.busy_selecting = False  # Busy making a selection
     self.clearSelection()
     #
     self.__initTabStops()
     self.reset()
Example #2
0
 def __init__(self, l=1, c=1):
     # Screen image
     self.lines = l
     self.columns = c
     self._image = [[DCA for _ in xrange(c)] for _ in xrange(l+1)]
     self._line_wrapped = [False for _ in xrange(l+1)]
     # History buffer
     self.hist_cursor = 0
     self._hist = HistoryScrollBuffer(1000)
     # Cursor location
     self._cu_x = 0
     self._cu_y = 0
     # Cursor color and rendition info
     self._cu_fg = 0
     self._cu_bg = 0
     self._cu_re = 0
     # Margins top / bottom
     self._margin_t = 0
     self._margin_b = 0
     # States
     self._curr_mode = [None, None, None, None, None, None]
     self._save_mode = [None, None, None, None, None, None]
     self.__tabStops = None
     # Effective color and rendition
     self._eff_fg = 0
     self._eff_bg = 0
     self._eff_re = 0
     # Save cursor location
     self.__saCuX = 0
     self.__saCuY = 0
     # save cursor color and rendition info
     self.__saCuFg = 0
     self.__saCuBg = 0
     self.__saCuRe = 0
     # Selection
     self.busy_selecting = False # Busy making a selection
     self.clearSelection()
     #
     self.__initTabStops()
     self.reset()
Example #3
0
class Screen(object):
    """
    the screen is a list of lists (columns * lines), i.e. get column 4
    of line 2 with _image[3][1] (index start at 0...)

    coordonate are stored as 2d point (y, x)
    """
    def __init__(self, l=1, c=1):
        # Screen image
        self.lines = l
        self.columns = c
        self._image = [[DCA for _ in xrange(c)] for _ in xrange(l + 1)]
        self._line_wrapped = [False for _ in xrange(l + 1)]
        # History buffer
        self.hist_cursor = 0
        self._hist = HistoryScrollBuffer(1000)
        # Cursor location
        self._cu_x = 0
        self._cu_y = 0
        # Cursor color and rendition info
        self._cu_fg = 0
        self._cu_bg = 0
        self._cu_re = 0
        # Margins top / bottom
        self._margin_t = 0
        self._margin_b = 0
        # States
        self._curr_mode = [None, None, None, None, None, None]
        self._save_mode = [None, None, None, None, None, None]
        self.__tabStops = None
        # Effective color and rendition
        self._eff_fg = 0
        self._eff_bg = 0
        self._eff_re = 0
        # Save cursor location
        self.__saCuX = 0
        self.__saCuY = 0
        # save cursor color and rendition info
        self.__saCuFg = 0
        self.__saCuBg = 0
        self.__saCuRe = 0
        # Selection
        self.busy_selecting = False  # Busy making a selection
        self.clearSelection()
        #
        self.__initTabStops()
        self.reset()

    # Screen operations

    # The `cursor' is a location within the screen that is implicitely used in
    # many operations. The operations within this section allow to manipulate
    # the cursor explicitly and to obtain it's value.

    # The position of the cursor is guarantied to be between (including) 0 and
    # `columns-1' and `lines-1'.

    # Cursor movement

    def cursorUp(self, n):
        """CUU"""
        if not n:
            n = 1
        if self._cu_y < self._margin_t:
            stop = 0
        else:
            stop = self._margin_t
        self._cu_x = min(self.columns - 1, self._cu_x)
        self._cu_y = max(stop, self._cu_y - n)

    def cursorDown(self, n):
        """CUD"""
        if not n:
            n = 1
        if self._cu_y > self._margin_t:
            stop = self.lines - 1
        else:
            stop = self._margin_b
        self._cu_x = min(self.columns - 1, self._cu_x)
        self._cu_y = max(stop, self._cu_y + n)

    def cursorLeft(self, n):
        """CUB"""
        if not n:
            n = 1
        self._cu_x = min(self.columns - 1, self._cu_x)
        self._cu_x = max(0, self._cu_x - n)

    def cursorRight(self, n):
        """CUF"""
        if not n:
            n = 1
        self._cu_x = min(self.columns - 1, self._cu_x + n)

    def setCursorX(self, x):
        if not x:
            x = 1
        x -= 1
        self._cu_x = max(0, min(self.columns - 1, x))

    def setCursorY(self, y):
        if not y:
            y = 1
        y -= 1
        if self.getMode(MODE_Origin):
            dy = self._margin_t
        else:
            dy = 0
        self._cu_y = max(0, min(self.lines - 1, y + dy))

    def setCursorYX(self, y, x):
        self.setCursorX(x)
        self.setCursorY(y)

    def setMargins(self, top, bot):
        """Set top and bottom margin"""
        if top == 0:
            top = 1
        if bot == 0:
            bot = self.lines
        top -= 1
        bot -= 1
        if not (0 <= top and top < bot and bot < self.lines):
            api.devlog("setMargins(%d, %d) : bad range" % (top, bot))
            return
        self._margin_t = top
        self._margin_b = bot
        self._cu_x = 0
        if self.getMode(MODE_Origin):
            self._cu_y = top
        else:
            self._cu_y = 0

    # Cursor movement with scrolling
    def newLine(self):
        """
        This behaves either as index() or as nextLine()
        depending on the NewLine Mode (LNM). This mode also
        affects the key sequence returned for newline ([CR]LF).
        """
        if self.getMode(MODE_NewLine):
            self.return_()
        self.index()

    def nextLine(self):
        self.return_()
        self.index()

    def index(self):
        """Move the cursor down one line.

        If cursor is on bottom margin, the region between the
        actual top and bottom margin is scrolled up instead.
        """
        if self._cu_y == self._margin_b:
            if self._margin_t == 0 and self._margin_b == self.lines - 1:
                self._addHistoryLine()
            self._scrollUp(self._margin_t, 1)
        elif self._cu_y < self.lines:
            self._cu_y += 1

    def reverseIndex(self):
        """Move the cursor up one line.

        If cursor is on the top margin, the region between the
        actual top and bottom margin is scrolled down instead.
        """
        if self._cu_y == self._margin_t:
            self._scrollDown(self._margin_t, 1)
        elif self._cu_y > 0:
            self._cu_y -= 1

    def return_(self):
        self._cu_x = 0

    def tabulate(self):
        self.cursorRight(1)
        while self._cu_x < self.columns - 1 and not self.__tabStops[
                self._cu_x]:
            self.cursorRight(1)

    def backSpace(self):
        """Move the cursor to left one column"""
        self._cu_x = max(0, self._cu_x - 1)
        if (BS_CLEARS):
            oldca = self._image[self._cu_y][self._cu_x]
            self._image[self._cu_y][self._cu_x] = Ca(u' ', oldca.f, oldca.b,
                                                     oldca.r)

    def clear(self):
        """Clear the entire screen and home the cursor"""
        self.clearEntireScreen()
        self.home()

    def home(self):
        """home the cursor"""
        self._cu_x = self._cu_y = 0

    def reset(self):
        self.setMode(MODE_Wrap)  # Wrap at end of margin
        self.saveMode(MODE_Wrap)
        self.resetMode(MODE_Origin)  # Position refere to [1,1]
        self.saveMode(MODE_Origin)
        self.resetMode(MODE_Insert)  # Overstroke
        self.saveMode(MODE_Insert)
        self.setMode(MODE_Cursor)  # Cursor visible
        self.resetMode(MODE_Screen)  # Screen not inversed
        self.resetMode(MODE_NewLine)
        self._margin_t = 0
        self._margin_b = self.lines - 1
        self.setDefaultRendition()
        self.saveCursor()
        self.clear()

    def eraseChars(self, n):
        if n == 0:
            n = 1
        p = max(0, min(self._cu_x + n - 1, self.columns - 1))
        self._clearImage([self._cu_y, self._cu_x], [self._cu_y, p], u' ')

    def deleteChars(self, n):
        if n == 0:
            n = 1
        p = max(0, min(self._cu_x + n, self.columns - 1))
        self._moveImage([self._cu_y, self._cu_x], [self._cu_y, p],
                        [self._cu_y, self.columns - 1])
        self._clearImage([self._cu_y, self.columns - n],
                         [self._cu_y, self.columns - 1], u' ')

    def insertChars(self, n):
        if n == 0:
            n = 1
        p = max(0, min(self.columns - 1 - n, self.columns - 1))
        q = max(0, min(self._cu_x + n, self.columns - 1))
        self._moveImage([self._cu_y, q], [self._cu_y, self._cu_x],
                        [self._cu_y, p])
        self._clearImage([self._cu_y, self._cu_x], [self._cu_y, q - 1], u' ')

    def deleteLines(self, n):
        if n == 0:
            n = 1
        self._scrollUp(self._cu_y, n)

    def insertLines(self, n):
        if n == 0:
            n = 1
        self._scrollDown(self._cu_y, n)

    def clearTabStops(self):
        for i in xrange(self.columns):
            self.__tabStops[i - 1] = False

    def changeTabStop(self, set):
        if self._cu_x >= self.columns:
            return
        self.__tabStops[self._cu_x] = set

    def setMode(self, m):
        self._curr_mode[m] = True
        if m == MODE_Origin:
            self._cu_x = 0
            self._cu_y = self._margin_t

    def resetMode(self, m):
        self._curr_mode[m] = False
        if m == MODE_Origin:
            self._cu_x = self._cu_y = 0

    def saveMode(self, m):
        self._save_mode[m] = self._curr_mode[m]

    def restoreMode(self, m):
        self._curr_mode[m] = self._save_mode[m]

    def saveCursor(self):
        self.__saCuX = self._cu_x
        self.__saCuY = self._cu_y
        self.__saCuRe = self._cu_re
        self.__saCuFg = self._cu_fg
        self.__saCuBg = self._cu_bg

    def restoreCursor(self):
        self._cu_x = min(self.__saCuX, self.columns - 1)
        self._cu_y = min(self.__saCuY, self.lines - 1)
        self._cu_re = self.__saCuRe
        self._cu_fg = self.__saCuFg
        self._cu_bg = self.__saCuBg
        self._effectiveRendition()

    def clearEntireScreen(self):
        self._clearImage([0, 0], [self.lines - 1, self.columns - 1], u' ')

    def clearToEndOfScreen(self):
        self._clearImage([self._cu_y, self._cu_x],
                         [self.lines - 1, self.columns - 1], u' ')

    def clearToBeginOfScreen(self):
        self._clearImage([0, 0], [self._cu_y, self._cu_x], u' ')

    def clearEntireLine(self):
        self._clearImage([self._cu_y, 0], [self._cu_y, self.columns - 1], u' ')

    def clearToEndOfLine(self):
        self._clearImage([self._cu_y, self._cu_x],
                         [self._cu_y, self.columns - 1], u' ')

    def clearToBeginOfLine(self):
        self._clearImage([self._cu_y, 0], [self._cu_y, self._cu_x], u' ')

    def helpAlign(self):
        self._clearImage([0, 0], [self.lines - 1, self.columns - 1], u'E')

    def setRendition(self, re):
        self._cu_re = self._cu_re | re
        self._effectiveRendition()

    def resetRendition(self, re):
        self._cu_re = self._cu_re & ~re
        self._effectiveRendition()

    def setForeColor(self, fgcolor):
        if fgcolor & 8:
            self._cu_fg = (fgcolor & 7) + 4 + 8
        else:
            self._cu_fg = (fgcolor & 7) + 2
        self._effectiveRendition()

    def setBackColor(self, bgcolor):
        if bgcolor & 8:
            self._cu_bg = (bgcolor & 7) + 4 + 8
        else:
            self._cu_bg = (bgcolor & 7) + 2
        self._effectiveRendition()

    def setDefaultRendition(self):
        self.setForeColorToDefault()
        self.setBackColorToDefault()
        self._cu_re = DEFAULT_RENDITION
        self._effectiveRendition()

    def setForeColorToDefault(self):
        self._cu_fg = DEFAULT_FORE_COLOR
        self._effectiveRendition()

    def setBackColorToDefault(self):
        self._cu_bg = DEFAULT_BACK_COLOR
        self._effectiveRendition()

    def getMode(self, n):
        return self._curr_mode[n]

    def getCursorX(self):
        return self._cu_x

    def getCursorY(self):
        return self._cu_y

    def showCharacter(self, c):
        #print 'screen.showcharacter', chr(c)
        w = wcWidth(c)
        if w <= 0:
            return
        if self._cu_x + w > self.columns:
            if self.getMode(MODE_Wrap):
                self._line_wrapped[self._cu_y] = True
                self.nextLine()
            else:
                self._cu_x = self.columns - w
        if self.getMode(MODE_Insert):
            self.insertChars(w)
        cpt = [self._cu_y, self._cu_x]
        self.checkSelection(cpt, cpt)
        line = self._image[self._cu_y]
        line[self._cu_x] = Ca(unichr(c), self._eff_fg, self._eff_bg,
                              self._eff_re)
        self._cu_x += w
        for i in xrange(1, w):
            line[self._cu_x + i] = Ca(None, self._eff_fg, self._eff_bg,
                                      self._eff_re)

    def resizeImage(self, lines, columns):
        if lines == self.lines and columns == self.columns:
            return
        if self._cu_y > lines + 1:
            self._margin_b = self.lines - 1
            for i in xrange(self._cu_y - (lines - 1)):
                self._addHistoryLine()
                self._scrollUp(self._margin_t, 1)
        # Make new image
        newimg = [[DCA for x in xrange(columns)] for y in xrange(lines + 1)]
        newwrapped = [False for y in xrange(lines + 1)]
        # Copy to new image
        for y in xrange(min(lines, self.lines)):
            for x in xrange(min(columns, self.columns)):
                newimg[y][x] = self._image[y][x]
            newwrapped[y] = self._line_wrapped[y]
        self._image = newimg
        self._line_wrapped = newwrapped
        self.lines = lines
        self.columns = columns
        self._cu_x = min(self._cu_x, self.columns - 1)
        self._cu_y = min(self._cu_y, lines - 1)
        self._margin_t = 0
        self._margin_b = self.lines - 1
        self.__initTabStops()
        self.clearSelection()

    def getCookedImage(self):
        image = [[DCA for x in xrange(self.columns)]
                 for y in xrange(self.lines)]
        wrapped = [False for i in xrange(self.lines)]
        hist = self._hist
        actual_y = hist.lines - self.hist_cursor
        # get lines from history
        for y in xrange(min(self.lines, actual_y)):
            yq = y + self.hist_cursor
            len_ = min(self.columns, hist.getLineLen(yq))
            image[y][:len_] = hist.getCells(yq, 0, len_)
            for x in xrange(self.columns):
                q = [yq, x]
                if q >= self._sel_topleft and q <= self._sel_bottomright:
                    self._reverseRendition(image, x, y)
            wrapped[y] = self._hist.isWrappedLine(y + self.hist_cursor)
        # get lines from the actual screen
        for y in xrange(actual_y, self.lines):
            yq = y + self.hist_cursor
            yr = y - actual_y
            for x in xrange(self.columns):
                q = [yq, x]
                image[y][x] = self._image[yr][x]
                if q >= self._sel_topleft and q <= self._sel_bottomright:
                    self._reverseRendition(image, x, y)

            wrapped[y] = self._line_wrapped[y - actual_y]
        # reverse rendition on screen mode
        if self.getMode(MODE_Screen):
            for y in xrange(self.lines):
                for x in xrange(self.columns):
                    self._reverseRendition(image, x, y)
        # update cursor
        cuy = self._cu_y + actual_y
        if self.getMode(MODE_Cursor) and \
               cuy < self.lines and self._cu_x < self.columns:
            ca = image[cuy][self._cu_x]
            image[cuy][self._cu_x] = Ca(ca.c, ca.f, ca.b, ca.r | RE_CURSOR)
        return image, wrapped

    def getHistLines(self):
        return self._hist.lines

    def setScroll(self, scroll_type):
        self.clearSelection()
        self._hist = scroll_type.getScroll(self._hist)
        self.hist_cursor = self._hist.lines

    def getScroll(self):
        return self._hist.getType()

    def hasScroll(self):
        return self._hist.hasScroll()

    def _clearImage(self, loca, loce, c):
        # Clear entire selection if overlaps region to be moved
        if self._overlapSelection(loca, loce):
            self.clearSelection()
        ca = Ca(c, self._eff_fg, self._eff_bg, DEFAULT_RENDITION)
        for y in xrange(loca[0], loce[0] + 1):
            for x in xrange(loca[1], loce[1] + 1):
                self._image[y][x] = ca
            self._line_wrapped[y] = False

    def _moveImage(self, dest, loca, loce):
        #print 'move image', dest, loca, loce
        assert loce >= loca
        # XXX x coordonates are not always considered. Is it enough actually ?
        ys = loca[0]
        if dest[0] != ys:
            dy = loce[0] - ys + 1
            self._image[dest[0]:dest[0] +
                        dy] = [lines[:] for lines in self._image[ys:ys + dy]]
            for i in xrange(dy):
                self._line_wrapped[dest[0] + i] = self._line_wrapped[ys + i]
        else:
            xs = loca[1]
            dx = loce[1] - xs + 1
            self._image[ys][dest[1]:dest[1] + dx] = self._image[ys][xs:xs + dx]
        # Adjust selection to follow scroll
        if self._sel_begin != [-1, -1]:
            beginIsSTL = (self._sel_begin == self._sel_topleft)
            diff = self._subPoints(dest, loca)  # Scroll by this amount
            scr_topleft = [self._hist.lines, 0]
            srca = self._addPoints(
                loca, scr_topleft)  # Translate index from screen to global
            srce = self._addPoints(loce, scr_topleft)
            desta = self._addPoints(srca, diff)
            deste = self._addPoints(srce, diff)
            if self._sel_topleft >= srca and self._sel_topleft <= srce:
                self._sel_topleft = self._addPoints(self._sel_topleft, diff)
            elif self._sel_topleft >= desta and self._sel_topleft <= deste:
                self._sel_bottomright = [-1, -1]  # Clear selection (see below)
            if self._sel_bottomright >= srca and self._sel_bottomright <= srce:
                self._sel_bottomright = self._addPoints(
                    self._sel_bottomright, diff)
            elif self._sel_bottomright >= desta and self._sel_bottomright <= deste:
                self._sel_bottomright = [-1, -1]  # Clear selection (see below)
            if self._sel_bottomright < [0, 0]:
                self.clearSelection()
            elif self._sel_topleft < [0, 0]:
                self._sel_topleft = [0, 0]
            if beginIsSTL:
                self._sel_begin = self._sel_topleft
            else:
                self._sel_begin = self._sel_bottomright

    def _scrollUp(self, from_, n):
        if n <= 0 or from_ + n > self._margin_b:
            return
        ecoord = [self._margin_b, self.columns - 1]
        self._moveImage([from_, 0], [from_ + n, 0], ecoord)
        self._clearImage([self._margin_b - n + 1, 0], ecoord, u' ')

    def _scrollDown(self, from_, n):
        if n <= 0 or from_ > self._margin_b:
            return
        if from_ + n > self._margin_b:
            n = self._margin_b - from_
        self._moveImage([from_ + n, 0], [from_, 0],
                        [self._margin_b - n, self.columns - 1])
        self._clearImage([from_, 0], [from_ + n - 1, self.columns - 1], u' ')

    def _addHistoryLine(self):
        """Add the first image's line to history buffer
        Take care about scrolling too...
        """
        assert self.hasScroll() or self.hist_cursor == 0
        if not self.hasScroll():
            return
        end = self.columns - 1
        while end >= 0 and (self._image[0][end] is DCA or self._image[0][end]
                            == DCA) and not self._line_wrapped[0]:
            end -= 1
        oldHistLines = self._hist.lines
        self._hist.addCells(self._image[0][:end + 1], self._line_wrapped[0])
        newHistLines = self._hist.lines
        # Adjust history cursor
        beginIsTL = (self._sel_begin == self._sel_topleft)
        if newHistLines > oldHistLines:
            self.hist_cursor += 1
            # Adjust selection for the new point of reference
            if self._sel_begin != [-1, -1]:
                self._sel_topleft[0] += 1
                self._sel_bottomright[0] += 1
        # Scroll up if user is looking at the history and we can scroll up
        if self.hist_cursor > 0 and (self.hist_cursor != newHistLines
                                     or self.busy_selecting):
            self.hist_cursor -= 1
        # Scroll selection in history up
        if self._sel_begin != [-1, -1]:
            topBR = [1 + newHistLines, 0]
            if self._sel_topleft < topBR:
                self._sel_topleft[0] -= 1
            if self._sel_bottomright < topBR:
                self._sel_bottomright[0] -= 1
            if self._sel_bottomright < [0, 0]:
                self.clearSelection()
            elif self._sel_topleft < [0, 0]:
                self._sel_topleft = [0, 0]
            if beginIsTL:
                self._sel_begin = self._sel_topleft
            else:
                self._sel_begin = self._sel_bottomright

    def __initTabStops(self):
        self.__tabStops = self.columns * [False]
        for i in xrange(self.columns):
            self.__tabStops[i] = ((i % 8 == 0) and i != 0)

    def _effectiveRendition(self):
        self._eff_re = self._cu_re & (RE_UNDERLINE | RE_BLINK)
        if self._cu_re & RE_REVERSE:
            self._eff_fg = self._cu_bg
            self._eff_bg = self._cu_fg
        else:
            self._eff_fg = self._cu_fg
            self._eff_bg = self._cu_bg
        if self._cu_re & RE_BOLD:
            if self._eff_fg < BASE_COLORS:
                self._eff_fg += BASE_COLORS
            else:
                self._eff_fg -= BASE_COLORS

    def _reverseRendition(self, image, x, y):
        #        image[coord] = p = image[coord].dump()
        p = image[y][x]
        image[y][x] = Ca(p.c, p.b, p.f, p.r)

    # selection handling ######################################################

    def setSelBeginXY(self, x, y):
        self._sel_begin = [y + self.hist_cursor, x]
        if x == self.columns:
            self._incPoint(self._sel_begin, -1)
        self._sel_bottomright = self._sel_begin
        self._sel_topleft = self._sel_begin

    def setSelExtendXY(self, x, y):
        if self._sel_begin == [-1, -1]:
            return
        l = [y + self.hist_cursor, x]
        if l < self._sel_begin:
            self._sel_topleft = l
            self._sel_bottomright = self._sel_begin
        else:
            if x == self.columns:
                self._incPoint(l, -1)
            self._sel_topleft = self._sel_begin
            self._sel_bottomright = l

    def testIsSelected(self, x, y):
        pos = [y + self.hist_cursor, x]
        return pos >= self._sel_topleft and pos <= self._sel_bottomright

    def clearSelection(self):
        self._sel_begin = [-1, -1]  # First location selected
        self._sel_topleft = [-1, -1]  # Top-left location
        self._sel_bottomright = [-1, -1]  # Bottom-right location

    def getSelText(self, preserve_line_break):
        if self._sel_begin == [-1, -1]:
            return
        histBR = [self._hist.lines, 0]
        hY = self._sel_topleft[0]
        hX = self._sel_topleft[1]
        m = []

        s = self._sel_topleft[:]

        if preserve_line_break:
            eol_char = '\n'
        else:
            eol_char = ' '

        #s es el begin!
        while s <= self._sel_bottomright:
            # XXX in the first if branch, eol is scalar while in the else branch, it's a point !
            if s < histBR:
                eol = self._hist.getLineLen(hY)
                if hY == self._sel_bottomright[
                        0] and eol > self._sel_bottomright[1]:
                    eol = self._sel_bottomright[1] + 1
                while hX < eol:
                    c = self._hist.getCells(hY, hX, 1)[0].c
                    if c is not None:
                        m.append(c)
                    self._incPoint(s)
                    hX += 1
                if s <= self._sel_bottomright:
                    if eol % self.columns == 0:
                        if eol == 0:
                            m.append(eol_char)
                        elif not self._hist.isWrappedLine(hY):
                            m.append(eol_char)
                    elif (eol + 1) % self.columns == 0:
                        if not self._hist.isWrappedLine(hY):
                            m.append(eol_char)
                    else:
                        m.append(eol_char)
                hY += 1
                hX = 0
                s = [hY, 0]
            else:
                eol = [s[0] + 1, 0]
                self._incPoint(eol, -1)
                addNewLine = False
                if eol < self._sel_bottomright:
                    while eol > s:
                        pt = self._subPoints(eol, histBR)
                        ca = self._image[pt[0]][pt[1]]

                        if (not ca.c or ca.isSpace()):
                            if not self._line_wrapped[pt[0]]:
                                break
                        else:
                            # found a valid char!
                            break
                        self._incPoint(eol, -1)
                elif eol == self._sel_bottomright:
                    pt = self._subPoints(eol, histBR)
                    if not self._line_wrapped[pt[0]]:
                        addNewLine = True
                else:
                    eol = self._sel_bottomright
                while s <= eol:
                    pt = self._subPoints(s, histBR)
                    c = self._image[pt[0]][pt[1]].c
                    if c is not None:
                        m.append(c)
                    self._incPoint(s)
                if eol < self._sel_bottomright:
                    if eol[1] + 1 == self.columns:  #(eol + 1) % self.columns == 0:
                        if (not self._hist.isWrappedLine(eol[0] - histBR[0])
                                and
                                not self._line_wrapped[eol[0] - histBR[0]]):
                            m.append(eol_char)
                    else:
                        m.append(eol_char)
                elif addNewLine and preserve_line_break:
                    m.append('\n')
                s = [eol[0] + 1, 0]
        # skip trailing spaces
        m = [line.rstrip() for line in ''.join(m).splitlines()]
        return '\n'.join(m)

    def setBackgroundColor(self, from_xy, to_xy, color):
        #self.setSelBeginXY(from_xy[0], from_xy[1])
        #self.setSelExtendXY(to_xy[0], to_xy[1])
        #self.getSelText(False)

        from_x = from_xy[0]
        from_y = from_xy[1]

        to_x = to_xy[0]
        to_y = to_xy[1]

        begin = [from_y + self.hist_cursor, from_x]
        end = [to_y + self.hist_cursor, to_x]

        if from_x == self.columns:
            self._incPoint(begin, -1)

        if end < begin:
            start_highl = end
            end_highl = begin
        else:
            if to_x == self.columns:
                self._incPoint(end, -1)
            start_highl = begin
            end_highl = end

        ##################################
        #self._sel_begin = begin
        #self._sel_topleft = self._sel_begin
        #self._sel_bottomright = end
        #self.getSelText(False)

        self._paintBackground(start_highl, end_highl, color)

    def _paintBackground(self, start_xy, end_xy, color):

        if start_xy == [-1, -1]:
            return

        histBR = [self._hist.lines, 0]
        hY = start_xy[0]
        hX = start_xy[1]
        m = []

        s = start_xy[:]
        eol_char = ' '

        #s es el begin!
        while s <= end_xy:
            # XXX in the first if branch, eol is scalar while in the else branch, it's a point !
            if s < histBR:
                eol = self._hist.getLineLen(hY)
                if hY == end_xy[0] and eol > end_xy[1]:
                    eol = end_xy[1] + 1
                while hX < eol:
                    ca = self._hist.getCells(hY, hX, 1)[0].b = color
                    self._incPoint(s)
                    hX += 1
                hY += 1
                hX = 0
                s = [hY, 0]
            else:
                eol = [s[0] + 1, 0]
                self._incPoint(eol, -1)

                if eol < end_xy:
                    while eol > s:
                        pt = self._subPoints(eol, histBR)
                        ca = self._image[pt[0]][pt[1]]
                        self._image[pt[0]][pt[1]] = Ca(ca.c, ca.f, color, ca.r)

                        self._incPoint(eol, -1)
                elif eol == end_xy:
                    pt = self._subPoints(eol, histBR)
                else:
                    eol = end_xy

                while s <= eol:
                    pt = self._subPoints(s, histBR)
                    ca = self._image[pt[0]][pt[1]]

                    self._image[pt[0]][pt[1]] = Ca(ca.c, ca.f, color, ca.r)
                    self._incPoint(s)

                s = [eol[0] + 1, 0]

    def checkSelection(self, from_, to):
        if self._sel_begin == [-1, -1]:
            return
        # Clear entire selection if overlaps region to be moved
        if self._overlapSelection(from_, to):
            self.clearSelection()

    def _overlapSelection(self, from_, to):
        assert isinstance(from_, list), from_
        assert isinstance(to, list), to
        scr_topleft = [self._hist.lines, 0]
        # Clear entire selection if overlaps region [from_, to]
        if self._sel_bottomright > self._addPoints(from_, scr_topleft) and \
               self._sel_topleft < self._addPoints(to, scr_topleft):
            return True
        return False

    # point manipulation ######################################################

    def _incPoint(self, point, inc=1):
        x = point[1] + inc
        if x < 0 or x >= self.columns:
            dy, x = divmod(x, self.columns)
            point[0] += dy
        point[1] = x

    def _addPoints(self, point1, point2):
        x = point1[1] + point2[1]
        y = point1[0] + point2[0]
        if x < 0 or x >= self.columns:
            dy, x = divmod(x, self.columns)
            y += dy
        return [y, x]

    def _subPoints(self, point1, point2):
        x = point1[1] - point2[1]
        y = point1[0] - point2[0]
        if x < 0 or x >= self.columns:
            dy, x = divmod(x, self.columns)
            y += dy
        return [y, x]
Example #4
0
class Screen(object):
    """
    the screen is a list of lists (columns * lines), i.e. get column 4
    of line 2 with _image[3][1] (index start at 0...)

    coordonate are stored as 2d point (y, x)
    """

    def __init__(self, l=1, c=1):
        # Screen image
        self.lines = l
        self.columns = c
        self._image = [[DCA for _ in xrange(c)] for _ in xrange(l+1)]
        self._line_wrapped = [False for _ in xrange(l+1)]
        # History buffer
        self.hist_cursor = 0
        self._hist = HistoryScrollBuffer(1000)
        # Cursor location
        self._cu_x = 0
        self._cu_y = 0
        # Cursor color and rendition info
        self._cu_fg = 0
        self._cu_bg = 0
        self._cu_re = 0
        # Margins top / bottom
        self._margin_t = 0
        self._margin_b = 0
        # States
        self._curr_mode = [None, None, None, None, None, None]
        self._save_mode = [None, None, None, None, None, None]
        self.__tabStops = None
        # Effective color and rendition
        self._eff_fg = 0
        self._eff_bg = 0
        self._eff_re = 0
        # Save cursor location
        self.__saCuX = 0
        self.__saCuY = 0
        # save cursor color and rendition info
        self.__saCuFg = 0
        self.__saCuBg = 0
        self.__saCuRe = 0
        # Selection
        self.busy_selecting = False # Busy making a selection
        self.clearSelection()
        #
        self.__initTabStops()
        self.reset()

    # Screen operations

    # The `cursor' is a location within the screen that is implicitely used in
    # many operations. The operations within this section allow to manipulate
    # the cursor explicitly and to obtain it's value.

    # The position of the cursor is guarantied to be between (including) 0 and
    # `columns-1' and `lines-1'.

    # Cursor movement

    def cursorUp(self, n):
        """CUU"""
        if not n:
            n = 1
        if self._cu_y < self._margin_t:
            stop = 0
        else:
            stop = self._margin_t
        self._cu_x = min(self.columns-1, self._cu_x)
        self._cu_y = max(stop, self._cu_y-n)

    def cursorDown(self, n):
        """CUD"""
        if not n:
            n = 1
        if self._cu_y > self._margin_t:
            stop = self.lines-1
        else:
            stop = self._margin_b
        self._cu_x = min(self.columns-1, self._cu_x)
        self._cu_y = max(stop, self._cu_y+n)

    def cursorLeft(self, n):
        """CUB"""
        if not n:
            n = 1
        self._cu_x = min(self.columns-1, self._cu_x)
        self._cu_x = max(0, self._cu_x-n)

    def cursorRight(self, n):
        """CUF"""
        if not n:
            n = 1
        self._cu_x = min(self.columns-1, self._cu_x+n)

    def setCursorX(self, x):
        if not x:
            x = 1
        x -= 1
        self._cu_x = max(0, min(self.columns-1, x))

    def setCursorY(self, y):
        if not y:
            y = 1
        y -= 1
        if self.getMode(MODE_Origin):
            dy = self._margin_t
        else:
            dy = 0
        self._cu_y = max(0, min(self.lines-1, y+dy))

    def setCursorYX(self, y, x):
        self.setCursorX(x)
        self.setCursorY(y)

    def setMargins(self, top, bot):
        """Set top and bottom margin"""
        if top == 0:
            top = 1
        if bot == 0:
            bot = self.lines
        top -= 1
        bot -= 1
        if not (0 <= top and top < bot and bot < self.lines):
            api.devlog("setMargins(%d, %d) : bad range" % (top, bot))
            return
        self._margin_t = top
        self._margin_b = bot
        self._cu_x = 0
        if self.getMode(MODE_Origin):
            self._cu_y = top
        else:
            self._cu_y = 0

    # Cursor movement with scrolling
    def newLine(self):
        """
        This behaves either as index() or as nextLine()
        depending on the NewLine Mode (LNM). This mode also
        affects the key sequence returned for newline ([CR]LF).
        """
        if self.getMode(MODE_NewLine):
            self.return_()
        self.index()

    def nextLine(self):
        self.return_()
        self.index()

    def index(self):
        """Move the cursor down one line.

        If cursor is on bottom margin, the region between the
        actual top and bottom margin is scrolled up instead.
        """
        if self._cu_y == self._margin_b:
            if self._margin_t == 0 and self._margin_b == self.lines-1:
                self._addHistoryLine()
            self._scrollUp(self._margin_t, 1)
        elif self._cu_y < self.lines:
            self._cu_y += 1

    def reverseIndex(self):
        """Move the cursor up one line.

        If cursor is on the top margin, the region between the
        actual top and bottom margin is scrolled down instead.
        """
        if self._cu_y == self._margin_t:
            self._scrollDown(self._margin_t, 1)
        elif self._cu_y > 0:
            self._cu_y -= 1

    def return_(self):
        self._cu_x = 0

    def tabulate(self):
        self.cursorRight(1)
        while self._cu_x < self.columns-1 and not self.__tabStops[self._cu_x]:
            self.cursorRight(1)

    def backSpace(self):
        """Move the cursor to left one column"""
        self._cu_x = max(0, self._cu_x-1)
        if (BS_CLEARS):
            oldca = self._image[self._cu_y][self._cu_x]
            self._image[self._cu_y][self._cu_x] = Ca(u' ', oldca.f, oldca.b, oldca.r)

    def clear(self):
        """Clear the entire screen and home the cursor"""
        self.clearEntireScreen()
        self.home()

    def home(self):
        """home the cursor"""
        self._cu_x = self._cu_y = 0

    def reset(self):
        self.setMode(MODE_Wrap)      # Wrap at end of margin
        self.saveMode(MODE_Wrap)
        self.resetMode(MODE_Origin)  # Position refere to [1,1]
        self.saveMode(MODE_Origin)
        self.resetMode(MODE_Insert)  # Overstroke
        self.saveMode(MODE_Insert)
        self.setMode(MODE_Cursor)    # Cursor visible
        self.resetMode(MODE_Screen)  # Screen not inversed
        self.resetMode(MODE_NewLine)
        self._margin_t = 0
        self._margin_b = self.lines-1
        self.setDefaultRendition()
        self.saveCursor()
        self.clear()

    def eraseChars(self, n):
        if n == 0:
            n = 1
        p = max(0, min(self._cu_x+n-1, self.columns-1))
        self._clearImage([self._cu_y, self._cu_x], [self._cu_y, p], u' ')

    def deleteChars(self, n):
        if n == 0:
            n = 1
        p = max(0, min(self._cu_x+n, self.columns-1))
        self._moveImage([self._cu_y, self._cu_x], [self._cu_y, p], [self._cu_y, self.columns-1])
        self._clearImage([self._cu_y, self.columns-n], [self._cu_y, self.columns-1], u' ')

    def insertChars(self, n):
        if n == 0:
            n = 1
        p = max(0, min(self.columns-1-n, self.columns-1))
        q = max(0, min(self._cu_x+n, self.columns-1))
        self._moveImage([self._cu_y, q], [self._cu_y, self._cu_x], [self._cu_y, p])
        self._clearImage([self._cu_y, self._cu_x], [self._cu_y, q-1], u' ')

    def deleteLines(self, n):
        if n == 0:
            n = 1
        self._scrollUp(self._cu_y, n)

    def insertLines(self, n):
        if n == 0:
            n = 1
        self._scrollDown(self._cu_y, n)

    def clearTabStops(self):
        for i in xrange(self.columns):
            self.__tabStops[i-1] = False

    def changeTabStop(self, set):
        if self._cu_x >= self.columns:
            return
        self.__tabStops[self._cu_x] = set

    def setMode(self, m):
        self._curr_mode[m] = True
        if m == MODE_Origin:
            self._cu_x = 0
            self._cu_y = self._margin_t

    def resetMode(self, m):
        self._curr_mode[m] = False
        if m == MODE_Origin:
            self._cu_x = self._cu_y = 0

    def saveMode(self, m):
        self._save_mode[m] = self._curr_mode[m]

    def restoreMode(self, m):
        self._curr_mode[m] = self._save_mode[m]

    def saveCursor(self):
        self.__saCuX = self._cu_x
        self.__saCuY = self._cu_y
        self.__saCuRe = self._cu_re
        self.__saCuFg = self._cu_fg
        self.__saCuBg = self._cu_bg

    def restoreCursor(self):
        self._cu_x = min(self.__saCuX, self.columns-1)
        self._cu_y = min(self.__saCuY, self.lines-1)
        self._cu_re = self.__saCuRe
        self._cu_fg = self.__saCuFg
        self._cu_bg = self.__saCuBg
        self._effectiveRendition()

    def clearEntireScreen(self):
        self._clearImage([0, 0], [self.lines-1, self.columns-1], u' ')

    def clearToEndOfScreen(self):
        self._clearImage([self._cu_y, self._cu_x],
                         [self.lines-1, self.columns-1], u' ')

    def clearToBeginOfScreen(self):
        self._clearImage([0, 0], [self._cu_y, self._cu_x], u' ')

    def clearEntireLine(self):
        self._clearImage([self._cu_y, 0], [self._cu_y, self.columns-1], u' ')

    def clearToEndOfLine(self):
        self._clearImage([self._cu_y, self._cu_x],
                         [self._cu_y, self.columns-1], u' ')

    def clearToBeginOfLine(self):
        self._clearImage([self._cu_y, 0], [self._cu_y, self._cu_x], u' ')

    def helpAlign(self):
        self._clearImage([0, 0], [self.lines-1, self.columns-1], u'E')

    def setRendition(self, re):
        self._cu_re = self._cu_re | re
        self._effectiveRendition()

    def resetRendition(self, re):
        self._cu_re = self._cu_re & ~re
        self._effectiveRendition()

    def setForeColor(self, fgcolor):
        if fgcolor & 8:
            self._cu_fg = (fgcolor & 7) + 4+8
        else:
            self._cu_fg = (fgcolor & 7) + 2
        self._effectiveRendition()

    def setBackColor(self, bgcolor):
        if bgcolor & 8:
            self._cu_bg = (bgcolor & 7) + 4+8
        else:
            self._cu_bg = (bgcolor & 7) + 2
        self._effectiveRendition()

    def setDefaultRendition(self):
        self.setForeColorToDefault()
        self.setBackColorToDefault()
        self._cu_re = DEFAULT_RENDITION
        self._effectiveRendition()

    def setForeColorToDefault(self):
        self._cu_fg = DEFAULT_FORE_COLOR
        self._effectiveRendition()

    def setBackColorToDefault(self):
        self._cu_bg = DEFAULT_BACK_COLOR
        self._effectiveRendition()

    def getMode(self, n):
        return self._curr_mode[n]

    def getCursorX(self):
        return self._cu_x

    def getCursorY(self):
        return self._cu_y

    def showCharacter(self, c):
        #print 'screen.showcharacter', chr(c)
        w = wcWidth(c)
        if w <= 0:
            return
        if self._cu_x+w > self.columns:
            if self.getMode(MODE_Wrap):
                self._line_wrapped[self._cu_y] = True
                self.nextLine()
            else:
                self._cu_x = self.columns-w
        if self.getMode(MODE_Insert):
            self.insertChars(w)
        cpt = [self._cu_y, self._cu_x]
        self.checkSelection(cpt, cpt)
        line = self._image[self._cu_y]
        line[self._cu_x] = Ca(unichr(c), self._eff_fg, self._eff_bg,
                              self._eff_re)
        self._cu_x += w
        for i in xrange(1, w):
            line[self._cu_x + i] = Ca(None, self._eff_fg, self._eff_bg,
                                      self._eff_re)

    def resizeImage(self, lines, columns):
        if lines == self.lines and columns == self.columns:
            return
        if self._cu_y > lines+1:
            self._margin_b = self.lines-1
            for i in xrange(self._cu_y - (lines-1)):
                self._addHistoryLine()
                self._scrollUp(self._margin_t, 1)
        # Make new image
        newimg = [[DCA for x in xrange(columns)] for y in xrange(lines+1)]
        newwrapped = [False for y in xrange(lines+1)]
        # Copy to new image
        for y in xrange(min(lines, self.lines)):
            for x in xrange(min(columns, self.columns)):
                newimg[y][x] = self._image[y][x]
            newwrapped[y] = self._line_wrapped[y]
        self._image = newimg
        self._line_wrapped = newwrapped
        self.lines = lines
        self.columns = columns
        self._cu_x = min(self._cu_x, self.columns-1)
        self._cu_y = min(self._cu_y, lines-1)
        self._margin_t = 0
        self._margin_b = self.lines - 1
        self.__initTabStops()
        self.clearSelection()

    def getCookedImage(self):
        image = [[DCA for x in xrange(self.columns)] for y in xrange(self.lines)]
        wrapped = [False for i in xrange(self.lines)]
        hist = self._hist
        actual_y = hist.lines - self.hist_cursor
        # get lines from history
        for y in xrange(min(self.lines, actual_y)):
            yq = y + self.hist_cursor
            len_ = min(self.columns, hist.getLineLen(yq))
            image[y][:len_] = hist.getCells(yq, 0, len_)
            for x in xrange(self.columns):
                q = [yq, x]
                if q >= self._sel_topleft and q <= self._sel_bottomright:
                    self._reverseRendition(image, x, y)
            wrapped[y] = self._hist.isWrappedLine(y+self.hist_cursor)
        # get lines from the actual screen
        for y in xrange(actual_y, self.lines):
            yq = y + self.hist_cursor
            yr = y - actual_y
            for x in xrange(self.columns):
                q = [yq, x]
                image[y][x] = self._image[yr][x]
                if q >= self._sel_topleft and q <= self._sel_bottomright:
                    self._reverseRendition(image, x, y)

            wrapped[y] = self._line_wrapped[y-actual_y]
        # reverse rendition on screen mode
        if self.getMode(MODE_Screen):
            for y in xrange(self.lines):
                for x in xrange(self.columns):
                    self._reverseRendition(image, x, y)
        # update cursor
        cuy = self._cu_y + actual_y
        if self.getMode(MODE_Cursor) and \
               cuy < self.lines and self._cu_x < self.columns:
            ca = image[cuy][self._cu_x]
            image[cuy][self._cu_x] = Ca(ca.c, ca.f, ca.b, ca.r | RE_CURSOR)
        return image, wrapped

    def getHistLines(self):
        return self._hist.lines

    def setScroll(self, scroll_type):
        self.clearSelection()
        self._hist = scroll_type.getScroll(self._hist)
        self.hist_cursor = self._hist.lines

    def getScroll(self):
        return self._hist.getType()

    def hasScroll(self):
        return self._hist.hasScroll()

    def _clearImage(self, loca, loce, c):
        # Clear entire selection if overlaps region to be moved
        if self._overlapSelection(loca, loce):
            self.clearSelection()
        ca = Ca(c, self._eff_fg, self._eff_bg, DEFAULT_RENDITION)
        for y in xrange(loca[0], loce[0]+1):
            for x in xrange(loca[1], loce[1]+1):
                self._image[y][x] = ca
            self._line_wrapped[y] = False

    def _moveImage(self, dest, loca, loce):
        #print 'move image', dest, loca, loce
        assert loce >= loca
        # XXX x coordonates are not always considered. Is it enough actually ?
        ys = loca[0]
        if dest[0] != ys:
            dy = loce[0] - ys + 1
            self._image[dest[0]:dest[0]+dy] = [lines[:] for lines in self._image[ys:ys+dy]]
            for i in xrange(dy):
                self._line_wrapped[dest[0]+i] = self._line_wrapped[ys+i]
        else:
            xs = loca[1]
            dx = loce[1] - xs + 1
            self._image[ys][dest[1]:dest[1]+dx] = self._image[ys][xs:xs+dx]
        # Adjust selection to follow scroll
        if self._sel_begin != [-1, -1]:
            beginIsSTL = (self._sel_begin == self._sel_topleft)
            diff = self._subPoints(dest, loca) # Scroll by this amount
            scr_topleft = [self._hist.lines, 0]
            srca = self._addPoints(loca, scr_topleft) # Translate index from screen to global
            srce = self._addPoints(loce, scr_topleft)
            desta = self._addPoints(srca, diff)
            deste = self._addPoints(srce, diff)
            if self._sel_topleft >= srca and self._sel_topleft <= srce:
                self._sel_topleft = self._addPoints(self._sel_topleft, diff)
            elif self._sel_topleft >= desta and self._sel_topleft <= deste:
                self._sel_bottomright = [-1, -1] # Clear selection (see below)
            if self._sel_bottomright >= srca and self._sel_bottomright <= srce:
                self._sel_bottomright = self._addPoints(self._sel_bottomright, diff)
            elif self._sel_bottomright >= desta and self._sel_bottomright <= deste:
                self._sel_bottomright = [-1, -1] # Clear selection (see below)
            if self._sel_bottomright < [0, 0]:
                self.clearSelection()
            elif self._sel_topleft < [0, 0]:
                self._sel_topleft = [0, 0]
            if beginIsSTL:
                self._sel_begin = self._sel_topleft
            else:
                self._sel_begin = self._sel_bottomright

    def _scrollUp(self, from_, n):
        if n <= 0 or from_+n > self._margin_b:
            return
        ecoord = [self._margin_b, self.columns-1]
        self._moveImage([from_, 0], [from_+n, 0], ecoord)
        self._clearImage([self._margin_b-n+1, 0], ecoord, u' ')

    def _scrollDown(self, from_, n):
        if n <= 0 or from_ > self._margin_b:
            return
        if from_+n > self._margin_b:
            n = self._margin_b-from_
        self._moveImage([from_+n, 0], [from_, 0],
                        [self._margin_b-n, self.columns-1])
        self._clearImage([from_, 0], [from_+n-1, self.columns-1], u' ')

    def _addHistoryLine(self):
        """Add the first image's line to history buffer
        Take care about scrolling too...
        """
        assert self.hasScroll() or self.hist_cursor == 0
        if not self.hasScroll():
            return
        end = self.columns - 1
        while end >= 0 and (self._image[0][end] is DCA or
                            self._image[0][end] == DCA) and not self._line_wrapped[0]:
            end -= 1
        oldHistLines = self._hist.lines
        self._hist.addCells(self._image[0][:end+1], self._line_wrapped[0])
        newHistLines = self._hist.lines
        # Adjust history cursor
        beginIsTL = (self._sel_begin == self._sel_topleft)
        if newHistLines > oldHistLines:
            self.hist_cursor += 1
            # Adjust selection for the new point of reference
            if self._sel_begin != [-1, -1]:
                self._sel_topleft[0] += 1
                self._sel_bottomright[0] += 1
        # Scroll up if user is looking at the history and we can scroll up
        if self.hist_cursor > 0 and (self.hist_cursor != newHistLines
                                     or self.busy_selecting):
            self.hist_cursor -= 1
        # Scroll selection in history up
        if self._sel_begin != [-1, -1]:
            topBR = [1+newHistLines, 0]
            if self._sel_topleft < topBR:
                self._sel_topleft[0] -= 1
            if self._sel_bottomright < topBR:
                self._sel_bottomright[0] -= 1
            if self._sel_bottomright < [0, 0]:
                self.clearSelection()
            elif self._sel_topleft < [0, 0]:
                self._sel_topleft = [0, 0]
            if beginIsTL:
                self._sel_begin = self._sel_topleft
            else:
                self._sel_begin = self._sel_bottomright

    def __initTabStops(self):
        self.__tabStops = self.columns*[False]
        for i in xrange(self.columns):
            self.__tabStops[i] = ((i % 8 == 0) and i != 0)

    def _effectiveRendition(self):
        self._eff_re = self._cu_re & (RE_UNDERLINE | RE_BLINK)
        if self._cu_re & RE_REVERSE:
            self._eff_fg = self._cu_bg
            self._eff_bg = self._cu_fg
        else:
            self._eff_fg = self._cu_fg
            self._eff_bg = self._cu_bg
        if self._cu_re & RE_BOLD:
            if self._eff_fg < BASE_COLORS:
                self._eff_fg += BASE_COLORS
            else:
                self._eff_fg -= BASE_COLORS

    def _reverseRendition(self, image, x, y):
#        image[coord] = p = image[coord].dump()
        p = image[y][x]
        image[y][x] = Ca(p.c, p.b, p.f, p.r)

    # selection handling ######################################################

    def setSelBeginXY(self, x, y):
        self._sel_begin = [y+self.hist_cursor, x]
        if x == self.columns:
            self._incPoint(self._sel_begin, -1)
        self._sel_bottomright = self._sel_begin
        self._sel_topleft = self._sel_begin

    def setSelExtendXY(self, x, y):
        if self._sel_begin == [-1, -1]:
            return
        l = [y+self.hist_cursor, x]
        if l < self._sel_begin:
            self._sel_topleft = l
            self._sel_bottomright = self._sel_begin
        else:
            if x == self.columns:
                self._incPoint(l, -1)
            self._sel_topleft = self._sel_begin
            self._sel_bottomright = l

    def testIsSelected(self, x, y):
        pos = [y+self.hist_cursor, x]
        return pos >= self._sel_topleft and pos <= self._sel_bottomright

    def clearSelection(self):
        self._sel_begin = [-1, -1]      # First location selected
        self._sel_topleft = [-1, -1]    # Top-left location
        self._sel_bottomright = [-1, -1]# Bottom-right location

    def getSelText(self, preserve_line_break):
        if self._sel_begin == [-1, -1]:
            return
        histBR = [self._hist.lines, 0]
        hY = self._sel_topleft[0]
        hX = self._sel_topleft[1]
        m = []

        s = self._sel_topleft[:]

        if preserve_line_break:
            eol_char = '\n'
        else:
            eol_char = ' '

        #s es el begin!
        while s <= self._sel_bottomright:
            # XXX in the first if branch, eol is scalar while in the else branch, it's a point !
            if s < histBR:
                eol = self._hist.getLineLen(hY)
                if hY == self._sel_bottomright[0] and eol > self._sel_bottomright[1]:
                    eol = self._sel_bottomright[1] + 1
                while hX < eol:
                    c = self._hist.getCells(hY, hX, 1)[0].c
                    if c is not None:
                        m.append(c)
                    self._incPoint(s)
                    hX += 1
                if s <= self._sel_bottomright:
                    if eol % self.columns == 0:
                        if eol == 0:
                            m.append(eol_char)
                        elif not self._hist.isWrappedLine(hY):
                            m.append(eol_char)
                    elif (eol + 1) % self.columns == 0:
                        if not self._hist.isWrappedLine(hY):
                            m.append(eol_char)
                    else:
                        m.append(eol_char)
                hY += 1
                hX = 0
                s = [hY, 0]
            else:
                eol = [s[0]+1, 0]
                self._incPoint(eol, -1)
                addNewLine = False
                if eol < self._sel_bottomright:
                    while eol > s:
                        pt = self._subPoints(eol, histBR)
                        ca = self._image[pt[0]][pt[1]]

                        if (not ca.c or ca.isSpace()):
                            if not self._line_wrapped[pt[0]]:
                                break
                        else:
                            # found a valid char!
                            break
                        self._incPoint(eol, -1)
                elif eol == self._sel_bottomright:
                    pt = self._subPoints(eol, histBR)
                    if not self._line_wrapped[pt[0]]:
                        addNewLine = True
                else:
                    eol = self._sel_bottomright
                while s <= eol:
                    pt = self._subPoints(s, histBR)
                    c = self._image[pt[0]][pt[1]].c
                    if c is not None:
                        m.append(c)
                    self._incPoint(s)
                if eol < self._sel_bottomright:
                    if eol[1] +1 == self.columns: #(eol + 1) % self.columns == 0:
                        if (not self._hist.isWrappedLine(eol[0]-histBR[0]) and
                            not self._line_wrapped[eol[0]-histBR[0]]):
                            m.append(eol_char)
                    else:
                        m.append(eol_char)
                elif addNewLine and preserve_line_break:
                    m.append('\n')
                s = [eol[0]+1, 0]
        # skip trailing spaces
        m = [line.rstrip() for line in ''.join(m).splitlines()]
        return '\n'.join(m)

    def setBackgroundColor(self, from_xy, to_xy, color):
        #self.setSelBeginXY(from_xy[0], from_xy[1])
        #self.setSelExtendXY(to_xy[0], to_xy[1])
        #self.getSelText(False)

        from_x = from_xy[0]
        from_y = from_xy[1]

        to_x   = to_xy[0]
        to_y   = to_xy[1]

        begin = [from_y+self.hist_cursor, from_x]
        end = [to_y+self.hist_cursor, to_x]

        if from_x == self.columns:
            self._incPoint(begin, -1)

        if end < begin:
            start_highl = end
            end_highl = begin
        else:
            if to_x == self.columns:
                self._incPoint(end, -1)
            start_highl = begin
            end_highl = end

        ##################################
        #self._sel_begin = begin
        #self._sel_topleft = self._sel_begin
        #self._sel_bottomright = end
        #self.getSelText(False)
        
        self._paintBackground(start_highl, end_highl, color)
        
    def _paintBackground(self, start_xy, end_xy, color):

        if start_xy == [-1, -1]:
            return

        histBR = [self._hist.lines, 0]
        hY = start_xy[0]
        hX = start_xy[1]
        m = []

        s = start_xy[:]
        eol_char = ' '

        #s es el begin!
        while s <= end_xy:
            # XXX in the first if branch, eol is scalar while in the else branch, it's a point !
            if s < histBR:
                eol = self._hist.getLineLen(hY)
                if hY == end_xy[0] and eol > end_xy[1]:
                    eol = end_xy[1] + 1
                while hX < eol:
                    ca = self._hist.getCells(hY, hX, 1)[0].b = color
                    self._incPoint(s)
                    hX += 1
                hY += 1
                hX = 0
                s = [hY, 0]
            else:
                eol = [s[0]+1, 0]
                self._incPoint(eol, -1)

                if eol < end_xy:
                    while eol > s:
                        pt = self._subPoints(eol, histBR)
                        ca = self._image[pt[0]][pt[1]]                        
                        self._image[pt[0]][pt[1]] = Ca(ca.c, ca.f, color, ca.r)

                        self._incPoint(eol, -1)
                elif eol == end_xy:
                    pt = self._subPoints(eol, histBR)
                else:
                    eol = end_xy

                while s <= eol:
                    pt = self._subPoints(s, histBR)
                    ca = self._image[pt[0]][pt[1]]

                    self._image[pt[0]][pt[1]] = Ca(ca.c, ca.f, color, ca.r)
                    self._incPoint(s)


                s = [eol[0]+1, 0]

    def checkSelection(self, from_, to):
        if self._sel_begin == [-1, -1]:
            return
        # Clear entire selection if overlaps region to be moved
        if self._overlapSelection(from_, to):
            self.clearSelection()

    def _overlapSelection(self, from_, to):
        assert isinstance(from_, list), from_
        assert isinstance(to, list), to
        scr_topleft = [self._hist.lines, 0]
        # Clear entire selection if overlaps region [from_, to]
        if self._sel_bottomright > self._addPoints(from_, scr_topleft) and \
               self._sel_topleft < self._addPoints(to, scr_topleft):
            return True
        return False


    # point manipulation ######################################################

    def _incPoint(self, point, inc=1):
        x = point[1] + inc
        if x < 0 or x >= self.columns:
            dy, x = divmod(x, self.columns)
            point[0] += dy
        point[1] = x

    def _addPoints(self, point1, point2):
        x = point1[1] + point2[1]
        y = point1[0] + point2[0]
        if x < 0 or x >= self.columns:
            dy, x = divmod(x, self.columns)
            y += dy
        return [y, x]

    def _subPoints(self, point1, point2):
        x = point1[1] - point2[1]
        y = point1[0] - point2[0]
        if x < 0 or x >= self.columns:
            dy, x = divmod(x, self.columns)
            y += dy
        return [y, x]