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