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