]> git.saurik.com Git - wxWidgets.git/blob - wxPython/samples/ide/activegrid/tool/AbstractEditor.py
fixed wxVsnprintf() to write as much as it can if the output buffer is too short
[wxWidgets.git] / wxPython / samples / ide / activegrid / tool / AbstractEditor.py
1 #----------------------------------------------------------------------------
2 # Name: AbstractEditor.py
3 # Purpose: Non-text editor for DataModel and Process
4 #
5 # Author: Peter Yared, Morgan Hua
6 #
7 # Created: 7/28/04
8 # CVS-ID: $Id$
9 # Copyright: (c) 2004-2005 ActiveGrid, Inc.
10 # License: wxWindows License
11 #----------------------------------------------------------------------------
12
13
14 import wx
15 import wx.lib.docview
16 import wx.lib.ogl as ogl
17 import PropertyService
18 _ = wx.GetTranslation
19
20
21 SELECT_BRUSH = wx.Brush("BLUE", wx.SOLID)
22 SHAPE_BRUSH = wx.Brush("WHEAT", wx.SOLID)
23 LINE_BRUSH = wx.BLACK_BRUSH
24 INACTIVE_SELECT_BRUSH = wx.Brush("LIGHT BLUE", wx.SOLID)
25
26 NORMALFONT = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
27 SLANTFONT = wx.Font(NORMALFONT.GetPointSize(), NORMALFONT.GetFamily(), wx.SLANT, NORMALFONT.GetWeight())
28 BOLDFONT = wx.Font(NORMALFONT.GetPointSize(), NORMALFONT.GetFamily(), NORMALFONT.GetStyle(), wx.BOLD)
29
30 DEFAULT_BACKGROUND_COLOR = wx.Colour(0xEE, 0xEE, 0xEE)
31 HEADER_BRUSH = wx.Brush(wx.Colour(0xDB, 0xEB, 0xFF), wx.SOLID)
32 BODY_BRUSH = wx.Brush(wx.WHITE, wx.SOLID)
33
34
35 PARKING_VERTICAL = 1
36 PARKING_HORIZONTAL = 2
37 PARKING_OFFSET = 30 # space between shapes
38
39 FORCE_REDRAW_METHOD = "ForceRedraw"
40
41 def GetRawModel(model):
42 if hasattr(model, "GetRawModel"):
43 rawModel = model.GetRawModel()
44 else:
45 rawModel = model
46 return rawModel
47
48
49 def GetLabel(model):
50 model = GetRawModel(model)
51 if hasattr(model, "__xmlname__"):
52 label = model.__xmlname__
53 try:
54 if (len(label) > 0):
55 label = label[0].upper() + label[1:]
56 if (hasattr(model, "complexType")):
57 label += ': %s/%s' % (model.complexType.name, model.name)
58 else:
59 if model.name:
60 label += ': %s' % model.name
61 elif model.ref:
62 label += ': %s' % model.ref
63 except AttributeError:
64 pass
65 else:
66 label = str(model)
67 return label
68
69
70 class CanvasView(wx.lib.docview.View):
71
72
73 #----------------------------------------------------------------------------
74 # Overridden methods
75 #----------------------------------------------------------------------------
76
77
78 def __init__(self, brush=SHAPE_BRUSH, background=DEFAULT_BACKGROUND_COLOR):
79 wx.lib.docview.View.__init__(self)
80 self._brush = brush
81 self._backgroundColor = background
82 self._canvas = None
83 self._pt1 = None
84 self._pt2 = None
85 self._needEraseLasso = False
86 self._propShape = None
87 self._maxWidth = 2000
88 self._maxHeight = 16000
89 self._valetParking = False
90
91
92 def OnDraw(self, dc):
93 """ for Print Preview and Print """
94 dc.BeginDrawing()
95 self._canvas.Redraw(dc)
96 dc.EndDrawing()
97
98
99 def OnCreate(self, doc, flags):
100 frame = wx.GetApp().CreateDocumentFrame(self, doc, flags)
101 frame.Show()
102 sizer = wx.BoxSizer()
103 self._CreateCanvas(frame)
104 sizer.Add(self._canvas, 1, wx.EXPAND, 0)
105 frame.SetSizer(sizer)
106 frame.Layout()
107 self.Activate()
108 wx.EVT_RIGHT_DOWN(self._canvas, self.OnRightClick)
109 return True
110
111
112 def OnActivateView(self, activate, activeView, deactiveView):
113 if activate and self._canvas:
114 # In MDI mode just calling set focus doesn't work and in SDI mode using CallAfter causes an endless loop
115 if self.GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI:
116 self.SetFocus()
117 else:
118 wx.CallAfter(self.SetFocus)
119
120
121 def SetFocus(self):
122 if self._canvas:
123 self._canvas.SetFocus()
124
125
126 def OnFocus(self, event):
127 self.FocusColorPropertyShape(True)
128 event.Skip()
129
130
131 def FocusOnClick(self, event):
132 self.SetFocus()
133 event.Skip()
134
135
136 def OnKillFocus(self, event):
137 self.FocusColorPropertyShape(False)
138 event.Skip()
139
140
141 def HasFocus(self):
142 winWithFocus = wx.Window.FindFocus()
143 if not winWithFocus:
144 return False
145 while winWithFocus:
146 if winWithFocus == self._canvas:
147 return True
148 winWithFocus = winWithFocus.GetParent()
149 return False
150
151
152 def OnClose(self, deleteWindow = True):
153 statusC = wx.GetApp().CloseChildDocuments(self.GetDocument())
154 statusP = wx.lib.docview.View.OnClose(self, deleteWindow = deleteWindow)
155 if hasattr(self, "ClearOutline"):
156 wx.CallAfter(self.ClearOutline) # need CallAfter because when closing the document, it is Activated and then Close, so need to match OnActivateView's CallAfter
157 if not (statusC and statusP):
158 return False
159 self.Activate(False)
160 if deleteWindow and self.GetFrame():
161 self.GetFrame().Destroy()
162 return True
163
164
165 def _CreateCanvas(self, parent):
166 self._canvas = ogl.ShapeCanvas(parent)
167 wx.EVT_LEFT_DOWN(self._canvas, self.OnLeftClick)
168 wx.EVT_LEFT_UP(self._canvas, self.OnLeftUp)
169 wx.EVT_MOTION(self._canvas, self.OnLeftDrag)
170 wx.EVT_LEFT_DCLICK(self._canvas, self.OnLeftDoubleClick)
171 wx.EVT_KEY_DOWN(self._canvas, self.OnKeyPressed)
172
173 # need this otherwise mouse clicks don't set focus to this view
174 wx.EVT_LEFT_DOWN(self._canvas, self.FocusOnClick)
175 wx.EVT_LEFT_DCLICK(self._canvas, self.FocusOnClick)
176 wx.EVT_RIGHT_DOWN(self._canvas, self.FocusOnClick)
177 wx.EVT_RIGHT_DCLICK(self._canvas, self.FocusOnClick)
178 wx.EVT_MIDDLE_DOWN(self._canvas, self.FocusOnClick)
179 wx.EVT_MIDDLE_DCLICK(self._canvas, self.FocusOnClick)
180
181 wx.EVT_KILL_FOCUS(self._canvas, self.OnKillFocus)
182 wx.EVT_SET_FOCUS(self._canvas, self.OnFocus)
183
184 self._canvas.SetScrollbars(20, 20, self._maxWidth / 20, self._maxHeight / 20)
185
186 self._canvas.SetBackgroundColour(self._backgroundColor)
187 self._diagram = ogl.Diagram()
188 self._canvas.SetDiagram(self._diagram)
189 self._diagram.SetCanvas(self._canvas)
190 self._canvas.SetFont(NORMALFONT)
191
192
193 def OnClear(self, event):
194 """ Deletion of selected objects from view.
195 *Must Override*
196 """
197 self.SetPropertyModel(None)
198
199
200 def SetLastRightClick(self, x, y):
201 self._lastRightClick = (x,y)
202
203
204 def GetLastRightClick(self):
205 if hasattr(self, "_lastRightClick"):
206 return self._lastRightClick
207 return (-1,-1)
208
209
210 def OnKeyPressed(self, event):
211 key = event.KeyCode()
212 if key == wx.WXK_DELETE:
213 self.OnClear(event)
214 else:
215 event.Skip()
216
217
218 def OnRightClick(self, event):
219 """ force selection underneath right click position. """
220 self.Activate()
221 self._canvas.SetFocus()
222
223 dc = wx.ClientDC(self._canvas)
224 self._canvas.PrepareDC(dc)
225 x, y = event.GetLogicalPosition(dc) # this takes into account scrollbar offset
226 self.SetLastRightClick(x, y)
227 shape = self._canvas.FindShape(x, y)[0]
228
229 model = None
230 if not shape:
231 self.SetSelection(None)
232 self.SetPropertyShape(None)
233 elif hasattr(shape, "GetModel"):
234 self.BringToFront(shape)
235 self.SetPropertyShape(shape)
236 self.SetSelection(shape)
237 shape.Select(True, dc)
238 model = shape.GetModel()
239 elif shape.GetParent() and isinstance(shape.GetParent(), ogl.CompositeShape): # ComplexTypeHeader for ComplexTypeShape
240 self.BringToFront(shape)
241 self.SetPropertyShape(shape.GetParent())
242 self.SetSelection(shape.GetParent())
243 shape.GetParent().Select(True, dc)
244 model = shape.GetParent().GetModel()
245
246 self.SetPropertyModel(model)
247
248 return (shape, model)
249
250
251 def OnLeftClick(self, event):
252 self.Activate()
253 self._canvas.SetFocus()
254
255 self.EraseRubberBand()
256
257 dc = wx.ClientDC(self._canvas)
258 self._canvas.PrepareDC(dc)
259
260 # keep track of mouse down for group select
261 self._pt1 = event.GetLogicalPosition(dc) # this takes into account scrollbar offset
262 self._pt2 = None
263
264 shape = self._canvas.FindShape(self._pt1[0], self._pt1[1])[0]
265 if shape:
266 self.BringToFront(shape)
267
268 self._pt1 = None
269 event.Skip() # pass on event to shape handler to take care of selection
270
271 return
272 elif event.ControlDown() or event.ShiftDown(): # extend select, don't deselect
273 pass
274 else:
275 # click on empty part of canvas, deselect everything
276 forceRedrawShapes = []
277 needRefresh = False
278 for shape in self._diagram.GetShapeList():
279 if hasattr(shape, "GetModel"):
280 if shape.Selected():
281 needRefresh = True
282 shape.Select(False, dc)
283 if hasattr(shape, FORCE_REDRAW_METHOD):
284 forceRedrawShapes.append(shape)
285 if needRefresh:
286 self._canvas.Redraw(dc)
287
288 self.SetPropertyModel(None)
289
290 if len(self.GetSelection()) == 0:
291 self.SetPropertyShape(None)
292
293 for shape in forceRedrawShapes:
294 shape.ForceRedraw()
295
296 def OnLeftDoubleClick(self, event):
297 propertyService = wx.GetApp().GetService(PropertyService.PropertyService)
298 if propertyService:
299 propertyService.ShowWindow()
300
301
302 def OnLeftDrag(self, event):
303 # draw lasso for group select
304 if self._pt1 and event.LeftIsDown(): # we are in middle of lasso selection
305 self.EraseRubberBand()
306
307 dc = wx.ClientDC(self._canvas)
308 self._canvas.PrepareDC(dc)
309 self._pt2 = event.GetLogicalPosition(dc) # this takes into account scrollbar offset
310 self.DrawRubberBand()
311 else:
312 event.Skip()
313
314
315 def OnLeftUp(self, event):
316 # do group select
317 if self._needEraseLasso:
318 self.EraseRubberBand()
319
320 dc = wx.ClientDC(self._canvas)
321 self._canvas.PrepareDC(dc)
322 x1, y1 = self._pt1
323 x2, y2 = event.GetLogicalPosition(dc) # this takes into account scrollbar offset
324
325 tol = self._diagram.GetMouseTolerance()
326 if abs(x1 - x2) > tol or abs(y1 - y2) > tol:
327 # make sure x1 < x2 and y1 < y2 to make comparison test easier
328 if x1 > x2:
329 temp = x1
330 x1 = x2
331 x2 = temp
332 if y1 > y2:
333 temp = y1
334 y1 = y2
335 y2 = temp
336
337 for shape in self._diagram.GetShapeList():
338 if not shape.GetParent() and hasattr(shape, "GetModel"): # if part of a composite, don't select it
339 x, y = shape.GetX(), shape.GetY()
340 width, height = shape.GetBoundingBoxMax()
341 selected = x1 < x - width/2 and x2 > x + width/2 and y1 < y - height/2 and y2 > y + height/2
342 if event.ControlDown() or event.ShiftDown(): # extend select, don't deselect
343 if selected:
344 shape.Select(selected, dc)
345 else: # select items in lasso and deselect items out of lasso
346 shape.Select(selected, dc)
347 self._canvas.Redraw(dc)
348 else:
349 event.Skip()
350 else:
351 event.Skip()
352
353
354 def EraseRubberBand(self):
355 if self._needEraseLasso:
356 self._needEraseLasso = False
357
358 dc = wx.ClientDC(self._canvas)
359 self._canvas.PrepareDC(dc)
360 dc.SetLogicalFunction(wx.XOR)
361 pen = wx.Pen(wx.Colour(200, 200, 200), 1, wx.SHORT_DASH)
362 dc.SetPen(pen)
363 brush = wx.Brush(wx.Colour(255, 255, 255), wx.TRANSPARENT)
364 dc.SetBrush(brush)
365 dc.ResetBoundingBox()
366 dc.BeginDrawing()
367
368 x1, y1 = self._pt1
369 x2, y2 = self._pt2
370
371 # make sure x1 < x2 and y1 < y2
372 # this will make (x1, y1) = upper left corner
373 if x1 > x2:
374 temp = x1
375 x1 = x2
376 x2 = temp
377 if y1 > y2:
378 temp = y1
379 y1 = y2
380 y2 = temp
381
382 # erase previous outline
383 dc.SetClippingRegion(x1, y1, x2 - x1, y2 - y1)
384 dc.DrawRectangle(x1, y1, x2 - x1, y2 - y1)
385 dc.EndDrawing()
386
387
388 def DrawRubberBand(self):
389 self._needEraseLasso = True
390
391 dc = wx.ClientDC(self._canvas)
392 self._canvas.PrepareDC(dc)
393 dc.SetLogicalFunction(wx.XOR)
394 pen = wx.Pen(wx.Colour(200, 200, 200), 1, wx.SHORT_DASH)
395 dc.SetPen(pen)
396 brush = wx.Brush(wx.Colour(255, 255, 255), wx.TRANSPARENT)
397 dc.SetBrush(brush)
398 dc.ResetBoundingBox()
399 dc.BeginDrawing()
400
401 x1, y1 = self._pt1
402 x2, y2 = self._pt2
403
404 # make sure x1 < x2 and y1 < y2
405 # this will make (x1, y1) = upper left corner
406 if x1 > x2:
407 temp = x1
408 x1 = x2
409 x2 = temp
410 if y1 > y2:
411 temp = y1
412 y1 = y2
413 y2 = temp
414
415 # draw outline
416 dc.SetClippingRegion(x1, y1, x2 - x1, y2 - y1)
417 dc.DrawRectangle(x1, y1, x2 - x1, y2 - y1)
418 dc.EndDrawing()
419
420
421 def SetValetParking(self, enable=True):
422 """ If valet parking is enabled, remember last parking spot and try for a spot near it """
423 self._valetParking = enable
424 if enable:
425 self._valetPosition = None
426
427
428 def FindParkingSpot(self, width, height, parking=PARKING_HORIZONTAL, x=PARKING_OFFSET, y=PARKING_OFFSET):
429 """
430 Given a width and height, find a upper left corner where shape can be parked without overlapping other shape
431 """
432 if self._valetParking and self._valetPosition:
433 x, y = self._valetPosition
434
435 max = 700 # max distance to the right where we'll place tables
436 noParkingSpot = True
437
438 while noParkingSpot:
439 point = self.isSpotOccupied(x, y, width, height)
440 if point:
441 if parking == PARKING_HORIZONTAL:
442 x = point[0] + PARKING_OFFSET
443 if x > max:
444 x = PARKING_OFFSET
445 y = point[1] + PARKING_OFFSET
446 else: # parking == PARKING_VERTICAL:
447 y = point[1] + PARKING_OFFSET
448 if y > max:
449 y = PARKING_OFFSET
450 x = point[0] + PARKING_OFFSET
451 else:
452 noParkingSpot = False
453
454 if self._valetParking:
455 self._valetPosition = (x, y)
456
457 return x, y
458
459
460 def isSpotOccupied(self, x, y, width, height):
461 """ returns None if at x,y,width,height no object occupies that rectangle,
462 otherwise returns lower right corner of object that occupies given x,y position
463 """
464 x2 = x + width
465 y2 = y + height
466
467 for shape in self._diagram.GetShapeList():
468 if isinstance(shape, ogl.RectangleShape) or isinstance(shape, ogl.EllipseShape) or isinstance(shape, ogl.PolygonShape):
469 if shape.GetParent() and isinstance(shape.GetParent(), ogl.CompositeShape):
470 # skip, part of a composite shape
471 continue
472
473 if hasattr(shape, "GetModel"):
474 other_x, other_y, other_width, other_height = shape.GetModel().getEditorBounds()
475 other_x2 = other_x + other_width
476 other_y2 = other_y + other_height
477 else:
478 # shapes x,y are at the center of the shape, need to transform to upper left coordinate
479 other_width, other_height = shape.GetBoundingBoxMax()
480 other_x = shape.GetX() - other_width/2
481 other_y = shape.GetY() - other_height/2
482
483 other_x2 = other_x + other_width
484 other_y2 = other_y + other_height
485 # intersection check
486 if ((other_x2 < other_x or other_x2 > x) and
487 (other_y2 < other_y or other_y2 > y) and
488 (x2 < x or x2 > other_x) and
489 (y2 < y or y2 > other_y)):
490 return (other_x2, other_y2)
491 return None
492
493
494 #----------------------------------------------------------------------------
495 # Canvas methods
496 #----------------------------------------------------------------------------
497
498 def AddShape(self, shape, x = None, y = None, pen = None, brush = None, text = None, eventHandler = None, shown=True):
499 if isinstance(shape, ogl.CompositeShape):
500 dc = wx.ClientDC(self._canvas)
501 self._canvas.PrepareDC(dc)
502 shape.Move(dc, x, y)
503 else:
504 shape.SetDraggable(True, True)
505 shape.SetCanvas(self._canvas)
506
507 if x:
508 shape.SetX(x)
509 if y:
510 shape.SetY(y)
511 shape.SetCentreResize(False)
512 if pen:
513 shape.SetPen(pen)
514 if brush:
515 shape.SetBrush(brush)
516 if text:
517 shape.AddText(text)
518 shape.SetShadowMode(ogl.SHADOW_NONE)
519 self._diagram.AddShape(shape)
520 shape.Show(shown)
521 if not eventHandler:
522 eventHandler = EditorCanvasShapeEvtHandler(self)
523 eventHandler.SetShape(shape)
524 eventHandler.SetPreviousHandler(shape.GetEventHandler())
525 shape.SetEventHandler(eventHandler)
526 return shape
527
528
529 def RemoveShape(self, model = None, shape = None):
530 if not model and not shape:
531 return
532
533 if not shape:
534 shape = self.GetShape(model)
535
536 if shape:
537 shape.Select(False)
538 for line in shape.GetLines():
539 shape.RemoveLine(line)
540 self._diagram.RemoveShape(line)
541 line.Delete()
542 for obj in self._diagram.GetShapeList():
543 for line in obj.GetLines():
544 if self.IsShapeContained(shape, line.GetTo()) or self.IsShapeContained(shape, line.GetFrom()):
545 obj.RemoveLine(line)
546 self._diagram.RemoveShape(line)
547 line.Delete()
548 if line == shape:
549 obj.RemoveLine(line)
550 self._diagram.RemoveShape(line)
551 line.Delete()
552
553 if self._canvas:
554 shape.RemoveFromCanvas(self._canvas)
555 self._diagram.RemoveShape(shape)
556 shape.Delete()
557
558
559 def IsShapeContained(self, parent, shape):
560 if parent == shape:
561 return True
562 elif shape.GetParent():
563 return self.IsShapeContained(parent, shape.GetParent())
564
565 return False
566
567
568 def UpdateShape(self, model):
569 for shape in self._diagram.GetShapeList():
570 if hasattr(shape, "GetModel") and shape.GetModel() == model:
571 oldw, oldh = shape.GetBoundingBoxMax()
572 oldx = shape.GetX()
573 oldy = shape.GetY()
574
575 x, y, w, h = model.getEditorBounds()
576 newX = x + w / 2
577 newY = y + h / 2
578
579 if oldw != w or oldh != h or oldx != newX or oldy != newY:
580 dc = wx.ClientDC(self._canvas)
581 self._canvas.PrepareDC(dc)
582 shape.SetSize(w, h, True) # wxBug: SetSize must be before Move because links won't go to the right place
583 shape.Move(dc, newX, newY) # wxBug: Move must be after SetSize because links won't go to the right place
584 shape.ResetControlPoints()
585 self._canvas.Refresh()
586
587 break
588
589
590 def GetShape(self, model):
591 for shape in self._diagram.GetShapeList():
592 if hasattr(shape, "GetModel") and shape.GetModel() == model:
593 return shape
594 return None
595
596
597 def GetShapeCount(self):
598 return self._diagram.GetCount()
599
600
601 def GetSelection(self):
602 return filter(lambda shape: shape.Selected(), self._diagram.GetShapeList())
603
604
605 def SetSelection(self, models, extendSelect = False):
606 dc = wx.ClientDC(self._canvas)
607 self._canvas.PrepareDC(dc)
608 update = False
609 if not isinstance(models, type([])) and not isinstance(models, type(())):
610 models = [models]
611 for shape in self._diagram.GetShapeList():
612 if hasattr(shape, "GetModel"):
613 if shape.Selected() and not shape.GetModel() in models: # was selected, but not in new list, so deselect, unless extend select
614 if not extendSelect:
615 shape.Select(False, dc)
616 update = True
617 elif not shape.Selected() and shape.GetModel() in models: # was not selected and in new list, so select
618 shape.Select(True, dc)
619 update = True
620 elif extendSelect and shape.Selected() and shape.GetModel() in models: # was selected, but extend select means to deselect
621 shape.Select(False, dc)
622 update = True
623 if update:
624 self._canvas.Redraw(dc)
625
626
627 def BringToFront(self, shape):
628 if shape.GetParent() and isinstance(shape.GetParent(), ogl.CompositeShape):
629 self._diagram.RemoveShape(shape.GetParent())
630 self._diagram.AddShape(shape.GetParent())
631 else:
632 self._diagram.RemoveShape(shape)
633 self._diagram.AddShape(shape)
634
635
636 def SendToBack(self, shape):
637 if shape.GetParent() and isinstance(shape.GetParent(), ogl.CompositeShape):
638 self._diagram.RemoveShape(shape.GetParent())
639 self._diagram.InsertShape(shape.GetParent())
640 else:
641 self._diagram.RemoveShape(shape)
642 self._diagram.InsertShape(shape)
643
644
645 def ScrollVisible(self, shape):
646 if not shape:
647 return
648
649 xUnit, yUnit = self._canvas.GetScrollPixelsPerUnit()
650 scrollX, scrollY = self._canvas.GetViewStart() # in scroll units
651 scrollW, scrollH = self._canvas.GetSize() # in pixels
652 w, h = shape.GetBoundingBoxMax() # in pixels
653 x = shape.GetX() - w/2 # convert to upper left coordinate from center
654 y = shape.GetY() - h/2 # convert to upper left coordinate from center
655
656 if x >= scrollX*xUnit and x <= scrollX*xUnit + scrollW: # don't scroll if already visible
657 x = -1
658 else:
659 x = x/xUnit
660
661 if y >= scrollY*yUnit and y <= scrollY*yUnit + scrollH: # don't scroll if already visible
662 y = -1
663 else:
664 y = y/yUnit
665
666 self._canvas.Scroll(x, y) # in scroll units
667
668
669 def SetPropertyShape(self, shape):
670 # no need to highlight if no PropertyService is running
671 propertyService = wx.GetApp().GetService(PropertyService.PropertyService)
672 if not propertyService:
673 return
674
675 if shape == self._propShape:
676 return
677
678 if hasattr(shape, "GetPropertyShape"):
679 shape = shape.GetPropertyShape()
680
681 dc = wx.ClientDC(self._canvas)
682 self._canvas.PrepareDC(dc)
683 dc.BeginDrawing()
684
685 # erase old selection if it still exists
686 if self._propShape and self._propShape in self._diagram.GetShapeList():
687 if hasattr(self._propShape, "DEFAULT_BRUSH"):
688 self._propShape.SetBrush(self._propShape.DEFAULT_BRUSH)
689 else:
690 self._propShape.SetBrush(self._brush)
691 if (self._propShape._textColourName in ["BLACK", "WHITE"]): # Would use GetTextColour() but it is broken
692 self._propShape.SetTextColour("BLACK", 0)
693 self._propShape.Draw(dc)
694
695 # set new selection
696 self._propShape = shape
697
698 # draw new selection
699 if self._propShape and self._propShape in self._diagram.GetShapeList():
700 if self.HasFocus():
701 self._propShape.SetBrush(SELECT_BRUSH)
702 else:
703 self._propShape.SetBrush(INACTIVE_SELECT_BRUSH)
704 if (self._propShape._textColourName in ["BLACK", "WHITE"]): # Would use GetTextColour() but it is broken
705 self._propShape.SetTextColour("WHITE", 0)
706 self._propShape.Draw(dc)
707
708 dc.EndDrawing()
709
710
711 def FocusColorPropertyShape(self, gotFocus=False):
712 # no need to change highlight if no PropertyService is running
713 propertyService = wx.GetApp().GetService(PropertyService.PropertyService)
714 if not propertyService:
715 return
716
717 if not self._propShape:
718 return
719
720 dc = wx.ClientDC(self._canvas)
721 self._canvas.PrepareDC(dc)
722 dc.BeginDrawing()
723
724 # draw deactivated selection
725 if self._propShape and self._propShape in self._diagram.GetShapeList():
726 if gotFocus:
727 self._propShape.SetBrush(SELECT_BRUSH)
728 else:
729 self._propShape.SetBrush(INACTIVE_SELECT_BRUSH)
730 if (self._propShape._textColourName in ["BLACK", "WHITE"]): # Would use GetTextColour() but it is broken
731 self._propShape.SetTextColour("WHITE", 0)
732 self._propShape.Draw(dc)
733
734 if hasattr(self._propShape, FORCE_REDRAW_METHOD):
735 self._propShape.ForceRedraw()
736
737 dc.EndDrawing()
738
739
740 #----------------------------------------------------------------------------
741 # Property Service methods
742 #----------------------------------------------------------------------------
743
744 def GetPropertyModel(self):
745 if hasattr(self, "_propModel"):
746 return self._propModel
747 return None
748
749
750 def SetPropertyModel(self, model):
751 # no need to set the model if no PropertyService is running
752 propertyService = wx.GetApp().GetService(PropertyService.PropertyService)
753 if not propertyService:
754 return
755
756 if hasattr(self, "_propModel") and model == self._propModel:
757 return
758
759 self._propModel = model
760 propertyService.LoadProperties(self._propModel, self.GetDocument())
761
762
763 class EditorCanvasShapeMixin:
764
765 def GetModel(self):
766 return self._model
767
768
769 def SetModel(self, model):
770 self._model = model
771
772
773 class EditorCanvasShapeEvtHandler(ogl.ShapeEvtHandler):
774
775 """ wxBug: Bug in OLG package. With wxShape.SetShadowMode() turned on, when we set the size,
776 the width/height is larger by 6 pixels. Need to subtract this value from width and height when we
777 resize the object.
778 """
779 SHIFT_KEY = 1
780 CONTROL_KEY = 2
781
782 def __init__(self, view):
783 ogl.ShapeEvtHandler.__init__(self)
784 self._view = view
785
786
787 def OnLeftClick(self, x, y, keys = 0, attachment = 0):
788 shape = self.GetShape()
789 if hasattr(shape, "GetModel"): # Workaround, on drag, we should deselect all other objects and select the clicked on object
790 model = shape.GetModel()
791 else:
792 shape = shape.GetParent()
793 if shape:
794 model = shape.GetModel()
795
796 if model:
797 self._view.SetSelection(model, keys == self.SHIFT_KEY or keys == self.CONTROL_KEY)
798 self._view.SetPropertyShape(shape)
799 self._view.SetPropertyModel(model)
800
801
802 def OnEndDragLeft(self, x, y, keys = 0, attachment = 0):
803 ogl.ShapeEvtHandler.OnEndDragLeft(self, x, y, keys, attachment)
804 shape = self.GetShape()
805 if hasattr(shape, "GetModel"): # Workaround, on drag, we should deselect all other objects and select the clicked on object
806 model = shape.GetModel()
807 else:
808 parentShape = shape.GetParent()
809 if parentShape:
810 model = parentShape.GetModel()
811 self._view.SetSelection(model, keys == self.SHIFT_KEY or keys == self.CONTROL_KEY)
812
813
814 def OnMovePre(self, dc, x, y, oldX, oldY, display):
815 """ Prevent objects from being dragged outside of viewable area """
816 if (x < 0) or (y < 0) or (x > self._view._maxWidth) or (y > self._view._maxHeight):
817 return False
818
819 return ogl.ShapeEvtHandler.OnMovePre(self, dc, x, y, oldX, oldY, display)
820
821
822 def OnMovePost(self, dc, x, y, oldX, oldY, display):
823 """ Update the model's record of where the shape should be. Also enable redo/undo. """
824 if x == oldX and y == oldY:
825 return
826 if not self._view.GetDocument():
827 return
828 shape = self.GetShape()
829 if isinstance(shape, EditorCanvasShapeMixin) and shape.Draggable():
830 model = shape.GetModel()
831 if hasattr(model, "getEditorBounds") and model.getEditorBounds():
832 x, y, w, h = model.getEditorBounds()
833 newX = shape.GetX() - shape.GetBoundingBoxMax()[0] / 2
834 newY = shape.GetY() - shape.GetBoundingBoxMax()[1] / 2
835 newWidth = shape.GetBoundingBoxMax()[0]
836 newHeight = shape.GetBoundingBoxMax()[1]
837 if shape._shadowMode != ogl.SHADOW_NONE:
838 newWidth -= shape._shadowOffsetX
839 newHeight -= shape._shadowOffsetY
840 newbounds = (newX, newY, newWidth, newHeight)
841
842 if x != newX or y != newY or w != newWidth or h != newHeight:
843 self._view.GetDocument().GetCommandProcessor().Submit(EditorCanvasUpdateShapeBoundariesCommand(self._view.GetDocument(), model, newbounds))
844
845
846 def Draw(self, dc):
847 pass
848
849
850 class EditorCanvasUpdateShapeBoundariesCommand(wx.lib.docview.Command):
851
852
853 def __init__(self, canvasDocument, model, newbounds):
854 wx.lib.docview.Command.__init__(self, canUndo = True)
855 self._canvasDocument = canvasDocument
856 self._model = model
857 self._oldbounds = model.getEditorBounds()
858 self._newbounds = newbounds
859
860
861 def GetName(self):
862 name = self._canvasDocument.GetNameForObject(self._model)
863 if not name:
864 name = ""
865 print "ERROR: AbstractEditor.EditorCanvasUpdateShapeBoundariesCommand.GetName: unable to get name for ", self._model
866 return _("Move/Resize %s") % name
867
868
869 def Do(self):
870 return self._canvasDocument.UpdateEditorBoundaries(self._model, self._newbounds)
871
872
873 def Undo(self):
874 return self._canvasDocument.UpdateEditorBoundaries(self._model, self._oldbounds)
875