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