abstracted drawing code

--HG--
branch : pmacs2
This commit is contained in:
moculus 2008-04-21 03:10:25 +00:00
parent ba7cc48dfb
commit f69c3b1995
6 changed files with 212 additions and 148 deletions

View File

@ -581,6 +581,9 @@ class Application(object):
if y >= len(w.buffer.lines) or x + swidth >= len(w.buffer.lines[y]):
x = 0
y += 1
#XYZ
while w.ishidden(y) and y < len(w.buffer.lines):
y += 1
else:
x += swidth
count += 1
@ -615,25 +618,40 @@ class Application(object):
for x in range(sx1, sx2):
self.highlight_char(sy, x, fg, bg)
def map_point(self, p):
count = 0
x, y = w.first.xy()
while count < slot.height:
if p1.y == y and p1.x >= x and p1.x - x < slot.width:
return (count, x)
if x + slot.width > len(w.buffer.lines[y]):
x = 0
y += 1
#XYZ
while w.ishidden(y) and y < len(w.buffer.lines):
y += 1
else:
x += slot.width
count += 1
def draw_slot(self, i):
assert self.active_slot < len(self.bufferlist.slots), "only two"
assert i < len(self.bufferlist.slots), "only three"
assert self.active_slot < len(self.bufferlist.slots), \
"strange: %d < %d" % (self.active_slot, len(self.bufferlist.slots))
assert i < len(self.bufferlist.slots), \
"puzzling: %d < %d" % (i, len(self.bufferlist.slots))
slot = self.bufferlist.slots[i]
if slot.window is None:
return
w = slot.window
modename = w.mode.name()
if modename in w.buffer.highlights:
self._draw_slot_lit(i)
else:
self._draw_slot_raw(i)
self._draw_slot(i)
# highlighted regions
for (high_w, p1, p2, fg, bg) in self.highlighted_ranges:
if w is high_w and p2 >= w.first and p1 <= w.last:
count = 0
(x, y) = w.first.xy()
x, y = w.first.xy()
px = p1.x
while count < slot.height:
if p1.y == y and px >= x and px - x < slot.width:
@ -646,6 +664,9 @@ class Application(object):
if x + slot.width > len(w.buffer.lines[y]):
x = 0
y += 1
#XYZ
while w.ishidden(y) and y < len(w.buffer.lines):
y += 1
else:
x += slot.width
count += 1
@ -658,122 +679,36 @@ class Application(object):
attr = color.build('default', shade, 'bold')
self.win.addstr(j + slot.y_offset, limit + w.mode.lmargin, char, attr)
def _draw_line_margins(self, slot, count, w, y, x):
lm, rm = w.mode.lmargin, w.mode.rmargin
if y >= len(w.buffer.lines):
ended = True
cont = False
else:
ended = False
cont = x + slot.width - lm - rm < len(w.buffer.lines[y])
i = slot.y_offset + count
if lm:
groups = w.mode.get_lmargin(y, x, ended, cont)
for (x, lmargin, lattr) in groups:
self.win.addstr(i, x, lmargin, lattr)
if rm:
groups = w.mode.get_rmargin(y, x, ended, cont)
for (x, rmargin, rattr) in groups:
self.win.addstr(i, slot.width - rm + x, rmargin, rattr)
def _draw_slot_raw(self, i):
def _draw_slot(self, i):
slot = self.bufferlist.slots[i]
w = slot.window
modename = w.mode.name()
redattr = color.build_attr(color.pairs('red', 'default'))
(x, y) = w.first.xy()
lines = w.buffer.lines
count = 0
lm, rm = w.mode.lmargin, w.mode.rmargin
swidth = slot.width - lm - rm
x, y = w.first.xy()
lines = w.buffer.lines
count = 0
lm, rm = w.mode.lmargin, w.mode.rmargin
modename = w.mode.name()
lit = w.mode.name() in w.buffer.highlights
ended = False
while count < slot.height:
#draw some margins
self._draw_line_margins(slot, count, w, y, x)
# if the window has no more lines, display the "empty" symbol
if y >= len(lines):
self.win.addstr(slot.y_offset + count, 0 + lm, '~', redattr)
count += 1
continue
# get the line to draw and draw it
line = lines[y]
s = line[x:x + swidth]
self.win.addstr(slot.y_offset + count, 0 + lm, s)
# see if we need to draw a line-continuation symbol or not
if x + swidth >= len(line):
x = 0
y += 1
if lit:
rlines = w.render_line_lit(y, slot.width - lm - rm)
else:
x += swidth
# move on to the next physical line
count += 1
def _draw_slot_lit(self, i):
slot = self.bufferlist.slots[i]
w = slot.window
modename = w.mode.name()
redattr = color.build_attr(color.pairs('red', 'default'))
highlighter = w.buffer.highlights[modename]
(x, y) = w.first.xy()
j = 0
count = 0
(lm, rm) = (w.mode.lmargin, w.mode.rmargin)
swidth = slot.width - lm - rm
assert len(w.buffer.lines) == len(highlighter.tokens)
while count < slot.height:
if y < len(w.buffer.lines):
while j < len(highlighter.tokens[y]):
token = highlighter.tokens[y][j]
if token.string.endswith('\n'):
tstring = token.string[:-1]
else:
tstring = token.string
assert token.y == y, '%d == %d' % (token.y, y)
s_offset = max(x - token.x, 0)
x_offset = max(token.x - x, 0)
assert x_offset <= swidth, '%d <= %d' % (x_offset, swidth)
s = tstring[s_offset:]
token_done = x_offset + len(s) <= swidth
token_wrap = x_offset + len(s) > swidth
# for debugging things like lexing/relexing/etc.
if token._debug:
attr = color.build('blue', 'green')
elif token.color:
attr = color.build(*token.color)
else:
attr = color.build("default", "default")
k = slot.y_offset + count
self.win.addstr(k, x_offset + lm, s[:swidth - x_offset], attr)
if token_wrap:
self._draw_line_margins(slot, count, w, y, x)
x += swidth
count += 1
if token_done:
j += 1
if count >= slot.height:
break
# we have finished this logical line of tokens
self._draw_line_margins(slot, count, w, y, x)
j = x = 0
y += 1
count += 1
else:
self._draw_line_margins(slot, count, w, y, x)
self.win.addstr(slot.y_offset + count, 0 + lm, '~', redattr)
rlines = w.render_line_raw(y, slot.width - lm - rm)
for j in range(0, len(rlines)):
if lm:
lcont = j > 0
for rstr in slot.window.mode.get_lmargin(w, y, x, ended, lcont):
rstr.draw(self.win, slot.y_offset + count, 0)
for rstr in rlines[j]:
rstr.draw(self.win, slot.y_offset + count, 0 + lm)
if rm:
rcont = j < len(rlines) - 1
for rstr in slot.window.mode.get_rmargin(w, y, x, ended, rcont):
rstr.draw(self.win, slot.y_offset + count, slot.width - rm)
count += 1
y += 1
ended = ended or y >= len(w.buffer.lines)
def draw_status_bar(self, slotname):
slot = self.bufferlist.slots[slotname]

View File

@ -989,3 +989,23 @@ class SetModeTabWidth(Method):
return
app.modes[mode].tabwidth = vargs['width']
w.set_error('Default tab width set to %d' % app.modes[mode].tabwidth)
class HideRange(Method):
def _execute(self, w, **vargs):
cursor = w.logical_cursor()
if cursor.y < w.mark.y:
y1, y2 = cursor.y, w.mark.y
elif w.mark.y < cursor.y:
y1, y2 = w.mark.y, cursor.y
else:
w.set_error("No line range selected")
w.hide(y1, y2)
w.set_error("Lines %d through %d hidden" % (y1, y2))
class UnhideLine(Method):
def _execute(self, w, **vargs):
cursor = w.logical_cursor()
if w.ishidden(cursor.y):
w.unhide(cursor.y)
w.set_error("Line %d is no longer hidden" % cursor.y)
else:
w.set_error("Line %d is not hidden" % cursor.y)

View File

@ -2,6 +2,7 @@ import math, os, sets, string
import color, method
from lex import Lexer
from point import Point
from render import RenderString
DEBUG = False
@ -224,27 +225,27 @@ class Fundamental(Handler):
self.show_line_numbers = True
l = len(self.window.buffer.lines)
self.lmargin = int(math.log(l, 10)) + 3
self.rmargin = 0
def disable_line_numbers(self):
self.show_line_numbers = False
self.lmargin = 0
self.rmargin = 1
def get_rmargin(self, y, x, ended=False, cont=False):
def get_rmargin(self, w, y, x, ended=False, cont=False):
c, fg, bg = " ", "red", "default"
if cont:
return ((0, '\\', color.build('red', 'default')),)
else:
return ((0, ' ', color.build('red', 'default')),)
def get_lmargin(self, y, x=0, ended=False, cont=False):
c = "\\"
if w.hiddenindicator(y):
bg = "green"
return [RenderString(c, 0, color.build(fg, bg))]
def get_lmargin(self, w, y, x, ended=False, cont=False):
lm = self.lmargin
if ended:
i = int(math.log(y, 10)) + 1
s = ('% *s' % (lm - 1, '-' * i))[-lm:] + ' '
elif x == 0:
elif not cont:
s = ('% *d' % (lm - 1, y + 1))[-lm:] + ' '
else:
s = ' ' * lm
return ((0, s, color.build('default', 'default', 'bold')),)
return [RenderString(s, 0, color.build('default', 'default', 'bold'))]
# get mode name
def name(self):

View File

@ -4,6 +4,7 @@ import color, mode
from lex import Grammar, PatternRule, RegionRule
from method import Method, Argument
from point import Point
from render import RenderString
class HexSetByteOrder(Method):
'''Sets the byte-order to use to 'little', 'big', or 'native' order'''
@ -310,16 +311,16 @@ class Hex(mode.Fundamental):
def get_address(self, y, x):
return (y * 16) + x
def get_lmargin(self, y, x=0, ended=False, cont=False):
def get_lmargin(self, w, y, x, ended=False, cont=False):
lm = self.lmargin
if ended:
s = ' -------- '
else:
addr = self.get_address(y, x)
s = '0x%08x ' % addr
return ((0, s, self.ccyan),)
return (RenderString(s, 0, self.ccyan),)
def get_rmargin(self, y, x=0, ended=False, cont=False):
def get_rmargin(self, w, y, x, ended=False, cont=False):
if ended:
return ((0, '', 0),)
else:
@ -329,17 +330,17 @@ class Hex(mode.Fundamental):
i = self.window.buffer.cursorx_to_datax(cy, cx)
if i is None:
rmargins = ((0, s, self.cgreen),)
rmargins = (RenderString(s, 0, self.cgreen),)
elif i < len(s):
rmargins = ((0, s[0:i], self.cgreen),
(i, s[i], self.ccursor),
(i + 1, s[i+1:], self.cgreen))
rmargins = (RenderString(s[0:i], 0, self.cgreen),
RenderString(s[i], i, self.ccursor),
RenderString(s[i+1:], i + 1, self.cgreen))
else:
rmargins= ((0, s[0:i], self.cgreen),
(i, s[i], self.cgreen),)
rmargins= (RenderString(s[0:i], 0, self.cgreen),
RenderString(s[i], i, self.cgreen))
return rmargins
else:
return ((0, s, self.cgreen),)
return (RenderString(s, 0, self.cgreen),)
def read_data(self, cy, ix, size):
b = self.window.buffer

12
render.py Normal file
View File

@ -0,0 +1,12 @@
import color
from point import Point
class RenderString(object):
def __init__(self, s, x=0, attrs=None):
if attrs is None:
attrs = color.build('default', 'default')
self.string = s
self.x = x
self.attrs = attrs
def draw(self, cwin, y, x):
cwin.addstr(y, self.x + x, self.string, self.attrs)

119
window.py
View File

@ -1,6 +1,7 @@
import os.path, string
import color, highlight, regex
from point import Point
from render import RenderString
WORD_LETTERS = list(string.letters + string.digits)
@ -11,23 +12,22 @@ WORD_LETTERS = list(string.letters + string.digits)
# error. both buffer and window need to be aware of this possibility for points.
class Window(object):
boxtype = 'window'
margins = ((80, 'blue'),)
margins_visible = False
def __init__(self, b, a, height=24, width=80, mode_name=None):
self.buffer = b
self.application = a
self.height = height
self.width = width
self.first = Point(0, 0)
self.last = None
self.cursor = Point(0, 0)
self.mark = None
self.active_point = None
self.height = height
self.width = width
self.input_line = ""
self.first = Point(0, 0)
self.last = None
self.cursor = Point(0, 0)
self.mark = None
self.active_point = None
self.input_line = ""
self.hidden_ranges = {}
self.hidden_lines = {}
if mode_name is not None:
pass
@ -585,6 +585,31 @@ class Window(object):
else:
return self.buffer.lines[y][x]
# hiding stuff
def hide(self, y1, y2):
if y2 == 0:
return
for (py1, py2) in self.hidden_ranges.itervalues():
if ((y1 >= py1 and y1 < py2) or
(y2 >= py1 and y2 < py2) or
(y1 <= py1 and y2 >= py2)):
return
for i in range(y1, y2):
self.hidden_lines[i] = y2
self.hidden_ranges[y2] = (y1, y2)
def ishidden(self, y):
return self.hidden_lines.setdefault(y, False)
def hiddenindicator(self, y):
return y in self.hidden_ranges
def unhide(self, y):
py2 = self.ishidden(y)
if not py2:
return
py1 = self.hidden_ranges[py2][0]
del self.hidden_ranges[py2]
for i in range(py1, py2):
self.hidden_lines[i] = False
# undo/redo
def undo(self):
p = self.buffer.undo()
@ -629,8 +654,78 @@ class Window(object):
def get_next_token_except_type_regex(self, p, name, regex):
l = lambda t: t.name != name or regex.match(t.string)
return self.get_next_token_by_lambda(p, l)
def get_next_token_by_types(self, p, *names):
return self.get_next_token_by_lambda(p, lambda t: t.name in names)
def get_next_token_except_types(self, p, *names):
return self.get_next_token_by_lambda(p, lambda t: t.name not in names)
# application drawing
#
# render methods return a list of lists of RenderString objects
# (i.e. multiple physical lines, each containing multiple areas to be drawn)
def render_line(self, y, width):
modename = self.mode.name()
if modename in self.buffer.highlights:
return self.render_line_lit(y, width)
else:
return self.render_line_raw(y, width)
def render_line_raw(self, y, width):
if y >= len(self.buffer.lines):
return [[RenderString('~', 0, color.build('red', 'default'))]]
x = 0
line = self.buffer.lines[y]
lines = []
if line:
while x < len(line):
lines.append(tuple([RenderString(line[x:x + width], 0)]))
x += width
else:
lines.append(tuple([RenderString('', 0)]))
return lines
def render_line_lit(self, y, width):
if y >= len(self.buffer.lines):
return [[RenderString('~', 0, color.build('red', 'default'))]]
modename = self.mode.name()
highlighter = self.buffer.highlights[modename]
line = []
lines = []
j = x = 0
while j < len(highlighter.tokens[y]):
# get our token, and do some basic checking/bleaching
token = highlighter.tokens[y][j]
if token.string.endswith('\n'):
tstring = token.string[:-1]
else:
tstring = token.string
assert token.y == y, '%d == %d' % (token.y, y)
# figure out what portion of the string to use
s_offset = max(x - token.x, 0)
s = tstring[s_offset:]
# for debugging things like lexing/relexing/etc.
if token._debug:
attr = color.build('blue', 'green')
elif token.color:
attr = color.build(*token.color)
else:
attr = color.build("default", "default")
# ok, so add a region with data, position, and color info
x_offset = max(token.x - x, 0)
line.append(RenderString(s[:width - x_offset], x_offset, attr))
# see if the token is wrapping, or if we move on to the next one
if x_offset + len(s) > width:
lines.append(line)
line = []
x += width
else:
j += 1
lines.append(line)
return lines