X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/510bb7480c5138dd5127ed3d8b1d9cbab39983c9..1f780e48af479e7bf9a07eaaa1ab6b41f1ffb17b:/wxPython/samples/ide/activegrid/tool/AbstractEditor.py diff --git a/wxPython/samples/ide/activegrid/tool/AbstractEditor.py b/wxPython/samples/ide/activegrid/tool/AbstractEditor.py new file mode 100644 index 0000000000..8bda44696d --- /dev/null +++ b/wxPython/samples/ide/activegrid/tool/AbstractEditor.py @@ -0,0 +1,641 @@ +#---------------------------------------------------------------------------- +# 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 + + +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 + + + 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._canvas.SetFocus() + else: + wx.CallAfter(self._canvas.SetFocus) + + + 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) + + maxWidth = 2000 + maxHeight = 16000 + self._canvas.SetScrollbars(20, 20, maxWidth / 20, 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_RIGHT) + 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) + self._diagram.RemoveShape(shape) + if isinstance(shape, ogl.CompositeShape): + shape.RemoveFromCanvas(self._canvas) + + + 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(): + self._propShape.SetBrush(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 OnMovePost(self, dc, x, y, oldX, oldY, display): + 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) +