2eeaec1909
"Closing view crashes Python" plus some new features: New feature added to the IDE is 'Extensions'. Under Tools|Options|Extensions, you can add calls to external programs. For example you can add a "Notepad" extension (under windows) that will exec Notepad on the currently open file. A new "Notepad" menu item will appear under the Tools menu. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@34638 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
749 lines
26 KiB
Python
749 lines
26 KiB
Python
#----------------------------------------------------------------------------
|
|
# Name: AbstractEditor.py
|
|
# Purpose: Non-text editor for DataModel and Process
|
|
#
|
|
# Author: Peter Yared, Morgan Hua
|
|
#
|
|
# Created: 7/28/04
|
|
# CVS-ID: $Id$
|
|
# Copyright: (c) 2004-2005 ActiveGrid, Inc.
|
|
# License: wxWindows License
|
|
#----------------------------------------------------------------------------
|
|
|
|
|
|
import wx
|
|
import wx.lib.docview
|
|
import wx.lib.ogl as ogl
|
|
import PropertyService
|
|
_ = wx.GetTranslation
|
|
|
|
|
|
SELECT_BRUSH = wx.Brush("BLUE", wx.SOLID)
|
|
SHAPE_BRUSH = wx.Brush("WHEAT", wx.SOLID)
|
|
LINE_BRUSH = wx.BLACK_BRUSH
|
|
INACTIVE_SELECT_BRUSH = wx.Brush("LIGHT BLUE", wx.SOLID)
|
|
|
|
|
|
def GetRawModel(model):
|
|
if hasattr(model, "GetRawModel"):
|
|
rawModel = model.GetRawModel()
|
|
else:
|
|
rawModel = model
|
|
return rawModel
|
|
|
|
|
|
class CanvasView(wx.lib.docview.View):
|
|
|
|
|
|
#----------------------------------------------------------------------------
|
|
# Overridden methods
|
|
#----------------------------------------------------------------------------
|
|
|
|
|
|
def __init__(self, brush = SHAPE_BRUSH):
|
|
wx.lib.docview.View.__init__(self)
|
|
self._brush = brush
|
|
self._canvas = None
|
|
self._pt1 = None
|
|
self._pt2 = None
|
|
self._needEraseLasso = False
|
|
self._propShape = None
|
|
self._maxWidth = 2000
|
|
self._maxHeight = 16000
|
|
|
|
|
|
def OnDraw(self, dc):
|
|
""" for Print Preview and Print """
|
|
dc.BeginDrawing()
|
|
self._canvas.Redraw(dc)
|
|
dc.EndDrawing()
|
|
|
|
|
|
def OnCreate(self, doc, flags):
|
|
frame = wx.GetApp().CreateDocumentFrame(self, doc, flags)
|
|
frame.Show()
|
|
sizer = wx.BoxSizer()
|
|
self._CreateCanvas(frame)
|
|
sizer.Add(self._canvas, 1, wx.EXPAND, 0)
|
|
frame.SetSizer(sizer)
|
|
frame.Layout()
|
|
self.Activate()
|
|
return True
|
|
|
|
|
|
def OnActivateView(self, activate, activeView, deactiveView):
|
|
if activate and self._canvas:
|
|
# In MDI mode just calling set focus doesn't work and in SDI mode using CallAfter causes an endless loop
|
|
if self.GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI:
|
|
self.SetFocus()
|
|
else:
|
|
wx.CallAfter(self.SetFocus)
|
|
|
|
|
|
def SetFocus(self):
|
|
if self._canvas:
|
|
self._canvas.SetFocus()
|
|
|
|
|
|
def OnFocus(self, event):
|
|
self.FocusColorPropertyShape(True)
|
|
event.Skip()
|
|
|
|
|
|
def FocusOnClick(self, event):
|
|
self.SetFocus()
|
|
event.Skip()
|
|
|
|
|
|
def OnKillFocus(self, event):
|
|
self.FocusColorPropertyShape(False)
|
|
event.Skip()
|
|
|
|
|
|
def HasFocus(self):
|
|
winWithFocus = wx.Window.FindFocus()
|
|
if not winWithFocus:
|
|
return False
|
|
while winWithFocus:
|
|
if winWithFocus == self._canvas:
|
|
return True
|
|
winWithFocus = winWithFocus.GetParent()
|
|
return False
|
|
|
|
|
|
def OnClose(self, deleteWindow = True):
|
|
statusC = wx.GetApp().CloseChildDocuments(self.GetDocument())
|
|
statusP = wx.lib.docview.View.OnClose(self, deleteWindow = deleteWindow)
|
|
if hasattr(self, "ClearOutline"):
|
|
wx.CallAfter(self.ClearOutline) # need CallAfter because when closing the document, it is Activated and then Close, so need to match OnActivateView's CallAfter
|
|
if not (statusC and statusP):
|
|
return False
|
|
self.Activate(False)
|
|
if deleteWindow and self.GetFrame():
|
|
self.GetFrame().Destroy()
|
|
return True
|
|
|
|
|
|
def _CreateCanvas(self, parent):
|
|
self._canvas = ogl.ShapeCanvas(parent)
|
|
wx.EVT_LEFT_DOWN(self._canvas, self.OnLeftClick)
|
|
wx.EVT_LEFT_UP(self._canvas, self.OnLeftUp)
|
|
wx.EVT_MOTION(self._canvas, self.OnLeftDrag)
|
|
wx.EVT_LEFT_DCLICK(self._canvas, self.OnLeftDoubleClick)
|
|
wx.EVT_KEY_DOWN(self._canvas, self.OnKeyPressed)
|
|
|
|
# need this otherwise mouse clicks don't set focus to this view
|
|
wx.EVT_LEFT_DOWN(self._canvas, self.FocusOnClick)
|
|
wx.EVT_LEFT_DCLICK(self._canvas, self.FocusOnClick)
|
|
wx.EVT_RIGHT_DOWN(self._canvas, self.FocusOnClick)
|
|
wx.EVT_RIGHT_DCLICK(self._canvas, self.FocusOnClick)
|
|
wx.EVT_MIDDLE_DOWN(self._canvas, self.FocusOnClick)
|
|
wx.EVT_MIDDLE_DCLICK(self._canvas, self.FocusOnClick)
|
|
|
|
wx.EVT_KILL_FOCUS(self._canvas, self.OnKillFocus)
|
|
wx.EVT_SET_FOCUS(self._canvas, self.OnFocus)
|
|
|
|
self._canvas.SetScrollbars(20, 20, self._maxWidth / 20, self._maxHeight / 20)
|
|
|
|
self._canvas.SetBackgroundColour(wx.WHITE)
|
|
self._diagram = ogl.Diagram()
|
|
self._canvas.SetDiagram(self._diagram)
|
|
self._diagram.SetCanvas(self._canvas)
|
|
|
|
|
|
def OnKeyPressed(self, event):
|
|
key = event.KeyCode()
|
|
if key == wx.WXK_DELETE:
|
|
self.OnClear(event)
|
|
else:
|
|
event.Skip()
|
|
|
|
|
|
def OnLeftClick(self, event):
|
|
self.EraseRubberBand()
|
|
|
|
dc = wx.ClientDC(self._canvas)
|
|
self._canvas.PrepareDC(dc)
|
|
|
|
# keep track of mouse down for group select
|
|
self._pt1 = event.GetLogicalPosition(dc) # this takes into account scrollbar offset
|
|
self._pt2 = None
|
|
|
|
shape = self._canvas.FindShape(self._pt1[0], self._pt1[1])[0]
|
|
if shape:
|
|
self.BringToFront(shape)
|
|
|
|
self._pt1 = None
|
|
event.Skip() # pass on event to shape handler to take care of selection
|
|
|
|
return
|
|
elif event.ControlDown() or event.ShiftDown(): # extend select, don't deselect
|
|
pass
|
|
else:
|
|
# click on empty part of canvas, deselect everything
|
|
needRefresh = False
|
|
for shape in self._diagram.GetShapeList():
|
|
if hasattr(shape, "GetModel"):
|
|
if shape.Selected():
|
|
needRefresh = True
|
|
shape.Select(False, dc)
|
|
if needRefresh:
|
|
self._canvas.Redraw(dc)
|
|
|
|
self.SetPropertyModel(None)
|
|
|
|
if len(self.GetSelection()) == 0:
|
|
self.SetPropertyShape(None)
|
|
|
|
|
|
|
|
def OnLeftDoubleClick(self, event):
|
|
propertyService = wx.GetApp().GetService(PropertyService.PropertyService)
|
|
if propertyService:
|
|
propertyService.ShowWindow()
|
|
|
|
|
|
def OnLeftDrag(self, event):
|
|
# draw lasso for group select
|
|
if self._pt1 and event.LeftIsDown(): # we are in middle of lasso selection
|
|
self.EraseRubberBand()
|
|
|
|
dc = wx.ClientDC(self._canvas)
|
|
self._canvas.PrepareDC(dc)
|
|
self._pt2 = event.GetLogicalPosition(dc) # this takes into account scrollbar offset
|
|
self.DrawRubberBand()
|
|
else:
|
|
event.Skip()
|
|
|
|
|
|
def OnLeftUp(self, event):
|
|
# do group select
|
|
if self._needEraseLasso:
|
|
self.EraseRubberBand()
|
|
|
|
dc = wx.ClientDC(self._canvas)
|
|
self._canvas.PrepareDC(dc)
|
|
x1, y1 = self._pt1
|
|
x2, y2 = event.GetLogicalPosition(dc) # this takes into account scrollbar offset
|
|
|
|
tol = self._diagram.GetMouseTolerance()
|
|
if abs(x1 - x2) > tol or abs(y1 - y2) > tol:
|
|
# make sure x1 < x2 and y1 < y2 to make comparison test easier
|
|
if x1 > x2:
|
|
temp = x1
|
|
x1 = x2
|
|
x2 = temp
|
|
if y1 > y2:
|
|
temp = y1
|
|
y1 = y2
|
|
y2 = temp
|
|
|
|
for shape in self._diagram.GetShapeList():
|
|
if not shape.GetParent() and hasattr(shape, "GetModel"): # if part of a composite, don't select it
|
|
x, y = shape.GetX(), shape.GetY()
|
|
width, height = shape.GetBoundingBoxMax()
|
|
selected = x1 < x - width/2 and x2 > x + width/2 and y1 < y - height/2 and y2 > y + height/2
|
|
if event.ControlDown() or event.ShiftDown(): # extend select, don't deselect
|
|
if selected:
|
|
shape.Select(selected, dc)
|
|
else: # select items in lasso and deselect items out of lasso
|
|
shape.Select(selected, dc)
|
|
self._canvas.Redraw(dc)
|
|
else:
|
|
event.Skip()
|
|
else:
|
|
event.Skip()
|
|
|
|
|
|
def EraseRubberBand(self):
|
|
if self._needEraseLasso:
|
|
self._needEraseLasso = False
|
|
|
|
dc = wx.ClientDC(self._canvas)
|
|
self._canvas.PrepareDC(dc)
|
|
dc.SetLogicalFunction(wx.XOR)
|
|
pen = wx.Pen(wx.Colour(200, 200, 200), 1, wx.SHORT_DASH)
|
|
dc.SetPen(pen)
|
|
brush = wx.Brush(wx.Colour(255, 255, 255), wx.TRANSPARENT)
|
|
dc.SetBrush(brush)
|
|
dc.ResetBoundingBox()
|
|
dc.BeginDrawing()
|
|
|
|
x1, y1 = self._pt1
|
|
x2, y2 = self._pt2
|
|
|
|
# make sure x1 < x2 and y1 < y2
|
|
# this will make (x1, y1) = upper left corner
|
|
if x1 > x2:
|
|
temp = x1
|
|
x1 = x2
|
|
x2 = temp
|
|
if y1 > y2:
|
|
temp = y1
|
|
y1 = y2
|
|
y2 = temp
|
|
|
|
# erase previous outline
|
|
dc.SetClippingRegion(x1, y1, x2 - x1, y2 - y1)
|
|
dc.DrawRectangle(x1, y1, x2 - x1, y2 - y1)
|
|
dc.EndDrawing()
|
|
|
|
|
|
def DrawRubberBand(self):
|
|
self._needEraseLasso = True
|
|
|
|
dc = wx.ClientDC(self._canvas)
|
|
self._canvas.PrepareDC(dc)
|
|
dc.SetLogicalFunction(wx.XOR)
|
|
pen = wx.Pen(wx.Colour(200, 200, 200), 1, wx.SHORT_DASH)
|
|
dc.SetPen(pen)
|
|
brush = wx.Brush(wx.Colour(255, 255, 255), wx.TRANSPARENT)
|
|
dc.SetBrush(brush)
|
|
dc.ResetBoundingBox()
|
|
dc.BeginDrawing()
|
|
|
|
x1, y1 = self._pt1
|
|
x2, y2 = self._pt2
|
|
|
|
# make sure x1 < x2 and y1 < y2
|
|
# this will make (x1, y1) = upper left corner
|
|
if x1 > x2:
|
|
temp = x1
|
|
x1 = x2
|
|
x2 = temp
|
|
if y1 > y2:
|
|
temp = y1
|
|
y1 = y2
|
|
y2 = temp
|
|
|
|
# draw outline
|
|
dc.SetClippingRegion(x1, y1, x2 - x1, y2 - y1)
|
|
dc.DrawRectangle(x1, y1, x2 - x1, y2 - y1)
|
|
dc.EndDrawing()
|
|
|
|
|
|
def FindParkingSpot(self, width, height):
|
|
""" given a width and height, find a upper left corner where shape can be parked without overlapping other shape """
|
|
offset = 30 # space between shapes
|
|
x = offset
|
|
y = offset
|
|
maxX = 700 # max distance to the right where we'll place tables
|
|
noParkingSpot = True
|
|
|
|
while noParkingSpot:
|
|
point = self.isSpotOccupied(x, y, width, height)
|
|
if point:
|
|
x = point[0] + offset
|
|
if x > maxX:
|
|
x = offset
|
|
y = point[1] + offset
|
|
else:
|
|
noParkingSpot = False
|
|
|
|
return x, y
|
|
|
|
|
|
def isSpotOccupied(self, x, y, width, height):
|
|
""" returns None if at x,y,width,height no object occupies that rectangle,
|
|
otherwise returns lower right corner of object that occupies given x,y position
|
|
"""
|
|
x2 = x + width
|
|
y2 = y + height
|
|
|
|
for shape in self._diagram.GetShapeList():
|
|
if isinstance(shape, ogl.RectangleShape) or isinstance(shape, ogl.EllipseShape):
|
|
if shape.GetParent() and isinstance(shape.GetParent(), ogl.CompositeShape):
|
|
# skip, part of a composite shape
|
|
continue
|
|
|
|
if hasattr(shape, "GetModel"):
|
|
other_x, other_y, other_width, other_height = shape.GetModel().getEditorBounds()
|
|
other_x2 = other_x + other_width
|
|
other_y2 = other_y + other_height
|
|
else:
|
|
# shapes x,y are at the center of the shape, need to transform to upper left coordinate
|
|
other_width, other_height = shape.GetBoundingBoxMax()
|
|
other_x = shape.GetX() - other_width/2
|
|
other_y = shape.GetY() - other_height/2
|
|
|
|
other_x2 = other_x + other_width
|
|
other_y2 = other_y + other_height
|
|
# intersection check
|
|
if ((other_x2 < other_x or other_x2 > x) and
|
|
(other_y2 < other_y or other_y2 > y) and
|
|
(x2 < x or x2 > other_x) and
|
|
(y2 < y or y2 > other_y)):
|
|
return (other_x2, other_y2)
|
|
return None
|
|
|
|
|
|
#----------------------------------------------------------------------------
|
|
# Canvas methods
|
|
#----------------------------------------------------------------------------
|
|
|
|
def AddShape(self, shape, x = None, y = None, pen = None, brush = None, text = None, eventHandler = None):
|
|
if isinstance(shape, ogl.CompositeShape):
|
|
dc = wx.ClientDC(self._canvas)
|
|
self._canvas.PrepareDC(dc)
|
|
shape.Move(dc, x, y)
|
|
else:
|
|
shape.SetDraggable(True, True)
|
|
shape.SetCanvas(self._canvas)
|
|
|
|
if x:
|
|
shape.SetX(x)
|
|
if y:
|
|
shape.SetY(y)
|
|
shape.SetCentreResize(False)
|
|
if pen:
|
|
shape.SetPen(pen)
|
|
if brush:
|
|
shape.SetBrush(brush)
|
|
if text:
|
|
shape.AddText(text)
|
|
shape.SetShadowMode(ogl.SHADOW_NONE)
|
|
self._diagram.AddShape(shape)
|
|
shape.Show(True)
|
|
if not eventHandler:
|
|
eventHandler = EditorCanvasShapeEvtHandler(self)
|
|
eventHandler.SetShape(shape)
|
|
eventHandler.SetPreviousHandler(shape.GetEventHandler())
|
|
shape.SetEventHandler(eventHandler)
|
|
return shape
|
|
|
|
|
|
def RemoveShape(self, model = None, shape = None):
|
|
if not model and not shape:
|
|
return
|
|
|
|
if not shape:
|
|
shape = self.GetShape(model)
|
|
|
|
if shape:
|
|
shape.Select(False)
|
|
for line in shape.GetLines():
|
|
shape.RemoveLine(line)
|
|
self._diagram.RemoveShape(line)
|
|
for obj in self._diagram.GetShapeList():
|
|
for line in obj.GetLines():
|
|
if self.IsShapeContained(shape, line.GetTo()) or self.IsShapeContained(shape, line.GetFrom()):
|
|
obj.RemoveLine(line)
|
|
self._diagram.RemoveShape(line)
|
|
if line == shape:
|
|
obj.RemoveLine(line)
|
|
|
|
shape.RemoveFromCanvas(self._canvas)
|
|
self._diagram.RemoveShape(shape)
|
|
|
|
|
|
def IsShapeContained(self, parent, shape):
|
|
if parent == shape:
|
|
return True
|
|
elif shape.GetParent():
|
|
return self.IsShapeContained(parent, shape.GetParent())
|
|
|
|
return False
|
|
|
|
|
|
def UpdateShape(self, model):
|
|
for shape in self._diagram.GetShapeList():
|
|
if hasattr(shape, "GetModel") and shape.GetModel() == model:
|
|
x, y, w, h = model.getEditorBounds()
|
|
newX = x + w / 2
|
|
newY = y + h / 2
|
|
changed = False
|
|
if isinstance(shape, ogl.CompositeShape):
|
|
if shape.GetX() != newX or shape.GetY() != newY:
|
|
dc = wx.ClientDC(self._canvas)
|
|
self._canvas.PrepareDC(dc)
|
|
shape.SetSize(w, h, True) # wxBug: SetSize must be before Move because links won't go to the right place
|
|
shape.Move(dc, newX, newY) # wxBug: Move must be before SetSize because links won't go to the right place
|
|
changed = True
|
|
else:
|
|
oldw, oldh = shape.GetBoundingBoxMax()
|
|
oldx = shape.GetX()
|
|
oldy = shape.GetY()
|
|
if oldw != w or oldh != h or oldx != newX or oldy != newY:
|
|
shape.SetSize(w, h)
|
|
shape.SetX(newX)
|
|
shape.SetY(newY)
|
|
changed = True
|
|
if changed:
|
|
shape.ResetControlPoints()
|
|
self._canvas.Refresh()
|
|
break
|
|
|
|
|
|
def GetShape(self, model):
|
|
for shape in self._diagram.GetShapeList():
|
|
if hasattr(shape, "GetModel") and shape.GetModel() == model:
|
|
return shape
|
|
return None
|
|
|
|
|
|
def GetSelection(self):
|
|
return filter(lambda shape: shape.Selected(), self._diagram.GetShapeList())
|
|
|
|
|
|
def SetSelection(self, models, extendSelect = False):
|
|
dc = wx.ClientDC(self._canvas)
|
|
self._canvas.PrepareDC(dc)
|
|
update = False
|
|
if not isinstance(models, type([])) and not isinstance(models, type(())):
|
|
models = [models]
|
|
for shape in self._diagram.GetShapeList():
|
|
if hasattr(shape, "GetModel"):
|
|
if shape.Selected() and not shape.GetModel() in models: # was selected, but not in new list, so deselect, unless extend select
|
|
if not extendSelect:
|
|
shape.Select(False, dc)
|
|
update = True
|
|
elif not shape.Selected() and shape.GetModel() in models: # was not selected and in new list, so select
|
|
shape.Select(True, dc)
|
|
update = True
|
|
elif extendSelect and shape.Selected() and shape.GetModel() in models: # was selected, but extend select means to deselect
|
|
shape.Select(False, dc)
|
|
update = True
|
|
if update:
|
|
self._canvas.Redraw(dc)
|
|
|
|
|
|
def BringToFront(self, shape):
|
|
if shape.GetParent() and isinstance(shape.GetParent(), ogl.CompositeShape):
|
|
self._diagram.RemoveShape(shape.GetParent())
|
|
self._diagram.AddShape(shape.GetParent())
|
|
else:
|
|
self._diagram.RemoveShape(shape)
|
|
self._diagram.AddShape(shape)
|
|
|
|
|
|
def SendToBack(self, shape):
|
|
if shape.GetParent() and isinstance(shape.GetParent(), ogl.CompositeShape):
|
|
self._diagram.RemoveShape(shape.GetParent())
|
|
self._diagram.InsertShape(shape.GetParent())
|
|
else:
|
|
self._diagram.RemoveShape(shape)
|
|
self._diagram.InsertShape(shape)
|
|
|
|
|
|
def ScrollVisible(self, shape):
|
|
xUnit, yUnit = shape._canvas.GetScrollPixelsPerUnit()
|
|
scrollX, scrollY = self._canvas.GetViewStart() # in scroll units
|
|
scrollW, scrollH = self._canvas.GetSize() # in pixels
|
|
w, h = shape.GetBoundingBoxMax() # in pixels
|
|
x = shape.GetX() - w/2 # convert to upper left coordinate from center
|
|
y = shape.GetY() - h/2 # convert to upper left coordinate from center
|
|
|
|
if x >= scrollX*xUnit and x <= scrollX*xUnit + scrollW: # don't scroll if already visible
|
|
x = -1
|
|
else:
|
|
x = x/xUnit
|
|
|
|
if y >= scrollY*yUnit and y <= scrollY*yUnit + scrollH: # don't scroll if already visible
|
|
y = -1
|
|
else:
|
|
y = y/yUnit
|
|
|
|
self._canvas.Scroll(x, y) # in scroll units
|
|
|
|
|
|
def SetPropertyShape(self, shape):
|
|
# no need to highlight if no PropertyService is running
|
|
propertyService = wx.GetApp().GetService(PropertyService.PropertyService)
|
|
if not propertyService:
|
|
return
|
|
|
|
if shape == self._propShape:
|
|
return
|
|
|
|
if hasattr(shape, "GetPropertyShape"):
|
|
shape = shape.GetPropertyShape()
|
|
|
|
dc = wx.ClientDC(self._canvas)
|
|
self._canvas.PrepareDC(dc)
|
|
dc.BeginDrawing()
|
|
|
|
# erase old selection if it still exists
|
|
if self._propShape and self._propShape in self._diagram.GetShapeList():
|
|
self._propShape.SetBrush(self._brush)
|
|
if (self._propShape._textColourName in ["BLACK", "WHITE"]): # Would use GetTextColour() but it is broken
|
|
self._propShape.SetTextColour("BLACK", 0)
|
|
self._propShape.Draw(dc)
|
|
|
|
# set new selection
|
|
self._propShape = shape
|
|
|
|
# draw new selection
|
|
if self._propShape and self._propShape in self._diagram.GetShapeList():
|
|
if self.HasFocus():
|
|
self._propShape.SetBrush(SELECT_BRUSH)
|
|
else:
|
|
self._propShape.SetBrush(INACTIVE_SELECT_BRUSH)
|
|
if (self._propShape._textColourName in ["BLACK", "WHITE"]): # Would use GetTextColour() but it is broken
|
|
self._propShape.SetTextColour("WHITE", 0)
|
|
self._propShape.Draw(dc)
|
|
|
|
dc.EndDrawing()
|
|
|
|
|
|
def FocusColorPropertyShape(self, gotFocus=False):
|
|
# no need to change highlight if no PropertyService is running
|
|
propertyService = wx.GetApp().GetService(PropertyService.PropertyService)
|
|
if not propertyService:
|
|
return
|
|
|
|
if not self._propShape:
|
|
return
|
|
|
|
dc = wx.ClientDC(self._canvas)
|
|
self._canvas.PrepareDC(dc)
|
|
dc.BeginDrawing()
|
|
|
|
# draw deactivated selection
|
|
if self._propShape and self._propShape in self._diagram.GetShapeList():
|
|
if gotFocus:
|
|
self._propShape.SetBrush(SELECT_BRUSH)
|
|
else:
|
|
self._propShape.SetBrush(INACTIVE_SELECT_BRUSH)
|
|
if (self._propShape._textColourName in ["BLACK", "WHITE"]): # Would use GetTextColour() but it is broken
|
|
self._propShape.SetTextColour("WHITE", 0)
|
|
self._propShape.Draw(dc)
|
|
|
|
dc.EndDrawing()
|
|
|
|
|
|
#----------------------------------------------------------------------------
|
|
# Property Service methods
|
|
#----------------------------------------------------------------------------
|
|
|
|
def GetPropertyModel(self):
|
|
if hasattr(self, "_propModel"):
|
|
return self._propModel
|
|
return None
|
|
|
|
|
|
def SetPropertyModel(self, model):
|
|
# no need to set the model if no PropertyService is running
|
|
propertyService = wx.GetApp().GetService(PropertyService.PropertyService)
|
|
if not propertyService:
|
|
return
|
|
|
|
if hasattr(self, "_propModel") and model == self._propModel:
|
|
return
|
|
|
|
self._propModel = model
|
|
propertyService.LoadProperties(self._propModel, self.GetDocument())
|
|
|
|
|
|
class EditorCanvasShapeMixin:
|
|
|
|
def GetModel(self):
|
|
return self._model
|
|
|
|
|
|
def SetModel(self, model):
|
|
self._model = model
|
|
|
|
|
|
class EditorCanvasShapeEvtHandler(ogl.ShapeEvtHandler):
|
|
|
|
""" wxBug: Bug in OLG package. With wxShape.SetShadowMode() turned on, when we set the size,
|
|
the width/height is larger by 6 pixels. Need to subtract this value from width and height when we
|
|
resize the object.
|
|
"""
|
|
SHIFT_KEY = 1
|
|
CONTROL_KEY = 2
|
|
|
|
def __init__(self, view):
|
|
ogl.ShapeEvtHandler.__init__(self)
|
|
self._view = view
|
|
|
|
|
|
def OnLeftClick(self, x, y, keys = 0, attachment = 0):
|
|
shape = self.GetShape()
|
|
if hasattr(shape, "GetModel"): # Workaround, on drag, we should deselect all other objects and select the clicked on object
|
|
model = shape.GetModel()
|
|
else:
|
|
shape = shape.GetParent()
|
|
if shape:
|
|
model = shape.GetModel()
|
|
|
|
self._view.SetSelection(model, keys == self.SHIFT_KEY or keys == self.CONTROL_KEY)
|
|
self._view.SetPropertyShape(shape)
|
|
self._view.SetPropertyModel(model)
|
|
|
|
|
|
def OnEndDragLeft(self, x, y, keys = 0, attachment = 0):
|
|
ogl.ShapeEvtHandler.OnEndDragLeft(self, x, y, keys, attachment)
|
|
shape = self.GetShape()
|
|
if hasattr(shape, "GetModel"): # Workaround, on drag, we should deselect all other objects and select the clicked on object
|
|
model = shape.GetModel()
|
|
else:
|
|
parentShape = shape.GetParent()
|
|
if parentShape:
|
|
model = parentShape.GetModel()
|
|
self._view.SetSelection(model, keys == self.SHIFT_KEY or keys == self.CONTROL_KEY)
|
|
|
|
|
|
def OnMovePre(self, dc, x, y, oldX, oldY, display):
|
|
""" Prevent objects from being dragged outside of viewable area """
|
|
if (x > self._view._maxWidth) or (y > self._view._maxHeight):
|
|
return False
|
|
|
|
return ogl.ShapeEvtHandler.OnMovePre(self, dc, x, y, oldX, oldY, display)
|
|
|
|
|
|
def OnMovePost(self, dc, x, y, oldX, oldY, display):
|
|
""" Update the model's record of where the shape should be. Also enable redo/undo. """
|
|
if x == oldX and y == oldY:
|
|
return
|
|
if not self._view.GetDocument():
|
|
return
|
|
shape = self.GetShape()
|
|
if isinstance(shape, EditorCanvasShapeMixin) and shape.Draggable():
|
|
model = shape.GetModel()
|
|
if hasattr(model, "getEditorBounds") and model.getEditorBounds():
|
|
x, y, w, h = model.getEditorBounds()
|
|
newX = shape.GetX() - shape.GetBoundingBoxMax()[0] / 2
|
|
newY = shape.GetY() - shape.GetBoundingBoxMax()[1] / 2
|
|
newWidth = shape.GetBoundingBoxMax()[0]
|
|
newHeight = shape.GetBoundingBoxMax()[1]
|
|
if shape._shadowMode != ogl.SHADOW_NONE:
|
|
newWidth -= shape._shadowOffsetX
|
|
newHeight -= shape._shadowOffsetY
|
|
newbounds = (newX, newY, newWidth, newHeight)
|
|
|
|
if x != newX or y != newY or w != newWidth or h != newHeight:
|
|
self._view.GetDocument().GetCommandProcessor().Submit(EditorCanvasUpdateShapeBoundariesCommand(self._view.GetDocument(), model, newbounds))
|
|
|
|
|
|
def Draw(self, dc):
|
|
pass
|
|
|
|
|
|
class EditorCanvasUpdateShapeBoundariesCommand(wx.lib.docview.Command):
|
|
|
|
|
|
def __init__(self, canvasDocument, model, newbounds):
|
|
wx.lib.docview.Command.__init__(self, canUndo = True)
|
|
self._canvasDocument = canvasDocument
|
|
self._model = model
|
|
self._oldbounds = model.getEditorBounds()
|
|
self._newbounds = newbounds
|
|
|
|
|
|
def GetName(self):
|
|
name = self._canvasDocument.GetNameForObject(self._model)
|
|
if not name:
|
|
name = ""
|
|
print "ERROR: AbstractEditor.EditorCanvasUpdateShapeBoundariesCommand.GetName: unable to get name for ", self._model
|
|
return _("Move/Resize %s") % name
|
|
|
|
|
|
def Do(self):
|
|
return self._canvasDocument.UpdateEditorBoundaries(self._model, self._newbounds)
|
|
|
|
|
|
def Undo(self):
|
|
return self._canvasDocument.UpdateEditorBoundaries(self._model, self._oldbounds)
|
|
|