X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/dbeac4bd72c8604616919c89f0450899b182313a..306dfc2b3d49ca45445bdfb2164fe1eea45ab321:/utils/ogl/samples/studio/shapes.cpp diff --git a/utils/ogl/samples/studio/shapes.cpp b/utils/ogl/samples/studio/shapes.cpp new file mode 100644 index 0000000000..b9197af2eb --- /dev/null +++ b/utils/ogl/samples/studio/shapes.cpp @@ -0,0 +1,1185 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: shapes.cpp +// Purpose: Implements Studio shapes +// Author: Julian Smart +// Modified by: +// Created: 12/07/98 +// RCS-ID: $Id$ +// Copyright: (c) Julian Smart +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#ifdef __GNUG__ +// #pragma implementation +#endif + +// For compilers that support precompilation, includes "wx.h". +#include + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + +#ifndef WX_PRECOMP +#include +#endif + +#if !wxUSE_DOC_VIEW_ARCHITECTURE +#error You must set wxUSE_DOC_VIEW_ARCHITECTURE to 1 in wx_setup.h! +#endif + +#include + +#include "studio.h" +#include "doc.h" +#include "shapes.h" +#include "view.h" +#include "basicp.h" +#include "linesp.h" +#include "cspalette.h" +#include "dialogs.h" + +#define csSTANDARD_SHAPE_WIDTH 100 + +IMPLEMENT_CLASS(csDiagram, wxDiagram) + +csDiagram::~csDiagram() +{ + DeleteAllShapes(); +} + +void csDiagram::Redraw(wxDC& dc) +{ + wxDiagram::Redraw(dc); + + // Draw line crossings + wxLineCrossings lineCrossings; + lineCrossings.FindCrossings(*this); + lineCrossings.DrawCrossings(*this, dc); +} + +/* + * csEvtHandler: an event handler class for all shapes + */ + +IMPLEMENT_DYNAMIC_CLASS(csEvtHandler, wxShapeEvtHandler) + +csEvtHandler::csEvtHandler(wxShapeEvtHandler *prev, wxShape *shape, const wxString& lab): + wxShapeEvtHandler(prev, shape) +{ + m_label = lab; +} + +csEvtHandler::~csEvtHandler() +{ +} + +// Copy any event handler data +void csEvtHandler::CopyData(wxShapeEvtHandler& copy) +{ + wxShapeEvtHandler::CopyData(copy); + + csEvtHandler& csCopy = (csEvtHandler&) copy; + csCopy.m_label = m_label; +} + +void csEvtHandler::OnLeftClick(double x, double y, int keys, int attachment) +{ + wxClientDC dc(GetShape()->GetCanvas()); + GetShape()->GetCanvas()->PrepareDC(dc); + + csDiagramView* view = ((csCanvas*)GetShape()->GetCanvas())->GetView(); + view->ReflectPointSize(GetShape()->GetFont()->GetPointSize()); + + if (GetShape()->IsKindOf(CLASSINFO(wxLineShape))) + view->ReflectArrowState((wxLineShape*) GetShape()); + + csEditorToolPalette *palette = wxGetApp().GetDiagramPalette(); + if (palette->GetSelection() == PALETTE_TEXT_TOOL) + { + view->ReflectPointSize(GetShape()->GetFont()->GetPointSize()); + + EditProperties(); +#if 0 + csLabelEditingDialog* dialog = new csLabelEditingDialog(GetShape()->GetCanvas()->GetParent()); + dialog->SetShapeLabel(m_label); + if (dialog->ShowModal() == wxID_CANCEL) + { + dialog->Destroy(); + return; + } + + wxString newLabel = dialog->GetShapeLabel(); + dialog->Destroy(); + + wxShape* newShape = GetShape()->CreateNewCopy(); + + csEvtHandler* handler = (csEvtHandler *)newShape->GetEventHandler(); + handler->m_label = newLabel; + + view->GetDocument()->GetCommandProcessor()->Submit(new csDiagramCommand("Edit label", (csDiagramDocument*) view->GetDocument(), + new csCommandState(ID_CS_EDIT_PROPERTIES, newShape, GetShape()))); +#endif + return; + } + + if (keys == 0) + { + // If no shift key, then everything is deselected. + // If the shape was selected, deselect it and vice versa. + bool selected = GetShape()->Selected(); + + view->SelectAll(FALSE); + + selected = !selected; + + GetShape()->Select(selected, &dc); + GetShape()->GetCanvas()->Redraw(dc); // Redraw because bits of objects will be missing + + view->SelectShape(GetShape(), selected); + } + else if (keys & KEY_SHIFT) + { + if (GetShape()->Selected()) + { + GetShape()->Select(FALSE, &dc); + view->SelectShape(GetShape(), FALSE); + } + else + { + GetShape()->Select(TRUE, &dc); + view->SelectShape(GetShape(), TRUE); + } + GetShape()->GetCanvas()->Redraw(dc); // Redraw because bits of objects will be missing + } + else if (keys & KEY_CTRL) + { + // Do something for CONTROL + } + else + { + ((wxFrame*)wxGetApp().GetTopWindow())->SetStatusText(m_label); + } +} + +void csEvtHandler::OnRightClick(double x, double y, int keys, int attachment) +{ + // Have to convert back to physical coordinates from logical coordinates. + + int viewStartX, viewStartY; + int unitX, unitY; + GetShape()->GetCanvas()->ViewStart(& viewStartX, & viewStartY); + GetShape()->GetCanvas()->GetScrollPixelsPerUnit(& unitX, & unitY); + + int x1 = (int)(x * GetShape()->GetCanvas()->GetScaleX()); + int y1 = (int)(y * GetShape()->GetCanvas()->GetScaleY()); + + int menuX = (int) (x1 - (viewStartX * unitX)) ; + int menuY = (int) (y1 - (viewStartY * unitY)); + + wxGetApp().GetShapeEditMenu()->SetClientData((char*) GetShape()); + wxGetApp().GetShapeEditMenu()->Enable(ID_CS_ROTATE_CLOCKWISE, !GetShape()->IsKindOf(CLASSINFO(wxLineShape))); + wxGetApp().GetShapeEditMenu()->Enable(ID_CS_ROTATE_ANTICLOCKWISE, !GetShape()->IsKindOf(CLASSINFO(wxLineShape))); + + GetShape()->GetCanvas()->PopupMenu(wxGetApp().GetShapeEditMenu(), menuX, menuY); +} + +/* + * Implement connection of two shapes by right-dragging between them. + */ + +void csEvtHandler::OnBeginDragRight(double x, double y, int keys, int attachment) +{ + wxClientDC dc(GetShape()->GetCanvas()); + GetShape()->GetCanvas()->PrepareDC(dc); + + wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT); + dc.SetLogicalFunction(wxXOR); + dc.SetPen(dottedPen); + double xp, yp; + GetShape()->GetAttachmentPositionEdge(attachment, &xp, &yp); + dc.DrawLine(xp, yp, x, y); + GetShape()->GetCanvas()->CaptureMouse(); +} + +void csEvtHandler::OnDragRight(bool draw, double x, double y, int keys, int attachment) +{ + wxClientDC dc(GetShape()->GetCanvas()); + GetShape()->GetCanvas()->PrepareDC(dc); + + wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT); + dc.SetLogicalFunction(wxXOR); + dc.SetPen(dottedPen); + double xp, yp; + GetShape()->GetAttachmentPositionEdge(attachment, &xp, &yp); + dc.DrawLine(xp, yp, x, y); +} + +void csEvtHandler::OnEndDragRight(double x, double y, int keys, int attachment) +{ + GetShape()->GetCanvas()->ReleaseMouse(); + csCanvas *canvas = (csCanvas *)GetShape()->GetCanvas(); + + // Check if we're on an object + int new_attachment; + wxShape *otherShape = canvas->FindFirstSensitiveShape(x, y, &new_attachment, OP_DRAG_RIGHT); + + if (otherShape && !otherShape->IsKindOf(CLASSINFO(wxLineShape))) + { + wxLineShape* theShape = new csLineShape; + + theShape->AssignNewIds(); + theShape->SetEventHandler(new csEvtHandler(theShape, theShape, wxString(""))); + theShape->SetPen(wxBLACK_PEN); + theShape->SetBrush(wxRED_BRUSH); + + wxToolBar* toolbar = wxGetApp().GetDiagramToolBar(); + bool haveArrow = toolbar->GetToolState(DIAGRAM_TOOLBAR_LINE_ARROW); + + wxLineShape *lineShape = (wxLineShape *)theShape; + + // Yes, you can have more than 2 control points, in which case + // it becomes a multi-segment line. + lineShape->MakeLineControlPoints(2); + + if (haveArrow) + lineShape->AddArrow(ARROW_ARROW, ARROW_POSITION_MIDDLE, 10.0, 0.0, "Normal arrowhead"); + + lineShape->SetFrom(GetShape()); + lineShape->SetTo(otherShape); + lineShape->SetAttachments(attachment, new_attachment); + + canvas->GetView()->GetDocument()->GetCommandProcessor()->Submit( + new csDiagramCommand("Line", (csDiagramDocument *)canvas->GetView()->GetDocument(), + new csCommandState(ID_CS_ADD_LINE, lineShape, NULL))); + } +} + +static double g_DragOffsetX = 0.0; +static double g_DragOffsetY = 0.0; +static double g_DragStartX = 0.0; +static double g_DragStartY = 0.0; + +void csEvtHandler::OnDragLeft(bool draw, double x, double y, int keys, int attachment) +{ + if ((GetShape()->GetSensitivityFilter() & OP_DRAG_LEFT) != OP_DRAG_LEFT) + { + attachment = 0; + double dist; + if (GetShape()->GetParent()) + { + GetShape()->GetParent()->HitTest(x, y, &attachment, &dist); + GetShape()->GetParent()->GetEventHandler()->OnDragLeft(draw, x, y, keys, attachment); + } + return; + } + + wxClientDC dc(GetShape()->GetCanvas()); + GetShape()->GetCanvas()->PrepareDC(dc); + + dc.SetLogicalFunction(wxXOR); + + wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT); + dc.SetPen(dottedPen); + dc.SetBrush(* wxTRANSPARENT_BRUSH); + + double xx, yy; + xx = x + g_DragOffsetX; + yy = y + g_DragOffsetY; + + GetShape()->GetCanvas()->Snap(&xx, &yy); + + double offsetX = xx - g_DragStartX; + double offsetY = yy - g_DragStartY; + +// m_xpos = xx; m_ypos = yy; + double w, h; + GetShape()->GetBoundingBoxMax(&w, &h); + GetShape()->GetEventHandler()->OnDrawOutline(dc, xx, yy, w, h); + + // Draw bounding box for other selected shapes + wxNode* node = GetShape()->GetCanvas()->GetDiagram()->GetShapeList()->First(); + while (node) + { + wxShape* shape = (wxShape*) node->Data(); + if (shape->Selected() && !shape->IsKindOf(CLASSINFO(wxLineShape)) && (shape != GetShape())) + { + shape->GetBoundingBoxMax(&w, &h); + shape->OnDrawOutline(dc, shape->GetX() + offsetX, shape->GetY() + offsetY, w, h); + } + node = node->Next(); + } +} + +void csEvtHandler::OnBeginDragLeft(double x, double y, int keys, int attachment) +{ + if ((GetShape()->GetSensitivityFilter() & OP_DRAG_LEFT) != OP_DRAG_LEFT) + { + attachment = 0; + double dist; + if (GetShape()->GetParent()) + { + GetShape()->GetParent()->HitTest(x, y, &attachment, &dist); + GetShape()->GetParent()->GetEventHandler()->OnBeginDragLeft(x, y, keys, attachment); + } + return; + } + + wxClientDC dc(GetShape()->GetCanvas()); + GetShape()->GetCanvas()->PrepareDC(dc); + + // New policy: don't erase shape until end of drag. +// Erase(dc); + + g_DragOffsetX = GetShape()->GetX() - x; + g_DragOffsetY = GetShape()->GetY() - y; + + double xx, yy; + xx = x + g_DragOffsetX; + yy = y + g_DragOffsetY; + + GetShape()->GetCanvas()->Snap(&xx, &yy); + + g_DragStartX = GetShape()->GetX(); + g_DragStartY = GetShape()->GetY(); + + double offsetX = xx - g_DragStartX; + double offsetY = yy - g_DragStartY; + + dc.SetLogicalFunction(wxXOR); + + wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT); + dc.SetPen(dottedPen); + dc.SetBrush((* wxTRANSPARENT_BRUSH)); + + double w, h; + GetShape()->GetBoundingBoxMax(&w, &h); + GetShape()->GetEventHandler()->OnDrawOutline(dc, xx, yy, w, h); + + // Draw bounding box for other selected shapes + wxNode* node = GetShape()->GetCanvas()->GetDiagram()->GetShapeList()->First(); + while (node) + { + wxShape* shape = (wxShape*) node->Data(); + if (shape->Selected() && !shape->IsKindOf(CLASSINFO(wxLineShape)) && (shape != GetShape())) + { + shape->GetBoundingBoxMax(&w, &h); + shape->OnDrawOutline(dc, shape->GetX() + offsetX, shape->GetY() + offsetY, w, h); + } + node = node->Next(); + } + + GetShape()->GetCanvas()->CaptureMouse(); +} + + +void csEvtHandler::OnEndDragLeft(double x, double y, int keys, int attachment) +{ + csCanvas *canvas = (csCanvas *)GetShape()->GetCanvas(); + + canvas->ReleaseMouse(); + if ((GetShape()->GetSensitivityFilter() & OP_DRAG_LEFT) != OP_DRAG_LEFT) + { + attachment = 0; + double dist; + if (GetShape()->GetParent()) + { + GetShape()->GetParent()->HitTest(x, y, &attachment, &dist); + GetShape()->GetParent()->GetEventHandler()->OnEndDragLeft(x, y, keys, attachment); + } + return; + } + + wxClientDC dc(canvas); + canvas->PrepareDC(dc); + + dc.SetLogicalFunction(wxCOPY); + + double xx = x + g_DragOffsetX; + double yy = y + g_DragOffsetY; + + canvas->Snap(&xx, &yy); + + double offsetX = xx - g_DragStartX; + double offsetY = yy - g_DragStartY; + + wxShape* newShape = GetShape()->CreateNewCopy(); + + newShape->SetX(xx); + newShape->SetY(yy); + + csDiagramCommand* cmd = new csDiagramCommand("Move", (csDiagramDocument*)canvas->GetView()->GetDocument(), + new csCommandState(ID_CS_MOVE, newShape, GetShape())); + + // Move line points + wxNode* node = GetShape()->GetCanvas()->GetDiagram()->GetShapeList()->First(); + while (node) + { + wxShape* shape = (wxShape*) node->Data(); + // Only move the line point(s) if both ends move too + if (shape->IsKindOf(CLASSINFO(wxLineShape)) && + ((wxLineShape*)shape)->GetTo()->Selected() && ((wxLineShape*)shape)->GetFrom()->Selected()) + { + wxLineShape* lineShape = (wxLineShape*) shape; + + if (lineShape->GetLineControlPoints()->Number() > 2) + { + wxLineShape* newLineShape = (wxLineShape*) lineShape->CreateNewCopy(); + + wxNode *node1 = newLineShape->GetLineControlPoints()->First(); + while (node1) + { + wxRealPoint *point = (wxRealPoint *)node1->Data(); + point->x += offsetX; + point->y += offsetY; + node1 = node1->Next(); + } + cmd->AddState(new csCommandState(ID_CS_MOVE_LINE_POINT, newLineShape, lineShape)); + lineShape->Erase(dc); + } + } + node = node->Next(); + } + + // Add other selected node shapes, if any + node = GetShape()->GetCanvas()->GetDiagram()->GetShapeList()->First(); + while (node) + { + wxShape* shape = (wxShape*) node->Data(); + if (shape->Selected() && !shape->IsKindOf(CLASSINFO(wxLineShape)) && (shape != GetShape())) + { + wxShape* newShape2 = shape->CreateNewCopy(); + newShape2->SetX(shape->GetX() + offsetX); + newShape2->SetY(shape->GetY() + offsetY); + cmd->AddState(new csCommandState(ID_CS_MOVE, newShape2, shape)); + } + node = node->Next(); + } + + canvas->GetView()->GetDocument()->GetCommandProcessor()->Submit(cmd); +} + +void csEvtHandler::OnSizingEndDragLeft(wxControlPoint* pt, double x, double y, int keys, int attachment) +{ + wxShape* shape = GetShape(); + csCanvas *canvas = (csCanvas *)GetShape()->GetCanvas(); + + if (shape->IsKindOf(CLASSINFO(wxLineShape))) + { + // TODO: Do/Undo support for line operations + ((wxLineShape*)shape)->wxLineShape::OnSizingEndDragLeft(pt, x, y, keys, attachment); +#if 0 + wxLineShape* lineShape = (wxLineShape*) shape; + + wxLineControlPoint* lpt = (wxLineControlPoint*) pt; + + wxClientDC dc(canvas); + canvas->PrepareDC(dc); + + shape->SetDisableLabel(FALSE); + + if (lpt->m_type == CONTROL_POINT_LINE) + { + canvas->Snap(&x, &y); + + dc.SetLogicalFunction(wxCOPY); + lpt->SetX(x); lpt->SetY(y); + lpt->m_point->x = x; lpt->m_point->y = y; + + this->OnMoveLink(dc); + } + if (lpt->m_type == CONTROL_POINT_ENDPOINT_FROM) + { + if (lpt->m_oldCursor) + canvas->SetCursor(lpt->m_oldCursor); + lineShape->Erase(dc); + + lpt->SetX(x); lpt->SetY(y); + + if (lineShape->GetFrom()) + { + lineShape->GetFrom()->MoveLineToNewAttachment(dc, lineShape, x, y); + } + } + if (lpt->m_type == CONTROL_POINT_ENDPOINT_TO) + { + if (lpt->m_oldCursor) + canvas->SetCursor(lpt->m_oldCursor); + + lpt->SetX(x); lpt->SetY(y); + + if (lineShape->GetTo()) + { + lineShape->GetTo()->MoveLineToNewAttachment(dc, lineShape, x, y); + } + } +#endif + return; + } + + wxClientDC dc(canvas); + canvas->PrepareDC(dc); + + canvas->ReleaseMouse(); + dc.SetLogicalFunction(wxCOPY); + +// shape->Erase(dc); +/* + shape->Recompute(); + shape->ResetControlPoints(); + if (!pt->m_eraseObject) + shape->Show(FALSE); +*/ + + wxShape* newShape = shape->CreateNewCopy(); + + if (newShape->IsKindOf(CLASSINFO(wxPolygonShape))) + { + wxPolygonControlPoint* ppt = (wxPolygonControlPoint*) pt; + newShape->SetSize(ppt->GetNewSize().x, ppt->GetNewSize().y); + + ((wxPolygonShape *)newShape)->CalculateBoundingBox(); + ((wxPolygonShape *)newShape)->CalculatePolygonCentre(); + newShape->ResetControlPoints(); + } + else + { + newShape->SetSize(pt->sm_controlPointDragEndWidth, pt->sm_controlPointDragEndHeight); + if (shape->GetCentreResize()) + { + // Old position is fine + } + else + { + newShape->SetX(pt->sm_controlPointDragPosX); + newShape->SetY(pt->sm_controlPointDragPosY); + } + } + + csDiagramCommand* cmd = new csDiagramCommand("Size", (csDiagramDocument*)canvas->GetView()->GetDocument(), + new csCommandState(ID_CS_SIZE, newShape, shape)); + + canvas->GetView()->GetDocument()->GetCommandProcessor()->Submit(cmd); + +} + +void csEvtHandler::OnEndSize(double x, double y) +{ + wxClientDC dc(GetShape()->GetCanvas()); + GetShape()->GetCanvas()->PrepareDC(dc); + + GetShape()->FormatText(dc, m_label); +} + +void csEvtHandler::OnChangeAttachment(int attachment, wxLineShape* line, wxList& ordering) +{ + csCanvas *canvas = (csCanvas *)GetShape()->GetCanvas(); + + // We actually submit two different states: one to change the ordering, and another + // to change the attachment for the line. + // Problem. If we refresh after the attachment change, we'll get a flicker. + // We really want to do both in a oner. + + csDiagramCommand* cmd = new csDiagramCommand("Change attachment", (csDiagramDocument*)canvas->GetView()->GetDocument()); + + wxLineShape* newLine = (wxLineShape*) line->CreateNewCopy(); + if (line->GetTo() == GetShape()) + newLine->SetAttachmentTo(attachment); + else + newLine->SetAttachmentFrom(attachment); + + cmd->AddState(new csCommandState(ID_CS_CHANGE_LINE_ATTACHMENT, newLine, line)); + + // Change ordering + wxShape* newShape = GetShape()->CreateNewCopy(); + newShape->ApplyAttachmentOrdering(ordering); + + cmd->AddState(new csCommandState(ID_CS_CHANGE_LINE_ORDERING, newShape, GetShape())); + + canvas->GetView()->GetDocument()->GetCommandProcessor()->Submit(cmd); +} + +void csEvtHandler::OnLeftDoubleClick(double x, double y, int keys, int attachment) +{ + EditProperties(); +} + +// Popup up a property dialog +bool csEvtHandler::EditProperties() +{ + wxShape* shape = GetShape(); + + // For now, no line property editing + if (shape->IsKindOf(CLASSINFO(wxLineShape))) + return FALSE; + + csDiagramView* view = ((csCanvas*)shape->GetCanvas())->GetView(); + + wxPanel* attributeDialog; + wxString attributeDialogName; + wxString title; + + if (shape->IsKindOf(CLASSINFO(csThinRectangleShape))) + { + attributeDialog = new csThinRectangleDialog; + attributeDialogName = "thin_rectangle"; + title = "Thin Rectangle Properties"; + } + else if (shape->IsKindOf(CLASSINFO(csWideRectangleShape))) + { + attributeDialog = new csWideRectangleDialog; + attributeDialogName = "wide_rectangle"; + title = "Wide Rectangle Properties"; + } + else if (shape->IsKindOf(CLASSINFO(csTriangleShape))) + { + attributeDialog = new csTriangleDialog; + attributeDialogName = "triangle"; + title = "Triangle Properties"; + } + else if (shape->IsKindOf(CLASSINFO(csSemiCircleShape))) + { + attributeDialog = new csSemiCircleDialog; + attributeDialogName = "semi_circle"; + title = "Semicircle Properties"; + } + else if (shape->IsKindOf(CLASSINFO(csCircleShape))) + { + attributeDialog = new csCircleDialog; + attributeDialogName = "circle"; + title = "Circle Properties"; + } + else if (shape->IsKindOf(CLASSINFO(csCircleShadowShape))) + { + attributeDialog = new csCircleShadowDialog; + attributeDialogName = "circle_shadow"; + title = "Circle Shadow Properties"; + } + else if (shape->IsKindOf(CLASSINFO(csTextBoxShape))) + { + attributeDialog = new csTextBoxDialog; + attributeDialogName = "text_box"; + title = "Text Box Properties"; + } + else if (shape->IsKindOf(CLASSINFO(csGroupShape))) + { + attributeDialog = new csGroupDialog; + attributeDialogName = "group"; + title = "Group Properties"; + } + else if (shape->IsKindOf(CLASSINFO(csOctagonShape))) + { + attributeDialog = new csOctagonDialog; + attributeDialogName = "octagon"; + title = "Octagon Properties"; + } + else + { + wxMessageBox("Unrecognised shape.", "Studio", wxICON_EXCLAMATION); + return FALSE; + } + + csShapePropertiesDialog* dialog = new csShapePropertiesDialog(shape->GetCanvas()->GetParent(), title, attributeDialog, attributeDialogName); + dialog->GetGeneralPropertiesDialog()->SetShapeLabel(m_label); + if (dialog->ShowModal() == wxID_CANCEL) + { + dialog->Destroy(); + return FALSE; + } + + wxString newLabel = dialog->GetGeneralPropertiesDialog()->GetShapeLabel(); + dialog->Destroy(); + + wxShape* newShape = shape->CreateNewCopy(); + + csEvtHandler* handler2 = (csEvtHandler *)newShape->GetEventHandler(); + handler2->m_label = newLabel; + + view->GetDocument()->GetCommandProcessor()->Submit(new csDiagramCommand("Edit properties", (csDiagramDocument*) view->GetDocument(), + new csCommandState(ID_CS_EDIT_PROPERTIES, newShape, shape))); + + return TRUE; +} + +/* + * Diagram + */ + +bool csDiagram::OnShapeSave(wxExprDatabase& db, wxShape& shape, wxExpr& expr) +{ + wxDiagram::OnShapeSave(db, shape, expr); + csEvtHandler *handler = (csEvtHandler *)shape.GetEventHandler(); + expr.AddAttributeValueString("label", handler->m_label); + return TRUE; +} + +bool csDiagram::OnShapeLoad(wxExprDatabase& db, wxShape& shape, wxExpr& expr) +{ + wxDiagram::OnShapeLoad(db, shape, expr); + wxString label(""); + expr.GetAttributeValue("label", label); + csEvtHandler *handler = new csEvtHandler(&shape, &shape, label); + shape.SetEventHandler(handler); + + return TRUE; +} + +IMPLEMENT_DYNAMIC_CLASS(csThinRectangleShape, wxDrawnShape) + +csThinRectangleShape::csThinRectangleShape() +{ + SetDrawnPen(wxBLACK_PEN); + wxBrush* brush = wxTheBrushList->FindOrCreateBrush(wxColour(220, 220, 220), wxSOLID); + SetDrawnBrush(brush); + + double w = csSTANDARD_SHAPE_WIDTH/2; + double h = csSTANDARD_SHAPE_WIDTH; + + DrawRectangle(wxRect(- w/2, - h/2, w, h)); + CalculateSize(); + + SetAttachmentMode(ATTACHMENT_MODE_BRANCHING); + SetBranchStyle(BRANCHING_ATTACHMENT_NORMAL|BRANCHING_ATTACHMENT_BLOB); + SetCentreResize(FALSE); +} + +IMPLEMENT_DYNAMIC_CLASS(csWideRectangleShape, wxDrawnShape) + +csWideRectangleShape::csWideRectangleShape() +{ + SetDrawnPen(wxBLACK_PEN); + wxBrush* brush = wxTheBrushList->FindOrCreateBrush(wxColour(220, 220, 220), wxSOLID); + SetDrawnBrush(brush); + + double w = csSTANDARD_SHAPE_WIDTH; + double h = w/2.0; + + DrawRoundedRectangle(wxRect(- w/2, - h/2, w, h), -0.3); + CalculateSize(); + + SetAttachmentMode(ATTACHMENT_MODE_BRANCHING); + SetBranchStyle(BRANCHING_ATTACHMENT_NORMAL|BRANCHING_ATTACHMENT_BLOB); + SetCentreResize(FALSE); +} + +IMPLEMENT_DYNAMIC_CLASS(csTriangleShape, wxDrawnShape) + +csTriangleShape::csTriangleShape() +{ + SetDrawnPen(wxBLACK_PEN); + wxBrush* brush = wxTheBrushList->FindOrCreateBrush(wxColour(220, 220, 220), wxSOLID); + SetDrawnBrush(brush); + + double w = csSTANDARD_SHAPE_WIDTH; + double h = (csSTANDARD_SHAPE_WIDTH*2.0)/3.0; + + // Triangle, from top vertex + wxPoint* points = new wxPoint[3]; + + + points[0] = wxPoint( 0 , - h / 2 ); + points[1] = wxPoint( w / 2 , h / 2 ); + points[2] = wxPoint( -w / 2, h / 2 ); + + DrawPolygon(3, points, oglMETAFLAGS_OUTLINE); + + delete[] points; + + // Add another triangle at the top for the black bit + SetDrawnBrush(wxBLACK_BRUSH); + + points = new wxPoint[3]; + + // Calculate where the new points will be, using the proportions + // of the triangle. + double h1 = 8; // Height of little triangle. + + /* + Formula: ((w/2) / h) = w1 / h1 + w1 = ((w/2) / h) * h1; + */ + double ratio = ((w/2.0) / h) ; + double w1 = ratio * h1; + + points[0] = wxPoint(0 , (int) (- h / 2 )); + points[1] = wxPoint( (int) w1, (int) (- h / 2 + h1)); + points[2] = wxPoint( (int) -w1, (int) (- h / 2 + h1)); + + DrawPolygon(3, points); + + delete[] points; + + CalculateSize(); + + SetAttachmentMode(ATTACHMENT_MODE_BRANCHING); + SetBranchStyle(BRANCHING_ATTACHMENT_NORMAL|BRANCHING_ATTACHMENT_BLOB); + SetCentreResize(FALSE); +} + +IMPLEMENT_DYNAMIC_CLASS(csSemiCircleShape, wxDrawnShape) + +csSemiCircleShape::csSemiCircleShape() +{ + // Zero degrees + DrawAtAngle(oglDRAWN_ANGLE_0); + + double w = csSTANDARD_SHAPE_WIDTH; + double h = w/2.0; + + SetDrawnPen(wxTRANSPARENT_PEN); + SetDrawnBrush(wxTRANSPARENT_BRUSH); + + // Draw a dummy rectangle that will be used for calculating the + // bounding box, since we can't calculate the bounding box for + // an arbitrary arc (not implemented) + + DrawRectangle(wxRect(-w/2.0, -h/2.0, w, h)); + + SetDrawnPen(wxBLACK_PEN); + wxBrush* brush = wxTheBrushList->FindOrCreateBrush(wxColour(220, 220, 220), wxSOLID); + SetDrawnBrush(brush); + + DrawEllipticArc(wxRect(-w/2, -h/2, w, 2*h), 0.0, 180.0); + DrawLine(wxPoint(-w/2, h/2), wxPoint(w/2, h/2)); + + CalculateSize(); + + /// 90 degrees + + w = csSTANDARD_SHAPE_WIDTH/2; + h = csSTANDARD_SHAPE_WIDTH; + + DrawAtAngle(oglDRAWN_ANGLE_90); + + SetDrawnPen(wxTRANSPARENT_PEN); + SetDrawnBrush(wxTRANSPARENT_BRUSH); + + DrawRectangle(wxRect(-w/2, -h/2, w, h)); + + SetDrawnPen(wxBLACK_PEN); + SetDrawnBrush(brush); + + DrawEllipticArc(wxRect(-w/2 - w, -h/2, 2*w, h), 270.0, 90.0); + DrawLine(wxPoint(-w/2, -h/2), wxPoint(-w/2, h/2)); + + CalculateSize(); + + /// 180 degrees + + DrawAtAngle(oglDRAWN_ANGLE_180); + + w = csSTANDARD_SHAPE_WIDTH; + h = csSTANDARD_SHAPE_WIDTH/2; + + SetDrawnPen(wxTRANSPARENT_PEN); + SetDrawnBrush(wxTRANSPARENT_BRUSH); + + DrawRectangle(wxRect(-w/2, -h/2, w, h)); + + SetDrawnPen(wxBLACK_PEN); + SetDrawnBrush(brush); + + DrawEllipticArc(wxRect(-w/2, -h/2 - h, w, 2*h), 180.0, 0.0); + DrawLine(wxPoint(-w/2, -h/2), wxPoint(w/2, -h/2)); + + CalculateSize(); + + /// 270 degrees + + DrawAtAngle(oglDRAWN_ANGLE_270); + + w = csSTANDARD_SHAPE_WIDTH/2; + h = csSTANDARD_SHAPE_WIDTH; + + SetDrawnPen(wxTRANSPARENT_PEN); + SetDrawnBrush(wxTRANSPARENT_BRUSH); + + DrawRectangle(wxRect(-w/2, -h/2, w, h)); + + SetDrawnPen(wxBLACK_PEN); + SetDrawnBrush(brush); + + DrawEllipticArc(wxRect(-w/2, -h/2, 2*w, h), 90.0, 270.0); + DrawLine(wxPoint(w/2, -h/2), wxPoint(w/2, h/2)); + + CalculateSize(); + + // Reset to zero + DrawAtAngle(oglDRAWN_ANGLE_0); + CalculateSize(); + + SetAttachmentMode(ATTACHMENT_MODE_BRANCHING); + SetBranchStyle(BRANCHING_ATTACHMENT_NORMAL|BRANCHING_ATTACHMENT_BLOB); + SetCentreResize(FALSE); +} + +IMPLEMENT_DYNAMIC_CLASS(csCircleShape, wxCircleShape) + +csCircleShape::csCircleShape() +{ + SetPen(wxBLACK_PEN); + wxBrush* brush = wxTheBrushList->FindOrCreateBrush(wxColour(220, 220, 220), wxSOLID); + SetBrush(brush); + + SetSize(csSTANDARD_SHAPE_WIDTH*0.6, csSTANDARD_SHAPE_WIDTH*0.6); + + SetAttachmentMode(ATTACHMENT_MODE_BRANCHING); + SetBranchStyle(BRANCHING_ATTACHMENT_NORMAL|BRANCHING_ATTACHMENT_BLOB); + SetCentreResize(FALSE); +} + +IMPLEMENT_DYNAMIC_CLASS(csCircleShadowShape, wxCircleShape) + +csCircleShadowShape::csCircleShadowShape() +{ + SetPen(wxBLACK_PEN); + wxBrush* brush = wxTheBrushList->FindOrCreateBrush(wxColour(220, 220, 220), wxSOLID); + SetBrush(brush); + + SetSize(csSTANDARD_SHAPE_WIDTH*0.6, csSTANDARD_SHAPE_WIDTH*0.6); + + SetAttachmentMode(ATTACHMENT_MODE_BRANCHING); + SetBranchStyle(BRANCHING_ATTACHMENT_NORMAL|BRANCHING_ATTACHMENT_BLOB); + SetCentreResize(FALSE); + SetShadowMode(SHADOW_RIGHT); +} + +IMPLEMENT_DYNAMIC_CLASS(csOctagonShape, wxPolygonShape) + +csOctagonShape::csOctagonShape() +{ + SetPen(wxBLACK_PEN); + SetBrush(wxTheBrushList->FindOrCreateBrush(wxColour(220, 220, 220), wxSOLID)); + + double w = csSTANDARD_SHAPE_WIDTH*0.5; + double h = csSTANDARD_SHAPE_WIDTH*0.5; + + double prop = h/3.0; + + wxList* points = new wxList; + points->Append((wxObject*) new wxRealPoint(-w/2.0 + prop, -h/2.0)); + points->Append((wxObject*) new wxRealPoint(w/2.0 - prop, -h/2.0)); + points->Append((wxObject*) new wxRealPoint(w/2.0, -h/2.0 + prop)); + points->Append((wxObject*) new wxRealPoint(w/2.0, h/2.0 - prop)); + points->Append((wxObject*) new wxRealPoint(w/2.0 - prop, h/2.0)); + points->Append((wxObject*) new wxRealPoint(-w/2.0 + prop, h/2.0)); + points->Append((wxObject*) new wxRealPoint(-w/2.0, h/2.0 - prop)); + points->Append((wxObject*) new wxRealPoint(-w/2.0, -h/2.0 + prop)); + + Create(points); + + SetAttachmentMode(ATTACHMENT_MODE_BRANCHING); + SetBranchStyle(BRANCHING_ATTACHMENT_NORMAL|BRANCHING_ATTACHMENT_BLOB); + SetCentreResize(FALSE); +} + +// This is a transparent shape for drawing around other shapes. +IMPLEMENT_DYNAMIC_CLASS(csGroupShape, wxRectangleShape) + +csGroupShape::csGroupShape() +{ + SetPen(wxThePenList->FindOrCreatePen("BLACK", 1, wxDOT)); + SetBrush(wxTRANSPARENT_BRUSH); + + SetSize(csSTANDARD_SHAPE_WIDTH, csSTANDARD_SHAPE_WIDTH); + SetCentreResize(FALSE); +} + +void csGroupShape::OnDraw(wxDC& dc) +{ + wxRectangleShape::OnDraw(dc); +} + +// Must modify the hit-test so it doesn't obscure shapes that are inside. +bool csGroupShape::HitTest(double x, double y, int* attachment, double* distance) +{ + *attachment = 0; + *distance = 0.0; + + double width = 0.0, height = 0.0; + GetBoundingBoxMin(&width, &height); + + double x1 = GetX() - (width/2.0); + double y1 = GetY() - (height/2.0); + double x2 = GetX() + (width/2.0); + double y2 = GetY() + (height/2.0); + + double edgeTolerance = 4.0; + + // Test each edge in turn + + // Top/bottom edges + if (x >= x1 && x <= x2) + { + if ((y >= y1 - edgeTolerance) && (y <= y1 + edgeTolerance)) + return TRUE; + if ((y <= y2 + edgeTolerance) && (y >= y2 - edgeTolerance)) + return TRUE; + } + // Left/right edges + if (y >= y1 && y <= y2) + { + if ((x >= x1 - edgeTolerance) && (x <= x1 + edgeTolerance)) + return TRUE; + if ((x <= x2 + edgeTolerance) && (x >= x2 - edgeTolerance)) + return TRUE; + } + + return FALSE; +} + +IMPLEMENT_DYNAMIC_CLASS(csTextBoxShape, wxRectangleShape) + +csTextBoxShape::csTextBoxShape() +{ + SetPen(wxTRANSPARENT_PEN); + SetBrush(wxTRANSPARENT_BRUSH); + + SetSize(csSTANDARD_SHAPE_WIDTH, csSTANDARD_SHAPE_WIDTH/2.0); + + SetAttachmentMode(ATTACHMENT_MODE_NONE); + SetBranchStyle(BRANCHING_ATTACHMENT_NORMAL|BRANCHING_ATTACHMENT_BLOB); + SetCentreResize(FALSE); +} + +IMPLEMENT_DYNAMIC_CLASS(csLineShape, wxLineShape) + +csLineShape::csLineShape() +{ +} + +bool csLineShape::OnMoveMiddleControlPoint(wxDC& dc, wxLineControlPoint* lpt, const wxRealPoint& pt) +{ + csDiagramView* view = ((csCanvas*)GetCanvas())->GetView(); + + // Temporarily set the new shape properties so we can copy it + lpt->SetX(pt.x); lpt->SetY(pt.y); + lpt->m_point->x = pt.x; lpt->m_point->y = pt.y; + + wxLineShape* newShape = (wxLineShape*) this->CreateNewCopy(); + + // Now set them back again + lpt->SetX(lpt->m_originalPos.x); lpt->SetY(lpt->m_originalPos.y); + lpt->m_point->x = lpt->m_originalPos.x; lpt->m_point->y = lpt->m_originalPos.y; + + view->GetDocument()->GetCommandProcessor()->Submit(new csDiagramCommand("Move line point", (csDiagramDocument*) view->GetDocument(), + new csCommandState(ID_CS_MOVE_LINE_POINT, newShape, this))); + + return TRUE; +} + +wxLabelShape* csLineShape::OnCreateLabelShape(wxLineShape *parent, wxShapeRegion *region, double w, double h) +{ + return new csLabelShape(parent, region, w, h); +} + +#if 0 +bool csLineShape::OnLabelMovePre(wxDC& dc, wxLabelShape* labelShape, double x, double y, double old_x, double old_y, bool display) +{ + csDiagramView* view = ((csCanvas*)GetCanvas())->GetView(); + + wxLineShape* newShape = (wxLineShape*) this->CreateNewCopy(); + + wxLineShape::OnLabelMovePre(dc, labelShape, x, y, old_x, old_y, display); + + view->GetDocument()->GetCommandProcessor()->Submit(new csDiagramCommand("Move label", (csDiagramDocument*) view->GetDocument(), + new csCommandState(ID_CS_MOVE_LABEL, newShape, this))); + return TRUE; +} +#endif + +IMPLEMENT_DYNAMIC_CLASS(csLabelShape, wxLabelShape) + +csLabelShape::csLabelShape(wxLineShape *parent, wxShapeRegion *region, double w, double h): + wxLabelShape(parent, region, w, h) +{ +} + +// TODO: not sure how intercept normal behaviour (OnMovePre) to make +// label movement undo-able. +void csLabelShape::OnEndDragLeft(double x, double y, int keys, int attachment) +{ + wxLabelShape::OnEndDragLeft(x, y, keys, attachment); +} + + +// Menu for editing shapes +void studioShapeEditProc(wxMenu& menu, wxCommandEvent& event) +{ + wxShape* shape = (wxShape*) menu.GetClientData(); + csDiagramView* view = ((csCanvas*)shape->GetCanvas())->GetView(); + + switch (event.GetId()) + { + case ID_CS_EDIT_PROPERTIES: + { + csEvtHandler* handler1 = (csEvtHandler *)shape->GetEventHandler(); + handler1->EditProperties(); +#if 0 + csEvtHandler* handler1 = (csEvtHandler *)shape->GetEventHandler(); + csLabelEditingDialog* dialog = new csLabelEditingDialog(shape->GetCanvas()->GetParent()); + dialog->SetShapeLabel(handler1->m_label); + if (dialog->ShowModal() == wxID_CANCEL) + { + dialog->Destroy(); + return; + } + + wxString newLabel = dialog->GetShapeLabel(); + dialog->Destroy(); + + wxShape* newShape = shape->CreateNewCopy(); + + csEvtHandler* handler2 = (csEvtHandler *)newShape->GetEventHandler(); + handler2->m_label = newLabel; + + view->GetDocument()->GetCommandProcessor()->Submit(new csDiagramCommand("Edit label", (csDiagramDocument*) view->GetDocument(), + new csCommandState(ID_CS_EDIT_LABEL, newShape, shape))); +#endif + break; + } + case wxID_CUT: + { + wxList list; + list.Append(shape); + view->DoCut(list); + break; + } + case ID_CS_ROTATE_CLOCKWISE: + case ID_CS_ROTATE_ANTICLOCKWISE: + { + if (shape->IsKindOf(CLASSINFO(wxLineShape))) + break; + + double theta = shape->GetRotation(); + const double myPi = 3.1415926535897932384626433832795 ; + double ninetyDegrees = myPi/2.0; + + wxString opStr; + if (event.GetId() == ID_CS_ROTATE_CLOCKWISE) + { + theta += ninetyDegrees; + opStr = "Rotate clockwise"; + } + else + { + theta -= ninetyDegrees; + opStr = "Rotate anticlockwise"; + } + + if (theta >= 2.0*myPi || theta < 0.0) + theta = 0.0; + wxShape* newShape = shape->CreateNewCopy(); + newShape->Rotate(0.0, 0.0, theta); + wxList newShapes; + wxList oldShapes; + newShapes.Append(newShape); + oldShapes.Append(shape); + view->DoCmd(newShapes, oldShapes, event.GetId(), opStr); + break; + } + default: + break; + } +} +