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]): if y >= len(w.buffer.lines) or x + swidth >= len(w.buffer.lines[y]):
x = 0 x = 0
y += 1 y += 1
#XYZ
while w.ishidden(y) and y < len(w.buffer.lines):
y += 1
else: else:
x += swidth x += swidth
count += 1 count += 1
@ -615,25 +618,40 @@ class Application(object):
for x in range(sx1, sx2): for x in range(sx1, sx2):
self.highlight_char(sy, x, fg, bg) 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): def draw_slot(self, i):
assert self.active_slot < len(self.bufferlist.slots), "only two" assert self.active_slot < len(self.bufferlist.slots), \
assert i < len(self.bufferlist.slots), "only three" "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] slot = self.bufferlist.slots[i]
if slot.window is None: if slot.window is None:
return return
w = slot.window w = slot.window
modename = w.mode.name() modename = w.mode.name()
if modename in w.buffer.highlights: self._draw_slot(i)
self._draw_slot_lit(i)
else:
self._draw_slot_raw(i)
# highlighted regions # highlighted regions
for (high_w, p1, p2, fg, bg) in self.highlighted_ranges: for (high_w, p1, p2, fg, bg) in self.highlighted_ranges:
if w is high_w and p2 >= w.first and p1 <= w.last: if w is high_w and p2 >= w.first and p1 <= w.last:
count = 0 count = 0
(x, y) = w.first.xy() x, y = w.first.xy()
px = p1.x px = p1.x
while count < slot.height: while count < slot.height:
if p1.y == y and px >= x and px - x < slot.width: 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]): if x + slot.width > len(w.buffer.lines[y]):
x = 0 x = 0
y += 1 y += 1
#XYZ
while w.ishidden(y) and y < len(w.buffer.lines):
y += 1
else: else:
x += slot.width x += slot.width
count += 1 count += 1
@ -657,123 +678,37 @@ class Application(object):
char = chr(self.win.inch(j + slot.y_offset, limit) & 255) char = chr(self.win.inch(j + slot.y_offset, limit) & 255)
attr = color.build('default', shade, 'bold') attr = color.build('default', shade, 'bold')
self.win.addstr(j + slot.y_offset, limit + w.mode.lmargin, char, attr) 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] slot = self.bufferlist.slots[i]
w = slot.window w = slot.window
modename = w.mode.name()
redattr = color.build_attr(color.pairs('red', 'default')) redattr = color.build_attr(color.pairs('red', 'default'))
x, y = w.first.xy()
(x, y) = w.first.xy() lines = w.buffer.lines
lines = w.buffer.lines count = 0
count = 0 lm, rm = w.mode.lmargin, w.mode.rmargin
lm, rm = w.mode.lmargin, w.mode.rmargin modename = w.mode.name()
swidth = slot.width - lm - rm lit = w.mode.name() in w.buffer.highlights
ended = False
while count < slot.height: while count < slot.height:
#draw some margins if lit:
self._draw_line_margins(slot, count, w, y, x) rlines = w.render_line_lit(y, slot.width - lm - rm)
# 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
else: else:
x += swidth rlines = w.render_line_raw(y, slot.width - lm - rm)
for j in range(0, len(rlines)):
# move on to the next physical line if lm:
count += 1 lcont = j > 0
for rstr in slot.window.mode.get_lmargin(w, y, x, ended, lcont):
def _draw_slot_lit(self, i): rstr.draw(self.win, slot.y_offset + count, 0)
slot = self.bufferlist.slots[i] for rstr in rlines[j]:
w = slot.window rstr.draw(self.win, slot.y_offset + count, 0 + lm)
modename = w.mode.name() if rm:
redattr = color.build_attr(color.pairs('red', 'default')) rcont = j < len(rlines) - 1
highlighter = w.buffer.highlights[modename] for rstr in slot.window.mode.get_rmargin(w, y, x, ended, rcont):
rstr.draw(self.win, slot.y_offset + count, slot.width - rm)
(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)
count += 1 count += 1
y += 1
ended = ended or y >= len(w.buffer.lines)
def draw_status_bar(self, slotname): def draw_status_bar(self, slotname):
slot = self.bufferlist.slots[slotname] slot = self.bufferlist.slots[slotname]

View File

@ -989,3 +989,23 @@ class SetModeTabWidth(Method):
return return
app.modes[mode].tabwidth = vargs['width'] app.modes[mode].tabwidth = vargs['width']
w.set_error('Default tab width set to %d' % app.modes[mode].tabwidth) 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 import color, method
from lex import Lexer from lex import Lexer
from point import Point from point import Point
from render import RenderString
DEBUG = False DEBUG = False
@ -224,27 +225,27 @@ class Fundamental(Handler):
self.show_line_numbers = True self.show_line_numbers = True
l = len(self.window.buffer.lines) l = len(self.window.buffer.lines)
self.lmargin = int(math.log(l, 10)) + 3 self.lmargin = int(math.log(l, 10)) + 3
self.rmargin = 0
def disable_line_numbers(self): def disable_line_numbers(self):
self.show_line_numbers = False self.show_line_numbers = False
self.lmargin = 0 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: if cont:
return ((0, '\\', color.build('red', 'default')),) c = "\\"
else: if w.hiddenindicator(y):
return ((0, ' ', color.build('red', 'default')),) bg = "green"
def get_lmargin(self, y, x=0, ended=False, cont=False): return [RenderString(c, 0, color.build(fg, bg))]
def get_lmargin(self, w, y, x, ended=False, cont=False):
lm = self.lmargin lm = self.lmargin
if ended: if ended:
i = int(math.log(y, 10)) + 1 i = int(math.log(y, 10)) + 1
s = ('% *s' % (lm - 1, '-' * i))[-lm:] + ' ' s = ('% *s' % (lm - 1, '-' * i))[-lm:] + ' '
elif x == 0: elif not cont:
s = ('% *d' % (lm - 1, y + 1))[-lm:] + ' ' s = ('% *d' % (lm - 1, y + 1))[-lm:] + ' '
else: else:
s = ' ' * lm s = ' ' * lm
return ((0, s, color.build('default', 'default', 'bold')),) return [RenderString(s, 0, color.build('default', 'default', 'bold'))]
# get mode name # get mode name
def name(self): def name(self):

View File

@ -4,6 +4,7 @@ import color, mode
from lex import Grammar, PatternRule, RegionRule from lex import Grammar, PatternRule, RegionRule
from method import Method, Argument from method import Method, Argument
from point import Point from point import Point
from render import RenderString
class HexSetByteOrder(Method): class HexSetByteOrder(Method):
'''Sets the byte-order to use to 'little', 'big', or 'native' order''' '''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): def get_address(self, y, x):
return (y * 16) + 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 lm = self.lmargin
if ended: if ended:
s = ' -------- ' s = ' -------- '
else: else:
addr = self.get_address(y, x) addr = self.get_address(y, x)
s = '0x%08x ' % addr 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: if ended:
return ((0, '', 0),) return ((0, '', 0),)
else: else:
@ -329,17 +330,17 @@ class Hex(mode.Fundamental):
i = self.window.buffer.cursorx_to_datax(cy, cx) i = self.window.buffer.cursorx_to_datax(cy, cx)
if i is None: if i is None:
rmargins = ((0, s, self.cgreen),) rmargins = (RenderString(s, 0, self.cgreen),)
elif i < len(s): elif i < len(s):
rmargins = ((0, s[0:i], self.cgreen), rmargins = (RenderString(s[0:i], 0, self.cgreen),
(i, s[i], self.ccursor), RenderString(s[i], i, self.ccursor),
(i + 1, s[i+1:], self.cgreen)) RenderString(s[i+1:], i + 1, self.cgreen))
else: else:
rmargins= ((0, s[0:i], self.cgreen), rmargins= (RenderString(s[0:i], 0, self.cgreen),
(i, s[i], self.cgreen),) RenderString(s[i], i, self.cgreen))
return rmargins return rmargins
else: else:
return ((0, s, self.cgreen),) return (RenderString(s, 0, self.cgreen),)
def read_data(self, cy, ix, size): def read_data(self, cy, ix, size):
b = self.window.buffer 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 os.path, string
import color, highlight, regex import color, highlight, regex
from point import Point from point import Point
from render import RenderString
WORD_LETTERS = list(string.letters + string.digits) 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. # error. both buffer and window need to be aware of this possibility for points.
class Window(object): class Window(object):
boxtype = 'window'
margins = ((80, 'blue'),) margins = ((80, 'blue'),)
margins_visible = False margins_visible = False
def __init__(self, b, a, height=24, width=80, mode_name=None): def __init__(self, b, a, height=24, width=80, mode_name=None):
self.buffer = b self.buffer = b
self.application = a self.application = a
self.height = height
self.width = width
self.first = Point(0, 0) self.first = Point(0, 0)
self.last = None self.last = None
self.cursor = Point(0, 0) self.cursor = Point(0, 0)
self.mark = None self.mark = None
self.active_point = None self.active_point = None
self.input_line = ""
self.height = height self.hidden_ranges = {}
self.width = width self.hidden_lines = {}
self.input_line = ""
if mode_name is not None: if mode_name is not None:
pass pass
@ -585,6 +585,31 @@ class Window(object):
else: else:
return self.buffer.lines[y][x] 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 # undo/redo
def undo(self): def undo(self):
p = self.buffer.undo() p = self.buffer.undo()
@ -629,8 +654,78 @@ class Window(object):
def get_next_token_except_type_regex(self, p, name, regex): def get_next_token_except_type_regex(self, p, name, regex):
l = lambda t: t.name != name or regex.match(t.string) l = lambda t: t.name != name or regex.match(t.string)
return self.get_next_token_by_lambda(p, l) return self.get_next_token_by_lambda(p, l)
def get_next_token_by_types(self, p, *names): def get_next_token_by_types(self, p, *names):
return self.get_next_token_by_lambda(p, lambda t: t.name in names) return self.get_next_token_by_lambda(p, lambda t: t.name in names)
def get_next_token_except_types(self, p, *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) 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