]> git.saurik.com Git - wxWidgets.git/blob - utils/ogl/src/ogldiag.cpp
More wxMotif work, OGL enhancements, USE_ macro corrections, object.cpp delete
[wxWidgets.git] / utils / ogl / src / ogldiag.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: ogldiag.cpp
3 // Purpose: wxDiagram
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 "ogldiag.h"
14 #endif
15
16 // For compilers that support precompilation, includes "wx.h".
17 #include <wx/wxprec.h>
18
19 #ifdef __BORLANDC__
20 #pragma hdrstop
21 #endif
22
23 #ifndef WX_PRECOMP
24 #include <wx/wx.h>
25 #endif
26
27 #ifdef PROLOGIO
28 #include <wx/wxexpr.h>
29 #endif
30
31 #if wxUSE_IOSTREAMH
32 #include <iostream.h>
33 #else
34 #include <iostream>
35 #endif
36
37 #include <fstream.h>
38 #include <ctype.h>
39 #include <math.h>
40 #include <stdlib.h>
41
42 #include "basic.h"
43 #include "basicp.h"
44 #include "canvas.h"
45 #include "ogldiag.h"
46 #include "lines.h"
47 #include "composit.h"
48 #include "misc.h"
49
50 IMPLEMENT_DYNAMIC_CLASS(wxDiagram, wxObject)
51
52 // Object canvas
53 wxDiagram::wxDiagram()
54 {
55 m_diagramCanvas = NULL;
56 m_quickEditMode = FALSE;
57 m_snapToGrid = TRUE;
58 m_gridSpacing = 5.0;
59 m_shapeList = new wxList;
60 m_mouseTolerance = DEFAULT_MOUSE_TOLERANCE;
61 }
62
63 wxDiagram::~wxDiagram()
64 {
65 if (m_shapeList)
66 delete m_shapeList;
67 }
68
69 void wxDiagram::SetSnapToGrid(bool snap)
70 {
71 m_snapToGrid = snap;
72 }
73
74 void wxDiagram::SetGridSpacing(double spacing)
75 {
76 m_gridSpacing = spacing;
77 }
78
79 void wxDiagram::Snap(double *x, double *y)
80 {
81 if (m_snapToGrid)
82 {
83 *x = m_gridSpacing * ((int)(*x/m_gridSpacing + 0.5));
84 *y = m_gridSpacing * ((int)(*y/m_gridSpacing + 0.5));
85 }
86 }
87
88
89 void wxDiagram::Redraw(wxDC& dc)
90 {
91 if (m_shapeList)
92 {
93 if (GetCanvas())
94 GetCanvas()->SetCursor(wxHOURGLASS_CURSOR);
95 wxNode *current = m_shapeList->First();
96
97 while (current)
98 {
99 wxShape *object = (wxShape *)current->Data();
100 if (!object->GetParent())
101 object->Draw(dc);
102
103 current = current->Next();
104 }
105 if (GetCanvas())
106 GetCanvas()->SetCursor(wxSTANDARD_CURSOR);
107 }
108 }
109
110 void wxDiagram::Clear(wxDC& dc)
111 {
112 dc.Clear();
113 }
114
115 // Insert object after addAfter, or at end of list.
116 void wxDiagram::AddShape(wxShape *object, wxShape *addAfter)
117 {
118 wxNode *nodeAfter = NULL;
119 if (addAfter)
120 nodeAfter = m_shapeList->Member(addAfter);
121
122 if (!m_shapeList->Member(object))
123 {
124 if (nodeAfter)
125 {
126 if (nodeAfter->Next())
127 m_shapeList->Insert(nodeAfter->Next(), object);
128 else
129 m_shapeList->Append(object);
130 }
131 else
132 m_shapeList->Append(object);
133 object->SetCanvas(GetCanvas());
134 }
135 }
136
137 void wxDiagram::InsertShape(wxShape *object)
138 {
139 m_shapeList->Insert(object);
140 object->SetCanvas(GetCanvas());
141 }
142
143 void wxDiagram::RemoveShape(wxShape *object)
144 {
145 m_shapeList->DeleteObject(object);
146 }
147
148 // Should this delete the actual objects too? I think not.
149 void wxDiagram::RemoveAllShapes()
150 {
151 m_shapeList->Clear();
152 }
153
154 void wxDiagram::DeleteAllShapes()
155 {
156 wxNode *node = m_shapeList->First();
157 while (node)
158 {
159 wxShape *shape = (wxShape *)node->Data();
160 if (!shape->GetParent())
161 {
162 RemoveShape(shape);
163 delete shape;
164 node = m_shapeList->First();
165 }
166 else
167 node = node->Next();
168 }
169 }
170
171 void wxDiagram::ShowAll(bool show)
172 {
173 wxNode *current = m_shapeList->First();
174
175 while (current)
176 {
177 wxShape *object = (wxShape *)current->Data();
178 object->Show(show);
179
180 current = current->Next();
181 }
182 }
183
184 void wxDiagram::DrawOutline(wxDC& dc, double x1, double y1, double x2, double y2)
185 {
186 wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
187 dc.SetPen(dottedPen);
188 dc.SetBrush((* wxTRANSPARENT_BRUSH));
189
190 wxPoint points[5];
191
192 points[0].x = x1;
193 points[0].y = y1;
194
195 points[1].x = x2;
196 points[1].y = y1;
197
198 points[2].x = x2;
199 points[2].y = y2;
200
201 points[3].x = x1;
202 points[3].y = y2;
203
204 points[4].x = x1;
205 points[4].y = y1;
206 dc.DrawLines(5, points);
207 }
208
209 // Make sure all text that should be centred, is centred.
210 void wxDiagram::RecentreAll(wxDC& dc)
211 {
212 wxNode *object_node = m_shapeList->First();
213 while (object_node)
214 {
215 wxShape *obj = (wxShape *)object_node->Data();
216 obj->Recentre(dc);
217 object_node = object_node->Next();
218 }
219 }
220
221 // Input/output
222 #ifdef PROLOGIO
223 bool wxDiagram::SaveFile(const wxString& filename)
224 {
225 wxBeginBusyCursor();
226
227 wxExprDatabase *database = new wxExprDatabase;
228
229 // First write the diagram type
230 wxExpr *header = new wxExpr("diagram");
231 OnHeaderSave(*database, *header);
232
233 database->Append(header);
234
235 wxNode *node = m_shapeList->First();
236 while (node)
237 {
238 wxShape *shape = (wxShape *)node->Data();
239
240 if (!shape->IsKindOf(CLASSINFO(wxControlPoint)))
241 {
242 wxExpr *expr = NULL;
243 if (shape->IsKindOf(CLASSINFO(wxLineShape)))
244 expr = new wxExpr("line");
245 else
246 expr = new wxExpr("shape");
247
248 OnShapeSave(*database, *shape, *expr);
249 }
250 node = node->Next();
251 }
252 OnDatabaseSave(*database);
253
254 char tempFile[400];
255 wxGetTempFileName("diag", tempFile);
256 ofstream stream(tempFile);
257 if (stream.bad())
258 {
259 wxEndBusyCursor();
260 delete database;
261 return FALSE;
262 }
263
264 database->Write(stream);
265 stream.close();
266 delete database;
267
268 /*
269 // Save backup
270 if (FileExists(filename))
271 {
272 char buf[400];
273 #ifdef __X__
274 sprintf(buf, "%s.bak", filename);
275 #endif
276 #ifdef __WXMSW__
277 sprintf(buf, "_diagram.bak");
278 #endif
279 if (FileExists(buf)) wxRemoveFile(buf);
280 if (!wxRenameFile(filename, buf))
281 {
282 wxCopyFile(filename, buf);
283 wxRemoveFile(filename);
284 }
285 }
286 */
287
288 // Copy the temporary file to the correct filename
289 if (!wxRenameFile(tempFile, filename))
290 {
291 wxCopyFile(tempFile, filename);
292 wxRemoveFile(tempFile);
293 }
294
295 wxEndBusyCursor();
296 return TRUE;
297 }
298
299 bool wxDiagram::LoadFile(const wxString& filename)
300 {
301 wxBeginBusyCursor();
302
303 wxExprDatabase database(wxExprInteger, "id");
304 if (!database.Read(filename))
305 {
306 wxEndBusyCursor();
307 return FALSE;
308 }
309
310 DeleteAllShapes();
311
312 database.BeginFind();
313 wxExpr *header = database.FindClauseByFunctor("diagram");
314
315 if (header)
316 OnHeaderLoad(database, *header);
317
318 // Scan through all clauses and register the ids
319 wxNode *node = database.First();
320 while (node)
321 {
322 wxExpr *clause = (wxExpr *)node->Data();
323 long id = -1;
324 clause->GetAttributeValue("id", id);
325 RegisterId(id);
326 node = node->Next();
327 }
328
329 ReadNodes(database);
330 ReadContainerGeometry(database);
331 ReadLines(database);
332
333 OnDatabaseLoad(database);
334
335 wxEndBusyCursor();
336
337 return TRUE;
338 }
339
340 void wxDiagram::ReadNodes(wxExprDatabase& database)
341 {
342 // Find and create the node images
343 database.BeginFind();
344 wxExpr *clause = database.FindClauseByFunctor("shape");
345 while (clause)
346 {
347 char *type = NULL;
348 long parentId = -1;
349
350 clause->AssignAttributeValue("type", &type);
351 clause->AssignAttributeValue("parent", &parentId);
352 wxClassInfo *classInfo = wxClassInfo::FindClass(type);
353 if (classInfo)
354 {
355 wxShape *shape = (wxShape *)classInfo->CreateObject();
356 OnShapeLoad(database, *shape, *clause);
357
358 shape->SetCanvas(GetCanvas());
359 shape->Show(TRUE);
360
361 m_shapeList->Append(shape);
362
363 // If child of composite, link up
364 if (parentId > -1)
365 {
366 wxExpr *parentExpr = database.HashFind("shape", parentId);
367 if (parentExpr && parentExpr->GetClientData())
368 {
369 wxShape *parent = (wxShape *)parentExpr->GetClientData();
370 shape->SetParent(parent);
371 parent->GetChildren().Append(shape);
372 }
373 }
374
375 clause->SetClientData(shape);
376 }
377 if (type)
378 delete[] type;
379
380 clause = database.FindClauseByFunctor("shape");
381 }
382 return;
383 }
384
385 void wxDiagram::ReadLines(wxExprDatabase& database)
386 {
387 database.BeginFind();
388 wxExpr *clause = database.FindClauseByFunctor("line");
389 while (clause)
390 {
391 wxString type("");
392 long parentId = -1;
393
394 clause->GetAttributeValue("type", type);
395 clause->GetAttributeValue("parent", parentId);
396 wxClassInfo *classInfo = wxClassInfo::FindClass((char*) (const char*) type);
397 if (classInfo)
398 {
399 wxLineShape *shape = (wxLineShape *)classInfo->CreateObject();
400 shape->Show(TRUE);
401
402 OnShapeLoad(database, *shape, *clause);
403 shape->SetCanvas(GetCanvas());
404
405 long image_to = -1; long image_from = -1;
406 clause->GetAttributeValue("to", image_to);
407 clause->GetAttributeValue("from", image_from);
408
409 wxExpr *image_to_expr = database.HashFind("shape", image_to);
410
411 if (!image_to_expr)
412 {
413 // Error
414 }
415 wxExpr *image_from_expr = database.HashFind("shape", image_from);
416
417 if (!image_from_expr)
418 {
419 // Error
420 }
421
422 if (image_to_expr && image_from_expr)
423 {
424 wxShape *image_to_object = (wxShape *)image_to_expr->GetClientData();
425 wxShape *image_from_object = (wxShape *)image_from_expr->GetClientData();
426
427 if (image_to_object && image_from_object)
428 {
429 image_from_object->AddLine(shape, image_to_object, shape->GetAttachmentFrom(), shape->GetAttachmentTo());
430 }
431 }
432 clause->SetClientData(shape);
433
434 m_shapeList->Append(shape);
435 }
436
437 clause = database.FindClauseByFunctor("line");
438 }
439 }
440
441 // Containers have divisions that reference adjoining divisions,
442 // so we need a separate pass to link everything up.
443 // Also used by Symbol Library.
444 void wxDiagram::ReadContainerGeometry(wxExprDatabase& database)
445 {
446 database.BeginFind();
447 wxExpr *clause = database.FindClauseByFunctor("shape");
448 while (clause)
449 {
450 wxShape *image = (wxShape *)clause->GetClientData();
451 if (image && image->IsKindOf(CLASSINFO(wxCompositeShape)))
452 {
453 wxCompositeShape *composite = (wxCompositeShape *)image;
454 wxExpr *divisionExpr = NULL;
455
456 // Find the list of divisions in the composite
457 clause->GetAttributeValue("divisions", &divisionExpr);
458 if (divisionExpr)
459 {
460 int i = 0;
461 wxExpr *idExpr = divisionExpr->Nth(i);
462 while (idExpr)
463 {
464 long divisionId = idExpr->IntegerValue();
465 wxExpr *childExpr = database.HashFind("shape", divisionId);
466 if (childExpr && childExpr->GetClientData())
467 {
468 wxDivisionShape *child = (wxDivisionShape *)childExpr->GetClientData();
469 composite->GetDivisions().Append(child);
470
471 // Find the adjoining shapes
472 long leftSideId = -1;
473 long topSideId = -1;
474 long rightSideId = -1;
475 long bottomSideId = -1;
476 childExpr->GetAttributeValue("left_side", leftSideId);
477 childExpr->GetAttributeValue("top_side", topSideId);
478 childExpr->GetAttributeValue("right_side", rightSideId);
479 childExpr->GetAttributeValue("bottom_side", bottomSideId);
480 if (leftSideId > -1)
481 {
482 wxExpr *leftExpr = database.HashFind("shape", leftSideId);
483 if (leftExpr && leftExpr->GetClientData())
484 {
485 wxDivisionShape *leftSide = (wxDivisionShape *)leftExpr->GetClientData();
486 child->SetLeftSide(leftSide);
487 }
488 }
489 if (topSideId > -1)
490 {
491 wxExpr *topExpr = database.HashFind("shape", topSideId);
492 if (topExpr && topExpr->GetClientData())
493 {
494 wxDivisionShape *topSide = (wxDivisionShape *)topExpr->GetClientData();
495 child->SetTopSide(topSide);
496 }
497 }
498 if (rightSideId > -1)
499 {
500 wxExpr *rightExpr = database.HashFind("shape", rightSideId);
501 if (rightExpr && rightExpr->GetClientData())
502 {
503 wxDivisionShape *rightSide = (wxDivisionShape *)rightExpr->GetClientData();
504 child->SetRightSide(rightSide);
505 }
506 }
507 if (bottomSideId > -1)
508 {
509 wxExpr *bottomExpr = database.HashFind("shape", bottomSideId);
510 if (bottomExpr && bottomExpr->GetClientData())
511 {
512 wxDivisionShape *bottomSide = (wxDivisionShape *)bottomExpr->GetClientData();
513 child->SetBottomSide(bottomSide);
514 }
515 }
516 }
517 i ++;
518 idExpr = divisionExpr->Nth(i);
519 }
520 }
521 }
522
523 clause = database.FindClauseByFunctor("shape");
524 }
525 }
526
527 // Allow for modifying file
528 bool wxDiagram::OnDatabaseLoad(wxExprDatabase& db)
529 {
530 return TRUE;
531 }
532
533 bool wxDiagram::OnDatabaseSave(wxExprDatabase& db)
534 {
535 return TRUE;
536 }
537
538 bool wxDiagram::OnShapeSave(wxExprDatabase& db, wxShape& shape, wxExpr& expr)
539 {
540 shape.WriteAttributes(&expr);
541 db.Append(&expr);
542
543 if (shape.IsKindOf(CLASSINFO(wxCompositeShape)))
544 {
545 wxNode *node = shape.GetChildren().First();
546 while (node)
547 {
548 wxShape *childShape = (wxShape *)node->Data();
549 wxExpr *childExpr = new wxExpr("shape");
550 OnShapeSave(db, *childShape, *childExpr);
551 node = node->Next();
552 }
553 }
554
555 return TRUE;
556 }
557
558 bool wxDiagram::OnShapeLoad(wxExprDatabase& db, wxShape& shape, wxExpr& expr)
559 {
560 shape.ReadAttributes(&expr);
561 return TRUE;
562 }
563
564 bool wxDiagram::OnHeaderSave(wxExprDatabase& db, wxExpr& expr)
565 {
566 return TRUE;
567 }
568
569 bool wxDiagram::OnHeaderLoad(wxExprDatabase& db, wxExpr& expr)
570 {
571 return TRUE;
572 }
573
574 #endif
575
576 void wxDiagram::SetCanvas(wxShapeCanvas *can)
577 {
578 m_diagramCanvas = can;
579 }
580
581 // Find a shape by its id
582 wxShape* wxDiagram::FindShape(long id) const
583 {
584 wxNode* node = GetShapeList()->First();
585 while (node)
586 {
587 wxShape* shape = (wxShape*) node->Data();
588 if (shape->GetId() == id)
589 return shape;
590 node = node->Next();
591 }
592 return NULL;
593 }
594
595
596 //// Crossings classes
597
598 wxLineCrossings::wxLineCrossings()
599 {
600 }
601
602 wxLineCrossings::~wxLineCrossings()
603 {
604 ClearCrossings();
605 }
606
607 void wxLineCrossings::FindCrossings(wxDiagram& diagram)
608 {
609 ClearCrossings();
610 wxNode* node1 = diagram.GetShapeList()->First();
611 while (node1)
612 {
613 wxShape* shape1 = (wxShape*) node1->Data();
614 if (shape1->IsKindOf(CLASSINFO(wxLineShape)))
615 {
616 wxLineShape* lineShape1 = (wxLineShape*) shape1;
617 // Iterate through the segments
618 wxList* pts1 = lineShape1->GetLineControlPoints();
619 int i;
620 for (i = 0; i < (pts1->Number() - 1); i++)
621 {
622 wxRealPoint* pt1_a = (wxRealPoint*) (pts1->Nth(i)->Data());
623 wxRealPoint* pt1_b = (wxRealPoint*) (pts1->Nth(i+1)->Data());
624
625 // Now we iterate through the segments again
626
627 wxNode* node2 = diagram.GetShapeList()->First();
628 while (node2)
629 {
630 wxShape* shape2 = (wxShape*) node2->Data();
631
632 // Assume that the same line doesn't cross itself
633 if (shape2->IsKindOf(CLASSINFO(wxLineShape)) && (shape1 != shape2))
634 {
635 wxLineShape* lineShape2 = (wxLineShape*) shape2;
636 // Iterate through the segments
637 wxList* pts2 = lineShape2->GetLineControlPoints();
638 int j;
639 for (j = 0; j < (pts2->Number() - 1); j++)
640 {
641 wxRealPoint* pt2_a = (wxRealPoint*) (pts2->Nth(j)->Data());
642 wxRealPoint* pt2_b = (wxRealPoint*) (pts2->Nth(j+1)->Data());
643
644 // Now let's see if these two segments cross.
645 double ratio1, ratio2;
646 oglCheckLineIntersection(pt1_a->x, pt1_a->y, pt1_b->x, pt1_b->y,
647 pt2_a->x, pt2_a->y, pt2_b->x, pt2_b->y,
648 & ratio1, & ratio2);
649
650 if ((ratio1 < 1.0) && (ratio1 > -1.0))
651 {
652 // Intersection!
653 wxLineCrossing* crossing = new wxLineCrossing;
654 crossing->m_intersect.x = (pt1_a->x + (pt1_b->x - pt1_a->x)*ratio1);
655 crossing->m_intersect.y = (pt1_a->y + (pt1_b->y - pt1_a->y)*ratio1);
656
657 crossing->m_pt1 = * pt1_a;
658 crossing->m_pt2 = * pt1_b;
659 crossing->m_pt3 = * pt2_a;
660 crossing->m_pt4 = * pt2_b;
661
662 crossing->m_lineShape1 = lineShape1;
663 crossing->m_lineShape2 = lineShape2;
664
665 m_crossings.Append(crossing);
666 }
667 }
668 }
669 node2 = node2->Next();
670 }
671 }
672 }
673
674 node1 = node1->Next();
675 }
676 }
677
678 void wxLineCrossings::DrawCrossings(wxDiagram& diagram, wxDC& dc)
679 {
680 dc.SetBrush(*wxTRANSPARENT_BRUSH);
681
682 long arcWidth = 8;
683
684 wxNode* node = m_crossings.First();
685 while (node)
686 {
687 wxLineCrossing* crossing = (wxLineCrossing*) node->Data();
688 // dc.DrawEllipse((long) (crossing->m_intersect.x - (arcWidth/2.0) + 0.5), (long) (crossing->m_intersect.y - (arcWidth/2.0) + 0.5),
689 // arcWidth, arcWidth);
690
691
692 // Let's do some geometry to find the points on either end of the arc.
693 /*
694
695 (x1, y1)
696 |\
697 | \
698 | \
699 | \
700 | \
701 | |\ c c1
702 | a | \
703 | \
704 | - x <-- centre of arc
705 a1 | b |\
706 | | \ c2
707 | a2 | \
708 | - \
709 | b2 \
710 | \
711 |_______________\ (x2, y2)
712 b1
713
714 */
715
716 double a1 = wxMax(crossing->m_pt1.y, crossing->m_pt2.y) - wxMin(crossing->m_pt1.y, crossing->m_pt2.y) ;
717 double b1 = wxMax(crossing->m_pt1.x, crossing->m_pt2.x) - wxMin(crossing->m_pt1.x, crossing->m_pt2.x) ;
718 double c1 = sqrt( (a1*a1) + (b1*b1) );
719
720 double c = arcWidth / 2.0;
721 double a = c * a1/c1 ;
722 double b = c * b1/c1 ;
723
724 // I'm not sure this is right, since we don't know which direction we should be going in - need
725 // to know which way the line slopes and choose the sign appropriately.
726 double arcX1 = crossing->m_intersect.x - b;
727 double arcY1 = crossing->m_intersect.y - a;
728
729 double arcX2 = crossing->m_intersect.x + b;
730 double arcY2 = crossing->m_intersect.y + a;
731
732 dc.SetPen(*wxBLACK_PEN);
733 dc.DrawArc( (long) arcX1, (long) arcY1, (long) arcX2, (long) arcY2,
734 (long) crossing->m_intersect.x, (long) crossing->m_intersect.y);
735
736 dc.SetPen(*wxWHITE_PEN);
737 dc.DrawLine( (long) arcX1, (long) arcY1, (long) arcX2, (long) arcY2 );
738
739 node = node->Next();
740 }
741 }
742
743 void wxLineCrossings::ClearCrossings()
744 {
745 wxNode* node = m_crossings.First();
746 while (node)
747 {
748 wxLineCrossing* crossing = (wxLineCrossing*) node->Data();
749 delete crossing;
750 node = node->Next();
751 }
752 m_crossings.Clear();
753 }
754