[shpy-commit] r2877 - in shpy/trunk/dist/shpy: . net ui_pygame
arigo@codespeak.net
arigo@codespeak.net
Thu, 22 Jan 2004 19:08:38 +0100 (MET)
Author: arigo
Date: Thu Jan 22 19:08:33 2004
New Revision: 2877
Added:
shpy/trunk/dist/shpy/ui_pygame/
shpy/trunk/dist/shpy/ui_pygame/__init__.py
shpy/trunk/dist/shpy/ui_pygame/autopath.py (props changed)
- copied unchanged from r2872, shpy/trunk/dist/shpy/autopath.py
shpy/trunk/dist/shpy/ui_pygame/decorate.py
shpy/trunk/dist/shpy/ui_pygame/ui_pygame.py (contents, props changed)
- copied, changed from r2873, shpy/trunk/dist/shpy/ui_pygame.py
Removed:
shpy/trunk/dist/shpy/ui_pygame.py
Modified:
shpy/trunk/dist/shpy/net/shared.py
shpy/trunk/dist/shpy/net/structure.py
Log:
Complete reorganization. Now ui_pygame.py looks nicer and doesn't need
to know about Structures any more: this is all done with "Decorators"
which are client-side classes wrapping the low-level Structures that are
passed around between machines.
Most of the hiding stuff is done by the classes in decorate.py.
(this is a join hpk+arigo commit)
Modified: shpy/trunk/dist/shpy/net/shared.py
==============================================================================
--- shpy/trunk/dist/shpy/net/shared.py (original)
+++ shpy/trunk/dist/shpy/net/shared.py Thu Jan 22 19:08:33 2004
@@ -14,30 +14,31 @@
class Structure:
- def __init__(self, **attributes):
+ def __init__(self, typeid, **attributes):
+ self.typeid = typeid
self.__dict__.update(attributes)
-
-def structurelist(*args):
+def cellstructure(*args):
l = []
for arg in args:
- l.append(Structure(content=arg))
- return l
+ l.append(Structure("line:0", content=arg))
+ return Structure("cell:0", lines=Structure("list:0", listitems=l))
-root = Structure()
+root = Structure("root:0")
-root.cells = Structure(list = [
- Structure(lines = structurelist(
+root.cells = Structure("list:0", listitems = [
+ cellstructure(
'def f():',
' for i in range(10):',
' print i * 42',
' break',
- )),
- Structure(lines = structurelist(
+ ),
+ cellstructure(
'def f():',
' print 42',
- )),
+ ),
]
)
-root.users = Structure()
+root.users = Structure("list:0", listitems = [])
+
Modified: shpy/trunk/dist/shpy/net/structure.py
==============================================================================
--- shpy/trunk/dist/shpy/net/structure.py (original)
+++ shpy/trunk/dist/shpy/net/structure.py Thu Jan 22 19:08:33 2004
@@ -7,8 +7,10 @@
+ATOMIC = (int, float, str, unicode, type(None))
+
def represent(obj, ref):
- if isinstance(obj, (int, float, str, unicode)):
+ if isinstance(obj, ATOMIC):
return repr(obj)
elif isinstance(obj, dict):
items = ['%s: %s' % (represent(key,ref), represent(value,ref))
Deleted: /shpy/trunk/dist/shpy/ui_pygame.py
==============================================================================
--- /shpy/trunk/dist/shpy/ui_pygame.py Thu Jan 22 19:08:33 2004
+++ (empty file)
@@ -1,337 +0,0 @@
-import autopath
-
-import sys
-import pygame
-from pygame.locals import *
-import shpy.net.register
-from shpy.net.structure import Structure, representstructure, getstructureid
-from shpy import info
-
-RESOLUTION = (768, 512)
-#RESOLUTION = (300, 300)
-#FONT = 'lucon.ttf'
-FONT = None
-HEIGHT = 24
-
-WAKEUPEVENT = USEREVENT
-
-
-KEYMAP = {}
-for name, value in pygame.locals.__dict__.items():
- if name.startswith('K_'):
- KEYMAP[value] = name
-
-##LINECACHE = {}
-##def rendertext(font, text, fgcolor, bgcolor=(255,255,255,255)):
-## key = (font, text, fgcolor, bgcolor)
-## try:
-## image = LINECACHE[key]
-## except KeyError:
-## image = LINECACHE[key] = font.render(text, 1, fgcolor, bgcolor)
-## return image
-
-
-class Terminal:
-
- def __init__(self, sharedserver, execserver):
- ns = {'terminal': self}
- self.servergateway = shpy.net.register.ServerGateway(sharedserver, ns)
- self.execgateway = shpy.net.register.ExecGateway(execserver)
-
- pygame.init()
- pygame.key.set_repeat(500,30)
- self.screen = pygame.display.set_mode(RESOLUTION, RESIZABLE)
- self.font = pygame.font.Font(FONT, HEIGHT)
- self.fontheight = self.font.size('X')[1]
- self.root = self.servergateway.registerclient()
- self.changed = {}
- username = info.getusername()
- try:
- self.cursor = getattr(self.root.users, username)
- except AttributeError:
- self.cursor = Structure(x=0, color=info.getcolor())
- setattr(self.root.users, username, self.cursor)
- #print self.root.users.__dict__
- if not hasattr(self.cursor, 'cell') or self.cursor.cell not in self.root.cells.list:
- self.cursor.cell = self.root.cells.list[-1]
- if not hasattr(self.cursor, 'line') or self.cursor.line not in self.cursor.cell.lines:
- self.cursor.line = self.cursor.cell.lines[-1]
- self.lastline_cell = self.cursor.cell
- self.lastline = self.lastline_cell.lines[-1]
- self.changed[self.cursor] = 1
- self.changed[self.root.users] = 1
-
- def repaint(self):
- while 1:
- self.screen.fill((255,255,255))
- self.line2ypos = {}
-
- index = self.lastline_cell.lines.index(self.lastline)
- lines = self.lastline_cell.lines[:index+1]
- ypos = self.drawlinesbackwards(lines)
- if ypos > 0:
- index = self.root.cells.list.index(self.lastline_cell)
- remainingcells = self.root.cells.list[:index]
- while remainingcells and ypos > 0:
- cell = remainingcells.pop()
- ypos = self.drawspacing(ypos)
- ypos = self.drawlinesbackwards(cell.lines, ypos)
- cursor_in_view = self.drawcursors()
- pygame.display.flip()
- if cursor_in_view:
- break
- self.scroll_a_bit_towards_cursor()
-
- def drawcursors(self):
- cursors = self.root.users.__dict__.values()
- assert self.cursor in cursors
- cursor_in_view = False
- for cursor in cursors:
- x = cursor.x
- line = cursor.line
- try:
- ypos = self.line2ypos[line]
- except KeyError:
- continue
- char = line.content[x:x+1]
- if not char:
- char = ' '
- charimage = self.font.render(char, 1, (255,255,255), cursor.color)
- startoflineimagesize = self.font.size(line.content[:x])
- self.screen.blit(charimage, (startoflineimagesize[0], ypos))
- if cursor is self.cursor and ypos >= 0:
- cursor_in_view = True
- return cursor_in_view
-
- def drawspacing(self, ypos):
- self.screen.fill((160, 160, 160), Rect(0, ypos-2, 100, 1))
- return ypos - 3
-
- def drawlinesbackwards(self, lines, ypos = None):
- if ypos is None:
- ypos = self.screen.get_size()[1]
- l = lines[:]
- while l and ypos > 0:
- line = l.pop()
- lineimage = self.font.render(line.content or ' ', 1, (0,0,0))
- ypos -= self.fontheight
- self.screen.blit(lineimage, (0, ypos))
- self.line2ypos[line] = ypos
- return ypos
-
- def scroll_a_bit_towards_cursor(self):
- if self.cursor.cell is self.lastline_cell:
- cursorindex = self.cursor.cell.lines.index(self.cursor.line)
- lastlineindex = self.lastline_cell.lines.index(self.lastline)
- else:
- cursorindex = self.root.cells.list.index(self.cursor.cell)
- lastlineindex = self.root.cells.list.index(self.lastline_cell)
- if cursorindex < lastlineindex:
- self.lastline, self.lastline_cell = self.previousline(self.lastline, self.lastline_cell)
- else:
- self.lastline, self.lastline_cell = self.nextline(self.lastline, self.lastline_cell)
-
- def previousline(self, line, cell):
- index = cell.lines.index(line)
- if index > 0:
- return cell.lines[index-1], cell
- else:
- # XXX this changes if we have output cells ...
- index = self.root.cells.list.index(cell)
- if index > 0:
- cell = self.root.cells.list[index-1]
- return cell.lines[-1], cell
- return line, cell
-
- def nextline(self, line, cell):
- index = cell.lines.index(line)
- if index + 1 < len(cell.lines):
- return cell.lines[index+1], cell
- else:
- # XXX this changes if we have output cells ...
- index = self.root.cells.list.index(cell)
- if index + 1 < len(self.root.cells.list):
- cell = self.root.cells.list[index+1]
- return cell.lines[0], cell
- return line, cell
-
-## def drawcell(self, screen, cell):
-## self.scroll_cursor_into_view(screen)
-## start = -self.vscroll//self.fontheight
-## stop = (-self.vscroll + screen.get_size()[1])//self.fontheight + 1
-## ypos = self.vscroll + start*self.fontheight
-## for line in cell.lines[start:stop]:
-## lineimage = self.font.render(line or ' ', 1, (0,0,0))
-## screen.blit(lineimage, (0, ypos))
-## ypos += self.fontheight
-## self.drawcursors(screen, cell)
-
-## def char_pos(self, cell, x, y):
-## line = cell.lines[y]
-## return (self.font.size(line[:x])[0],
-## y*self.fontheight + self.vscroll)
-
-## def char_rect(self, cell, x, y):
-## c = cell.lines[y][x:x+1]
-## if not c:
-## c = ' '
-## return Rect(self.char_pos(x, y), self.font.size(c))
-
-## def scroll_cursor_into_view(self, screen):
-## ypos = self.vscroll + self.cursor.y * self.fontheight
-## if ypos < 0:
-## self.vscroll -= ypos
-## elif ypos + self.fontheight > screen.get_size()[1]:
-## self.vscroll -= ypos + self.fontheight - screen.get_size()[1]
-
-## def drawcursors(self, screen, cell):
-## for cursor in self.root.users.__dict__.values():
-## x = cursor.x
-## y = cursor.y
-## if y >= len(cell.lines):
-## y = len(cell.lines)-1
-## char = cell.lines[y][x:x+1]
-## if not char:
-## char = ' '
-## charimage = self.font.render(char, 1, (255,255,255), cursor.color)
-## screen.blit(charimage, self.char_pos(cell, x, y))
-
- def PRINTABLE_KEY(self, event):
- assert event.unicode
- x = self.cursor.x
- line = self.cursor.line
- line.content = line.content[:x] + event.unicode + line.content[x:]
- self.cursor.x += len(event.unicode)
- self.changed[line] = 1
-
- def CTRL_K_a(self, event):
- self.cursor.x = 0
-
- def CTRL_K_e(self, event):
- self.cursor.x = len(self.cursor.line.content)
-
- def K_UP(self, event):
- self.cursor.line, self.cursor.cell = self.previousline(self.cursor.line, self.cursor.cell)
-
- def K_DOWN(self, event):
- self.cursor.line, self.cursor.cell = self.nextline(self.cursor.line, self.cursor.cell)
-
- def K_LEFT(self, event):
- if self.cursor.x > 0:
- self.cursor.x -= 1
-
- def K_RIGHT(self, event):
- if self.cursor.x < len(self.cursor.line.content):
- self.cursor.x += 1
-
- def K_BACKSPACE(self, event):
- x = self.cursor.x
- line = self.cursor.line
- if x > 0:
- line.content = line.content[:x-1] + line.content[x:]
- self.changed[line] = 1
- self.cursor.x -= 1
- else:
- index = self.cursor.cell.lines.index(line)
- if index > 0:
- prevline = self.cursor.cell.lines[index-1]
- prevline.content += line.content
- del self.cursor.cell.lines[index]
- self.changed[prevline] = 1
- self.changed[self.cursor.cell] = 1
- self.cursor.line = prevline
- self.cursor.x = len(prevline.content)
-
- def K_RETURN(self, event):
- x = self.cursor.x
- line = self.cursor.line
- newline = Structure(content=line.content[x:])
- line.content = line.content[:x]
- index = self.cursor.cell.lines.index(line)
- self.cursor.cell.lines.insert(index+1, newline)
- self.cursor.line = newline
- self.cursor.x = 0
- self.changed[self.cursor.cell] = 1
- self.changed[line] = 1
- self.changed[newline] = 1
-
- def ALT_K_RETURN(self, event):
- inputcell = self.cursor.cell
- lines = [line.content for line in inputcell.lines]
- outputcell = getattr(inputcell, 'outputcell', None)
- if outputcell is None:
- outputcell = Structure()
- inputcell.outputcell = outputcell
- index = self.root.cells.list.index(inputcell)
- self.root.cells.list.insert(index+1, outputcell)
- self.changed[inputcell] = 1
- self.changed[self.root.cells] = 1
- outputcell.lines = [Structure(content="")]
- self.changed[outputcell] = 1
- self.changed[outputcell.lines[0]] = 1
- def fill_output_cell(value):
- lines = value.split('\n')
- outputcell.lines = [Structure(content=line) for line in lines]
- self.changed[outputcell] = 1
- for s in outputcell.lines:
- self.changed[s] = 1
- self.execgateway.userexec_remote(lines, fill_output_cell)
-
- def run(self):
- self.invalid = True
- while 1:
- if self.changed:
- self.servergateway.notifychanges(*self.changed.keys())
- self.changed.clear()
- self.invalid = True
- if self.invalid:
- self.invalid = False
- self.repaint()
- event = pygame.event.wait()
- print event
- if event.type == KEYDOWN:
- keynames = []
- if event.key in KEYMAP:
- keyname = KEYMAP[event.key]
- if event.mod & KMOD_ALT:
- keyname = 'ALT_' + keyname
- if event.mod & KMOD_CTRL:
- keyname = 'CTRL_' + keyname
- keynames.append(keyname)
- if event.unicode:
- keynames.append("PRINTABLE_KEY")
- for keyname in keynames:
- method = getattr(self, keyname, None)
- if method:
- method(event)
- self.changed[self.cursor] = 1
- break
- if event.type == QUIT:
- break
- if event.type == VIDEORESIZE:
- self.screen = pygame.display.set_mode(event.size, RESIZABLE)
- self.invalid = True
-
- def close(self):
- print "trying to quit the gateway ..."
- self.servergateway.exit()
- print "calling pygame.quit()"
- pygame.quit()
-
- def postrepaintevent(self):
- self.invalid = True
- pygame.event.post(pygame.event.Event(WAKEUPEVENT))
-
-
-if __name__ == '__main__':
- sharedserver = sys.argv[1]
- if len(sys.argv) > 2:
- execserver = sys.argv[2]
- else:
- execserver = 'localhost:8888'
- t = Terminal(sharedserver, execserver)
- try:
- t.run()
- finally:
- t.close()
-
Added: shpy/trunk/dist/shpy/ui_pygame/__init__.py
==============================================================================
Added: shpy/trunk/dist/shpy/ui_pygame/decorate.py
==============================================================================
--- (empty file)
+++ shpy/trunk/dist/shpy/ui_pygame/decorate.py Thu Jan 22 19:08:33 2004
@@ -0,0 +1,156 @@
+from __future__ import generators
+import inspect, new
+from shpy.net.structure import Structure, ATOMIC
+
+
+class Decorator:
+ #typeid = 'structure:0' # should always be overridden by subclasses
+ #slots = ()
+
+ def __init__(self, **kwds):
+ if not hasattr(self.__class__, 'typeid2class'):
+ raise TypeError, 'cannot instantiate class (use collectdecorators)'
+ _value = Structure(typeid = self.__class__.typeid)
+ self.__dict__['_value'] = _value
+ for key, value in kwds.items():
+ if key not in self.__class__.slots:
+ raise TypeError, 'no %r slot allowed' % (key,)
+ setattr(self._value, key, undecorate(value))
+ self.init_more()
+ self.changed()
+
+ def init_more(self):
+ pass
+
+ def __eq__(self, other):
+ return self.__class__ is other.__class__ and self._value is other._value
+
+ def __ne__(self, other):
+ return not (self == other)
+
+ def __hash__(self):
+ return id(self._value)
+
+ def __getattr__(self, attr):
+ x = getattr(self._value, attr)
+ return self.decorate(x)
+
+ def __setattr__(self, attr, value):
+ if attr not in self.__class__.slots:
+ self.__dict__[attr] = value
+ else:
+ setattr(self._value, attr, undecorate(value))
+ self.changed()
+
+ def changed(self):
+ self._changedict[self._value] = True
+
+ def decorate(cls, value):
+ if isinstance(value, Structure):
+ typeid2class = cls.typeid2class
+ try:
+ classobj = typeid2class[value.typeid]
+ except KeyError:
+ raise DecorateError, 'no decorator for %r' % (value.typeid,)
+ decorator = new.instance(classobj, {'_value': value})
+ decorator.init_more()
+ return decorator
+ elif isinstance(value, ATOMIC):
+ return value
+ elif isinstance(value, tuple):
+ return tuple([cls.decorate(v) for v in value])
+ else:
+ raise TypeError, '%r cannot be decorated' % (value,)
+ decorate = classmethod(decorate)
+
+
+class List(Decorator):
+ typeid = 'list:0'
+ slots = 'listitems',
+
+ def __init__(self, items=[]):
+ Decorator.__init__(self)
+ self._value.listitems = []
+ for item in items:
+ self.append(item)
+
+ def append(self, item):
+ self._value.listitems.append(undecorate(item))
+ self.changed()
+
+ def insertafter(self, previtem, newitem):
+ previtem = undecorate(previtem)
+ newitem = undecorate(newitem)
+ index = self._value.listitems.index(previtem)
+ self._value.listitems.insert(index+1, newitem)
+ self.changed()
+
+ def insertbefore(self, nextitem, newitem):
+ nextitem = undecorate(nextitem)
+ newitem = undecorate(newitem)
+ index = self._value.listitems.index(nextitem)
+ self._value.listitems.insert(index, newitem)
+ self.changed()
+
+ def remove(self, item):
+ index = self.index(item)
+ del self._value.listitems[index]
+ self.changed()
+
+ def prev(self, item):
+ item = undecorate(item)
+ index = self._value.listitems.index(item)
+ if index > 0:
+ return self.decorate(self._value.listitems[index-1])
+ else:
+ raise IndexError
+
+ def next(self, item):
+ item = undecorate(item)
+ index = self._value.listitems.index(item)
+ return self.decorate(self._value.listitems[index+1])
+
+ def __getitem__(self, index):
+ return self.decorate(self._value.listitems[index])
+
+ def __len__(self):
+ return len(self._value.listitems)
+
+ def index(self, item):
+ item = undecorate(item)
+ return self._value.listitems.index(item)
+
+ def __iter__(self):
+ for listitem in self._value.listitems:
+ yield self.decorate(listitem)
+
+ def iterfrom(self, item):
+ index = self.index(item)
+ for listitem in self._value.listitems[index:]:
+ yield self.decorate(listitem)
+
+
+def collectdecorators(changedict, classes):
+ result = {}
+ typeid2class = {}
+ for existingcls in classes:
+ newcls = new.classobj(existingcls.__name__, (existingcls,),
+ {'typeid2class': typeid2class,
+ '_changedict': changedict})
+ typeid2class[existingcls.typeid] = newcls
+ result[existingcls.__name__] = newcls
+ return result
+
+
+class DecorateError(Exception):
+ pass
+
+def undecorate(value):
+ if isinstance(value, Decorator):
+ return value._value
+ elif isinstance(value, ATOMIC):
+ return value
+ elif isinstance(value, tuple):
+ return tuple([undecorate(v) for v in value])
+ else:
+ raise TypeError, '%r is not simple enough to be stored' % (value,)
Copied: shpy/trunk/dist/shpy/ui_pygame/ui_pygame.py (from r2873, shpy/trunk/dist/shpy/ui_pygame.py)
==============================================================================
--- shpy/trunk/dist/shpy/ui_pygame.py (original)
+++ shpy/trunk/dist/shpy/ui_pygame/ui_pygame.py Thu Jan 22 19:08:33 2004
@@ -4,8 +4,9 @@
import pygame
from pygame.locals import *
import shpy.net.register
-from shpy.net.structure import Structure, representstructure, getstructureid
-from shpy import info
+from shpy.net.structure import representstructure, getstructureid
+from shpy import info
+from shpy.ui_pygame import decorate
RESOLUTION = (768, 512)
#RESOLUTION = (300, 300)
@@ -31,6 +32,78 @@
## return image
+class Cursor(decorate.Decorator):
+ typeid = 'cursor:0'
+ slots = 'x','line','cell','color','name',
+
+ def init_more(self):
+ self.fixme()
+ self.lineindex = self.cell.lines.index(self.line)
+
+ def moveup(self):
+ try:
+ self.line = self.cell.lines.prev(self.line)
+ except IndexError:
+ self.cell = self.terminal.root.cells.prev(self.cell)
+ self.line = self.cell.lines[-1]
+
+ def movedown(self):
+ try:
+ self.line = self.cell.lines.next(self.line)
+ except IndexError:
+ self.cell = self.terminal.root.cells.next(self.cell)
+ self.line = self.cell.lines[0]
+
+ def moveleft(self):
+ x, line = self.xline()
+ if x > 0:
+ self.x = x - 1
+ else:
+ self.line = self.cell.lines.prev(line)
+ self.x = len(self.line.content)
+
+ def moveright(self):
+ x, line = self.xline()
+ if x < len(line.content):
+ self.x = x + 1
+ else:
+ self.line = self.cell.lines.next(line)
+ self.x = 0
+
+ def isbelow(self, other):
+ if self.cell == other.cell:
+ selfindex = self .cell.lines.index(self .line)
+ otherindex = other.cell.lines.index(other.line)
+ else:
+ selfindex = self .terminal.root.cells.index(self .cell)
+ otherindex = other.terminal.root.cells.index(other.cell)
+ return selfindex > otherindex
+
+ def xline(self):
+ if self.x > len(self.line.content):
+ self.x = len(self.line.content)
+ return self.x, self.line
+
+ def fixme(self):
+ if self.cell not in self.terminal.root.cells:
+ self.cell = self.terminal.root.cells[0] # or somewhere else
+ if self.line not in self.cell.lines:
+ self.line = self.cell.lines[0]
+
+
+class Root(decorate.Decorator):
+ typeid = 'root:0'
+ slots = 'cells', 'users',
+
+class Cell(decorate.Decorator):
+ typeid = 'cell:0'
+ slots = 'lines',
+
+class Line(decorate.Decorator):
+ typeid = 'line:0'
+ slots = 'content',
+
+
class Terminal:
def __init__(self, sharedserver, execserver):
@@ -43,39 +116,45 @@
self.screen = pygame.display.set_mode(RESOLUTION, RESIZABLE)
self.font = pygame.font.Font(FONT, HEIGHT)
self.fontheight = self.font.size('X')[1]
- self.root = self.servergateway.registerclient()
- self.changed = {}
+ self.builddecorators()
+ self.root = self.Root.decorate(self.servergateway.registerclient())
username = info.getusername()
- try:
- self.cursor = getattr(self.root.users, username)
- except AttributeError:
- self.cursor = Structure(x=0, color=info.getcolor())
- setattr(self.root.users, username, self.cursor)
- #print self.root.users.__dict__
- if not hasattr(self.cursor, 'cell') or self.cursor.cell not in self.root.cells.list:
- self.cursor.cell = self.root.cells.list[-1]
- if not hasattr(self.cursor, 'line') or self.cursor.line not in self.cursor.cell.lines:
- self.cursor.line = self.cursor.cell.lines[-1]
- self.lastline_cell = self.cursor.cell
- self.lastline = self.lastline_cell.lines[-1]
- self.changed[self.cursor] = 1
- self.changed[self.root.users] = 1
+ for cursor in self.root.users:
+ if cursor.name == username:
+ self.cursor = cursor
+ break
+ else:
+ self.cursor = self.Cursor(name=username, x=0, color=info.getcolor(),
+ cell=None, line=None)
+ self.root.users.append(self.cursor)
+ self.firstline = self.Cursor(x=0, cell=None, line=None)#, _private=True)
+
+ def builddecorators(self):
+ self.changed = {}
+ decorators = decorate.collectdecorators(self.changed, [
+ Root, Cursor, Cell, Line, decorate.List])
+ for name, cls in decorators.items():
+ cls.terminal = self
+ setattr(self, name, cls)
def repaint(self):
+ screenheight = self.screen.get_size()[1]
while 1:
self.screen.fill((255,255,255))
self.line2ypos = {}
-
- index = self.lastline_cell.lines.index(self.lastline)
- lines = self.lastline_cell.lines[:index+1]
- ypos = self.drawlinesbackwards(lines)
- if ypos > 0:
- index = self.root.cells.list.index(self.lastline_cell)
- remainingcells = self.root.cells.list[:index]
- while remainingcells and ypos > 0:
- cell = remainingcells.pop()
+ ypos = 0
+ line_inside_cell = self.firstline.line
+ for cell in self.root.cells.iterfrom(self.firstline.cell):
+ if line_inside_cell is not None:
+ lines = cell.lines.iterfrom(line_inside_cell)
+ line_inside_cell = None
+ else:
+ lines = cell.lines
ypos = self.drawspacing(ypos)
- ypos = self.drawlinesbackwards(cell.lines, ypos)
+ ypos = self.drawlinesforward(lines, ypos)
+ if ypos >= screenheight:
+ break
+
cursor_in_view = self.drawcursors()
pygame.display.flip()
if cursor_in_view:
@@ -83,10 +162,9 @@
self.scroll_a_bit_towards_cursor()
def drawcursors(self):
- cursors = self.root.users.__dict__.values()
- assert self.cursor in cursors
+ assert self.cursor in self.root.users
cursor_in_view = False
- for cursor in cursors:
+ for cursor in self.root.users:
x = cursor.x
line = cursor.line
try:
@@ -99,110 +177,36 @@
charimage = self.font.render(char, 1, (255,255,255), cursor.color)
startoflineimagesize = self.font.size(line.content[:x])
self.screen.blit(charimage, (startoflineimagesize[0], ypos))
- if cursor is self.cursor and ypos >= 0:
+ if cursor == self.cursor and ypos >= 0:
cursor_in_view = True
return cursor_in_view
def drawspacing(self, ypos):
- self.screen.fill((160, 160, 160), Rect(0, ypos-2, 100, 1))
- return ypos - 3
+ self.screen.fill((160, 160, 160), Rect(0, ypos+1, 100, 1))
+ return ypos + 3
- def drawlinesbackwards(self, lines, ypos = None):
- if ypos is None:
- ypos = self.screen.get_size()[1]
- l = lines[:]
- while l and ypos > 0:
- line = l.pop()
+ def drawlinesforward(self, lines, ypos):
+ screenheight = self.screen.get_size()[1]
+ for line in lines:
+ if ypos > screenheight:
+ break
lineimage = self.font.render(line.content or ' ', 1, (0,0,0))
- ypos -= self.fontheight
self.screen.blit(lineimage, (0, ypos))
self.line2ypos[line] = ypos
+ ypos += self.fontheight
return ypos
def scroll_a_bit_towards_cursor(self):
- if self.cursor.cell is self.lastline_cell:
- cursorindex = self.cursor.cell.lines.index(self.cursor.line)
- lastlineindex = self.lastline_cell.lines.index(self.lastline)
- else:
- cursorindex = self.root.cells.list.index(self.cursor.cell)
- lastlineindex = self.root.cells.list.index(self.lastline_cell)
- if cursorindex < lastlineindex:
- self.lastline, self.lastline_cell = self.previousline(self.lastline, self.lastline_cell)
- else:
- self.lastline, self.lastline_cell = self.nextline(self.lastline, self.lastline_cell)
-
- def previousline(self, line, cell):
- index = cell.lines.index(line)
- if index > 0:
- return cell.lines[index-1], cell
- else:
- # XXX this changes if we have output cells ...
- index = self.root.cells.list.index(cell)
- if index > 0:
- cell = self.root.cells.list[index-1]
- return cell.lines[-1], cell
- return line, cell
-
- def nextline(self, line, cell):
- index = cell.lines.index(line)
- if index + 1 < len(cell.lines):
- return cell.lines[index+1], cell
- else:
- # XXX this changes if we have output cells ...
- index = self.root.cells.list.index(cell)
- if index + 1 < len(self.root.cells.list):
- cell = self.root.cells.list[index+1]
- return cell.lines[0], cell
- return line, cell
-
-## def drawcell(self, screen, cell):
-## self.scroll_cursor_into_view(screen)
-## start = -self.vscroll//self.fontheight
-## stop = (-self.vscroll + screen.get_size()[1])//self.fontheight + 1
-## ypos = self.vscroll + start*self.fontheight
-## for line in cell.lines[start:stop]:
-## lineimage = self.font.render(line or ' ', 1, (0,0,0))
-## screen.blit(lineimage, (0, ypos))
-## ypos += self.fontheight
-## self.drawcursors(screen, cell)
-
-## def char_pos(self, cell, x, y):
-## line = cell.lines[y]
-## return (self.font.size(line[:x])[0],
-## y*self.fontheight + self.vscroll)
-
-## def char_rect(self, cell, x, y):
-## c = cell.lines[y][x:x+1]
-## if not c:
-## c = ' '
-## return Rect(self.char_pos(x, y), self.font.size(c))
-
-## def scroll_cursor_into_view(self, screen):
-## ypos = self.vscroll + self.cursor.y * self.fontheight
-## if ypos < 0:
-## self.vscroll -= ypos
-## elif ypos + self.fontheight > screen.get_size()[1]:
-## self.vscroll -= ypos + self.fontheight - screen.get_size()[1]
-
-## def drawcursors(self, screen, cell):
-## for cursor in self.root.users.__dict__.values():
-## x = cursor.x
-## y = cursor.y
-## if y >= len(cell.lines):
-## y = len(cell.lines)-1
-## char = cell.lines[y][x:x+1]
-## if not char:
-## char = ' '
-## charimage = self.font.render(char, 1, (255,255,255), cursor.color)
-## screen.blit(charimage, self.char_pos(cell, x, y))
+ if self.cursor.isbelow(self.firstline):
+ self.firstline.movedown()
+ else:
+ self.firstline.moveup()
def PRINTABLE_KEY(self, event):
assert event.unicode
- x = self.cursor.x
- line = self.cursor.line
+ x, line = self.cursor.xline()
line.content = line.content[:x] + event.unicode + line.content[x:]
self.cursor.x += len(event.unicode)
- self.changed[line] = 1
def CTRL_K_a(self, event):
self.cursor.x = 0
@@ -211,78 +215,60 @@
self.cursor.x = len(self.cursor.line.content)
def K_UP(self, event):
- self.cursor.line, self.cursor.cell = self.previousline(self.cursor.line, self.cursor.cell)
+ self.cursor.moveup()
def K_DOWN(self, event):
- self.cursor.line, self.cursor.cell = self.nextline(self.cursor.line, self.cursor.cell)
+ self.cursor.movedown()
def K_LEFT(self, event):
- if self.cursor.x > 0:
- self.cursor.x -= 1
+ self.cursor.moveleft()
def K_RIGHT(self, event):
- if self.cursor.x < len(self.cursor.line.content):
- self.cursor.x += 1
+ self.cursor.moveright()
- def K_BACKSPACE(self, event):
- x = self.cursor.x
- line = self.cursor.line
- if x > 0:
- line.content = line.content[:x-1] + line.content[x:]
- self.changed[line] = 1
- self.cursor.x -= 1
+ def K_DELETE(self, event):
+ x, line = self.cursor.xline()
+ if x < len(line.content):
+ line.content = line.content[:x] + line.content[x+1:]
else:
- index = self.cursor.cell.lines.index(line)
- if index > 0:
- prevline = self.cursor.cell.lines[index-1]
- prevline.content += line.content
- del self.cursor.cell.lines[index]
- self.changed[prevline] = 1
- self.changed[self.cursor.cell] = 1
- self.cursor.line = prevline
- self.cursor.x = len(prevline.content)
+ nextline = self.cursor.cell.lines.next(line)
+ line.content += nextline.content
+ self.cursor.cell.lines.remove(nextline)
+ def K_BACKSPACE(self, event):
+ self.cursor.moveleft()
+ self.K_DELETE(event) # hack
+
def K_RETURN(self, event):
x = self.cursor.x
line = self.cursor.line
- newline = Structure(content=line.content[x:])
+ newline = self.Line(content=line.content[x:])
line.content = line.content[:x]
- index = self.cursor.cell.lines.index(line)
- self.cursor.cell.lines.insert(index+1, newline)
+ self.cursor.cell.lines.insertafter(line, newline)
self.cursor.line = newline
self.cursor.x = 0
- self.changed[self.cursor.cell] = 1
- self.changed[line] = 1
- self.changed[newline] = 1
def ALT_K_RETURN(self, event):
inputcell = self.cursor.cell
lines = [line.content for line in inputcell.lines]
outputcell = getattr(inputcell, 'outputcell', None)
if outputcell is None:
- outputcell = Structure()
+ outputcell = self.Cell()
inputcell.outputcell = outputcell
- index = self.root.cells.list.index(inputcell)
- self.root.cells.list.insert(index+1, outputcell)
- self.changed[inputcell] = 1
- self.changed[self.root.cells] = 1
- outputcell.lines = [Structure(content="")]
- self.changed[outputcell] = 1
- self.changed[outputcell.lines[0]] = 1
+ self.root.cells.insertafter(inputcell, outputcell)
+ outputcell.lines = self.List([self.Line(content="")])
def fill_output_cell(value):
lines = value.split('\n')
- outputcell.lines = [Structure(content=line) for line in lines]
- self.changed[outputcell] = 1
- for s in outputcell.lines:
- self.changed[s] = 1
+ outputcell.lines = self.List([self.Line(content=line) for line in lines])
self.execgateway.userexec_remote(lines, fill_output_cell)
def run(self):
self.invalid = True
while 1:
if self.changed:
- self.servergateway.notifychanges(*self.changed.keys())
+ keys = self.changed.keys()
self.changed.clear()
+ self.servergateway.notifychanges(*keys)
self.invalid = True
if self.invalid:
self.invalid = False
@@ -303,8 +289,10 @@
for keyname in keynames:
method = getattr(self, keyname, None)
if method:
- method(event)
- self.changed[self.cursor] = 1
+ try:
+ method(event)
+ except IndexError:
+ print "XXX flash red"
break
if event.type == QUIT:
break