]> git.saurik.com Git - wxWidgets.git/blob - contrib/samples/ogl/studio/shapes.cpp
wxURI is enough for the basic url operations.
[wxWidgets.git] / contrib / samples / ogl / studio / shapes.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: shapes.cpp
3 // Purpose: Implements Studio shapes
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 12/07/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #ifdef __GNUG__
13 // #pragma implementation
14 #endif
15
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
18
19 #ifdef __BORLANDC__
20 #pragma hdrstop
21 #endif
22
23 #ifndef WX_PRECOMP
24 #include <wx/wx.h>
25 #endif
26
27 #if !wxUSE_DOC_VIEW_ARCHITECTURE
28 #error You must set wxUSE_DOC_VIEW_ARCHITECTURE to 1 in wx_setup.h!
29 #endif
30
31 #include <wx/deprecated/setup.h>
32 #include <wx/deprecated/wxexpr.h>
33
34 #include "studio.h"
35 #include "doc.h"
36 #include "shapes.h"
37 #include "view.h"
38 #include <wx/ogl/basicp.h>
39 #include <wx/ogl/linesp.h>
40 #include "cspalette.h"
41 #include "dialogs.h"
42
43 #define csSTANDARD_SHAPE_WIDTH 100
44
45 IMPLEMENT_CLASS(csDiagram, wxDiagram)
46
47 csDiagram::~csDiagram()
48 {
49 DeleteAllShapes();
50 }
51
52 void csDiagram::Redraw(wxDC& dc)
53 {
54 wxDiagram::Redraw(dc);
55
56 // Draw line crossings
57 wxLineCrossings lineCrossings;
58 lineCrossings.FindCrossings(*this);
59 lineCrossings.DrawCrossings(*this, dc);
60 }
61
62 /*
63 * csEvtHandler: an event handler class for all shapes
64 */
65
66 IMPLEMENT_DYNAMIC_CLASS(csEvtHandler, wxShapeEvtHandler)
67
68 csEvtHandler::csEvtHandler(wxShapeEvtHandler *prev, wxShape *shape, const wxString& lab):
69 wxShapeEvtHandler(prev, shape)
70 {
71 m_label = lab;
72 }
73
74 csEvtHandler::~csEvtHandler()
75 {
76 }
77
78 // Copy any event handler data
79 void csEvtHandler::CopyData(wxShapeEvtHandler& copy)
80 {
81 wxShapeEvtHandler::CopyData(copy);
82
83 csEvtHandler& csCopy = (csEvtHandler&) copy;
84 csCopy.m_label = m_label;
85 }
86
87 void csEvtHandler::OnLeftClick(double WXUNUSED(x), double WXUNUSED(y), int keys, int WXUNUSED(attachment))
88 {
89 wxClientDC dc(GetShape()->GetCanvas());
90 GetShape()->GetCanvas()->PrepareDC(dc);
91
92 csDiagramView* view = ((csCanvas*)GetShape()->GetCanvas())->GetView();
93 view->ReflectPointSize(GetShape()->GetFont()->GetPointSize());
94
95 if (GetShape()->IsKindOf(CLASSINFO(wxLineShape)))
96 view->ReflectArrowState((wxLineShape*) GetShape());
97
98 csEditorToolPalette *palette = wxGetApp().GetDiagramPalette();
99 if (palette->GetSelection() == PALETTE_TEXT_TOOL)
100 {
101 view->ReflectPointSize(GetShape()->GetFont()->GetPointSize());
102
103 EditProperties();
104 #if 0
105 csLabelEditingDialog* dialog = new csLabelEditingDialog(GetShape()->GetCanvas()->GetParent());
106 dialog->SetShapeLabel(m_label);
107 if (dialog->ShowModal() == wxID_CANCEL)
108 {
109 dialog->Destroy();
110 return;
111 }
112
113 wxString newLabel = dialog->GetShapeLabel();
114 dialog->Destroy();
115
116 wxShape* newShape = GetShape()->CreateNewCopy();
117
118 csEvtHandler* handler = (csEvtHandler *)newShape->GetEventHandler();
119 handler->m_label = newLabel;
120
121 view->GetDocument()->GetCommandProcessor()->Submit(new csDiagramCommand("Edit label", (csDiagramDocument*) view->GetDocument(),
122 new csCommandState(ID_CS_EDIT_PROPERTIES, newShape, GetShape())));
123 #endif
124 return;
125 }
126
127 if (keys == 0)
128 {
129 // If no shift key, then everything is deselected.
130 // If the shape was selected, deselect it and vice versa.
131 bool selected = GetShape()->Selected();
132
133 view->SelectAll(false);
134
135 selected = !selected;
136
137 GetShape()->Select(selected, &dc);
138 GetShape()->GetCanvas()->Redraw(dc); // Redraw because bits of objects will be missing
139
140 view->SelectShape(GetShape(), selected);
141 }
142 else if (keys & KEY_SHIFT)
143 {
144 if (GetShape()->Selected())
145 {
146 GetShape()->Select(false, &dc);
147 view->SelectShape(GetShape(), false);
148 }
149 else
150 {
151 GetShape()->Select(true, &dc);
152 view->SelectShape(GetShape(), true);
153 }
154 GetShape()->GetCanvas()->Redraw(dc); // Redraw because bits of objects will be missing
155 }
156 else if (keys & KEY_CTRL)
157 {
158 // Do something for CONTROL
159 }
160 else
161 {
162 #if wxUSE_STATUSBAR
163 ((wxFrame*)wxGetApp().GetTopWindow())->SetStatusText(m_label);
164 #endif // wxUSE_STATUSBAR
165 }
166 }
167
168 void csEvtHandler::OnRightClick(double x, double y, int WXUNUSED(keys), int WXUNUSED(attachment))
169 {
170 // Have to convert back to physical coordinates from logical coordinates.
171
172 int viewStartX, viewStartY;
173 int unitX, unitY;
174 GetShape()->GetCanvas()->GetViewStart(& viewStartX, & viewStartY);
175 GetShape()->GetCanvas()->GetScrollPixelsPerUnit(& unitX, & unitY);
176
177 int x1 = (int)(x * GetShape()->GetCanvas()->GetScaleX());
178 int y1 = (int)(y * GetShape()->GetCanvas()->GetScaleY());
179
180 int menuX = (int) (x1 - (viewStartX * unitX)) ;
181 int menuY = (int) (y1 - (viewStartY * unitY));
182
183 wxGetApp().GetShapeEditMenu()->SetClientData((char*) GetShape());
184 wxGetApp().GetShapeEditMenu()->Enable(ID_CS_ROTATE_CLOCKWISE, !GetShape()->IsKindOf(CLASSINFO(wxLineShape)));
185 wxGetApp().GetShapeEditMenu()->Enable(ID_CS_ROTATE_ANTICLOCKWISE, !GetShape()->IsKindOf(CLASSINFO(wxLineShape)));
186
187 GetShape()->GetCanvas()->PopupMenu(wxGetApp().GetShapeEditMenu(), menuX, menuY);
188 }
189
190 /*
191 * Implement connection of two shapes by right-dragging between them.
192 */
193
194 void csEvtHandler::OnBeginDragRight(double x, double y, int WXUNUSED(keys), int attachment)
195 {
196 wxClientDC dc(GetShape()->GetCanvas());
197 GetShape()->GetCanvas()->PrepareDC(dc);
198
199 wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
200 dc.SetLogicalFunction(OGLRBLF);
201 dc.SetPen(dottedPen);
202 double xp, yp;
203 GetShape()->GetAttachmentPositionEdge(attachment, &xp, &yp);
204 dc.DrawLine((wxCoord)xp, (wxCoord)yp, (wxCoord)x, (wxCoord)y);
205 GetShape()->GetCanvas()->CaptureMouse();
206 }
207
208 void csEvtHandler::OnDragRight(bool WXUNUSED(draw), double x, double y, int WXUNUSED(keys), int attachment)
209 {
210 wxClientDC dc(GetShape()->GetCanvas());
211 GetShape()->GetCanvas()->PrepareDC(dc);
212
213 wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
214 dc.SetLogicalFunction(OGLRBLF);
215 dc.SetPen(dottedPen);
216 double xp, yp;
217 GetShape()->GetAttachmentPositionEdge(attachment, &xp, &yp);
218 dc.DrawLine((wxCoord)xp, (wxCoord)yp, (wxCoord)x, (wxCoord)y);
219 }
220
221 void csEvtHandler::OnEndDragRight(double x, double y, int WXUNUSED(keys), int attachment)
222 {
223 GetShape()->GetCanvas()->ReleaseMouse();
224 csCanvas *canvas = (csCanvas *)GetShape()->GetCanvas();
225
226 // Check if we're on an object
227 int new_attachment;
228 wxShape *otherShape = canvas->FindFirstSensitiveShape(x, y, &new_attachment, OP_DRAG_RIGHT);
229
230 if (otherShape && !otherShape->IsKindOf(CLASSINFO(wxLineShape)))
231 {
232 wxLineShape* theShape = new csLineShape;
233
234 theShape->AssignNewIds();
235 theShape->SetEventHandler(new csEvtHandler(theShape, theShape, wxEmptyString));
236 theShape->SetPen(wxBLACK_PEN);
237 theShape->SetBrush(wxRED_BRUSH);
238
239 wxToolBar* toolbar = wxGetApp().GetDiagramToolBar();
240 bool haveArrow = toolbar->GetToolState(DIAGRAM_TOOLBAR_LINE_ARROW);
241
242 wxLineShape *lineShape = (wxLineShape *)theShape;
243
244 // Yes, you can have more than 2 control points, in which case
245 // it becomes a multi-segment line.
246 lineShape->MakeLineControlPoints(2);
247
248 if (haveArrow)
249 lineShape->AddArrow(ARROW_ARROW, ARROW_POSITION_MIDDLE, 10.0, 0.0, _T("Normal arrowhead"));
250
251 lineShape->SetFrom(GetShape());
252 lineShape->SetTo(otherShape);
253 lineShape->SetAttachments(attachment, new_attachment);
254
255 canvas->GetView()->GetDocument()->GetCommandProcessor()->Submit(
256 new csDiagramCommand(_T("Line"), (csDiagramDocument *)canvas->GetView()->GetDocument(),
257 new csCommandState(ID_CS_ADD_LINE, lineShape, NULL)));
258 }
259 }
260
261 static double g_DragOffsetX = 0.0;
262 static double g_DragOffsetY = 0.0;
263 static double g_DragStartX = 0.0;
264 static double g_DragStartY = 0.0;
265
266 void csEvtHandler::OnDragLeft(bool draw, double x, double y, int keys, int attachment)
267 {
268 if ((GetShape()->GetSensitivityFilter() & OP_DRAG_LEFT) != OP_DRAG_LEFT)
269 {
270 attachment = 0;
271 double dist;
272 if (GetShape()->GetParent())
273 {
274 GetShape()->GetParent()->HitTest(x, y, &attachment, &dist);
275 GetShape()->GetParent()->GetEventHandler()->OnDragLeft(draw, x, y, keys, attachment);
276 }
277 return;
278 }
279
280 wxClientDC dc(GetShape()->GetCanvas());
281 GetShape()->GetCanvas()->PrepareDC(dc);
282
283 dc.SetLogicalFunction(OGLRBLF);
284
285 wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
286 dc.SetPen(dottedPen);
287 dc.SetBrush(* wxTRANSPARENT_BRUSH);
288
289 double xx, yy;
290 xx = x + g_DragOffsetX;
291 yy = y + g_DragOffsetY;
292
293 GetShape()->GetCanvas()->Snap(&xx, &yy);
294
295 double offsetX = xx - g_DragStartX;
296 double offsetY = yy - g_DragStartY;
297
298 // m_xpos = xx; m_ypos = yy;
299 double w, h;
300 GetShape()->GetBoundingBoxMax(&w, &h);
301 GetShape()->GetEventHandler()->OnDrawOutline(dc, xx, yy, w, h);
302
303 // Draw bounding box for other selected shapes
304 wxObjectList::compatibility_iterator node = GetShape()->GetCanvas()->GetDiagram()->GetShapeList()->GetFirst();
305 while (node)
306 {
307 wxShape* shape = (wxShape*) node->GetData();
308 if (shape->Selected() && !shape->IsKindOf(CLASSINFO(wxLineShape)) && (shape != GetShape()))
309 {
310 shape->GetBoundingBoxMax(&w, &h);
311 shape->OnDrawOutline(dc, shape->GetX() + offsetX, shape->GetY() + offsetY, w, h);
312 }
313 node = node->GetNext();
314 }
315 }
316
317 void csEvtHandler::OnBeginDragLeft(double x, double y, int keys, int attachment)
318 {
319 if ((GetShape()->GetSensitivityFilter() & OP_DRAG_LEFT) != OP_DRAG_LEFT)
320 {
321 attachment = 0;
322 double dist;
323 if (GetShape()->GetParent())
324 {
325 GetShape()->GetParent()->HitTest(x, y, &attachment, &dist);
326 GetShape()->GetParent()->GetEventHandler()->OnBeginDragLeft(x, y, keys, attachment);
327 }
328 return;
329 }
330
331 wxClientDC dc(GetShape()->GetCanvas());
332 GetShape()->GetCanvas()->PrepareDC(dc);
333
334 // New policy: don't erase shape until end of drag.
335 // Erase(dc);
336
337 g_DragOffsetX = GetShape()->GetX() - x;
338 g_DragOffsetY = GetShape()->GetY() - y;
339
340 double xx, yy;
341 xx = x + g_DragOffsetX;
342 yy = y + g_DragOffsetY;
343
344 GetShape()->GetCanvas()->Snap(&xx, &yy);
345
346 g_DragStartX = GetShape()->GetX();
347 g_DragStartY = GetShape()->GetY();
348
349 double offsetX = xx - g_DragStartX;
350 double offsetY = yy - g_DragStartY;
351
352 dc.SetLogicalFunction(OGLRBLF);
353
354 wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
355 dc.SetPen(dottedPen);
356 dc.SetBrush((* wxTRANSPARENT_BRUSH));
357
358 double w, h;
359 GetShape()->GetBoundingBoxMax(&w, &h);
360 GetShape()->GetEventHandler()->OnDrawOutline(dc, xx, yy, w, h);
361
362 // Draw bounding box for other selected shapes
363 wxObjectList::compatibility_iterator node = GetShape()->GetCanvas()->GetDiagram()->GetShapeList()->GetFirst();
364 while (node)
365 {
366 wxShape* shape = (wxShape*) node->GetData();
367 if (shape->Selected() && !shape->IsKindOf(CLASSINFO(wxLineShape)) && (shape != GetShape()))
368 {
369 shape->GetBoundingBoxMax(&w, &h);
370 shape->OnDrawOutline(dc, shape->GetX() + offsetX, shape->GetY() + offsetY, w, h);
371 }
372 node = node->GetNext();
373 }
374
375 GetShape()->GetCanvas()->CaptureMouse();
376 }
377
378
379 void csEvtHandler::OnEndDragLeft(double x, double y, int keys, int attachment)
380 {
381 csCanvas *canvas = (csCanvas *)GetShape()->GetCanvas();
382
383 canvas->ReleaseMouse();
384 if ((GetShape()->GetSensitivityFilter() & OP_DRAG_LEFT) != OP_DRAG_LEFT)
385 {
386 attachment = 0;
387 double dist;
388 if (GetShape()->GetParent())
389 {
390 GetShape()->GetParent()->HitTest(x, y, &attachment, &dist);
391 GetShape()->GetParent()->GetEventHandler()->OnEndDragLeft(x, y, keys, attachment);
392 }
393 return;
394 }
395
396 wxClientDC dc(canvas);
397 canvas->PrepareDC(dc);
398
399 dc.SetLogicalFunction(wxCOPY);
400
401 double xx = x + g_DragOffsetX;
402 double yy = y + g_DragOffsetY;
403
404 canvas->Snap(&xx, &yy);
405
406 double offsetX = xx - g_DragStartX;
407 double offsetY = yy - g_DragStartY;
408
409 wxShape* newShape = GetShape()->CreateNewCopy();
410
411 newShape->SetX(xx);
412 newShape->SetY(yy);
413
414 csDiagramCommand* cmd = new csDiagramCommand(_T("Move"), (csDiagramDocument*)canvas->GetView()->GetDocument(),
415 new csCommandState(ID_CS_MOVE, newShape, GetShape()));
416
417 // Move line points
418 wxObjectList::compatibility_iterator node = GetShape()->GetCanvas()->GetDiagram()->GetShapeList()->GetFirst();
419 while (node)
420 {
421 wxShape* shape = (wxShape*) node->GetData();
422 // Only move the line point(s) if both ends move too
423 if (shape->IsKindOf(CLASSINFO(wxLineShape)) &&
424 ((wxLineShape*)shape)->GetTo()->Selected() && ((wxLineShape*)shape)->GetFrom()->Selected())
425 {
426 wxLineShape* lineShape = (wxLineShape*) shape;
427
428 if (lineShape->GetLineControlPoints()->GetCount() > 2)
429 {
430 wxLineShape* newLineShape = (wxLineShape*) lineShape->CreateNewCopy();
431
432 wxObjectList::compatibility_iterator node1 = newLineShape->GetLineControlPoints()->GetFirst();
433 while (node1)
434 {
435 wxRealPoint *point = (wxRealPoint *)node1->GetData();
436 point->x += offsetX;
437 point->y += offsetY;
438 node1 = node1->GetNext();
439 }
440 cmd->AddState(new csCommandState(ID_CS_MOVE_LINE_POINT, newLineShape, lineShape));
441 lineShape->Erase(dc);
442 }
443 }
444 node = node->GetNext();
445 }
446
447 // Add other selected node shapes, if any
448 node = GetShape()->GetCanvas()->GetDiagram()->GetShapeList()->GetFirst();
449 while (node)
450 {
451 wxShape* shape = (wxShape*) node->GetData();
452 if (shape->Selected() && !shape->IsKindOf(CLASSINFO(wxLineShape)) && (shape != GetShape()))
453 {
454 wxShape* newShape2 = shape->CreateNewCopy();
455 newShape2->SetX(shape->GetX() + offsetX);
456 newShape2->SetY(shape->GetY() + offsetY);
457 cmd->AddState(new csCommandState(ID_CS_MOVE, newShape2, shape));
458 }
459 node = node->GetNext();
460 }
461
462 canvas->GetView()->GetDocument()->GetCommandProcessor()->Submit(cmd);
463 }
464
465 void csEvtHandler::OnSizingEndDragLeft(wxControlPoint* pt, double x, double y, int keys, int attachment)
466 {
467 wxShape* shape = GetShape();
468 csCanvas *canvas = (csCanvas *)GetShape()->GetCanvas();
469
470 if (shape->IsKindOf(CLASSINFO(wxLineShape)))
471 {
472 // TODO: Do/Undo support for line operations
473 ((wxLineShape*)shape)->wxLineShape::OnSizingEndDragLeft(pt, x, y, keys, attachment);
474 #if 0
475 wxLineShape* lineShape = (wxLineShape*) shape;
476
477 wxLineControlPoint* lpt = (wxLineControlPoint*) pt;
478
479 wxClientDC dc(canvas);
480 canvas->PrepareDC(dc);
481
482 shape->SetDisableLabel(false);
483
484 if (lpt->m_type == CONTROL_POINT_LINE)
485 {
486 canvas->Snap(&x, &y);
487
488 dc.SetLogicalFunction(wxCOPY);
489 lpt->SetX(x); lpt->SetY(y);
490 lpt->m_point->x = x; lpt->m_point->y = y;
491
492 this->OnMoveLink(dc);
493 }
494 if (lpt->m_type == CONTROL_POINT_ENDPOINT_FROM)
495 {
496 if (lpt->m_oldCursor)
497 canvas->SetCursor(lpt->m_oldCursor);
498 lineShape->Erase(dc);
499
500 lpt->SetX(x); lpt->SetY(y);
501
502 if (lineShape->GetFrom())
503 {
504 lineShape->GetFrom()->MoveLineToNewAttachment(dc, lineShape, x, y);
505 }
506 }
507 if (lpt->m_type == CONTROL_POINT_ENDPOINT_TO)
508 {
509 if (lpt->m_oldCursor)
510 canvas->SetCursor(lpt->m_oldCursor);
511
512 lpt->SetX(x); lpt->SetY(y);
513
514 if (lineShape->GetTo())
515 {
516 lineShape->GetTo()->MoveLineToNewAttachment(dc, lineShape, x, y);
517 }
518 }
519 #endif
520 return;
521 }
522
523 wxClientDC dc(canvas);
524 canvas->PrepareDC(dc);
525
526 canvas->ReleaseMouse();
527 dc.SetLogicalFunction(wxCOPY);
528
529 // shape->Erase(dc);
530 /*
531 shape->Recompute();
532 shape->ResetControlPoints();
533 if (!pt->m_eraseObject)
534 shape->Show(false);
535 */
536
537 wxShape* newShape = shape->CreateNewCopy();
538
539 if (newShape->IsKindOf(CLASSINFO(wxPolygonShape)))
540 {
541 wxPolygonControlPoint* ppt = (wxPolygonControlPoint*) pt;
542 newShape->SetSize(ppt->GetNewSize().x, ppt->GetNewSize().y);
543
544 ((wxPolygonShape *)newShape)->CalculateBoundingBox();
545 ((wxPolygonShape *)newShape)->CalculatePolygonCentre();
546 newShape->ResetControlPoints();
547 }
548 else
549 {
550 newShape->SetSize(pt->sm_controlPointDragEndWidth, pt->sm_controlPointDragEndHeight);
551 if (shape->GetCentreResize())
552 {
553 // Old position is fine
554 }
555 else
556 {
557 newShape->SetX(pt->sm_controlPointDragPosX);
558 newShape->SetY(pt->sm_controlPointDragPosY);
559 }
560 }
561
562 csDiagramCommand* cmd = new csDiagramCommand(_T("Size"), (csDiagramDocument*)canvas->GetView()->GetDocument(),
563 new csCommandState(ID_CS_SIZE, newShape, shape));
564
565 canvas->GetView()->GetDocument()->GetCommandProcessor()->Submit(cmd);
566
567 }
568
569 void csEvtHandler::OnEndSize(double WXUNUSED(x), double WXUNUSED(y))
570 {
571 wxClientDC dc(GetShape()->GetCanvas());
572 GetShape()->GetCanvas()->PrepareDC(dc);
573
574 GetShape()->FormatText(dc, m_label);
575 }
576
577 void csEvtHandler::OnChangeAttachment(int attachment, wxLineShape* line, wxList& ordering)
578 {
579 csCanvas *canvas = (csCanvas *)GetShape()->GetCanvas();
580
581 // We actually submit two different states: one to change the ordering, and another
582 // to change the attachment for the line.
583 // Problem. If we refresh after the attachment change, we'll get a flicker.
584 // We really want to do both in a oner.
585
586 csDiagramCommand* cmd = new csDiagramCommand(_T("Change attachment"), (csDiagramDocument*)canvas->GetView()->GetDocument());
587
588 wxLineShape* newLine = (wxLineShape*) line->CreateNewCopy();
589 if (line->GetTo() == GetShape())
590 newLine->SetAttachmentTo(attachment);
591 else
592 newLine->SetAttachmentFrom(attachment);
593
594 cmd->AddState(new csCommandState(ID_CS_CHANGE_LINE_ATTACHMENT, newLine, line));
595
596 // Change ordering
597 wxShape* newShape = GetShape()->CreateNewCopy();
598 newShape->ApplyAttachmentOrdering(ordering);
599
600 cmd->AddState(new csCommandState(ID_CS_CHANGE_LINE_ORDERING, newShape, GetShape()));
601
602 canvas->GetView()->GetDocument()->GetCommandProcessor()->Submit(cmd);
603 }
604
605 void csEvtHandler::OnLeftDoubleClick(double WXUNUSED(x), double WXUNUSED(y), int WXUNUSED(keys), int WXUNUSED(attachment))
606 {
607 EditProperties();
608 }
609
610 // Popup up a property dialog
611 bool csEvtHandler::EditProperties()
612 {
613 wxShape* shape = GetShape();
614
615 // For now, no line property editing
616 if (shape->IsKindOf(CLASSINFO(wxLineShape)))
617 return false;
618
619 csDiagramView* view = ((csCanvas*)shape->GetCanvas())->GetView();
620
621 wxPanel* attributeDialog;
622 wxString attributeDialogName;
623 wxString title;
624
625 if (shape->IsKindOf(CLASSINFO(csThinRectangleShape)))
626 {
627 attributeDialog = new csThinRectangleDialog;
628 attributeDialogName = _T("thin_rectangle");
629 title = _T("Thin Rectangle Properties");
630 }
631 else if (shape->IsKindOf(CLASSINFO(csWideRectangleShape)))
632 {
633 attributeDialog = new csWideRectangleDialog;
634 attributeDialogName = _T("wide_rectangle");
635 title = _T("Wide Rectangle Properties");
636 }
637 else if (shape->IsKindOf(CLASSINFO(csTriangleShape)))
638 {
639 attributeDialog = new csTriangleDialog;
640 attributeDialogName = _T("triangle");
641 title = _T("Triangle Properties");
642 }
643 else if (shape->IsKindOf(CLASSINFO(csSemiCircleShape)))
644 {
645 attributeDialog = new csSemiCircleDialog;
646 attributeDialogName = _T("semi_circle");
647 title = _T("Semicircle Properties");
648 }
649 else if (shape->IsKindOf(CLASSINFO(csCircleShape)))
650 {
651 attributeDialog = new csCircleDialog;
652 attributeDialogName = _T("circle");
653 title = _T("Circle Properties");
654 }
655 else if (shape->IsKindOf(CLASSINFO(csCircleShadowShape)))
656 {
657 attributeDialog = new csCircleShadowDialog;
658 attributeDialogName = _T("circle_shadow");
659 title = _T("Circle Shadow Properties");
660 }
661 else if (shape->IsKindOf(CLASSINFO(csTextBoxShape)))
662 {
663 attributeDialog = new csTextBoxDialog;
664 attributeDialogName = _T("text_box");
665 title = _T("Text Box Properties");
666 }
667 else if (shape->IsKindOf(CLASSINFO(csGroupShape)))
668 {
669 attributeDialog = new csGroupDialog;
670 attributeDialogName = _T("group");
671 title = _T("Group Properties");
672 }
673 else if (shape->IsKindOf(CLASSINFO(csOctagonShape)))
674 {
675 attributeDialog = new csOctagonDialog;
676 attributeDialogName = _T("octagon");
677 title = _T("Octagon Properties");
678 }
679 else
680 {
681 wxMessageBox(_T("Unrecognised shape."), _T("Studio"), wxICON_EXCLAMATION);
682 return false;
683 }
684
685 csShapePropertiesDialog* dialog = new csShapePropertiesDialog(shape->GetCanvas()->GetParent(), title, attributeDialog, attributeDialogName);
686 dialog->GetGeneralPropertiesDialog()->SetShapeLabel(m_label);
687 if (dialog->ShowModal() == wxID_CANCEL)
688 {
689 dialog->Destroy();
690 return false;
691 }
692
693 wxString newLabel = dialog->GetGeneralPropertiesDialog()->GetShapeLabel();
694 dialog->Destroy();
695
696 wxShape* newShape = shape->CreateNewCopy();
697
698 csEvtHandler* handler2 = (csEvtHandler *)newShape->GetEventHandler();
699 handler2->m_label = newLabel;
700
701 view->GetDocument()->GetCommandProcessor()->Submit(new csDiagramCommand(_T("Edit properties"), (csDiagramDocument*) view->GetDocument(),
702 new csCommandState(ID_CS_EDIT_PROPERTIES, newShape, shape)));
703
704 return true;
705 }
706
707 /*
708 * Diagram
709 */
710
711 bool csDiagram::OnShapeSave(wxExprDatabase& db, wxShape& shape, wxExpr& expr)
712 {
713 wxDiagram::OnShapeSave(db, shape, expr);
714 csEvtHandler *handler = (csEvtHandler *)shape.GetEventHandler();
715 expr.AddAttributeValueString(_T("label"), handler->m_label);
716 return true;
717 }
718
719 bool csDiagram::OnShapeLoad(wxExprDatabase& db, wxShape& shape, wxExpr& expr)
720 {
721 wxDiagram::OnShapeLoad(db, shape, expr);
722 wxString label = wxEmptyString;
723 expr.GetAttributeValue(_T("label"), label);
724 csEvtHandler *handler = new csEvtHandler(&shape, &shape, label);
725 shape.SetEventHandler(handler);
726
727 return true;
728 }
729
730 IMPLEMENT_DYNAMIC_CLASS(csThinRectangleShape, wxDrawnShape)
731
732 csThinRectangleShape::csThinRectangleShape()
733 {
734 SetDrawnPen(wxBLACK_PEN);
735 wxBrush* brush = wxTheBrushList->FindOrCreateBrush(wxColour(220, 220, 220), wxSOLID);
736 SetDrawnBrush(brush);
737
738 double w = csSTANDARD_SHAPE_WIDTH/2;
739 double h = csSTANDARD_SHAPE_WIDTH;
740
741 DrawRectangle(wxRect((int)(- w/2), (int)(- h/2), (int)(w), (int)(h)));
742 CalculateSize();
743
744 SetAttachmentMode(ATTACHMENT_MODE_BRANCHING);
745 SetBranchStyle(BRANCHING_ATTACHMENT_NORMAL|BRANCHING_ATTACHMENT_BLOB);
746 SetCentreResize(false);
747 }
748
749 IMPLEMENT_DYNAMIC_CLASS(csWideRectangleShape, wxDrawnShape)
750
751 csWideRectangleShape::csWideRectangleShape()
752 {
753 SetDrawnPen(wxBLACK_PEN);
754 wxBrush* brush = wxTheBrushList->FindOrCreateBrush(wxColour(220, 220, 220), wxSOLID);
755 SetDrawnBrush(brush);
756
757 double w = csSTANDARD_SHAPE_WIDTH;
758 double h = w/2.0;
759
760 DrawRoundedRectangle(wxRect((int)(- w/2), (int)(- h/2), (int)(w), (int)(h)), -0.3);
761 CalculateSize();
762
763 SetAttachmentMode(ATTACHMENT_MODE_BRANCHING);
764 SetBranchStyle(BRANCHING_ATTACHMENT_NORMAL|BRANCHING_ATTACHMENT_BLOB);
765 SetCentreResize(false);
766 }
767
768 IMPLEMENT_DYNAMIC_CLASS(csTriangleShape, wxDrawnShape)
769
770 csTriangleShape::csTriangleShape()
771 {
772 SetDrawnPen(wxBLACK_PEN);
773 wxBrush* brush = wxTheBrushList->FindOrCreateBrush(wxColour(220, 220, 220), wxSOLID);
774 SetDrawnBrush(brush);
775
776 double w = csSTANDARD_SHAPE_WIDTH;
777 double h = (csSTANDARD_SHAPE_WIDTH*2.0)/3.0;
778
779 // Triangle, from top vertex
780 wxPoint* points = new wxPoint[3];
781
782
783 points[0] = wxPoint( 0 , (int)(- h / 2) );
784 points[1] = wxPoint( (int)(w / 2) , (int)(h / 2) );
785 points[2] = wxPoint( (int)(-w / 2), (int)(h / 2) );
786
787 DrawPolygon(3, points, oglMETAFLAGS_OUTLINE);
788
789 delete[] points;
790
791 // Add another triangle at the top for the black bit
792 SetDrawnBrush(wxBLACK_BRUSH);
793
794 points = new wxPoint[3];
795
796 // Calculate where the new points will be, using the proportions
797 // of the triangle.
798 double h1 = 8; // Height of little triangle.
799
800 /*
801 Formula: ((w/2) / h) = w1 / h1
802 w1 = ((w/2) / h) * h1;
803 */
804 double ratio = ((w/2.0) / h) ;
805 double w1 = ratio * h1;
806
807 points[0] = wxPoint(0 , (int) (- h / 2 ));
808 points[1] = wxPoint( (int) w1, (int) (- h / 2 + h1));
809 points[2] = wxPoint( (int) -w1, (int) (- h / 2 + h1));
810
811 DrawPolygon(3, points);
812
813 delete[] points;
814
815 CalculateSize();
816
817 SetAttachmentMode(ATTACHMENT_MODE_BRANCHING);
818 SetBranchStyle(BRANCHING_ATTACHMENT_NORMAL|BRANCHING_ATTACHMENT_BLOB);
819 SetCentreResize(false);
820 }
821
822 IMPLEMENT_DYNAMIC_CLASS(csSemiCircleShape, wxDrawnShape)
823
824 csSemiCircleShape::csSemiCircleShape()
825 {
826 // Zero degrees
827 DrawAtAngle(oglDRAWN_ANGLE_0);
828
829 double w = csSTANDARD_SHAPE_WIDTH;
830 double h = w/2.0;
831
832 SetDrawnPen(wxTRANSPARENT_PEN);
833 SetDrawnBrush(wxTRANSPARENT_BRUSH);
834
835 // Draw a dummy rectangle that will be used for calculating the
836 // bounding box, since we can't calculate the bounding box for
837 // an arbitrary arc (not implemented)
838
839 DrawRectangle(wxRect((int)(-w/2.0), (int)(-h/2.0), (int)(w), (int)(h)));
840
841 SetDrawnPen(wxBLACK_PEN);
842 wxBrush* brush = wxTheBrushList->FindOrCreateBrush(wxColour(220, 220, 220), wxSOLID);
843 SetDrawnBrush(brush);
844
845 DrawEllipticArc(wxRect((int)(-w/2), (int)(-h/2), (int)(w), (int)(2*h)), 0.0, 180.0);
846 DrawLine(wxPoint((int)(-w/2), (int)(h/2)), wxPoint((int)(w/2), (int)(h/2)));
847
848 CalculateSize();
849
850 /// 90 degrees
851
852 w = csSTANDARD_SHAPE_WIDTH/2;
853 h = csSTANDARD_SHAPE_WIDTH;
854
855 DrawAtAngle(oglDRAWN_ANGLE_90);
856
857 SetDrawnPen(wxTRANSPARENT_PEN);
858 SetDrawnBrush(wxTRANSPARENT_BRUSH);
859
860 DrawRectangle(wxRect((int)(-w/2), (int)(-h/2), (int)(w), (int)(h)));
861
862 SetDrawnPen(wxBLACK_PEN);
863 SetDrawnBrush(brush);
864
865 DrawEllipticArc(wxRect((int)(-w/2 - w), (int)(-h/2), (int)(2*w), (int)(h)), 270.0, 90.0);
866 DrawLine(wxPoint((int)(-w/2), (int)(-h/2)), wxPoint((int)(-w/2), (int)(h/2)));
867
868 CalculateSize();
869
870 /// 180 degrees
871
872 DrawAtAngle(oglDRAWN_ANGLE_180);
873
874 w = csSTANDARD_SHAPE_WIDTH;
875 h = csSTANDARD_SHAPE_WIDTH/2;
876
877 SetDrawnPen(wxTRANSPARENT_PEN);
878 SetDrawnBrush(wxTRANSPARENT_BRUSH);
879
880 DrawRectangle(wxRect((int)(-w/2), (int)(-h/2), (int)(w), (int)(h)));
881
882 SetDrawnPen(wxBLACK_PEN);
883 SetDrawnBrush(brush);
884
885 DrawEllipticArc(wxRect((int)(-w/2), (int)(-h/2 - h), (int)(w), (int)(2*h)), 180.0, 0.0);
886 DrawLine(wxPoint((int)(-w/2), (int)(-h/2)), wxPoint((int)(w/2), (int)(-h/2)));
887
888 CalculateSize();
889
890 /// 270 degrees
891
892 DrawAtAngle(oglDRAWN_ANGLE_270);
893
894 w = csSTANDARD_SHAPE_WIDTH/2;
895 h = csSTANDARD_SHAPE_WIDTH;
896
897 SetDrawnPen(wxTRANSPARENT_PEN);
898 SetDrawnBrush(wxTRANSPARENT_BRUSH);
899
900 DrawRectangle(wxRect((int)(-w/2), (int)(-h/2), (int)(w), (int)(h)));
901
902 SetDrawnPen(wxBLACK_PEN);
903 SetDrawnBrush(brush);
904
905 DrawEllipticArc(wxRect((int)(-w/2), (int)(-h/2), (int)(2*w), (int)(h)), 90.0, 270.0);
906 DrawLine(wxPoint((int)(w/2),(int)(-h/2)), wxPoint((int)(w/2), (int)(h/2)));
907
908 CalculateSize();
909
910 // Reset to zero
911 DrawAtAngle(oglDRAWN_ANGLE_0);
912 CalculateSize();
913
914 SetAttachmentMode(ATTACHMENT_MODE_BRANCHING);
915 SetBranchStyle(BRANCHING_ATTACHMENT_NORMAL|BRANCHING_ATTACHMENT_BLOB);
916 SetCentreResize(false);
917 }
918
919 IMPLEMENT_DYNAMIC_CLASS(csCircleShape, wxCircleShape)
920
921 csCircleShape::csCircleShape()
922 {
923 SetPen(wxBLACK_PEN);
924 wxBrush* brush = wxTheBrushList->FindOrCreateBrush(wxColour(220, 220, 220), wxSOLID);
925 SetBrush(brush);
926
927 SetSize(csSTANDARD_SHAPE_WIDTH*0.6, csSTANDARD_SHAPE_WIDTH*0.6);
928
929 SetAttachmentMode(ATTACHMENT_MODE_BRANCHING);
930 SetBranchStyle(BRANCHING_ATTACHMENT_NORMAL|BRANCHING_ATTACHMENT_BLOB);
931 SetCentreResize(false);
932 }
933
934 IMPLEMENT_DYNAMIC_CLASS(csCircleShadowShape, wxCircleShape)
935
936 csCircleShadowShape::csCircleShadowShape()
937 {
938 SetPen(wxBLACK_PEN);
939 wxBrush* brush = wxTheBrushList->FindOrCreateBrush(wxColour(220, 220, 220), wxSOLID);
940 SetBrush(brush);
941
942 SetSize(csSTANDARD_SHAPE_WIDTH*0.6, csSTANDARD_SHAPE_WIDTH*0.6);
943
944 SetAttachmentMode(ATTACHMENT_MODE_BRANCHING);
945 SetBranchStyle(BRANCHING_ATTACHMENT_NORMAL|BRANCHING_ATTACHMENT_BLOB);
946 SetCentreResize(false);
947 SetShadowMode(SHADOW_RIGHT);
948 }
949
950 IMPLEMENT_DYNAMIC_CLASS(csOctagonShape, wxPolygonShape)
951
952 csOctagonShape::csOctagonShape()
953 {
954 SetPen(wxBLACK_PEN);
955 SetBrush(wxTheBrushList->FindOrCreateBrush(wxColour(220, 220, 220), wxSOLID));
956
957 double w = csSTANDARD_SHAPE_WIDTH*0.5;
958 double h = csSTANDARD_SHAPE_WIDTH*0.5;
959
960 double prop = h/3.0;
961
962 wxList* points = new wxList;
963 points->Append((wxObject*) new wxRealPoint(-w/2.0 + prop, -h/2.0));
964 points->Append((wxObject*) new wxRealPoint(w/2.0 - prop, -h/2.0));
965 points->Append((wxObject*) new wxRealPoint(w/2.0, -h/2.0 + prop));
966 points->Append((wxObject*) new wxRealPoint(w/2.0, h/2.0 - prop));
967 points->Append((wxObject*) new wxRealPoint(w/2.0 - prop, h/2.0));
968 points->Append((wxObject*) new wxRealPoint(-w/2.0 + prop, h/2.0));
969 points->Append((wxObject*) new wxRealPoint(-w/2.0, h/2.0 - prop));
970 points->Append((wxObject*) new wxRealPoint(-w/2.0, -h/2.0 + prop));
971
972 Create(points);
973
974 SetAttachmentMode(ATTACHMENT_MODE_BRANCHING);
975 SetBranchStyle(BRANCHING_ATTACHMENT_NORMAL|BRANCHING_ATTACHMENT_BLOB);
976 SetCentreResize(false);
977 }
978
979 // This is a transparent shape for drawing around other shapes.
980 IMPLEMENT_DYNAMIC_CLASS(csGroupShape, wxRectangleShape)
981
982 csGroupShape::csGroupShape()
983 {
984 SetPen(wxThePenList->FindOrCreatePen(_T("BLACK"), 1, wxDOT));
985 SetBrush(wxTRANSPARENT_BRUSH);
986
987 SetSize(csSTANDARD_SHAPE_WIDTH, csSTANDARD_SHAPE_WIDTH);
988 SetCentreResize(false);
989 }
990
991 void csGroupShape::OnDraw(wxDC& dc)
992 {
993 wxRectangleShape::OnDraw(dc);
994 }
995
996 // Must modify the hit-test so it doesn't obscure shapes that are inside.
997 bool csGroupShape::HitTest(double x, double y, int* attachment, double* distance)
998 {
999 *attachment = 0;
1000 *distance = 0.0;
1001
1002 double width = 0.0, height = 0.0;
1003 GetBoundingBoxMin(&width, &height);
1004
1005 double x1 = GetX() - (width/2.0);
1006 double y1 = GetY() - (height/2.0);
1007 double x2 = GetX() + (width/2.0);
1008 double y2 = GetY() + (height/2.0);
1009
1010 double edgeTolerance = 4.0;
1011
1012 // Test each edge in turn
1013
1014 // Top/bottom edges
1015 if (x >= x1 && x <= x2)
1016 {
1017 if ((y >= y1 - edgeTolerance) && (y <= y1 + edgeTolerance))
1018 return true;
1019 if ((y <= y2 + edgeTolerance) && (y >= y2 - edgeTolerance))
1020 return true;
1021 }
1022 // Left/right edges
1023 if (y >= y1 && y <= y2)
1024 {
1025 if ((x >= x1 - edgeTolerance) && (x <= x1 + edgeTolerance))
1026 return true;
1027 if ((x <= x2 + edgeTolerance) && (x >= x2 - edgeTolerance))
1028 return true;
1029 }
1030
1031 return false;
1032 }
1033
1034 IMPLEMENT_DYNAMIC_CLASS(csTextBoxShape, wxRectangleShape)
1035
1036 csTextBoxShape::csTextBoxShape()
1037 {
1038 SetPen(wxTRANSPARENT_PEN);
1039 SetBrush(wxTRANSPARENT_BRUSH);
1040
1041 SetSize(csSTANDARD_SHAPE_WIDTH, csSTANDARD_SHAPE_WIDTH/2.0);
1042
1043 SetAttachmentMode(ATTACHMENT_MODE_NONE);
1044 SetBranchStyle(BRANCHING_ATTACHMENT_NORMAL|BRANCHING_ATTACHMENT_BLOB);
1045 SetCentreResize(false);
1046 }
1047
1048 IMPLEMENT_DYNAMIC_CLASS(csLineShape, wxLineShape)
1049
1050 csLineShape::csLineShape()
1051 {
1052 }
1053
1054 bool csLineShape::OnMoveMiddleControlPoint(wxDC& WXUNUSED(dc), wxLineControlPoint* lpt, const wxRealPoint& pt)
1055 {
1056 csDiagramView* view = ((csCanvas*)GetCanvas())->GetView();
1057
1058 // Temporarily set the new shape properties so we can copy it
1059 lpt->SetX(pt.x); lpt->SetY(pt.y);
1060 lpt->m_point->x = pt.x; lpt->m_point->y = pt.y;
1061
1062 wxLineShape* newShape = (wxLineShape*) this->CreateNewCopy();
1063
1064 // Now set them back again
1065 lpt->SetX(lpt->m_originalPos.x); lpt->SetY(lpt->m_originalPos.y);
1066 lpt->m_point->x = lpt->m_originalPos.x; lpt->m_point->y = lpt->m_originalPos.y;
1067
1068 view->GetDocument()->GetCommandProcessor()->Submit(new csDiagramCommand(_T("Move line point"), (csDiagramDocument*) view->GetDocument(),
1069 new csCommandState(ID_CS_MOVE_LINE_POINT, newShape, this)));
1070
1071 return true;
1072 }
1073
1074 wxLabelShape* csLineShape::OnCreateLabelShape(wxLineShape *parent, wxShapeRegion *region, double w, double h)
1075 {
1076 return new csLabelShape(parent, region, w, h);
1077 }
1078
1079 #if 0
1080 bool csLineShape::OnLabelMovePre(wxDC& dc, wxLabelShape* labelShape, double x, double y, double old_x, double old_y, bool display)
1081 {
1082 csDiagramView* view = ((csCanvas*)GetCanvas())->GetView();
1083
1084 wxLineShape* newShape = (wxLineShape*) this->CreateNewCopy();
1085
1086 wxLineShape::OnLabelMovePre(dc, labelShape, x, y, old_x, old_y, display);
1087
1088 view->GetDocument()->GetCommandProcessor()->Submit(new csDiagramCommand("Move label", (csDiagramDocument*) view->GetDocument(),
1089 new csCommandState(ID_CS_MOVE_LABEL, newShape, this)));
1090 return true;
1091 }
1092 #endif
1093
1094 IMPLEMENT_DYNAMIC_CLASS(csLabelShape, wxLabelShape)
1095
1096 csLabelShape::csLabelShape(wxLineShape *parent, wxShapeRegion *region, double w, double h):
1097 wxLabelShape(parent, region, w, h)
1098 {
1099 }
1100
1101 // TODO: not sure how intercept normal behaviour (OnMovePre) to make
1102 // label movement undo-able.
1103 void csLabelShape::OnEndDragLeft(double x, double y, int keys, int attachment)
1104 {
1105 wxLabelShape::OnEndDragLeft(x, y, keys, attachment);
1106 }
1107
1108
1109 // Menu for editing shapes
1110 void studioShapeEditProc(wxMenu& menu, wxCommandEvent& event)
1111 {
1112 wxShape* shape = (wxShape*) menu.GetClientData();
1113 csDiagramView* view = ((csCanvas*)shape->GetCanvas())->GetView();
1114
1115 switch (event.GetId())
1116 {
1117 case ID_CS_EDIT_PROPERTIES:
1118 {
1119 csEvtHandler* handler1 = (csEvtHandler *)shape->GetEventHandler();
1120 handler1->EditProperties();
1121 #if 0
1122 csEvtHandler* handler1 = (csEvtHandler *)shape->GetEventHandler();
1123 csLabelEditingDialog* dialog = new csLabelEditingDialog(shape->GetCanvas()->GetParent());
1124 dialog->SetShapeLabel(handler1->m_label);
1125 if (dialog->ShowModal() == wxID_CANCEL)
1126 {
1127 dialog->Destroy();
1128 return;
1129 }
1130
1131 wxString newLabel = dialog->GetShapeLabel();
1132 dialog->Destroy();
1133
1134 wxShape* newShape = shape->CreateNewCopy();
1135
1136 csEvtHandler* handler2 = (csEvtHandler *)newShape->GetEventHandler();
1137 handler2->m_label = newLabel;
1138
1139 view->GetDocument()->GetCommandProcessor()->Submit(new csDiagramCommand("Edit label", (csDiagramDocument*) view->GetDocument(),
1140 new csCommandState(ID_CS_EDIT_LABEL, newShape, shape)));
1141 #endif
1142 break;
1143 }
1144 case wxID_CUT:
1145 {
1146 wxList list;
1147 list.Append(shape);
1148 view->DoCut(list);
1149 break;
1150 }
1151 case ID_CS_ROTATE_CLOCKWISE:
1152 case ID_CS_ROTATE_ANTICLOCKWISE:
1153 {
1154 if (shape->IsKindOf(CLASSINFO(wxLineShape)))
1155 break;
1156
1157 double theta = shape->GetRotation();
1158 const double myPi = M_PI;
1159 double ninetyDegrees = myPi/2.0;
1160
1161 wxString opStr;
1162 if (event.GetId() == ID_CS_ROTATE_CLOCKWISE)
1163 {
1164 theta += ninetyDegrees;
1165 opStr = _T("Rotate clockwise");
1166 }
1167 else
1168 {
1169 theta -= ninetyDegrees;
1170 opStr = _T("Rotate anticlockwise");
1171 }
1172
1173 if (theta >= 2.0*myPi || theta < 0.0)
1174 theta = 0.0;
1175 wxShape* newShape = shape->CreateNewCopy();
1176 newShape->Rotate(0.0, 0.0, theta);
1177 wxList newShapes;
1178 wxList oldShapes;
1179 newShapes.Append(newShape);
1180 oldShapes.Append(shape);
1181 view->DoCmd(newShapes, oldShapes, event.GetId(), opStr);
1182 break;
1183 }
1184 default:
1185 break;
1186 }
1187 }
1188
1189 BEGIN_EVENT_TABLE(ShapeEditMenu, wxMenu)
1190 EVT_COMMAND_RANGE(1, 65000, wxEVT_COMMAND_MENU_SELECTED, ShapeEditMenu::OnCommand)
1191 END_EVENT_TABLE()
1192
1193 void ShapeEditMenu::OnCommand(wxCommandEvent& event)
1194 {
1195 studioShapeEditProc(*this, event);
1196 }
1197