1///////////////////////////////////////////////////////////////////////////// 2// Name: contrib/samples/ogl/studio/shapes.cpp 3// Purpose: Implements Studio shapes 4// Author: Julian Smart 5// Modified by: 6// Created: 12/07/98 7// RCS-ID: $Id: shapes.cpp 37440 2006-02-10 11:59:52Z ABX $ 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 38IMPLEMENT_CLASS(csDiagram, wxDiagram) 39 40csDiagram::~csDiagram() 41{ 42 DeleteAllShapes(); 43} 44 45void 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 59IMPLEMENT_DYNAMIC_CLASS(csEvtHandler, wxShapeEvtHandler) 60 61csEvtHandler::csEvtHandler(wxShapeEvtHandler *prev, wxShape *shape, const wxString& lab): 62 wxShapeEvtHandler(prev, shape) 63{ 64 m_label = lab; 65} 66 67csEvtHandler::~csEvtHandler() 68{ 69} 70 71// Copy any event handler data 72void csEvtHandler::CopyData(wxShapeEvtHandler& copy) 73{ 74 wxShapeEvtHandler::CopyData(copy); 75 76 csEvtHandler& csCopy = (csEvtHandler&) copy; 77 csCopy.m_label = m_label; 78} 79 80void 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 161void 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 187void 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 201void 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 214void 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 254static double g_DragOffsetX = 0.0; 255static double g_DragOffsetY = 0.0; 256static double g_DragStartX = 0.0; 257static double g_DragStartY = 0.0; 258 259void 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 310void 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 372void 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 458void 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 562void 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 570void 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 598void 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 604bool 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 711bool 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 719bool 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 731IMPLEMENT_DYNAMIC_CLASS(csThinRectangleShape, wxDrawnShape) 732 733csThinRectangleShape::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 750IMPLEMENT_DYNAMIC_CLASS(csWideRectangleShape, wxDrawnShape) 751 752csWideRectangleShape::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 769IMPLEMENT_DYNAMIC_CLASS(csTriangleShape, wxDrawnShape) 770 771csTriangleShape::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 823IMPLEMENT_DYNAMIC_CLASS(csSemiCircleShape, wxDrawnShape) 824 825csSemiCircleShape::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 920IMPLEMENT_DYNAMIC_CLASS(csCircleShape, wxCircleShape) 921 922csCircleShape::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 935IMPLEMENT_DYNAMIC_CLASS(csCircleShadowShape, wxCircleShape) 936 937csCircleShadowShape::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 951IMPLEMENT_DYNAMIC_CLASS(csOctagonShape, wxPolygonShape) 952 953csOctagonShape::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. 981IMPLEMENT_DYNAMIC_CLASS(csGroupShape, wxRectangleShape) 982 983csGroupShape::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 992void 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. 998bool 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 1035IMPLEMENT_DYNAMIC_CLASS(csTextBoxShape, wxRectangleShape) 1036 1037csTextBoxShape::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 1049IMPLEMENT_DYNAMIC_CLASS(csLineShape, wxLineShape) 1050 1051csLineShape::csLineShape() 1052{ 1053} 1054 1055bool 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 1075wxLabelShape* csLineShape::OnCreateLabelShape(wxLineShape *parent, wxShapeRegion *region, double w, double h) 1076{ 1077 return new csLabelShape(parent, region, w, h); 1078} 1079 1080#if 0 1081bool 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 1095IMPLEMENT_DYNAMIC_CLASS(csLabelShape, wxLabelShape) 1096 1097csLabelShape::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. 1104void 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 1111void 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 1190BEGIN_EVENT_TABLE(ShapeEditMenu, wxMenu) 1191 EVT_COMMAND_RANGE(1, 65000, wxEVT_COMMAND_MENU_SELECTED, ShapeEditMenu::OnCommand) 1192END_EVENT_TABLE() 1193 1194void ShapeEditMenu::OnCommand(wxCommandEvent& event) 1195{ 1196 studioShapeEditProc(*this, event); 1197} 1198