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