1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: Implements Studio shapes
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
13 // #pragma implementation
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
27 #if !wxUSE_DOC_VIEW_ARCHITECTURE
28 #error You must set wxUSE_DOC_VIEW_ARCHITECTURE to 1 in wx_setup.h!
31 #include <wx/deprecated/setup.h>
32 #include <wx/deprecated/wxexpr.h>
38 #include <wx/ogl/basicp.h>
39 #include <wx/ogl/linesp.h>
40 #include "cspalette.h"
43 #define csSTANDARD_SHAPE_WIDTH 100
45 IMPLEMENT_CLASS(csDiagram
, wxDiagram
)
47 csDiagram::~csDiagram()
52 void csDiagram::Redraw(wxDC
& dc
)
54 wxDiagram::Redraw(dc
);
56 // Draw line crossings
57 wxLineCrossings lineCrossings
;
58 lineCrossings
.FindCrossings(*this);
59 lineCrossings
.DrawCrossings(*this, dc
);
63 * csEvtHandler: an event handler class for all shapes
66 IMPLEMENT_DYNAMIC_CLASS(csEvtHandler
, wxShapeEvtHandler
)
68 csEvtHandler::csEvtHandler(wxShapeEvtHandler
*prev
, wxShape
*shape
, const wxString
& lab
):
69 wxShapeEvtHandler(prev
, shape
)
74 csEvtHandler::~csEvtHandler()
78 // Copy any event handler data
79 void csEvtHandler::CopyData(wxShapeEvtHandler
& copy
)
81 wxShapeEvtHandler::CopyData(copy
);
83 csEvtHandler
& csCopy
= (csEvtHandler
&) copy
;
84 csCopy
.m_label
= m_label
;
87 void csEvtHandler::OnLeftClick(double WXUNUSED(x
), double WXUNUSED(y
), int keys
, int WXUNUSED(attachment
))
89 wxClientDC
dc(GetShape()->GetCanvas());
90 GetShape()->GetCanvas()->PrepareDC(dc
);
92 csDiagramView
* view
= ((csCanvas
*)GetShape()->GetCanvas())->GetView();
93 view
->ReflectPointSize(GetShape()->GetFont()->GetPointSize());
95 if (GetShape()->IsKindOf(CLASSINFO(wxLineShape
)))
96 view
->ReflectArrowState((wxLineShape
*) GetShape());
98 csEditorToolPalette
*palette
= wxGetApp().GetDiagramPalette();
99 if (palette
->GetSelection() == PALETTE_TEXT_TOOL
)
101 view
->ReflectPointSize(GetShape()->GetFont()->GetPointSize());
105 csLabelEditingDialog
* dialog
= new csLabelEditingDialog(GetShape()->GetCanvas()->GetParent());
106 dialog
->SetShapeLabel(m_label
);
107 if (dialog
->ShowModal() == wxID_CANCEL
)
113 wxString newLabel
= dialog
->GetShapeLabel();
116 wxShape
* newShape
= GetShape()->CreateNewCopy();
118 csEvtHandler
* handler
= (csEvtHandler
*)newShape
->GetEventHandler();
119 handler
->m_label
= newLabel
;
121 view
->GetDocument()->GetCommandProcessor()->Submit(new csDiagramCommand("Edit label", (csDiagramDocument
*) view
->GetDocument(),
122 new csCommandState(ID_CS_EDIT_PROPERTIES
, newShape
, GetShape())));
129 // If no shift key, then everything is deselected.
130 // If the shape was selected, deselect it and vice versa.
131 bool selected
= GetShape()->Selected();
133 view
->SelectAll(false);
135 selected
= !selected
;
137 GetShape()->Select(selected
, &dc
);
138 GetShape()->GetCanvas()->Redraw(dc
); // Redraw because bits of objects will be missing
140 view
->SelectShape(GetShape(), selected
);
142 else if (keys
& KEY_SHIFT
)
144 if (GetShape()->Selected())
146 GetShape()->Select(false, &dc
);
147 view
->SelectShape(GetShape(), false);
151 GetShape()->Select(true, &dc
);
152 view
->SelectShape(GetShape(), true);
154 GetShape()->GetCanvas()->Redraw(dc
); // Redraw because bits of objects will be missing
156 else if (keys
& KEY_CTRL
)
158 // Do something for CONTROL
163 ((wxFrame
*)wxGetApp().GetTopWindow())->SetStatusText(m_label
);
164 #endif // wxUSE_STATUSBAR
168 void csEvtHandler::OnRightClick(double x
, double y
, int WXUNUSED(keys
), int WXUNUSED(attachment
))
170 // Have to convert back to physical coordinates from logical coordinates.
172 int viewStartX
, viewStartY
;
174 GetShape()->GetCanvas()->GetViewStart(& viewStartX
, & viewStartY
);
175 GetShape()->GetCanvas()->GetScrollPixelsPerUnit(& unitX
, & unitY
);
177 int x1
= (int)(x
* GetShape()->GetCanvas()->GetScaleX());
178 int y1
= (int)(y
* GetShape()->GetCanvas()->GetScaleY());
180 int menuX
= (int) (x1
- (viewStartX
* unitX
)) ;
181 int menuY
= (int) (y1
- (viewStartY
* unitY
));
183 wxGetApp().GetShapeEditMenu()->SetClientData((char*) GetShape());
184 wxGetApp().GetShapeEditMenu()->Enable(ID_CS_ROTATE_CLOCKWISE
, !GetShape()->IsKindOf(CLASSINFO(wxLineShape
)));
185 wxGetApp().GetShapeEditMenu()->Enable(ID_CS_ROTATE_ANTICLOCKWISE
, !GetShape()->IsKindOf(CLASSINFO(wxLineShape
)));
187 GetShape()->GetCanvas()->PopupMenu(wxGetApp().GetShapeEditMenu(), menuX
, menuY
);
191 * Implement connection of two shapes by right-dragging between them.
194 void csEvtHandler::OnBeginDragRight(double x
, double y
, int WXUNUSED(keys
), int attachment
)
196 wxClientDC
dc(GetShape()->GetCanvas());
197 GetShape()->GetCanvas()->PrepareDC(dc
);
199 wxPen
dottedPen(wxColour(0, 0, 0), 1, wxDOT
);
200 dc
.SetLogicalFunction(OGLRBLF
);
201 dc
.SetPen(dottedPen
);
203 GetShape()->GetAttachmentPositionEdge(attachment
, &xp
, &yp
);
204 dc
.DrawLine((wxCoord
)xp
, (wxCoord
)yp
, (wxCoord
)x
, (wxCoord
)y
);
205 GetShape()->GetCanvas()->CaptureMouse();
208 void csEvtHandler::OnDragRight(bool WXUNUSED(draw
), double x
, double y
, int WXUNUSED(keys
), int attachment
)
210 wxClientDC
dc(GetShape()->GetCanvas());
211 GetShape()->GetCanvas()->PrepareDC(dc
);
213 wxPen
dottedPen(wxColour(0, 0, 0), 1, wxDOT
);
214 dc
.SetLogicalFunction(OGLRBLF
);
215 dc
.SetPen(dottedPen
);
217 GetShape()->GetAttachmentPositionEdge(attachment
, &xp
, &yp
);
218 dc
.DrawLine((wxCoord
)xp
, (wxCoord
)yp
, (wxCoord
)x
, (wxCoord
)y
);
221 void csEvtHandler::OnEndDragRight(double x
, double y
, int WXUNUSED(keys
), int attachment
)
223 GetShape()->GetCanvas()->ReleaseMouse();
224 csCanvas
*canvas
= (csCanvas
*)GetShape()->GetCanvas();
226 // Check if we're on an object
228 wxShape
*otherShape
= canvas
->FindFirstSensitiveShape(x
, y
, &new_attachment
, OP_DRAG_RIGHT
);
230 if (otherShape
&& !otherShape
->IsKindOf(CLASSINFO(wxLineShape
)))
232 wxLineShape
* theShape
= new csLineShape
;
234 theShape
->AssignNewIds();
235 theShape
->SetEventHandler(new csEvtHandler(theShape
, theShape
, wxEmptyString
));
236 theShape
->SetPen(wxBLACK_PEN
);
237 theShape
->SetBrush(wxRED_BRUSH
);
239 wxToolBar
* toolbar
= wxGetApp().GetDiagramToolBar();
240 bool haveArrow
= toolbar
->GetToolState(DIAGRAM_TOOLBAR_LINE_ARROW
);
242 wxLineShape
*lineShape
= (wxLineShape
*)theShape
;
244 // Yes, you can have more than 2 control points, in which case
245 // it becomes a multi-segment line.
246 lineShape
->MakeLineControlPoints(2);
249 lineShape
->AddArrow(ARROW_ARROW
, ARROW_POSITION_MIDDLE
, 10.0, 0.0, _T("Normal arrowhead"));
251 lineShape
->SetFrom(GetShape());
252 lineShape
->SetTo(otherShape
);
253 lineShape
->SetAttachments(attachment
, new_attachment
);
255 canvas
->GetView()->GetDocument()->GetCommandProcessor()->Submit(
256 new csDiagramCommand(_T("Line"), (csDiagramDocument
*)canvas
->GetView()->GetDocument(),
257 new csCommandState(ID_CS_ADD_LINE
, lineShape
, NULL
)));
261 static double g_DragOffsetX
= 0.0;
262 static double g_DragOffsetY
= 0.0;
263 static double g_DragStartX
= 0.0;
264 static double g_DragStartY
= 0.0;
266 void csEvtHandler::OnDragLeft(bool draw
, double x
, double y
, int keys
, int attachment
)
268 if ((GetShape()->GetSensitivityFilter() & OP_DRAG_LEFT
) != OP_DRAG_LEFT
)
272 if (GetShape()->GetParent())
274 GetShape()->GetParent()->HitTest(x
, y
, &attachment
, &dist
);
275 GetShape()->GetParent()->GetEventHandler()->OnDragLeft(draw
, x
, y
, keys
, attachment
);
280 wxClientDC
dc(GetShape()->GetCanvas());
281 GetShape()->GetCanvas()->PrepareDC(dc
);
283 dc
.SetLogicalFunction(OGLRBLF
);
285 wxPen
dottedPen(wxColour(0, 0, 0), 1, wxDOT
);
286 dc
.SetPen(dottedPen
);
287 dc
.SetBrush(* wxTRANSPARENT_BRUSH
);
290 xx
= x
+ g_DragOffsetX
;
291 yy
= y
+ g_DragOffsetY
;
293 GetShape()->GetCanvas()->Snap(&xx
, &yy
);
295 double offsetX
= xx
- g_DragStartX
;
296 double offsetY
= yy
- g_DragStartY
;
298 // m_xpos = xx; m_ypos = yy;
300 GetShape()->GetBoundingBoxMax(&w
, &h
);
301 GetShape()->GetEventHandler()->OnDrawOutline(dc
, xx
, yy
, w
, h
);
303 // Draw bounding box for other selected shapes
304 wxNode
* node
= GetShape()->GetCanvas()->GetDiagram()->GetShapeList()->GetFirst();
307 wxShape
* shape
= (wxShape
*) node
->GetData();
308 if (shape
->Selected() && !shape
->IsKindOf(CLASSINFO(wxLineShape
)) && (shape
!= GetShape()))
310 shape
->GetBoundingBoxMax(&w
, &h
);
311 shape
->OnDrawOutline(dc
, shape
->GetX() + offsetX
, shape
->GetY() + offsetY
, w
, h
);
313 node
= node
->GetNext();
317 void csEvtHandler::OnBeginDragLeft(double x
, double y
, int keys
, int attachment
)
319 if ((GetShape()->GetSensitivityFilter() & OP_DRAG_LEFT
) != OP_DRAG_LEFT
)
323 if (GetShape()->GetParent())
325 GetShape()->GetParent()->HitTest(x
, y
, &attachment
, &dist
);
326 GetShape()->GetParent()->GetEventHandler()->OnBeginDragLeft(x
, y
, keys
, attachment
);
331 wxClientDC
dc(GetShape()->GetCanvas());
332 GetShape()->GetCanvas()->PrepareDC(dc
);
334 // New policy: don't erase shape until end of drag.
337 g_DragOffsetX
= GetShape()->GetX() - x
;
338 g_DragOffsetY
= GetShape()->GetY() - y
;
341 xx
= x
+ g_DragOffsetX
;
342 yy
= y
+ g_DragOffsetY
;
344 GetShape()->GetCanvas()->Snap(&xx
, &yy
);
346 g_DragStartX
= GetShape()->GetX();
347 g_DragStartY
= GetShape()->GetY();
349 double offsetX
= xx
- g_DragStartX
;
350 double offsetY
= yy
- g_DragStartY
;
352 dc
.SetLogicalFunction(OGLRBLF
);
354 wxPen
dottedPen(wxColour(0, 0, 0), 1, wxDOT
);
355 dc
.SetPen(dottedPen
);
356 dc
.SetBrush((* wxTRANSPARENT_BRUSH
));
359 GetShape()->GetBoundingBoxMax(&w
, &h
);
360 GetShape()->GetEventHandler()->OnDrawOutline(dc
, xx
, yy
, w
, h
);
362 // Draw bounding box for other selected shapes
363 wxNode
* node
= GetShape()->GetCanvas()->GetDiagram()->GetShapeList()->GetFirst();
366 wxShape
* shape
= (wxShape
*) node
->GetData();
367 if (shape
->Selected() && !shape
->IsKindOf(CLASSINFO(wxLineShape
)) && (shape
!= GetShape()))
369 shape
->GetBoundingBoxMax(&w
, &h
);
370 shape
->OnDrawOutline(dc
, shape
->GetX() + offsetX
, shape
->GetY() + offsetY
, w
, h
);
372 node
= node
->GetNext();
375 GetShape()->GetCanvas()->CaptureMouse();
379 void csEvtHandler::OnEndDragLeft(double x
, double y
, int keys
, int attachment
)
381 csCanvas
*canvas
= (csCanvas
*)GetShape()->GetCanvas();
383 canvas
->ReleaseMouse();
384 if ((GetShape()->GetSensitivityFilter() & OP_DRAG_LEFT
) != OP_DRAG_LEFT
)
388 if (GetShape()->GetParent())
390 GetShape()->GetParent()->HitTest(x
, y
, &attachment
, &dist
);
391 GetShape()->GetParent()->GetEventHandler()->OnEndDragLeft(x
, y
, keys
, attachment
);
396 wxClientDC
dc(canvas
);
397 canvas
->PrepareDC(dc
);
399 dc
.SetLogicalFunction(wxCOPY
);
401 double xx
= x
+ g_DragOffsetX
;
402 double yy
= y
+ g_DragOffsetY
;
404 canvas
->Snap(&xx
, &yy
);
406 double offsetX
= xx
- g_DragStartX
;
407 double offsetY
= yy
- g_DragStartY
;
409 wxShape
* newShape
= GetShape()->CreateNewCopy();
414 csDiagramCommand
* cmd
= new csDiagramCommand(_T("Move"), (csDiagramDocument
*)canvas
->GetView()->GetDocument(),
415 new csCommandState(ID_CS_MOVE
, newShape
, GetShape()));
418 wxNode
* node
= GetShape()->GetCanvas()->GetDiagram()->GetShapeList()->GetFirst();
421 wxShape
* shape
= (wxShape
*) node
->GetData();
422 // Only move the line point(s) if both ends move too
423 if (shape
->IsKindOf(CLASSINFO(wxLineShape
)) &&
424 ((wxLineShape
*)shape
)->GetTo()->Selected() && ((wxLineShape
*)shape
)->GetFrom()->Selected())
426 wxLineShape
* lineShape
= (wxLineShape
*) shape
;
428 if (lineShape
->GetLineControlPoints()->GetCount() > 2)
430 wxLineShape
* newLineShape
= (wxLineShape
*) lineShape
->CreateNewCopy();
432 wxNode
*node1
= newLineShape
->GetLineControlPoints()->GetFirst();
435 wxRealPoint
*point
= (wxRealPoint
*)node1
->GetData();
438 node1
= node1
->GetNext();
440 cmd
->AddState(new csCommandState(ID_CS_MOVE_LINE_POINT
, newLineShape
, lineShape
));
441 lineShape
->Erase(dc
);
444 node
= node
->GetNext();
447 // Add other selected node shapes, if any
448 node
= GetShape()->GetCanvas()->GetDiagram()->GetShapeList()->GetFirst();
451 wxShape
* shape
= (wxShape
*) node
->GetData();
452 if (shape
->Selected() && !shape
->IsKindOf(CLASSINFO(wxLineShape
)) && (shape
!= GetShape()))
454 wxShape
* newShape2
= shape
->CreateNewCopy();
455 newShape2
->SetX(shape
->GetX() + offsetX
);
456 newShape2
->SetY(shape
->GetY() + offsetY
);
457 cmd
->AddState(new csCommandState(ID_CS_MOVE
, newShape2
, shape
));
459 node
= node
->GetNext();
462 canvas
->GetView()->GetDocument()->GetCommandProcessor()->Submit(cmd
);
465 void csEvtHandler::OnSizingEndDragLeft(wxControlPoint
* pt
, double x
, double y
, int keys
, int attachment
)
467 wxShape
* shape
= GetShape();
468 csCanvas
*canvas
= (csCanvas
*)GetShape()->GetCanvas();
470 if (shape
->IsKindOf(CLASSINFO(wxLineShape
)))
472 // TODO: Do/Undo support for line operations
473 ((wxLineShape
*)shape
)->wxLineShape::OnSizingEndDragLeft(pt
, x
, y
, keys
, attachment
);
475 wxLineShape
* lineShape
= (wxLineShape
*) shape
;
477 wxLineControlPoint
* lpt
= (wxLineControlPoint
*) pt
;
479 wxClientDC
dc(canvas
);
480 canvas
->PrepareDC(dc
);
482 shape
->SetDisableLabel(false);
484 if (lpt
->m_type
== CONTROL_POINT_LINE
)
486 canvas
->Snap(&x
, &y
);
488 dc
.SetLogicalFunction(wxCOPY
);
489 lpt
->SetX(x
); lpt
->SetY(y
);
490 lpt
->m_point
->x
= x
; lpt
->m_point
->y
= y
;
492 this->OnMoveLink(dc
);
494 if (lpt
->m_type
== CONTROL_POINT_ENDPOINT_FROM
)
496 if (lpt
->m_oldCursor
)
497 canvas
->SetCursor(lpt
->m_oldCursor
);
498 lineShape
->Erase(dc
);
500 lpt
->SetX(x
); lpt
->SetY(y
);
502 if (lineShape
->GetFrom())
504 lineShape
->GetFrom()->MoveLineToNewAttachment(dc
, lineShape
, x
, y
);
507 if (lpt
->m_type
== CONTROL_POINT_ENDPOINT_TO
)
509 if (lpt
->m_oldCursor
)
510 canvas
->SetCursor(lpt
->m_oldCursor
);
512 lpt
->SetX(x
); lpt
->SetY(y
);
514 if (lineShape
->GetTo())
516 lineShape
->GetTo()->MoveLineToNewAttachment(dc
, lineShape
, x
, y
);
523 wxClientDC
dc(canvas
);
524 canvas
->PrepareDC(dc
);
526 canvas
->ReleaseMouse();
527 dc
.SetLogicalFunction(wxCOPY
);
532 shape->ResetControlPoints();
533 if (!pt->m_eraseObject)
537 wxShape
* newShape
= shape
->CreateNewCopy();
539 if (newShape
->IsKindOf(CLASSINFO(wxPolygonShape
)))
541 wxPolygonControlPoint
* ppt
= (wxPolygonControlPoint
*) pt
;
542 newShape
->SetSize(ppt
->GetNewSize().x
, ppt
->GetNewSize().y
);
544 ((wxPolygonShape
*)newShape
)->CalculateBoundingBox();
545 ((wxPolygonShape
*)newShape
)->CalculatePolygonCentre();
546 newShape
->ResetControlPoints();
550 newShape
->SetSize(pt
->sm_controlPointDragEndWidth
, pt
->sm_controlPointDragEndHeight
);
551 if (shape
->GetCentreResize())
553 // Old position is fine
557 newShape
->SetX(pt
->sm_controlPointDragPosX
);
558 newShape
->SetY(pt
->sm_controlPointDragPosY
);
562 csDiagramCommand
* cmd
= new csDiagramCommand(_T("Size"), (csDiagramDocument
*)canvas
->GetView()->GetDocument(),
563 new csCommandState(ID_CS_SIZE
, newShape
, shape
));
565 canvas
->GetView()->GetDocument()->GetCommandProcessor()->Submit(cmd
);
569 void csEvtHandler::OnEndSize(double WXUNUSED(x
), double WXUNUSED(y
))
571 wxClientDC
dc(GetShape()->GetCanvas());
572 GetShape()->GetCanvas()->PrepareDC(dc
);
574 GetShape()->FormatText(dc
, m_label
);
577 void csEvtHandler::OnChangeAttachment(int attachment
, wxLineShape
* line
, wxList
& ordering
)
579 csCanvas
*canvas
= (csCanvas
*)GetShape()->GetCanvas();
581 // We actually submit two different states: one to change the ordering, and another
582 // to change the attachment for the line.
583 // Problem. If we refresh after the attachment change, we'll get a flicker.
584 // We really want to do both in a oner.
586 csDiagramCommand
* cmd
= new csDiagramCommand(_T("Change attachment"), (csDiagramDocument
*)canvas
->GetView()->GetDocument());
588 wxLineShape
* newLine
= (wxLineShape
*) line
->CreateNewCopy();
589 if (line
->GetTo() == GetShape())
590 newLine
->SetAttachmentTo(attachment
);
592 newLine
->SetAttachmentFrom(attachment
);
594 cmd
->AddState(new csCommandState(ID_CS_CHANGE_LINE_ATTACHMENT
, newLine
, line
));
597 wxShape
* newShape
= GetShape()->CreateNewCopy();
598 newShape
->ApplyAttachmentOrdering(ordering
);
600 cmd
->AddState(new csCommandState(ID_CS_CHANGE_LINE_ORDERING
, newShape
, GetShape()));
602 canvas
->GetView()->GetDocument()->GetCommandProcessor()->Submit(cmd
);
605 void csEvtHandler::OnLeftDoubleClick(double WXUNUSED(x
), double WXUNUSED(y
), int WXUNUSED(keys
), int WXUNUSED(attachment
))
610 // Popup up a property dialog
611 bool csEvtHandler::EditProperties()
613 wxShape
* shape
= GetShape();
615 // For now, no line property editing
616 if (shape
->IsKindOf(CLASSINFO(wxLineShape
)))
619 csDiagramView
* view
= ((csCanvas
*)shape
->GetCanvas())->GetView();
621 wxPanel
* attributeDialog
;
622 wxString attributeDialogName
;
625 if (shape
->IsKindOf(CLASSINFO(csThinRectangleShape
)))
627 attributeDialog
= new csThinRectangleDialog
;
628 attributeDialogName
= _T("thin_rectangle");
629 title
= _T("Thin Rectangle Properties");
631 else if (shape
->IsKindOf(CLASSINFO(csWideRectangleShape
)))
633 attributeDialog
= new csWideRectangleDialog
;
634 attributeDialogName
= _T("wide_rectangle");
635 title
= _T("Wide Rectangle Properties");
637 else if (shape
->IsKindOf(CLASSINFO(csTriangleShape
)))
639 attributeDialog
= new csTriangleDialog
;
640 attributeDialogName
= _T("triangle");
641 title
= _T("Triangle Properties");
643 else if (shape
->IsKindOf(CLASSINFO(csSemiCircleShape
)))
645 attributeDialog
= new csSemiCircleDialog
;
646 attributeDialogName
= _T("semi_circle");
647 title
= _T("Semicircle Properties");
649 else if (shape
->IsKindOf(CLASSINFO(csCircleShape
)))
651 attributeDialog
= new csCircleDialog
;
652 attributeDialogName
= _T("circle");
653 title
= _T("Circle Properties");
655 else if (shape
->IsKindOf(CLASSINFO(csCircleShadowShape
)))
657 attributeDialog
= new csCircleShadowDialog
;
658 attributeDialogName
= _T("circle_shadow");
659 title
= _T("Circle Shadow Properties");
661 else if (shape
->IsKindOf(CLASSINFO(csTextBoxShape
)))
663 attributeDialog
= new csTextBoxDialog
;
664 attributeDialogName
= _T("text_box");
665 title
= _T("Text Box Properties");
667 else if (shape
->IsKindOf(CLASSINFO(csGroupShape
)))
669 attributeDialog
= new csGroupDialog
;
670 attributeDialogName
= _T("group");
671 title
= _T("Group Properties");
673 else if (shape
->IsKindOf(CLASSINFO(csOctagonShape
)))
675 attributeDialog
= new csOctagonDialog
;
676 attributeDialogName
= _T("octagon");
677 title
= _T("Octagon Properties");
681 wxMessageBox(_T("Unrecognised shape."), _T("Studio"), wxICON_EXCLAMATION
);
685 csShapePropertiesDialog
* dialog
= new csShapePropertiesDialog(shape
->GetCanvas()->GetParent(), title
, attributeDialog
, attributeDialogName
);
686 dialog
->GetGeneralPropertiesDialog()->SetShapeLabel(m_label
);
687 if (dialog
->ShowModal() == wxID_CANCEL
)
693 wxString newLabel
= dialog
->GetGeneralPropertiesDialog()->GetShapeLabel();
696 wxShape
* newShape
= shape
->CreateNewCopy();
698 csEvtHandler
* handler2
= (csEvtHandler
*)newShape
->GetEventHandler();
699 handler2
->m_label
= newLabel
;
701 view
->GetDocument()->GetCommandProcessor()->Submit(new csDiagramCommand(_T("Edit properties"), (csDiagramDocument
*) view
->GetDocument(),
702 new csCommandState(ID_CS_EDIT_PROPERTIES
, newShape
, shape
)));
711 bool csDiagram::OnShapeSave(wxExprDatabase
& db
, wxShape
& shape
, wxExpr
& expr
)
713 wxDiagram::OnShapeSave(db
, shape
, expr
);
714 csEvtHandler
*handler
= (csEvtHandler
*)shape
.GetEventHandler();
715 expr
.AddAttributeValueString(_T("label"), handler
->m_label
);
719 bool csDiagram::OnShapeLoad(wxExprDatabase
& db
, wxShape
& shape
, wxExpr
& expr
)
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
);
730 IMPLEMENT_DYNAMIC_CLASS(csThinRectangleShape
, wxDrawnShape
)
732 csThinRectangleShape::csThinRectangleShape()
734 SetDrawnPen(wxBLACK_PEN
);
735 wxBrush
* brush
= wxTheBrushList
->FindOrCreateBrush(wxColour(220, 220, 220), wxSOLID
);
736 SetDrawnBrush(brush
);
738 double w
= csSTANDARD_SHAPE_WIDTH
/2;
739 double h
= csSTANDARD_SHAPE_WIDTH
;
741 DrawRectangle(wxRect((int)(- w
/2), (int)(- h
/2), (int)(w
), (int)(h
)));
744 SetAttachmentMode(ATTACHMENT_MODE_BRANCHING
);
745 SetBranchStyle(BRANCHING_ATTACHMENT_NORMAL
|BRANCHING_ATTACHMENT_BLOB
);
746 SetCentreResize(false);
749 IMPLEMENT_DYNAMIC_CLASS(csWideRectangleShape
, wxDrawnShape
)
751 csWideRectangleShape::csWideRectangleShape()
753 SetDrawnPen(wxBLACK_PEN
);
754 wxBrush
* brush
= wxTheBrushList
->FindOrCreateBrush(wxColour(220, 220, 220), wxSOLID
);
755 SetDrawnBrush(brush
);
757 double w
= csSTANDARD_SHAPE_WIDTH
;
760 DrawRoundedRectangle(wxRect((int)(- w
/2), (int)(- h
/2), (int)(w
), (int)(h
)), -0.3);
763 SetAttachmentMode(ATTACHMENT_MODE_BRANCHING
);
764 SetBranchStyle(BRANCHING_ATTACHMENT_NORMAL
|BRANCHING_ATTACHMENT_BLOB
);
765 SetCentreResize(false);
768 IMPLEMENT_DYNAMIC_CLASS(csTriangleShape
, wxDrawnShape
)
770 csTriangleShape::csTriangleShape()
772 SetDrawnPen(wxBLACK_PEN
);
773 wxBrush
* brush
= wxTheBrushList
->FindOrCreateBrush(wxColour(220, 220, 220), wxSOLID
);
774 SetDrawnBrush(brush
);
776 double w
= csSTANDARD_SHAPE_WIDTH
;
777 double h
= (csSTANDARD_SHAPE_WIDTH
*2.0)/3.0;
779 // Triangle, from top vertex
780 wxPoint
* points
= new wxPoint
[3];
783 points
[0] = wxPoint( 0 , (int)(- h
/ 2) );
784 points
[1] = wxPoint( (int)(w
/ 2) , (int)(h
/ 2) );
785 points
[2] = wxPoint( (int)(-w
/ 2), (int)(h
/ 2) );
787 DrawPolygon(3, points
, oglMETAFLAGS_OUTLINE
);
791 // Add another triangle at the top for the black bit
792 SetDrawnBrush(wxBLACK_BRUSH
);
794 points
= new wxPoint
[3];
796 // Calculate where the new points will be, using the proportions
798 double h1
= 8; // Height of little triangle.
801 Formula: ((w/2) / h) = w1 / h1
802 w1 = ((w/2) / h) * h1;
804 double ratio
= ((w
/2.0) / h
) ;
805 double w1
= ratio
* h1
;
807 points
[0] = wxPoint(0 , (int) (- h
/ 2 ));
808 points
[1] = wxPoint( (int) w1
, (int) (- h
/ 2 + h1
));
809 points
[2] = wxPoint( (int) -w1
, (int) (- h
/ 2 + h1
));
811 DrawPolygon(3, points
);
817 SetAttachmentMode(ATTACHMENT_MODE_BRANCHING
);
818 SetBranchStyle(BRANCHING_ATTACHMENT_NORMAL
|BRANCHING_ATTACHMENT_BLOB
);
819 SetCentreResize(false);
822 IMPLEMENT_DYNAMIC_CLASS(csSemiCircleShape
, wxDrawnShape
)
824 csSemiCircleShape::csSemiCircleShape()
827 DrawAtAngle(oglDRAWN_ANGLE_0
);
829 double w
= csSTANDARD_SHAPE_WIDTH
;
832 SetDrawnPen(wxTRANSPARENT_PEN
);
833 SetDrawnBrush(wxTRANSPARENT_BRUSH
);
835 // Draw a dummy rectangle that will be used for calculating the
836 // bounding box, since we can't calculate the bounding box for
837 // an arbitrary arc (not implemented)
839 DrawRectangle(wxRect((int)(-w
/2.0), (int)(-h
/2.0), (int)(w
), (int)(h
)));
841 SetDrawnPen(wxBLACK_PEN
);
842 wxBrush
* brush
= wxTheBrushList
->FindOrCreateBrush(wxColour(220, 220, 220), wxSOLID
);
843 SetDrawnBrush(brush
);
845 DrawEllipticArc(wxRect((int)(-w
/2), (int)(-h
/2), (int)(w
), (int)(2*h
)), 0.0, 180.0);
846 DrawLine(wxPoint((int)(-w
/2), (int)(h
/2)), wxPoint((int)(w
/2), (int)(h
/2)));
852 w
= csSTANDARD_SHAPE_WIDTH
/2;
853 h
= csSTANDARD_SHAPE_WIDTH
;
855 DrawAtAngle(oglDRAWN_ANGLE_90
);
857 SetDrawnPen(wxTRANSPARENT_PEN
);
858 SetDrawnBrush(wxTRANSPARENT_BRUSH
);
860 DrawRectangle(wxRect((int)(-w
/2), (int)(-h
/2), (int)(w
), (int)(h
)));
862 SetDrawnPen(wxBLACK_PEN
);
863 SetDrawnBrush(brush
);
865 DrawEllipticArc(wxRect((int)(-w
/2 - w
), (int)(-h
/2), (int)(2*w
), (int)(h
)), 270.0, 90.0);
866 DrawLine(wxPoint((int)(-w
/2), (int)(-h
/2)), wxPoint((int)(-w
/2), (int)(h
/2)));
872 DrawAtAngle(oglDRAWN_ANGLE_180
);
874 w
= csSTANDARD_SHAPE_WIDTH
;
875 h
= csSTANDARD_SHAPE_WIDTH
/2;
877 SetDrawnPen(wxTRANSPARENT_PEN
);
878 SetDrawnBrush(wxTRANSPARENT_BRUSH
);
880 DrawRectangle(wxRect((int)(-w
/2), (int)(-h
/2), (int)(w
), (int)(h
)));
882 SetDrawnPen(wxBLACK_PEN
);
883 SetDrawnBrush(brush
);
885 DrawEllipticArc(wxRect((int)(-w
/2), (int)(-h
/2 - h
), (int)(w
), (int)(2*h
)), 180.0, 0.0);
886 DrawLine(wxPoint((int)(-w
/2), (int)(-h
/2)), wxPoint((int)(w
/2), (int)(-h
/2)));
892 DrawAtAngle(oglDRAWN_ANGLE_270
);
894 w
= csSTANDARD_SHAPE_WIDTH
/2;
895 h
= csSTANDARD_SHAPE_WIDTH
;
897 SetDrawnPen(wxTRANSPARENT_PEN
);
898 SetDrawnBrush(wxTRANSPARENT_BRUSH
);
900 DrawRectangle(wxRect((int)(-w
/2), (int)(-h
/2), (int)(w
), (int)(h
)));
902 SetDrawnPen(wxBLACK_PEN
);
903 SetDrawnBrush(brush
);
905 DrawEllipticArc(wxRect((int)(-w
/2), (int)(-h
/2), (int)(2*w
), (int)(h
)), 90.0, 270.0);
906 DrawLine(wxPoint((int)(w
/2),(int)(-h
/2)), wxPoint((int)(w
/2), (int)(h
/2)));
911 DrawAtAngle(oglDRAWN_ANGLE_0
);
914 SetAttachmentMode(ATTACHMENT_MODE_BRANCHING
);
915 SetBranchStyle(BRANCHING_ATTACHMENT_NORMAL
|BRANCHING_ATTACHMENT_BLOB
);
916 SetCentreResize(false);
919 IMPLEMENT_DYNAMIC_CLASS(csCircleShape
, wxCircleShape
)
921 csCircleShape::csCircleShape()
924 wxBrush
* brush
= wxTheBrushList
->FindOrCreateBrush(wxColour(220, 220, 220), wxSOLID
);
927 SetSize(csSTANDARD_SHAPE_WIDTH
*0.6, csSTANDARD_SHAPE_WIDTH
*0.6);
929 SetAttachmentMode(ATTACHMENT_MODE_BRANCHING
);
930 SetBranchStyle(BRANCHING_ATTACHMENT_NORMAL
|BRANCHING_ATTACHMENT_BLOB
);
931 SetCentreResize(false);
934 IMPLEMENT_DYNAMIC_CLASS(csCircleShadowShape
, wxCircleShape
)
936 csCircleShadowShape::csCircleShadowShape()
939 wxBrush
* brush
= wxTheBrushList
->FindOrCreateBrush(wxColour(220, 220, 220), wxSOLID
);
942 SetSize(csSTANDARD_SHAPE_WIDTH
*0.6, csSTANDARD_SHAPE_WIDTH
*0.6);
944 SetAttachmentMode(ATTACHMENT_MODE_BRANCHING
);
945 SetBranchStyle(BRANCHING_ATTACHMENT_NORMAL
|BRANCHING_ATTACHMENT_BLOB
);
946 SetCentreResize(false);
947 SetShadowMode(SHADOW_RIGHT
);
950 IMPLEMENT_DYNAMIC_CLASS(csOctagonShape
, wxPolygonShape
)
952 csOctagonShape::csOctagonShape()
955 SetBrush(wxTheBrushList
->FindOrCreateBrush(wxColour(220, 220, 220), wxSOLID
));
957 double w
= csSTANDARD_SHAPE_WIDTH
*0.5;
958 double h
= csSTANDARD_SHAPE_WIDTH
*0.5;
962 wxList
* points
= new wxList
;
963 points
->Append((wxObject
*) new wxRealPoint(-w
/2.0 + prop
, -h
/2.0));
964 points
->Append((wxObject
*) new wxRealPoint(w
/2.0 - prop
, -h
/2.0));
965 points
->Append((wxObject
*) new wxRealPoint(w
/2.0, -h
/2.0 + prop
));
966 points
->Append((wxObject
*) new wxRealPoint(w
/2.0, h
/2.0 - prop
));
967 points
->Append((wxObject
*) new wxRealPoint(w
/2.0 - prop
, h
/2.0));
968 points
->Append((wxObject
*) new wxRealPoint(-w
/2.0 + prop
, h
/2.0));
969 points
->Append((wxObject
*) new wxRealPoint(-w
/2.0, h
/2.0 - prop
));
970 points
->Append((wxObject
*) new wxRealPoint(-w
/2.0, -h
/2.0 + prop
));
974 SetAttachmentMode(ATTACHMENT_MODE_BRANCHING
);
975 SetBranchStyle(BRANCHING_ATTACHMENT_NORMAL
|BRANCHING_ATTACHMENT_BLOB
);
976 SetCentreResize(false);
979 // This is a transparent shape for drawing around other shapes.
980 IMPLEMENT_DYNAMIC_CLASS(csGroupShape
, wxRectangleShape
)
982 csGroupShape::csGroupShape()
984 SetPen(wxThePenList
->FindOrCreatePen(_T("BLACK"), 1, wxDOT
));
985 SetBrush(wxTRANSPARENT_BRUSH
);
987 SetSize(csSTANDARD_SHAPE_WIDTH
, csSTANDARD_SHAPE_WIDTH
);
988 SetCentreResize(false);
991 void csGroupShape::OnDraw(wxDC
& dc
)
993 wxRectangleShape::OnDraw(dc
);
996 // Must modify the hit-test so it doesn't obscure shapes that are inside.
997 bool csGroupShape::HitTest(double x
, double y
, int* attachment
, double* distance
)
1002 double width
= 0.0, height
= 0.0;
1003 GetBoundingBoxMin(&width
, &height
);
1005 double x1
= GetX() - (width
/2.0);
1006 double y1
= GetY() - (height
/2.0);
1007 double x2
= GetX() + (width
/2.0);
1008 double y2
= GetY() + (height
/2.0);
1010 double edgeTolerance
= 4.0;
1012 // Test each edge in turn
1015 if (x
>= x1
&& x
<= x2
)
1017 if ((y
>= y1
- edgeTolerance
) && (y
<= y1
+ edgeTolerance
))
1019 if ((y
<= y2
+ edgeTolerance
) && (y
>= y2
- edgeTolerance
))
1023 if (y
>= y1
&& y
<= y2
)
1025 if ((x
>= x1
- edgeTolerance
) && (x
<= x1
+ edgeTolerance
))
1027 if ((x
<= x2
+ edgeTolerance
) && (x
>= x2
- edgeTolerance
))
1034 IMPLEMENT_DYNAMIC_CLASS(csTextBoxShape
, wxRectangleShape
)
1036 csTextBoxShape::csTextBoxShape()
1038 SetPen(wxTRANSPARENT_PEN
);
1039 SetBrush(wxTRANSPARENT_BRUSH
);
1041 SetSize(csSTANDARD_SHAPE_WIDTH
, csSTANDARD_SHAPE_WIDTH
/2.0);
1043 SetAttachmentMode(ATTACHMENT_MODE_NONE
);
1044 SetBranchStyle(BRANCHING_ATTACHMENT_NORMAL
|BRANCHING_ATTACHMENT_BLOB
);
1045 SetCentreResize(false);
1048 IMPLEMENT_DYNAMIC_CLASS(csLineShape
, wxLineShape
)
1050 csLineShape::csLineShape()
1054 bool csLineShape::OnMoveMiddleControlPoint(wxDC
& WXUNUSED(dc
), wxLineControlPoint
* lpt
, const wxRealPoint
& pt
)
1056 csDiagramView
* view
= ((csCanvas
*)GetCanvas())->GetView();
1058 // Temporarily set the new shape properties so we can copy it
1059 lpt
->SetX(pt
.x
); lpt
->SetY(pt
.y
);
1060 lpt
->m_point
->x
= pt
.x
; lpt
->m_point
->y
= pt
.y
;
1062 wxLineShape
* newShape
= (wxLineShape
*) this->CreateNewCopy();
1064 // Now set them back again
1065 lpt
->SetX(lpt
->m_originalPos
.x
); lpt
->SetY(lpt
->m_originalPos
.y
);
1066 lpt
->m_point
->x
= lpt
->m_originalPos
.x
; lpt
->m_point
->y
= lpt
->m_originalPos
.y
;
1068 view
->GetDocument()->GetCommandProcessor()->Submit(new csDiagramCommand(_T("Move line point"), (csDiagramDocument
*) view
->GetDocument(),
1069 new csCommandState(ID_CS_MOVE_LINE_POINT
, newShape
, this)));
1074 wxLabelShape
* csLineShape::OnCreateLabelShape(wxLineShape
*parent
, wxShapeRegion
*region
, double w
, double h
)
1076 return new csLabelShape(parent
, region
, w
, h
);
1080 bool csLineShape::OnLabelMovePre(wxDC
& dc
, wxLabelShape
* labelShape
, double x
, double y
, double old_x
, double old_y
, bool display
)
1082 csDiagramView
* view
= ((csCanvas
*)GetCanvas())->GetView();
1084 wxLineShape
* newShape
= (wxLineShape
*) this->CreateNewCopy();
1086 wxLineShape::OnLabelMovePre(dc
, labelShape
, x
, y
, old_x
, old_y
, display
);
1088 view
->GetDocument()->GetCommandProcessor()->Submit(new csDiagramCommand("Move label", (csDiagramDocument
*) view
->GetDocument(),
1089 new csCommandState(ID_CS_MOVE_LABEL
, newShape
, this)));
1094 IMPLEMENT_DYNAMIC_CLASS(csLabelShape
, wxLabelShape
)
1096 csLabelShape::csLabelShape(wxLineShape
*parent
, wxShapeRegion
*region
, double w
, double h
):
1097 wxLabelShape(parent
, region
, w
, h
)
1101 // TODO: not sure how intercept normal behaviour (OnMovePre) to make
1102 // label movement undo-able.
1103 void csLabelShape::OnEndDragLeft(double x
, double y
, int keys
, int attachment
)
1105 wxLabelShape::OnEndDragLeft(x
, y
, keys
, attachment
);
1109 // Menu for editing shapes
1110 void studioShapeEditProc(wxMenu
& menu
, wxCommandEvent
& event
)
1112 wxShape
* shape
= (wxShape
*) menu
.GetClientData();
1113 csDiagramView
* view
= ((csCanvas
*)shape
->GetCanvas())->GetView();
1115 switch (event
.GetId())
1117 case ID_CS_EDIT_PROPERTIES
:
1119 csEvtHandler
* handler1
= (csEvtHandler
*)shape
->GetEventHandler();
1120 handler1
->EditProperties();
1122 csEvtHandler
* handler1
= (csEvtHandler
*)shape
->GetEventHandler();
1123 csLabelEditingDialog
* dialog
= new csLabelEditingDialog(shape
->GetCanvas()->GetParent());
1124 dialog
->SetShapeLabel(handler1
->m_label
);
1125 if (dialog
->ShowModal() == wxID_CANCEL
)
1131 wxString newLabel
= dialog
->GetShapeLabel();
1134 wxShape
* newShape
= shape
->CreateNewCopy();
1136 csEvtHandler
* handler2
= (csEvtHandler
*)newShape
->GetEventHandler();
1137 handler2
->m_label
= newLabel
;
1139 view
->GetDocument()->GetCommandProcessor()->Submit(new csDiagramCommand("Edit label", (csDiagramDocument
*) view
->GetDocument(),
1140 new csCommandState(ID_CS_EDIT_LABEL
, newShape
, shape
)));
1151 case ID_CS_ROTATE_CLOCKWISE
:
1152 case ID_CS_ROTATE_ANTICLOCKWISE
:
1154 if (shape
->IsKindOf(CLASSINFO(wxLineShape
)))
1157 double theta
= shape
->GetRotation();
1158 const double myPi
= 3.1415926535897932384626433832795 ;
1159 double ninetyDegrees
= myPi
/2.0;
1162 if (event
.GetId() == ID_CS_ROTATE_CLOCKWISE
)
1164 theta
+= ninetyDegrees
;
1165 opStr
= _T("Rotate clockwise");
1169 theta
-= ninetyDegrees
;
1170 opStr
= _T("Rotate anticlockwise");
1173 if (theta
>= 2.0*myPi
|| theta
< 0.0)
1175 wxShape
* newShape
= shape
->CreateNewCopy();
1176 newShape
->Rotate(0.0, 0.0, theta
);
1179 newShapes
.Append(newShape
);
1180 oldShapes
.Append(shape
);
1181 view
->DoCmd(newShapes
, oldShapes
, event
.GetId(), opStr
);
1189 BEGIN_EVENT_TABLE(ShapeEditMenu
, wxMenu
)
1190 EVT_COMMAND_RANGE(1, 65000, wxEVT_COMMAND_MENU_SELECTED
, ShapeEditMenu::OnCommand
)
1193 void ShapeEditMenu::OnCommand(wxCommandEvent
& event
)
1195 studioShapeEditProc(*this, event
);