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