]>
Commit | Line | Data |
---|---|---|
1f780e48 RD |
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 | |
b792147d | 24 | INACTIVE_SELECT_BRUSH = wx.Brush("LIGHT BLUE", wx.SOLID) |
1f780e48 RD |
25 | |
26 | ||
27 | def GetRawModel(model): | |
28 | if hasattr(model, "GetRawModel"): | |
29 | rawModel = model.GetRawModel() | |
30 | else: | |
31 | rawModel = model | |
32 | return rawModel | |
33 | ||
34 | ||
35 | class CanvasView(wx.lib.docview.View): | |
36 | ||
37 | ||
38 | #---------------------------------------------------------------------------- | |
39 | # Overridden methods | |
40 | #---------------------------------------------------------------------------- | |
41 | ||
42 | ||
43 | def __init__(self, brush = SHAPE_BRUSH): | |
44 | wx.lib.docview.View.__init__(self) | |
45 | self._brush = brush | |
46 | self._canvas = None | |
47 | self._pt1 = None | |
48 | self._pt2 = None | |
49 | self._needEraseLasso = False | |
50 | self._propShape = None | |
26ee3a06 RD |
51 | self._maxWidth = 2000 |
52 | self._maxHeight = 16000 | |
1f780e48 RD |
53 | |
54 | ||
26ee3a06 RD |
55 | def OnDraw(self, dc): |
56 | """ for Print Preview and Print """ | |
57 | dc.BeginDrawing() | |
58 | self._canvas.Redraw(dc) | |
59 | dc.EndDrawing() | |
60 | ||
61 | ||
1f780e48 RD |
62 | def OnCreate(self, doc, flags): |
63 | frame = wx.GetApp().CreateDocumentFrame(self, doc, flags) | |
64 | frame.Show() | |
65 | sizer = wx.BoxSizer() | |
66 | self._CreateCanvas(frame) | |
67 | sizer.Add(self._canvas, 1, wx.EXPAND, 0) | |
68 | frame.SetSizer(sizer) | |
69 | frame.Layout() | |
70 | self.Activate() | |
71 | return True | |
b792147d | 72 | |
1f780e48 RD |
73 | |
74 | def OnActivateView(self, activate, activeView, deactiveView): | |
75 | if activate and self._canvas: | |
76 | # In MDI mode just calling set focus doesn't work and in SDI mode using CallAfter causes an endless loop | |
77 | if self.GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI: | |
6f1a3f9c | 78 | self.SetFocus() |
1f780e48 | 79 | else: |
6f1a3f9c RD |
80 | wx.CallAfter(self.SetFocus) |
81 | ||
82 | ||
83 | def SetFocus(self): | |
84 | if self._canvas: | |
85 | self._canvas.SetFocus() | |
1f780e48 RD |
86 | |
87 | ||
b792147d | 88 | def OnFocus(self, event): |
6f1a3f9c | 89 | self.SetFocus() |
b792147d RD |
90 | self.FocusColorPropertyShape(True) |
91 | event.Skip() | |
92 | ||
93 | ||
94 | def OnKillFocus(self, event): | |
95 | self.FocusColorPropertyShape(False) | |
96 | event.Skip() | |
97 | ||
98 | ||
6f1a3f9c RD |
99 | def HasFocus(self): |
100 | winWithFocus = wx.Window.FindFocus() | |
101 | if not winWithFocus: | |
102 | return False | |
103 | while winWithFocus: | |
104 | if winWithFocus == self._canvas: | |
105 | return True | |
106 | winWithFocus = winWithFocus.GetParent() | |
107 | return False | |
108 | ||
109 | ||
1f780e48 RD |
110 | def OnClose(self, deleteWindow = True): |
111 | statusC = wx.GetApp().CloseChildDocuments(self.GetDocument()) | |
112 | statusP = wx.lib.docview.View.OnClose(self, deleteWindow = deleteWindow) | |
113 | if hasattr(self, "ClearOutline"): | |
114 | wx.CallAfter(self.ClearOutline) # need CallAfter because when closing the document, it is Activated and then Close, so need to match OnActivateView's CallAfter | |
115 | if not (statusC and statusP): | |
116 | return False | |
117 | self.Activate(False) | |
118 | if deleteWindow and self.GetFrame(): | |
119 | self.GetFrame().Destroy() | |
120 | return True | |
121 | ||
122 | ||
123 | def _CreateCanvas(self, parent): | |
124 | self._canvas = ogl.ShapeCanvas(parent) | |
125 | wx.EVT_LEFT_DOWN(self._canvas, self.OnLeftClick) | |
126 | wx.EVT_LEFT_UP(self._canvas, self.OnLeftUp) | |
127 | wx.EVT_MOTION(self._canvas, self.OnLeftDrag) | |
128 | wx.EVT_LEFT_DCLICK(self._canvas, self.OnLeftDoubleClick) | |
129 | wx.EVT_KEY_DOWN(self._canvas, self.OnKeyPressed) | |
b792147d RD |
130 | |
131 | # need this otherwise mouse clicks don't set focus to this view | |
132 | wx.EVT_LEFT_DOWN(self._canvas, self.OnFocus) | |
133 | wx.EVT_LEFT_DCLICK(self._canvas, self.OnFocus) | |
134 | wx.EVT_RIGHT_DOWN(self._canvas, self.OnFocus) | |
135 | wx.EVT_RIGHT_DCLICK(self._canvas, self.OnFocus) | |
136 | wx.EVT_MIDDLE_DOWN(self._canvas, self.OnFocus) | |
137 | wx.EVT_MIDDLE_DCLICK(self._canvas, self.OnFocus) | |
138 | ||
139 | wx.EVT_KILL_FOCUS(self._canvas, self.OnKillFocus) | |
140 | wx.EVT_SET_FOCUS(self._canvas, self.OnFocus) | |
1f780e48 | 141 | |
26ee3a06 | 142 | self._canvas.SetScrollbars(20, 20, self._maxWidth / 20, self._maxHeight / 20) |
1f780e48 RD |
143 | |
144 | self._canvas.SetBackgroundColour(wx.WHITE) | |
145 | self._diagram = ogl.Diagram() | |
146 | self._canvas.SetDiagram(self._diagram) | |
147 | self._diagram.SetCanvas(self._canvas) | |
148 | ||
149 | ||
150 | def OnKeyPressed(self, event): | |
151 | key = event.KeyCode() | |
152 | if key == wx.WXK_DELETE: | |
153 | self.OnClear(event) | |
154 | else: | |
155 | event.Skip() | |
156 | ||
157 | ||
158 | def OnLeftClick(self, event): | |
159 | self.EraseRubberBand() | |
160 | ||
161 | dc = wx.ClientDC(self._canvas) | |
162 | self._canvas.PrepareDC(dc) | |
163 | ||
164 | # keep track of mouse down for group select | |
165 | self._pt1 = event.GetLogicalPosition(dc) # this takes into account scrollbar offset | |
166 | self._pt2 = None | |
167 | ||
168 | shape = self._canvas.FindShape(self._pt1[0], self._pt1[1])[0] | |
169 | if shape: | |
170 | self.BringToFront(shape) | |
171 | ||
172 | self._pt1 = None | |
173 | event.Skip() # pass on event to shape handler to take care of selection | |
174 | ||
175 | return | |
176 | elif event.ControlDown() or event.ShiftDown(): # extend select, don't deselect | |
177 | pass | |
178 | else: | |
179 | # click on empty part of canvas, deselect everything | |
180 | needRefresh = False | |
181 | for shape in self._diagram.GetShapeList(): | |
182 | if hasattr(shape, "GetModel"): | |
183 | if shape.Selected(): | |
184 | needRefresh = True | |
185 | shape.Select(False, dc) | |
186 | if needRefresh: | |
187 | self._canvas.Redraw(dc) | |
188 | ||
189 | self.SetPropertyModel(None) | |
190 | ||
191 | if len(self.GetSelection()) == 0: | |
192 | self.SetPropertyShape(None) | |
193 | ||
194 | ||
195 | ||
196 | def OnLeftDoubleClick(self, event): | |
197 | propertyService = wx.GetApp().GetService(PropertyService.PropertyService) | |
198 | if propertyService: | |
199 | propertyService.ShowWindow() | |
200 | ||
201 | ||
202 | def OnLeftDrag(self, event): | |
203 | # draw lasso for group select | |
204 | if self._pt1 and event.LeftIsDown(): # we are in middle of lasso selection | |
205 | self.EraseRubberBand() | |
206 | ||
207 | dc = wx.ClientDC(self._canvas) | |
208 | self._canvas.PrepareDC(dc) | |
209 | self._pt2 = event.GetLogicalPosition(dc) # this takes into account scrollbar offset | |
210 | self.DrawRubberBand() | |
211 | else: | |
212 | event.Skip() | |
213 | ||
214 | ||
215 | def OnLeftUp(self, event): | |
216 | # do group select | |
217 | if self._needEraseLasso: | |
218 | self.EraseRubberBand() | |
219 | ||
220 | dc = wx.ClientDC(self._canvas) | |
221 | self._canvas.PrepareDC(dc) | |
222 | x1, y1 = self._pt1 | |
223 | x2, y2 = event.GetLogicalPosition(dc) # this takes into account scrollbar offset | |
224 | ||
225 | tol = self._diagram.GetMouseTolerance() | |
226 | if abs(x1 - x2) > tol or abs(y1 - y2) > tol: | |
227 | # make sure x1 < x2 and y1 < y2 to make comparison test easier | |
228 | if x1 > x2: | |
229 | temp = x1 | |
230 | x1 = x2 | |
231 | x2 = temp | |
232 | if y1 > y2: | |
233 | temp = y1 | |
234 | y1 = y2 | |
235 | y2 = temp | |
236 | ||
237 | for shape in self._diagram.GetShapeList(): | |
238 | if not shape.GetParent() and hasattr(shape, "GetModel"): # if part of a composite, don't select it | |
239 | x, y = shape.GetX(), shape.GetY() | |
240 | width, height = shape.GetBoundingBoxMax() | |
241 | selected = x1 < x - width/2 and x2 > x + width/2 and y1 < y - height/2 and y2 > y + height/2 | |
242 | if event.ControlDown() or event.ShiftDown(): # extend select, don't deselect | |
243 | if selected: | |
244 | shape.Select(selected, dc) | |
245 | else: # select items in lasso and deselect items out of lasso | |
246 | shape.Select(selected, dc) | |
247 | self._canvas.Redraw(dc) | |
248 | else: | |
249 | event.Skip() | |
250 | else: | |
251 | event.Skip() | |
252 | ||
253 | ||
254 | def EraseRubberBand(self): | |
255 | if self._needEraseLasso: | |
256 | self._needEraseLasso = False | |
257 | ||
258 | dc = wx.ClientDC(self._canvas) | |
259 | self._canvas.PrepareDC(dc) | |
260 | dc.SetLogicalFunction(wx.XOR) | |
261 | pen = wx.Pen(wx.Colour(200, 200, 200), 1, wx.SHORT_DASH) | |
262 | dc.SetPen(pen) | |
263 | brush = wx.Brush(wx.Colour(255, 255, 255), wx.TRANSPARENT) | |
264 | dc.SetBrush(brush) | |
265 | dc.ResetBoundingBox() | |
266 | dc.BeginDrawing() | |
267 | ||
268 | x1, y1 = self._pt1 | |
269 | x2, y2 = self._pt2 | |
270 | ||
271 | # make sure x1 < x2 and y1 < y2 | |
272 | # this will make (x1, y1) = upper left corner | |
273 | if x1 > x2: | |
274 | temp = x1 | |
275 | x1 = x2 | |
276 | x2 = temp | |
277 | if y1 > y2: | |
278 | temp = y1 | |
279 | y1 = y2 | |
280 | y2 = temp | |
281 | ||
282 | # erase previous outline | |
283 | dc.SetClippingRegion(x1, y1, x2 - x1, y2 - y1) | |
284 | dc.DrawRectangle(x1, y1, x2 - x1, y2 - y1) | |
285 | dc.EndDrawing() | |
286 | ||
287 | ||
288 | def DrawRubberBand(self): | |
289 | self._needEraseLasso = True | |
290 | ||
291 | dc = wx.ClientDC(self._canvas) | |
292 | self._canvas.PrepareDC(dc) | |
293 | dc.SetLogicalFunction(wx.XOR) | |
294 | pen = wx.Pen(wx.Colour(200, 200, 200), 1, wx.SHORT_DASH) | |
295 | dc.SetPen(pen) | |
296 | brush = wx.Brush(wx.Colour(255, 255, 255), wx.TRANSPARENT) | |
297 | dc.SetBrush(brush) | |
298 | dc.ResetBoundingBox() | |
299 | dc.BeginDrawing() | |
300 | ||
301 | x1, y1 = self._pt1 | |
302 | x2, y2 = self._pt2 | |
303 | ||
304 | # make sure x1 < x2 and y1 < y2 | |
305 | # this will make (x1, y1) = upper left corner | |
306 | if x1 > x2: | |
307 | temp = x1 | |
308 | x1 = x2 | |
309 | x2 = temp | |
310 | if y1 > y2: | |
311 | temp = y1 | |
312 | y1 = y2 | |
313 | y2 = temp | |
314 | ||
315 | # draw outline | |
316 | dc.SetClippingRegion(x1, y1, x2 - x1, y2 - y1) | |
317 | dc.DrawRectangle(x1, y1, x2 - x1, y2 - y1) | |
318 | dc.EndDrawing() | |
319 | ||
320 | ||
321 | def FindParkingSpot(self, width, height): | |
322 | """ given a width and height, find a upper left corner where shape can be parked without overlapping other shape """ | |
323 | offset = 30 # space between shapes | |
324 | x = offset | |
325 | y = offset | |
326 | maxX = 700 # max distance to the right where we'll place tables | |
327 | noParkingSpot = True | |
328 | ||
329 | while noParkingSpot: | |
330 | point = self.isSpotOccupied(x, y, width, height) | |
331 | if point: | |
332 | x = point[0] + offset | |
333 | if x > maxX: | |
334 | x = offset | |
335 | y = point[1] + offset | |
336 | else: | |
337 | noParkingSpot = False | |
338 | ||
339 | return x, y | |
340 | ||
341 | ||
342 | def isSpotOccupied(self, x, y, width, height): | |
343 | """ returns None if at x,y,width,height no object occupies that rectangle, | |
344 | otherwise returns lower right corner of object that occupies given x,y position | |
345 | """ | |
346 | x2 = x + width | |
347 | y2 = y + height | |
348 | ||
349 | for shape in self._diagram.GetShapeList(): | |
350 | if isinstance(shape, ogl.RectangleShape) or isinstance(shape, ogl.EllipseShape): | |
351 | if shape.GetParent() and isinstance(shape.GetParent(), ogl.CompositeShape): | |
352 | # skip, part of a composite shape | |
353 | continue | |
354 | ||
355 | if hasattr(shape, "GetModel"): | |
356 | other_x, other_y, other_width, other_height = shape.GetModel().getEditorBounds() | |
357 | other_x2 = other_x + other_width | |
358 | other_y2 = other_y + other_height | |
359 | else: | |
360 | # shapes x,y are at the center of the shape, need to transform to upper left coordinate | |
361 | other_width, other_height = shape.GetBoundingBoxMax() | |
362 | other_x = shape.GetX() - other_width/2 | |
363 | other_y = shape.GetY() - other_height/2 | |
364 | ||
365 | other_x2 = other_x + other_width | |
366 | other_y2 = other_y + other_height | |
367 | # intersection check | |
368 | if ((other_x2 < other_x or other_x2 > x) and | |
369 | (other_y2 < other_y or other_y2 > y) and | |
370 | (x2 < x or x2 > other_x) and | |
371 | (y2 < y or y2 > other_y)): | |
372 | return (other_x2, other_y2) | |
373 | return None | |
374 | ||
375 | ||
376 | #---------------------------------------------------------------------------- | |
377 | # Canvas methods | |
378 | #---------------------------------------------------------------------------- | |
379 | ||
380 | def AddShape(self, shape, x = None, y = None, pen = None, brush = None, text = None, eventHandler = None): | |
381 | if isinstance(shape, ogl.CompositeShape): | |
382 | dc = wx.ClientDC(self._canvas) | |
383 | self._canvas.PrepareDC(dc) | |
384 | shape.Move(dc, x, y) | |
385 | else: | |
386 | shape.SetDraggable(True, True) | |
387 | shape.SetCanvas(self._canvas) | |
388 | ||
389 | if x: | |
390 | shape.SetX(x) | |
391 | if y: | |
392 | shape.SetY(y) | |
393 | shape.SetCentreResize(False) | |
394 | if pen: | |
395 | shape.SetPen(pen) | |
396 | if brush: | |
397 | shape.SetBrush(brush) | |
398 | if text: | |
399 | shape.AddText(text) | |
400 | shape.SetShadowMode(ogl.SHADOW_RIGHT) | |
401 | self._diagram.AddShape(shape) | |
402 | shape.Show(True) | |
403 | if not eventHandler: | |
404 | eventHandler = EditorCanvasShapeEvtHandler(self) | |
405 | eventHandler.SetShape(shape) | |
406 | eventHandler.SetPreviousHandler(shape.GetEventHandler()) | |
407 | shape.SetEventHandler(eventHandler) | |
408 | return shape | |
409 | ||
410 | ||
411 | def RemoveShape(self, model = None, shape = None): | |
412 | if not model and not shape: | |
413 | return | |
414 | ||
415 | if not shape: | |
416 | shape = self.GetShape(model) | |
417 | ||
418 | if shape: | |
419 | shape.Select(False) | |
420 | self._diagram.RemoveShape(shape) | |
421 | if isinstance(shape, ogl.CompositeShape): | |
422 | shape.RemoveFromCanvas(self._canvas) | |
423 | ||
424 | ||
425 | def UpdateShape(self, model): | |
426 | for shape in self._diagram.GetShapeList(): | |
427 | if hasattr(shape, "GetModel") and shape.GetModel() == model: | |
428 | x, y, w, h = model.getEditorBounds() | |
429 | newX = x + w / 2 | |
430 | newY = y + h / 2 | |
431 | changed = False | |
432 | if isinstance(shape, ogl.CompositeShape): | |
433 | if shape.GetX() != newX or shape.GetY() != newY: | |
434 | dc = wx.ClientDC(self._canvas) | |
435 | self._canvas.PrepareDC(dc) | |
436 | shape.SetSize(w, h, True) # wxBug: SetSize must be before Move because links won't go to the right place | |
437 | shape.Move(dc, newX, newY) # wxBug: Move must be before SetSize because links won't go to the right place | |
438 | changed = True | |
439 | else: | |
440 | oldw, oldh = shape.GetBoundingBoxMax() | |
441 | oldx = shape.GetX() | |
442 | oldy = shape.GetY() | |
443 | if oldw != w or oldh != h or oldx != newX or oldy != newY: | |
444 | shape.SetSize(w, h) | |
445 | shape.SetX(newX) | |
446 | shape.SetY(newY) | |
447 | changed = True | |
448 | if changed: | |
449 | shape.ResetControlPoints() | |
450 | self._canvas.Refresh() | |
451 | break | |
452 | ||
453 | ||
454 | def GetShape(self, model): | |
455 | for shape in self._diagram.GetShapeList(): | |
456 | if hasattr(shape, "GetModel") and shape.GetModel() == model: | |
457 | return shape | |
458 | return None | |
459 | ||
460 | ||
461 | def GetSelection(self): | |
462 | return filter(lambda shape: shape.Selected(), self._diagram.GetShapeList()) | |
463 | ||
464 | ||
465 | def SetSelection(self, models, extendSelect = False): | |
466 | dc = wx.ClientDC(self._canvas) | |
467 | self._canvas.PrepareDC(dc) | |
468 | update = False | |
469 | if not isinstance(models, type([])) and not isinstance(models, type(())): | |
470 | models = [models] | |
471 | for shape in self._diagram.GetShapeList(): | |
472 | if hasattr(shape, "GetModel"): | |
473 | if shape.Selected() and not shape.GetModel() in models: # was selected, but not in new list, so deselect, unless extend select | |
474 | if not extendSelect: | |
475 | shape.Select(False, dc) | |
476 | update = True | |
477 | elif not shape.Selected() and shape.GetModel() in models: # was not selected and in new list, so select | |
478 | shape.Select(True, dc) | |
479 | update = True | |
480 | elif extendSelect and shape.Selected() and shape.GetModel() in models: # was selected, but extend select means to deselect | |
481 | shape.Select(False, dc) | |
482 | update = True | |
483 | if update: | |
484 | self._canvas.Redraw(dc) | |
485 | ||
486 | ||
487 | def BringToFront(self, shape): | |
488 | if shape.GetParent() and isinstance(shape.GetParent(), ogl.CompositeShape): | |
489 | self._diagram.RemoveShape(shape.GetParent()) | |
490 | self._diagram.AddShape(shape.GetParent()) | |
491 | else: | |
492 | self._diagram.RemoveShape(shape) | |
493 | self._diagram.AddShape(shape) | |
494 | ||
495 | ||
496 | def SendToBack(self, shape): | |
497 | if shape.GetParent() and isinstance(shape.GetParent(), ogl.CompositeShape): | |
498 | self._diagram.RemoveShape(shape.GetParent()) | |
499 | self._diagram.InsertShape(shape.GetParent()) | |
500 | else: | |
501 | self._diagram.RemoveShape(shape) | |
502 | self._diagram.InsertShape(shape) | |
503 | ||
504 | ||
505 | def ScrollVisible(self, shape): | |
506 | xUnit, yUnit = shape._canvas.GetScrollPixelsPerUnit() | |
507 | scrollX, scrollY = self._canvas.GetViewStart() # in scroll units | |
508 | scrollW, scrollH = self._canvas.GetSize() # in pixels | |
509 | w, h = shape.GetBoundingBoxMax() # in pixels | |
510 | x = shape.GetX() - w/2 # convert to upper left coordinate from center | |
511 | y = shape.GetY() - h/2 # convert to upper left coordinate from center | |
512 | ||
513 | if x >= scrollX*xUnit and x <= scrollX*xUnit + scrollW: # don't scroll if already visible | |
514 | x = -1 | |
515 | else: | |
516 | x = x/xUnit | |
517 | ||
518 | if y >= scrollY*yUnit and y <= scrollY*yUnit + scrollH: # don't scroll if already visible | |
519 | y = -1 | |
520 | else: | |
521 | y = y/yUnit | |
522 | ||
523 | self._canvas.Scroll(x, y) # in scroll units | |
524 | ||
525 | ||
526 | def SetPropertyShape(self, shape): | |
527 | # no need to highlight if no PropertyService is running | |
528 | propertyService = wx.GetApp().GetService(PropertyService.PropertyService) | |
529 | if not propertyService: | |
530 | return | |
531 | ||
532 | if shape == self._propShape: | |
533 | return | |
534 | ||
535 | if hasattr(shape, "GetPropertyShape"): | |
536 | shape = shape.GetPropertyShape() | |
537 | ||
538 | dc = wx.ClientDC(self._canvas) | |
539 | self._canvas.PrepareDC(dc) | |
540 | dc.BeginDrawing() | |
541 | ||
542 | # erase old selection if it still exists | |
543 | if self._propShape and self._propShape in self._diagram.GetShapeList(): | |
544 | self._propShape.SetBrush(self._brush) | |
545 | if (self._propShape._textColourName in ["BLACK", "WHITE"]): # Would use GetTextColour() but it is broken | |
546 | self._propShape.SetTextColour("BLACK", 0) | |
547 | self._propShape.Draw(dc) | |
548 | ||
549 | # set new selection | |
550 | self._propShape = shape | |
551 | ||
552 | # draw new selection | |
553 | if self._propShape and self._propShape in self._diagram.GetShapeList(): | |
6f1a3f9c RD |
554 | if self.HasFocus(): |
555 | self._propShape.SetBrush(SELECT_BRUSH) | |
556 | else: | |
557 | self._propShape.SetBrush(INACTIVE_SELECT_BRUSH) | |
1f780e48 RD |
558 | if (self._propShape._textColourName in ["BLACK", "WHITE"]): # Would use GetTextColour() but it is broken |
559 | self._propShape.SetTextColour("WHITE", 0) | |
560 | self._propShape.Draw(dc) | |
561 | ||
562 | dc.EndDrawing() | |
563 | ||
564 | ||
b792147d RD |
565 | def FocusColorPropertyShape(self, gotFocus=False): |
566 | # no need to change highlight if no PropertyService is running | |
567 | propertyService = wx.GetApp().GetService(PropertyService.PropertyService) | |
568 | if not propertyService: | |
569 | return | |
570 | ||
571 | if not self._propShape: | |
572 | return | |
573 | ||
574 | dc = wx.ClientDC(self._canvas) | |
575 | self._canvas.PrepareDC(dc) | |
576 | dc.BeginDrawing() | |
577 | ||
578 | # draw deactivated selection | |
579 | if self._propShape and self._propShape in self._diagram.GetShapeList(): | |
580 | if gotFocus: | |
581 | self._propShape.SetBrush(SELECT_BRUSH) | |
582 | else: | |
583 | self._propShape.SetBrush(INACTIVE_SELECT_BRUSH) | |
584 | if (self._propShape._textColourName in ["BLACK", "WHITE"]): # Would use GetTextColour() but it is broken | |
585 | self._propShape.SetTextColour("WHITE", 0) | |
586 | self._propShape.Draw(dc) | |
587 | ||
588 | dc.EndDrawing() | |
589 | ||
590 | ||
1f780e48 RD |
591 | #---------------------------------------------------------------------------- |
592 | # Property Service methods | |
593 | #---------------------------------------------------------------------------- | |
594 | ||
595 | def GetPropertyModel(self): | |
596 | if hasattr(self, "_propModel"): | |
597 | return self._propModel | |
598 | return None | |
599 | ||
600 | ||
601 | def SetPropertyModel(self, model): | |
602 | # no need to set the model if no PropertyService is running | |
603 | propertyService = wx.GetApp().GetService(PropertyService.PropertyService) | |
604 | if not propertyService: | |
605 | return | |
606 | ||
607 | if hasattr(self, "_propModel") and model == self._propModel: | |
608 | return | |
609 | ||
610 | self._propModel = model | |
611 | propertyService.LoadProperties(self._propModel, self.GetDocument()) | |
612 | ||
613 | ||
614 | class EditorCanvasShapeMixin: | |
615 | ||
616 | def GetModel(self): | |
617 | return self._model | |
618 | ||
619 | ||
620 | def SetModel(self, model): | |
621 | self._model = model | |
622 | ||
623 | ||
624 | class EditorCanvasShapeEvtHandler(ogl.ShapeEvtHandler): | |
625 | ||
626 | """ wxBug: Bug in OLG package. With wxShape.SetShadowMode() turned on, when we set the size, | |
627 | the width/height is larger by 6 pixels. Need to subtract this value from width and height when we | |
628 | resize the object. | |
629 | """ | |
630 | SHIFT_KEY = 1 | |
631 | CONTROL_KEY = 2 | |
632 | ||
633 | def __init__(self, view): | |
634 | ogl.ShapeEvtHandler.__init__(self) | |
635 | self._view = view | |
636 | ||
637 | ||
638 | def OnLeftClick(self, x, y, keys = 0, attachment = 0): | |
639 | shape = self.GetShape() | |
640 | if hasattr(shape, "GetModel"): # Workaround, on drag, we should deselect all other objects and select the clicked on object | |
641 | model = shape.GetModel() | |
642 | else: | |
643 | shape = shape.GetParent() | |
644 | if shape: | |
645 | model = shape.GetModel() | |
646 | ||
647 | self._view.SetSelection(model, keys == self.SHIFT_KEY or keys == self.CONTROL_KEY) | |
648 | self._view.SetPropertyShape(shape) | |
649 | self._view.SetPropertyModel(model) | |
650 | ||
651 | ||
652 | def OnEndDragLeft(self, x, y, keys = 0, attachment = 0): | |
653 | ogl.ShapeEvtHandler.OnEndDragLeft(self, x, y, keys, attachment) | |
654 | shape = self.GetShape() | |
655 | if hasattr(shape, "GetModel"): # Workaround, on drag, we should deselect all other objects and select the clicked on object | |
656 | model = shape.GetModel() | |
657 | else: | |
658 | parentShape = shape.GetParent() | |
659 | if parentShape: | |
660 | model = parentShape.GetModel() | |
661 | self._view.SetSelection(model, keys == self.SHIFT_KEY or keys == self.CONTROL_KEY) | |
662 | ||
663 | ||
26ee3a06 RD |
664 | def OnMovePre(self, dc, x, y, oldX, oldY, display): |
665 | """ Prevent objects from being dragged outside of viewable area """ | |
666 | if (x > self._view._maxWidth) or (y > self._view._maxHeight): | |
667 | return False | |
668 | ||
669 | return ogl.ShapeEvtHandler.OnMovePre(self, dc, x, y, oldX, oldY, display) | |
670 | ||
671 | ||
1f780e48 | 672 | def OnMovePost(self, dc, x, y, oldX, oldY, display): |
26ee3a06 | 673 | """ Update the model's record of where the shape should be. Also enable redo/undo. """ |
1f780e48 RD |
674 | if x == oldX and y == oldY: |
675 | return | |
676 | if not self._view.GetDocument(): | |
677 | return | |
678 | shape = self.GetShape() | |
679 | if isinstance(shape, EditorCanvasShapeMixin) and shape.Draggable(): | |
680 | model = shape.GetModel() | |
681 | if hasattr(model, "getEditorBounds") and model.getEditorBounds(): | |
682 | x, y, w, h = model.getEditorBounds() | |
683 | newX = shape.GetX() - shape.GetBoundingBoxMax()[0] / 2 | |
684 | newY = shape.GetY() - shape.GetBoundingBoxMax()[1] / 2 | |
685 | newWidth = shape.GetBoundingBoxMax()[0] | |
686 | newHeight = shape.GetBoundingBoxMax()[1] | |
687 | if shape._shadowMode != ogl.SHADOW_NONE: | |
688 | newWidth -= shape._shadowOffsetX | |
689 | newHeight -= shape._shadowOffsetY | |
690 | newbounds = (newX, newY, newWidth, newHeight) | |
691 | ||
692 | if x != newX or y != newY or w != newWidth or h != newHeight: | |
693 | self._view.GetDocument().GetCommandProcessor().Submit(EditorCanvasUpdateShapeBoundariesCommand(self._view.GetDocument(), model, newbounds)) | |
694 | ||
695 | ||
696 | def Draw(self, dc): | |
697 | pass | |
698 | ||
699 | ||
700 | class EditorCanvasUpdateShapeBoundariesCommand(wx.lib.docview.Command): | |
701 | ||
702 | ||
703 | def __init__(self, canvasDocument, model, newbounds): | |
704 | wx.lib.docview.Command.__init__(self, canUndo = True) | |
705 | self._canvasDocument = canvasDocument | |
706 | self._model = model | |
707 | self._oldbounds = model.getEditorBounds() | |
708 | self._newbounds = newbounds | |
709 | ||
710 | ||
711 | def GetName(self): | |
712 | name = self._canvasDocument.GetNameForObject(self._model) | |
713 | if not name: | |
714 | name = "" | |
715 | print "ERROR: AbstractEditor.EditorCanvasUpdateShapeBoundariesCommand.GetName: unable to get name for ", self._model | |
716 | return _("Move/Resize %s") % name | |
717 | ||
718 | ||
719 | def Do(self): | |
720 | return self._canvasDocument.UpdateEditorBoundaries(self._model, self._newbounds) | |
721 | ||
722 | ||
723 | def Undo(self): | |
724 | return self._canvasDocument.UpdateEditorBoundaries(self._model, self._oldbounds) | |
725 |