[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[gnue] r8332 - in trunk/gnue-designer/src: forms/PagePainter uidrivers/w
From: |
jcater |
Subject: |
[gnue] r8332 - in trunk/gnue-designer/src: forms/PagePainter uidrivers/wx/uihelpers/documentcanvas |
Date: |
Mon, 3 Apr 2006 18:24:44 -0500 (CDT) |
Author: jcater
Date: 2006-03-31 22:42:18 -0600 (Fri, 31 Mar 2006)
New Revision: 8332
Modified:
trunk/gnue-designer/src/forms/PagePainter/PagePainter.py
trunk/gnue-designer/src/uidrivers/wx/uihelpers/documentcanvas/canvas.py
trunk/gnue-designer/src/uidrivers/wx/uihelpers/documentcanvas/widget.py
Log:
implemented a live feedback of moving with the mouse
Modified: trunk/gnue-designer/src/forms/PagePainter/PagePainter.py
===================================================================
--- trunk/gnue-designer/src/forms/PagePainter/PagePainter.py 2006-03-31
23:46:55 UTC (rev 8331)
+++ trunk/gnue-designer/src/forms/PagePainter/PagePainter.py 2006-04-01
04:42:18 UTC (rev 8332)
@@ -103,13 +103,13 @@
canvas = self.canvas
cls = getWidgetSkinClass(object)
-
+
widget = cls(object, canvas)
canvas.add(widget)
# Debugging...
widget.set_selected(True)
-
+
print "Added %s" % object._type
@@ -144,18 +144,18 @@
# form widgets
#--------------------------------------------------------------------------
-def getWidgetSkinClass(object):
- if object._type == 'GFLabel':
- cls = LabelWidget
- elif object._type == 'GFEntry':
- cls = EntryWidget
+def getWidgetSkinClass(object):
+ if object._type == 'GFLabel':
+ cls = LabelWidget
+ elif object._type == 'GFEntry':
+ cls = EntryWidget
else:
cls = UnknownWidget
return cls
-class FormWidget(DocumentWidget):
+class FormWidget(DocumentWidget):
"""
A DocumentWidget specific to the forms document type
"""
@@ -170,15 +170,15 @@
char_min_width = 1
char_max_height = 99999999
char_max_width = 99999999
-
+
# Compensation for the border drawn around objects if selected
- border_compensation = 4
-
-
+ border_compensation = 4
+
+
def calc_metrics(self):
-
+
canvas = self.canvas
-
+
object = self.gobject
# This gives us an offset onto the current drawing canvas
@@ -192,50 +192,50 @@
try:
w = object['Char:width']
- except KeyError:
- w = self.char_default_width
+ except KeyError:
+ w = self.char_default_width
- try:
+ try:
h = object['Char:height']
- except KeyError:
+ except KeyError:
h = self.char_default_height
-
+
# Convert from Char positions into wx positions
x *= xscale
y *= yscale
h *= xscale
w *= xscale
-
+
# Used to figure in compensation needed for selection box
# decorations and borders
compensation = self.border_compensation
-
+
# Save the bounds, relative to the canvas, of the area
- # that needs to be refreshed when this widget changes.
+ # that needs to be refreshed when this widget changes.
# (Includes all generic decoration, like selection borders)
- self.refresh_bounds = wx.Rect(x + documentX - compensation,
+ self.refresh_bounds = wx.Rect(x + documentX - compensation,
y + documentY - compensation,
- w + compensation * 2,
+ w + compensation * 2,
h + compensation * 2)
-
+
# Save the bounds, relative to the canvas, of the area
- # that the widget sans decorations occupy on the
- self.hit_test_bounds = wx.Rect(x + documentX,
- y + documentY,
+ # that the widget sans decorations occupy on the
+ self.hit_test_bounds = wx.Rect(x + documentX,
+ y + documentY,
w, h)
# Save the bounds, relative to being drawn in a context at (0,0)
self.draw_bounds = wx.Rect(compensation, compensation, w, h)
-
-
-
- #def move(self, x, y):
-class LabelWidget(FormWidget):
-
+
+ #def move(self, x, y):
+
+
+class LabelWidget(FormWidget):
+
char_max_height = 1
-
+
def draw_widget(self, dc):
dc.SetPen(wx.Pen(colorIndex['text']))
object = self.gobject
@@ -248,14 +248,14 @@
x += xscale
-class EntryWidget(FormWidget):
+class EntryWidget(FormWidget):
def draw_widget(self, dc):
dc.SetPen(wx.Pen(colorIndex['widget']))
x,y,w,h = self.draw_bounds.Get()
dc.DrawRoundedRectangle(x,y,w,h, 1)
-class UnknownWidget(FormWidget):
+class UnknownWidget(FormWidget):
def draw_widget(self, dc):
dc.SetPen(wx.Pen(colorIndex['widget']))
object = self.gobject
@@ -269,11 +269,11 @@
#==========================================================================
class PainterCanvas(DocumentCanvas):
- def __init__(self, *args, **parms):
+ def __init__(self, *args, **parms):
DocumentCanvas.__init__(self, *args, **parms)
-
+
self.document_origin_x = xscale * 2
self.document_origin_y = xscale * 2
-
+
Modified:
trunk/gnue-designer/src/uidrivers/wx/uihelpers/documentcanvas/canvas.py
===================================================================
--- trunk/gnue-designer/src/uidrivers/wx/uihelpers/documentcanvas/canvas.py
2006-03-31 23:46:55 UTC (rev 8331)
+++ trunk/gnue-designer/src/uidrivers/wx/uihelpers/documentcanvas/canvas.py
2006-04-01 04:42:18 UTC (rev 8332)
@@ -27,15 +27,15 @@
from colors import colorIndex, buildColorIndex
-cursor_map = {'resize-top': wx.CURSOR_SIZENS,
- 'resize-bottom': wx.CURSOR_SIZENS,
- 'resize-left': wx.CURSOR_SIZEWE,
- 'resize-right': wx.CURSOR_SIZEWE,
- 'resize-ul': wx.CURSOR_SIZENWSE,
+cursor_map = {'resize-top': wx.CURSOR_SIZENS,
+ 'resize-bottom': wx.CURSOR_SIZENS,
+ 'resize-left': wx.CURSOR_SIZEWE,
+ 'resize-right': wx.CURSOR_SIZEWE,
+ 'resize-ul': wx.CURSOR_SIZENWSE,
'resize-lr': wx.CURSOR_SIZENWSE,
- 'resize-ll': wx.CURSOR_SIZENESW,
- 'resize-ur': wx.CURSOR_SIZENESW,
- 'move': wx.CURSOR_SIZING,
+ 'resize-ll': wx.CURSOR_SIZENESW,
+ 'resize-ur': wx.CURSOR_SIZENESW,
+ 'move': wx.CURSOR_HAND,
}
# ===================================================================
@@ -59,13 +59,15 @@
self.Bind(wx.EVT_ERASE_BACKGROUND, self.__wx_on_erase_background)
self.Bind(wx.EVT_MOUSE_EVENTS, self.__wx_on_mouse_event)
-
+
# Mouse events
self.__mouse_hot_spots = []
self.__current_cursor = None
self.__mouse_mode = 'select'
self.__mouse_click_function = None
- self.__mouse_current_action = ""
+ self.__mouse_current_action = ""
+ self.__mouse_move_buffer = None
+ self.__mouse_move_bounds = None
def __del__(self):
@@ -73,32 +75,32 @@
item.Destroy()
self.ordered_widget_list = None
- def set_mouse_mode(self, mode='select', function=None):
+ def set_mouse_mode(self, mode='select', function=None):
"""
- mode String that is one of:
-
+ mode String that is one of:
+
select: The default mouse behavior, in which existing objects
can be selected, resized, moved, etc.
-
- edit: Clicking any object "edits" that object. (Mainly,
+
+ edit: Clicking any object "edits" that object. (Mainly,
used to change text on labels). Clicking the canvas
has no action.
-
- create: Clicking the canvas creates a new object. This mode
- doesn't actually do anything... your "method" passed in
- should handle the actual creation.
-
+
+ create: Clicking the canvas creates a new object. This mode
+ doesn't actually do anything... your "method" passed in
+ should handle the actual creation.
+
delete: Clicking an object "deletes" the object. This mode
- doesn't do anything ... your method should handle it.
-
+ doesn't do anything ... your method should handle it.
- method A python method to call once the target action happens.
-
+
+ method A python method to call once the target action happens.
+
"""
self.__mouse_mode = mode
self.__mouse_click_function = method
-
+
def set_grid_scale(self, x, y):
"""
Sets the grid x,y spacing
@@ -115,7 +117,7 @@
"""
Catch the wx OnPaint event and draw the canvas and any widgets
"""
-
+
# The updateRegion is not in scrolled coordinates.
scrollWindowOriginX, scrollWindowOriginY =
self.CalcUnscrolledPosition(0, 0)
@@ -163,72 +165,78 @@
memoryDC,
bufferX, bufferY)
+ # Draw the mouse move buffer, if applicable
+ if self.__mouse_move_bounds:
+ x,y,w,h = self.__mouse_move_bounds.Get()
+ paintDC.Blit(x, y, w, h, self.__mouse_move_buffer, 0, 0,
+ wx.AND, True, 0, 0)
+
memoryDC.EndDrawing()
def draw_widgets(self, dc):
"""
Draw the individual widgets
"""
-
+
updateRegion = self.GetUpdateRegion()
-
- (scrollWindowOriginX,
+
+ (scrollWindowOriginX,
scrollWindowOriginY) = self.CalcUnscrolledPosition(0, 0)
-
+
dcOriginX, dcOriginY = dc.GetDeviceOrigin()
-
+
selection_bounds = None
index = len(self.ordered_widget_list) - 1
-
-
+
+
while index >= 0:
drawableObject = self.ordered_widget_list [index]
- refresh_bounds = drawableObject.refresh_bounds
-
- (objectX, objectY,
+ refresh_bounds = drawableObject.refresh_bounds
+
+ (objectX, objectY,
objectWidth, objectHeight) = refresh_bounds.Get()
# Calculate the bounds, offset by the scrollwindow's origin
bounds = wx.Rect(objectX - scrollWindowOriginX,
- objectY - scrollWindowOriginY,
+ objectY - scrollWindowOriginY,
objectWidth, objectHeight)
-
- # Keep track of the overall bounds of all the selected
+
+ # Keep track of the overall bounds of all the selected
# widgets, so we can draw a selection box later
- if drawableObject.selected:
- if selection_bounds is None:
- selection_bounds = bounds
- else:
- selection_bounds = selection_bounds.Union(bounds)
+ if drawableObject.selected:
+ if selection_bounds is None:
+ selection_bounds = bounds
+ else:
+ selection_bounds = selection_bounds.Union(bounds)
- if (updateRegion.ContainsRect(bounds) != wx.OutRegion and
+ if (updateRegion.ContainsRect(bounds) != wx.OutRegion and
drawableObject.visible):
-
+
dc.SetDeviceOrigin(objectX + dcOriginX, objectY + dcOriginY)
drawableObject.draw(dc)
dc.SetDeviceOrigin(dcOriginX, dcOriginY)
index -= 1
-
+
# TODO: Somehow move most of this logic out of draw...
-
- # Draw a box around all the selected widgets
+
+ # Draw a box around all the selected widgets
if selection_bounds is not None:
self.__mouse_hot_spots = []
add_hotspot = self.__mouse_hot_spots.append
-
+
x, y, w, h = selection_bounds.Get()
-
- # Not sure why we have to do this, but it makes the
+
+ # Not sure why we have to do this, but it makes the
# box draw correctly.
x += 2
y += 2
w -= 4
h -= 4
-
+
# TODO: quick fix for enforcing a min size (fix!)
w = max(w, 17)
h = max(h, 17)
@@ -238,11 +246,11 @@
# Draw the main border
dc.SetBrush(wx.Brush(wx.BLACK, wx.TRANSPARENT))
dc.SetPen(wx.Pen(colorIndex['selectionframe']))
- dc.DrawRectangleRect(selection_bounds)
-
+ dc.DrawRectangleRect(selection_bounds)
+
# Draw the sizer grippers
dc.SetBrush(wx.Brush(wx.WHITE))
- for bx, by, position in (
+ for bx, by, position in (
(x - 2, y - 2, 'resize-ul'), # upper left
(x + w - 3, y - 2, 'resize-ur'), # upper right
(x - 2, y + h - 3, 'resize-ll'), # lower left
@@ -257,13 +265,14 @@
dc.DrawRectangleRect(rect)
add_hotspot((rect,position))
- # Add hotspot markers for the selection box
+ # Add hotspot markers for the selection box
add_hotspot((wx.Rect(x, y, 1, h), 'resize-left'))
add_hotspot((wx.Rect(x, y, w, 1), 'resize-top'))
add_hotspot((wx.Rect(x + w - 1, y, 1, h), 'resize-right'))
add_hotspot((wx.Rect(x, y + h - 1, w, 1), 'resize-bottom'))
add_hotspot((wx.Rect(x + 1, y + 1, w - 1, h - 1), 'move'))
-
+ else:
+ self.__selection_bounds = None
def draw_background(self, dc):
@@ -347,19 +356,19 @@
mode = self.__mouse_mode
current_action = self.__mouse_current_action
-
- if event.Dragging():
+
+ if event.Dragging():
if mode == 'select':
- if current_action == 'rubberband':
- self.__update_rubberband(x,y)
-
- elif current_action == 'move':
+ if current_action == 'rubberband':
+ self.__update_rubberband(x,y)
+
+ elif current_action == 'move':
self.__update_moving(x,y)
-
+
elif current_action == 'resize':
self.__update_resizing(x,y)
-
-
+
+
elif event.Moving():
cursor = None
if mode == 'select':
@@ -369,74 +378,74 @@
if rect.InsideXY(x, y):
cursor = cursor_map[position]
break
- if not cursor:
+ if not cursor:
cursor = wx.CURSOR_ARROW
if cursor != self.__current_cursor:
self.SetCursor(wx.StockCursor(cursor))
-
+
return True
-
- elif event.LeftDown():
- if mode == 'select':
+ elif event.LeftDown():
- if not current_action:
- # No previous action.
+ if mode == 'select':
+
+ if not current_action:
+ # No previous action.
new_action = None
-
- # Check to see if a currently selected area has been
+
+ # Check to see if a currently selected area has been
# clicked, and whether it will be moved or resized:
for rect, action in self.__mouse_hot_spots:
if rect.InsideXY(x, y):
new_action = action
break
- # Check to see if any widgets were clicked
- if not new_action:
+ # Check to see if any widgets were clicked
+ if not new_action:
for widget in self.ordered_widget_list:
- if widget.select_hit_test(x,y):
+ if widget.select_hit_test(x,y):
new_action = 'move'
break
-
+
# Finally, assume we're creating a rubberband selector
- if not new_action:
+ if not new_action:
new_action = 'rubberband'
self.__start_rubberband(x, y)
-
- elif new_action == 'move':
+
+ elif new_action == 'move':
self.__start_moving(x,y)
- elif new_action.startswith('resize-'):
+ elif new_action.startswith('resize-'):
self.__start_resizing(x, y,
new_action.split('-')[1])
new_action = 'resize'
self.__mouse_current_action = new_action
-
+
return True
-
- elif event.LeftUp():
- if mode == 'select':
+ elif event.LeftUp():
- if current_action == 'rubberband':
+ if mode == 'select':
+
+ if current_action == 'rubberband':
self.__stop_rubberband()
-
- elif current_action == 'move':
+
+ elif current_action == 'move':
self.__stop_moving()
-
- elif current_action == 'resize':
+
+ elif current_action == 'resize':
self.__stop_resizing()
self.__mouse_current_action = ''
return True
-
+
return False
@@ -445,7 +454,7 @@
# ---------------------------------------------------------------
# Get all selected widgets
# ---------------------------------------------------------------
- def get_selected_widgets(self):
+ def get_selected_widgets(self):
"""
Return a list of the currently selected widgets
"""
@@ -454,49 +463,101 @@
for widget in self.ordered_widget_list:
if widget.selected:
append(widget)
-
+
return results
# ---------------------------------------------------------------
# Support for visual moving hints
# ---------------------------------------------------------------
- def __start_moving(self, x, y):
+ def __start_moving(self, x, y):
self.__mouse_move_items = widgets = self.get_selected_widgets()
self.CaptureMouse()
- for widget in widgets:
+ bounds = wx.Rect()
+ for widget in widgets:
widget.selected = False
- widget.show(False)
-
- def __update_moving(self, x, y):
- pass
-
- def __stop_moving(self):
+ widget.visible = False
+ bounds = bounds.Union(widget.refresh_bounds)
+
+
+ # Create a bitmap of our widgets in an offscreen buffer
+ # so we can redraw them as the mouse moves
+
+ bufferX = bounds.GetLeft()
+ bufferY = bounds.GetTop()
+ bufferWidth = bounds.GetWidth()
+ bufferHeight = bounds.GetHeight()
+
+ memoryDC = wx.MemoryDC()
+ offscreenBuffer = wx.EmptyBitmap(bufferWidth, bufferHeight)
+ maskColor = wx.WHITE
+ offscreenBuffer.SetMaskColour(maskColor)
+
+ memoryDC.SelectObject(offscreenBuffer)
+ memoryDC.SetDeviceOrigin(-bufferX, -bufferY)
+
+ memoryDC.SetBackground(wx.Brush(maskColor))
+ memoryDC.SetBackgroundMode(wx.TRANSPARENT)
+ memoryDC.Clear()
+
+ for widget in widgets:
+ (objectX, objectY,
+ objectWidth, objectHeight) = widget.refresh_bounds.Get()
+
+ memoryDC.SetDeviceOrigin(objectX - bufferX, objectY - bufferY)
+ widget.draw_widget(memoryDC)
+ memoryDC.SetDeviceOrigin(0,0)
+
+ memoryDC.EndDrawing()
+
+ self.__mouse_move_dx = x - bufferX
+ self.__mouse_move_dy = y - bufferY
+ self.__mouse_move_buffer = memoryDC
+ self.__mouse_move_bounds = bounds
+
+ # Hide the real widgets
+ self.refresh_scrolled_rect(bounds)
+
+
+ def __update_moving(self, x, y):
+ old_bounds = wx.Rect(*self.__mouse_move_bounds.Get())
+ self.__mouse_move_bounds.SetLeft(x - self.__mouse_move_dx)
+ self.__mouse_move_bounds.SetTop(y - self.__mouse_move_dy)
+ self.refresh_scrolled_rect(old_bounds.Union(self.__mouse_move_bounds))
+
+
+ def __stop_moving(self):
self.ReleaseMouse()
- for widget in self.__mouse_move_items:
+ old_bounds = self.__mouse_move_bounds
+ self.__mouse_move_buffer = None
+ self.__mouse_move_bounds = None
+
+ for widget in self.__mouse_move_items:
widget.selected = True
- widget.show(True)
+ widget.show()
+
self.__mouse_move_items = None
+ self.refresh_scrolled_rect(old_bounds)
# ---------------------------------------------------------------
# Support for visual resizing hints
# ---------------------------------------------------------------
- def __start_resizing(self, x, y, orientation):
+ def __start_resizing(self, x, y, orientation):
self.__mouse_resize_orientation = orientation
self.CaptureMouse()
-
- def __update_resizing(self, x, y):
+
+ def __update_resizing(self, x, y):
orientation = self.__mouse_resize_orientation
-
- def __stop_resizing(self):
+
+ def __stop_resizing(self):
self.ReleaseMouse()
-
-
+
+
# ---------------------------------------------------------------
# Support for click and drag rubberband boxes
# ---------------------------------------------------------------
- def __start_rubberband(self, x, y):
- """
+ def __start_rubberband(self, x, y):
+ """
Start a rubberband box
"""
self.__mouse_rubberband_orig_cursor = self.GetCursor()
@@ -504,8 +565,8 @@
self.__mouse_rubberband_last = None
self.SetCursor(wx.StockCursor(wx.CURSOR_CROSS))
self.CaptureMouse()
-
- def __update_rubberband(self, x=None, y=None):
+
+ def __update_rubberband(self, x=None, y=None):
"""
Draw one box shape and possibly erase another.
"""
@@ -517,30 +578,30 @@
start = self.__mouse_rubberband_start
last = self.__mouse_rubberband_last
-
+
# Erase the old box
- if last:
+ if last:
r = wx.RectPP(start, last)
dc.DrawRectangleRect(r)
# Draw the new box
- if x is not None:
+ if x is not None:
r = wx.RectPP(start, (x,y))
dc.DrawRectangleRect(r)
self.__mouse_rubberband_last = (x,y)
-
+
dc.EndDrawing()
-
- def __stop_rubberband(self):
- """
- Reset the cursor
+
+ def __stop_rubberband(self):
"""
+ Reset the cursor
+ """
self.SetCursor(self.__mouse_rubberband_orig_cursor)
self.__update_rubberband(None)
self.ReleaseMouse()
-
+
# ---------------------------------------------------------------
# Drag and Drop support
# ---------------------------------------------------------------
Modified:
trunk/gnue-designer/src/uidrivers/wx/uihelpers/documentcanvas/widget.py
===================================================================
--- trunk/gnue-designer/src/uidrivers/wx/uihelpers/documentcanvas/widget.py
2006-03-31 23:46:55 UTC (rev 8331)
+++ trunk/gnue-designer/src/uidrivers/wx/uihelpers/documentcanvas/widget.py
2006-04-01 04:42:18 UTC (rev 8332)
@@ -40,7 +40,7 @@
self.visible = True
self.selected = False
- # Coordinates (relative to canvas) that should register hit tests
+ # Coordinates (relative to canvas) that should register hit tests
self.hit_test_bounds = wx.Rect()
self.hit_test_exclusions = [] # Can be a list of wx.Rect()
@@ -57,8 +57,8 @@
self.force_canvas_refresh()
self.recalc_metrics()
self.force_canvas_refresh()
-
+
def drag_resize(self, width, height):
self.force_canvas_refresh()
# TODO
@@ -87,7 +87,7 @@
event.Skip()
return False
- def show(self, show):
+ def show(self, show=True):
# Make sure show is a Boolean
show = bool(show)
@@ -167,7 +167,7 @@
def set_selected(self, selected=True):
"""
- Sets the selected bit for this object. Does a canvas refresh on
+ Sets the selected bit for this object. Does a canvas refresh on
the object if it has changed state. Does not call Update on the canvas.
"""
selected = bool(selected)
@@ -178,26 +178,26 @@
self.force_canvas_refresh()
- def recalc_metrics(self):
+ def recalc_metrics(self):
"""
Force a recalculation of all metric information
"""
self.calc_metrics()
-
- def force_canvas_refresh(self):
+
+ def force_canvas_refresh(self):
"""
Signal to the canvas that are area changed
"""
self.canvas.refresh_scrolled_rect(self.refresh_bounds)
-
+
def draw(self, dc):
"""
- Draw the widget and any decorations.
+ Draw the widget and any decorations.
"""
self.draw_widget(dc)
- if self.selected:
+ if self.selected:
self.draw_border(dc)
@@ -214,13 +214,13 @@
dc.SetPen(wx.Pen(colorIndex['selectedframe']))
dc.DrawRectangle(3, 3, w + 2, h + 2)
-
+
def select_hit_test(self, x, y):
"""
- You must implement this routine to do hit testing to identify
+ You must implement this routine to do hit testing to identify
the region for selecting the object.
-
- By default, if not subclassed, we just use the bounds defined for the
+
+ By default, if not subclassed, we just use the bounds defined for the
object.
"""
self.hit_test_bounds.InsideXY(x,y)
@@ -230,19 +230,19 @@
"""
You must implement this routine to do hit testing for dragable region
of drawable object
-
+
By default, we use the bounds of the object.
"""
# Should also take into account self.hit_test_exclusions
dragRect = wx.Rect((x1, y1), (x2, y2))
- result = self.hit_test_bounds.Intersects(dragRect)
-
- if result:
+ result = self.hit_test_bounds.Intersects(dragRect)
+
+ if result:
# If the entire dragRect is in an exclusion area, return false
- for exclusion in self.hit_test_exclusions:
+ for exclusion in self.hit_test_exclusions:
if exclusion.Intersect(dragRect) == dragRect:
return False
-
+
return result
@@ -277,16 +277,16 @@
"""
pass
-
+
# ====================================================================
# You *must* implement the following functions
# ====================================================================
- def calc_metrics(self):
+ def calc_metrics(self):
"""
- Calculate hit_test_bounds, refresh_bounds, and any
- class-specific bounds your draw_widget methods might need.
+ Calculate hit_test_bounds, refresh_bounds, and any
+ class-specific bounds your draw_widget methods might need.
"""
assert(False)
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [gnue] r8332 - in trunk/gnue-designer/src: forms/PagePainter uidrivers/wx/uihelpers/documentcanvas,
jcater <=