1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        lines.cpp
3 // Purpose:     wxLineShape
4 // Author:      Julian Smart
5 // Modified by:
6 // Created:     12/07/98
7 // RCS-ID:      $Id: lines.cpp 38810 2006-04-18 22:26:26Z PC $
8 // Copyright:   (c) Julian Smart
9 // Licence:     wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11 
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
14 
15 #ifdef __BORLANDC__
16 #pragma hdrstop
17 #endif
18 
19 #ifndef WX_PRECOMP
20 #include "wx/wx.h"
21 #endif
22 
23 #if wxUSE_PROLOGIO
24 #include "wx/deprecated/wxexpr.h"
25 #endif
26 
27 #ifdef new
28 #undef new
29 #endif
30 
31 #include <ctype.h>
32 
33 #include "wx/ogl/ogl.h"
34 
35 
36 // Line shape
IMPLEMENT_DYNAMIC_CLASS(wxLineShape,wxShape)37 IMPLEMENT_DYNAMIC_CLASS(wxLineShape, wxShape)
38 
39 wxLineShape::wxLineShape()
40 {
41   m_sensitivity = OP_CLICK_LEFT | OP_CLICK_RIGHT;
42   m_draggable = false;
43   m_attachmentTo = 0;
44   m_attachmentFrom = 0;
45 /*
46   m_actualTextWidth = 0.0;
47   m_actualTextHeight = 0.0;
48 */
49   m_from = NULL;
50   m_to = NULL;
51   m_erasing = false;
52   m_arrowSpacing = 5.0; // For the moment, don't bother saving this to file.
53   m_ignoreArrowOffsets = false;
54   m_isSpline = false;
55   m_maintainStraightLines = false;
56   m_alignmentStart = 0;
57   m_alignmentEnd = 0;
58 
59   m_lineControlPoints = NULL;
60 
61   // Clear any existing regions (created in an earlier constructor)
62   // and make the three line regions.
63   ClearRegions();
64   wxShapeRegion *newRegion = new wxShapeRegion;
65   newRegion->SetName(wxT("Middle"));
66   newRegion->SetSize(150, 50);
67   m_regions.Append((wxObject *)newRegion);
68 
69   newRegion = new wxShapeRegion;
70   newRegion->SetName(wxT("Start"));
71   newRegion->SetSize(150, 50);
72   m_regions.Append((wxObject *)newRegion);
73 
74   newRegion = new wxShapeRegion;
75   newRegion->SetName(wxT("End"));
76   newRegion->SetSize(150, 50);
77   m_regions.Append((wxObject *)newRegion);
78 
79   for (int i = 0; i < 3; i++)
80     m_labelObjects[i] = NULL;
81 }
82 
~wxLineShape()83 wxLineShape::~wxLineShape()
84 {
85   if (m_lineControlPoints)
86   {
87     ClearPointList(*m_lineControlPoints);
88     delete m_lineControlPoints;
89   }
90   for (int i = 0; i < 3; i++)
91   {
92     if (m_labelObjects[i])
93     {
94       m_labelObjects[i]->Select(false);
95       m_labelObjects[i]->RemoveFromCanvas(m_canvas);
96       delete m_labelObjects[i];
97       m_labelObjects[i] = NULL;
98     }
99   }
100   ClearArrowsAtPosition(-1);
101 }
102 
MakeLineControlPoints(int n)103 void wxLineShape::MakeLineControlPoints(int n)
104 {
105   if (m_lineControlPoints)
106   {
107     ClearPointList(*m_lineControlPoints);
108     delete m_lineControlPoints;
109   }
110   m_lineControlPoints = new wxList;
111 
112   for (int i = 0; i < n; i++)
113   {
114     wxRealPoint *point = new wxRealPoint(-999, -999);
115     m_lineControlPoints->Append((wxObject*) point);
116   }
117 }
118 
InsertLineControlPoint(wxDC * dc)119 wxNode *wxLineShape::InsertLineControlPoint(wxDC* dc)
120 {
121     if (dc)
122         Erase(*dc);
123 
124   wxNode *last = m_lineControlPoints->GetLast();
125   wxNode *second_last = last->GetPrevious();
126   wxRealPoint *last_point = (wxRealPoint *)last->GetData();
127   wxRealPoint *second_last_point = (wxRealPoint *)second_last->GetData();
128 
129   // Choose a point half way between the last and penultimate points
130   double line_x = ((last_point->x + second_last_point->x)/2);
131   double line_y = ((last_point->y + second_last_point->y)/2);
132 
133   wxRealPoint *point = new wxRealPoint(line_x, line_y);
134   wxNode *node = m_lineControlPoints->Insert(last, (wxObject*) point);
135   return node;
136 }
137 
DeleteLineControlPoint()138 bool wxLineShape::DeleteLineControlPoint()
139 {
140   if (m_lineControlPoints->GetCount() < 3)
141     return false;
142 
143   wxNode *last = m_lineControlPoints->GetLast();
144   wxNode *second_last = last->GetPrevious();
145 
146   wxRealPoint *second_last_point = (wxRealPoint *)second_last->GetData();
147   delete second_last_point;
148   delete second_last;
149 
150   return true;
151 }
152 
Initialise()153 void wxLineShape::Initialise()
154 {
155   if (m_lineControlPoints)
156   {
157     // Just move the first and last control points
158     wxNode *first = m_lineControlPoints->GetFirst();
159     wxRealPoint *first_point = (wxRealPoint *)first->GetData();
160 
161     wxNode *last = m_lineControlPoints->GetLast();
162     wxRealPoint *last_point = (wxRealPoint *)last->GetData();
163 
164     // If any of the line points are at -999, we must
165     // initialize them by placing them half way between the first
166     // and the last.
167     wxNode *node = first->GetNext();
168     while (node)
169     {
170       wxRealPoint *point = (wxRealPoint *)node->GetData();
171       if (point->x == -999)
172       {
173         double x1, y1, x2, y2;
174         if (first_point->x < last_point->x)
175           { x1 = first_point->x; x2 = last_point->x; }
176         else
177           { x2 = first_point->x; x1 = last_point->x; }
178 
179         if (first_point->y < last_point->y)
180           { y1 = first_point->y; y2 = last_point->y; }
181         else
182           { y2 = first_point->y; y1 = last_point->y; }
183 
184         point->x = ((x2 - x1)/2 + x1);
185         point->y = ((y2 - y1)/2 + y1);
186       }
187       node = node->GetNext();
188     }
189   }
190 }
191 
192 // Format a text string according to the region size, adding
193 // strings with positions to region text list
FormatText(wxDC & dc,const wxString & s,int i)194 void wxLineShape::FormatText(wxDC& dc, const wxString& s, int i)
195 {
196   double w, h;
197   ClearText(i);
198 
199   if (m_regions.GetCount() < 1)
200     return;
201   wxNode *node = m_regions.Item(i);
202   if (!node)
203     return;
204 
205   wxShapeRegion *region = (wxShapeRegion *)node->GetData();
206   region->SetText(s);
207   dc.SetFont(* region->GetFont());
208 
209   region->GetSize(&w, &h);
210   // Initialize the size if zero
211   if (((w == 0) || (h == 0)) && (s.Length() > 0))
212   {
213     w = 100; h = 50;
214     region->SetSize(w, h);
215   }
216 
217   wxStringList *string_list = oglFormatText(dc, s, (w-5), (h-5), region->GetFormatMode());
218   node = (wxNode*)string_list->GetFirst();
219   while (node)
220   {
221     wxChar *s = (wxChar *)node->GetData();
222     wxShapeTextLine *line = new wxShapeTextLine(0.0, 0.0, s);
223     region->GetFormattedText().Append((wxObject *)line);
224     node = node->GetNext();
225   }
226   delete string_list;
227   double actualW = w;
228   double actualH = h;
229   if (region->GetFormatMode() & FORMAT_SIZE_TO_CONTENTS)
230   {
231     oglGetCentredTextExtent(dc, &(region->GetFormattedText()), m_xpos, m_ypos, w, h, &actualW, &actualH);
232     if ((actualW != w ) || (actualH != h))
233     {
234       double xx, yy;
235       GetLabelPosition(i, &xx, &yy);
236       EraseRegion(dc, region, xx, yy);
237       if (m_labelObjects[i])
238       {
239         m_labelObjects[i]->Select(false, &dc);
240         m_labelObjects[i]->Erase(dc);
241         m_labelObjects[i]->SetSize(actualW, actualH);
242       }
243 
244       region->SetSize(actualW, actualH);
245 
246       if (m_labelObjects[i])
247       {
248         m_labelObjects[i]->Select(true, & dc);
249         m_labelObjects[i]->Draw(dc);
250       }
251     }
252   }
253   oglCentreText(dc, &(region->GetFormattedText()), m_xpos, m_ypos, actualW, actualH, region->GetFormatMode());
254   m_formatted = true;
255 }
256 
DrawRegion(wxDC & dc,wxShapeRegion * region,double x,double y)257 void wxLineShape::DrawRegion(wxDC& dc, wxShapeRegion *region, double x, double y)
258 {
259   if (GetDisableLabel())
260     return;
261 
262   double w, h;
263   double xx, yy;
264   region->GetSize(&w, &h);
265 
266   // Get offset from x, y
267   region->GetPosition(&xx, &yy);
268 
269   double xp = xx + x;
270   double yp = yy + y;
271 
272   // First, clear a rectangle for the text IF there is any
273   if (region->GetFormattedText().GetCount() > 0)
274   {
275       dc.SetPen(GetBackgroundPen());
276       dc.SetBrush(GetBackgroundBrush());
277 
278       // Now draw the text
279       if (region->GetFont()) dc.SetFont(* region->GetFont());
280 
281       dc.DrawRectangle((long)(xp - w/2.0), (long)(yp - h/2.0), (long)w, (long)h);
282 
283       if (m_pen) dc.SetPen(* m_pen);
284       dc.SetTextForeground(region->GetActualColourObject());
285 
286 #ifdef __WXMSW__
287       dc.SetTextBackground(GetBackgroundBrush().GetColour());
288 #endif
289 
290       oglDrawFormattedText(dc, &(region->GetFormattedText()), xp, yp, w, h, region->GetFormatMode());
291   }
292 }
293 
EraseRegion(wxDC & dc,wxShapeRegion * region,double x,double y)294 void wxLineShape::EraseRegion(wxDC& dc, wxShapeRegion *region, double x, double y)
295 {
296   if (GetDisableLabel())
297     return;
298 
299   double w, h;
300   double xx, yy;
301   region->GetSize(&w, &h);
302 
303   // Get offset from x, y
304   region->GetPosition(&xx, &yy);
305 
306   double xp = xx + x;
307   double yp = yy + y;
308 
309   if (region->GetFormattedText().GetCount() > 0)
310   {
311       dc.SetPen(GetBackgroundPen());
312       dc.SetBrush(GetBackgroundBrush());
313 
314       dc.DrawRectangle((long)(xp - w/2.0), (long)(yp - h/2.0), (long)w, (long)h);
315   }
316 }
317 
318 // Get the reference point for a label. Region x and y
319 // are offsets from this.
320 // position is 0, 1, 2
GetLabelPosition(int position,double * x,double * y)321 void wxLineShape::GetLabelPosition(int position, double *x, double *y)
322 {
323   switch (position)
324   {
325     case 0:
326     {
327       // Want to take the middle section for the label
328       int n = m_lineControlPoints->GetCount();
329       int half_way = (int)(n/2);
330 
331       // Find middle of this line
332       wxNode *node = m_lineControlPoints->Item(half_way - 1);
333       wxRealPoint *point = (wxRealPoint *)node->GetData();
334       wxNode *next_node = node->GetNext();
335       wxRealPoint *next_point = (wxRealPoint *)next_node->GetData();
336 
337       double dx = (next_point->x - point->x);
338       double dy = (next_point->y - point->y);
339       *x = (double)(point->x + dx/2.0);
340       *y = (double)(point->y + dy/2.0);
341       break;
342     }
343     case 1:
344     {
345       wxNode *node = m_lineControlPoints->GetFirst();
346       *x = ((wxRealPoint *)node->GetData())->x;
347       *y = ((wxRealPoint *)node->GetData())->y;
348       break;
349     }
350     case 2:
351     {
352       wxNode *node = m_lineControlPoints->GetLast();
353       *x = ((wxRealPoint *)node->GetData())->x;
354       *y = ((wxRealPoint *)node->GetData())->y;
355       break;
356     }
357     default:
358       break;
359   }
360 }
361 
362 /*
363  * Find whether line is supposed to be vertical or horizontal and
364  * make it so.
365  *
366  */
GraphicsStraightenLine(wxRealPoint * point1,wxRealPoint * point2)367 void GraphicsStraightenLine(wxRealPoint *point1, wxRealPoint *point2)
368 {
369   double dx = point2->x - point1->x;
370   double dy = point2->y - point1->y;
371 
372   if (dx == 0.0)
373     return;
374   else if (fabs(dy/dx) > 1.0)
375   {
376     point2->x = point1->x;
377   }
378   else point2->y = point1->y;
379 }
380 
Straighten(wxDC * dc)381 void wxLineShape::Straighten(wxDC *dc)
382 {
383   if (!m_lineControlPoints || m_lineControlPoints->GetCount() < 3)
384     return;
385 
386   if (dc)
387     Erase(* dc);
388 
389   wxNode *first_point_node = m_lineControlPoints->GetFirst();
390   wxNode *last_point_node = m_lineControlPoints->GetLast();
391   wxNode *second_last_point_node = last_point_node->GetPrevious();
392 
393   wxRealPoint *last_point = (wxRealPoint *)last_point_node->GetData();
394   wxRealPoint *second_last_point = (wxRealPoint *)second_last_point_node->GetData();
395 
396   GraphicsStraightenLine(last_point, second_last_point);
397 
398   wxNode *node = first_point_node;
399   while (node && (node != second_last_point_node))
400   {
401     wxRealPoint *point = (wxRealPoint *)node->GetData();
402     wxRealPoint *next_point = (wxRealPoint *)(node->GetNext()->GetData());
403 
404     GraphicsStraightenLine(point, next_point);
405     node = node->GetNext();
406   }
407 
408   if (dc)
409     Draw(* dc);
410 }
411 
412 
Unlink()413 void wxLineShape::Unlink()
414 {
415   if (m_to)
416     m_to->GetLines().DeleteObject(this);
417   if (m_from)
418     m_from->GetLines().DeleteObject(this);
419   m_to = NULL;
420   m_from = NULL;
421 }
422 
SetEnds(double x1,double y1,double x2,double y2)423 void wxLineShape::SetEnds(double x1, double y1, double x2, double y2)
424 {
425   // Find centre point
426   wxNode *first_point_node = m_lineControlPoints->GetFirst();
427   wxNode *last_point_node = m_lineControlPoints->GetLast();
428   wxRealPoint *first_point = (wxRealPoint *)first_point_node->GetData();
429   wxRealPoint *last_point = (wxRealPoint *)last_point_node->GetData();
430 
431   first_point->x = x1;
432   first_point->y = y1;
433   last_point->x = x2;
434   last_point->y = y2;
435 
436   m_xpos = (double)((x1 + x2)/2.0);
437   m_ypos = (double)((y1 + y2)/2.0);
438 }
439 
440 // Get absolute positions of ends
GetEnds(double * x1,double * y1,double * x2,double * y2)441 void wxLineShape::GetEnds(double *x1, double *y1, double *x2, double *y2)
442 {
443   wxNode *first_point_node = m_lineControlPoints->GetFirst();
444   wxNode *last_point_node = m_lineControlPoints->GetLast();
445   wxRealPoint *first_point = (wxRealPoint *)first_point_node->GetData();
446   wxRealPoint *last_point = (wxRealPoint *)last_point_node->GetData();
447 
448   *x1 = first_point->x; *y1 = first_point->y;
449   *x2 = last_point->x; *y2 = last_point->y;
450 }
451 
SetAttachments(int from_attach,int to_attach)452 void wxLineShape::SetAttachments(int from_attach, int to_attach)
453 {
454   m_attachmentFrom = from_attach;
455   m_attachmentTo = to_attach;
456 }
457 
HitTest(double x,double y,int * attachment,double * distance)458 bool wxLineShape::HitTest(double x, double y, int *attachment, double *distance)
459 {
460   if (!m_lineControlPoints)
461     return false;
462 
463   // Look at label regions in case mouse is over a label
464   bool inLabelRegion = false;
465   for (int i = 0; i < 3; i ++)
466   {
467     wxNode *regionNode = m_regions.Item(i);
468     if (regionNode)
469     {
470       wxShapeRegion *region = (wxShapeRegion *)regionNode->GetData();
471       if (region->m_formattedText.GetCount() > 0)
472       {
473         double xp, yp, cx, cy, cw, ch;
474         GetLabelPosition(i, &xp, &yp);
475         // Offset region from default label position
476         region->GetPosition(&cx, &cy);
477         region->GetSize(&cw, &ch);
478         cx += xp;
479         cy += yp;
480         double rLeft = (double)(cx - (cw/2.0));
481         double rTop = (double)(cy - (ch/2.0));
482         double rRight = (double)(cx + (cw/2.0));
483         double rBottom = (double)(cy + (ch/2.0));
484         if (x > rLeft && x < rRight && y > rTop && y < rBottom)
485         {
486           inLabelRegion = true;
487           i = 3;
488         }
489       }
490     }
491   }
492 
493   wxNode *node = m_lineControlPoints->GetFirst();
494 
495   while (node && node->GetNext())
496   {
497     wxRealPoint *point1 = (wxRealPoint *)node->GetData();
498     wxRealPoint *point2 = (wxRealPoint *)node->GetNext()->GetData();
499 
500     // For inaccurate mousing allow 8 pixel corridor
501     int extra = 4;
502 
503     double dx = point2->x - point1->x;
504     double dy = point2->y - point1->y;
505     double seg_len = sqrt(dx*dx+dy*dy);
506     double distance_from_seg =
507       seg_len*((x-point1->x)*dy-(y-point1->y)*dx)/(dy*dy+dx*dx);
508     double distance_from_prev =
509       seg_len*((y-point1->y)*dy+(x-point1->x)*dx)/(dy*dy+dx*dx);
510 
511     if ((fabs(distance_from_seg) < extra &&
512          distance_from_prev >= 0 && distance_from_prev <= seg_len)
513         || inLabelRegion)
514     {
515       *attachment = 0;
516       *distance = distance_from_seg;
517       return true;
518     }
519 
520     node = node->GetNext();
521   }
522   return false;
523 }
524 
DrawArrows(wxDC & dc)525 void wxLineShape::DrawArrows(wxDC& dc)
526 {
527   // Distance along line of each arrow: space them out evenly.
528   double startArrowPos = 0.0;
529   double endArrowPos = 0.0;
530   double middleArrowPos = 0.0;
531 
532   wxNode *node = m_arcArrows.GetFirst();
533   while (node)
534   {
535     wxArrowHead *arrow = (wxArrowHead *)node->GetData();
536     switch (arrow->GetArrowEnd())
537     {
538       case ARROW_POSITION_START:
539       {
540         if ((arrow->GetXOffset() != 0.0) && !m_ignoreArrowOffsets)
541           // If specified, x offset is proportional to line length
542           DrawArrow(dc, arrow, arrow->GetXOffset(), true);
543         else
544         {
545           DrawArrow(dc, arrow, startArrowPos, false);      // Absolute distance
546           startArrowPos += arrow->GetSize() + arrow->GetSpacing();
547         }
548         break;
549       }
550       case ARROW_POSITION_END:
551       {
552         if ((arrow->GetXOffset() != 0.0) && !m_ignoreArrowOffsets)
553           DrawArrow(dc, arrow, arrow->GetXOffset(), true);
554         else
555         {
556           DrawArrow(dc, arrow, endArrowPos, false);
557           endArrowPos += arrow->GetSize() + arrow->GetSpacing();
558         }
559         break;
560       }
561       case ARROW_POSITION_MIDDLE:
562       {
563         arrow->SetXOffset(middleArrowPos);
564         if ((arrow->GetXOffset() != 0.0) && !m_ignoreArrowOffsets)
565           DrawArrow(dc, arrow, arrow->GetXOffset(), true);
566         else
567         {
568           DrawArrow(dc, arrow, middleArrowPos, false);
569           middleArrowPos += arrow->GetSize() + arrow->GetSpacing();
570         }
571         break;
572       }
573     }
574     node = node->GetNext();
575   }
576 }
577 
DrawArrow(wxDC & dc,wxArrowHead * arrow,double xOffset,bool proportionalOffset)578 void wxLineShape::DrawArrow(wxDC& dc, wxArrowHead *arrow, double xOffset, bool proportionalOffset)
579 {
580   wxNode *first_line_node = m_lineControlPoints->GetFirst();
581   wxRealPoint *first_line_point = (wxRealPoint *)first_line_node->GetData();
582   wxNode *second_line_node = first_line_node->GetNext();
583   wxRealPoint *second_line_point = (wxRealPoint *)second_line_node->GetData();
584 
585   wxNode *last_line_node = m_lineControlPoints->GetLast();
586   wxRealPoint *last_line_point = (wxRealPoint *)last_line_node->GetData();
587   wxNode *second_last_line_node = last_line_node->GetPrevious();
588   wxRealPoint *second_last_line_point = (wxRealPoint *)second_last_line_node->GetData();
589 
590   // Position where we want to start drawing
591   double positionOnLineX = 0.0, positionOnLineY = 0.0;
592 
593   // Position of start point of line, at the end of which we draw the arrow.
594   double startPositionX = 0.0 , startPositionY = 0.0;
595 
596   switch (arrow->GetPosition())
597   {
598     case ARROW_POSITION_START:
599     {
600       // If we're using a proportional offset, calculate just where this will
601       // be on the line.
602       double realOffset = xOffset;
603       if (proportionalOffset)
604       {
605         double totalLength =
606           (double)sqrt((second_line_point->x - first_line_point->x)*(second_line_point->x - first_line_point->x) +
607                       (second_line_point->y - first_line_point->y)*(second_line_point->y - first_line_point->y));
608         realOffset = (double)(xOffset * totalLength);
609       }
610       GetPointOnLine(second_line_point->x, second_line_point->y,
611                      first_line_point->x, first_line_point->y,
612                      realOffset, &positionOnLineX, &positionOnLineY);
613       startPositionX = second_line_point->x;
614       startPositionY = second_line_point->y;
615       break;
616     }
617     case ARROW_POSITION_END:
618     {
619       // If we're using a proportional offset, calculate just where this will
620       // be on the line.
621       double realOffset = xOffset;
622       if (proportionalOffset)
623       {
624         double totalLength =
625           (double)sqrt((second_last_line_point->x - last_line_point->x)*(second_last_line_point->x - last_line_point->x) +
626                       (second_last_line_point->y - last_line_point->y)*(second_last_line_point->y - last_line_point->y));
627         realOffset = (double)(xOffset * totalLength);
628       }
629       GetPointOnLine(second_last_line_point->x, second_last_line_point->y,
630                      last_line_point->x, last_line_point->y,
631                      realOffset, &positionOnLineX, &positionOnLineY);
632       startPositionX = second_last_line_point->x;
633       startPositionY = second_last_line_point->y;
634       break;
635     }
636     case ARROW_POSITION_MIDDLE:
637     {
638       // Choose a point half way between the last and penultimate points
639       double x = ((last_line_point->x + second_last_line_point->x)/2);
640       double y = ((last_line_point->y + second_last_line_point->y)/2);
641 
642       // If we're using a proportional offset, calculate just where this will
643       // be on the line.
644       double realOffset = xOffset;
645       if (proportionalOffset)
646       {
647         double totalLength =
648           (double)sqrt((second_last_line_point->x - x)*(second_last_line_point->x - x) +
649                       (second_last_line_point->y - y)*(second_last_line_point->y - y));
650         realOffset = (double)(xOffset * totalLength);
651       }
652 
653       GetPointOnLine(second_last_line_point->x, second_last_line_point->y,
654                      x, y, realOffset, &positionOnLineX, &positionOnLineY);
655       startPositionX = second_last_line_point->x;
656       startPositionY = second_last_line_point->y;
657       break;
658     }
659   }
660 
661   /*
662    * Add yOffset to arrow, if any
663    */
664 
665   const double myPi = (double) M_PI;
666   // The translation that the y offset may give
667   double deltaX = 0.0;
668   double deltaY = 0.0;
669   if ((arrow->GetYOffset() != 0.0) && !m_ignoreArrowOffsets)
670   {
671     /*
672                                  |(x4, y4)
673                                  |d
674                                  |
675        (x1, y1)--------------(x3, y3)------------------(x2, y2)
676        x4 = x3 - d * sin(theta)
677        y4 = y3 + d * cos(theta)
678 
679        Where theta = tan(-1) of (y3-y1)/(x3-x1)
680      */
681      double x1 = startPositionX;
682      double y1 = startPositionY;
683      double x3 = positionOnLineX;
684      double y3 = positionOnLineY;
685      double d = -arrow->GetYOffset(); // Negate so +offset is above line
686 
687      double theta;
688      if (x3 == x1)
689        theta = (double)(myPi/2.0);
690      else
691        theta = (double)atan((y3-y1)/(x3-x1));
692 
693      double x4 = (double)(x3 - (d*sin(theta)));
694      double y4 = (double)(y3 + (d*cos(theta)));
695 
696      deltaX = x4 - positionOnLineX;
697      deltaY = y4 - positionOnLineY;
698   }
699 
700   switch (arrow->_GetType())
701   {
702     case ARROW_ARROW:
703     {
704       double arrowLength = arrow->GetSize();
705       double arrowWidth = (double)(arrowLength/3.0);
706 
707       double tip_x, tip_y, side1_x, side1_y, side2_x, side2_y;
708       oglGetArrowPoints(startPositionX+deltaX, startPositionY+deltaY,
709                        positionOnLineX+deltaX, positionOnLineY+deltaY,
710                        arrowLength, arrowWidth, &tip_x, &tip_y,
711                        &side1_x, &side1_y, &side2_x, &side2_y);
712 
713       wxPoint points[4];
714       points[0].x = (int) tip_x; points[0].y = (int) tip_y;
715       points[1].x = (int) side1_x; points[1].y = (int) side1_y;
716       points[2].x = (int) side2_x; points[2].y = (int) side2_y;
717       points[3].x = (int) tip_x; points[3].y = (int) tip_y;
718 
719       dc.SetPen(* m_pen);
720       dc.SetBrush(* m_brush);
721       dc.DrawPolygon(4, points);
722       break;
723     }
724     case ARROW_HOLLOW_CIRCLE:
725     case ARROW_FILLED_CIRCLE:
726     {
727       // Find point on line of centre of circle, which is a radius away
728       // from the end position
729       double diameter = (double)(arrow->GetSize());
730       double x, y;
731       GetPointOnLine(startPositionX+deltaX, startPositionY+deltaY,
732                    positionOnLineX+deltaX, positionOnLineY+deltaY,
733                    (double)(diameter/2.0),
734                    &x, &y);
735 
736       // Convert ellipse centre to top-left coordinates
737       double x1 = (double)(x - (diameter/2.0));
738       double y1 = (double)(y - (diameter/2.0));
739 
740       dc.SetPen(* m_pen);
741       if (arrow->_GetType() == ARROW_HOLLOW_CIRCLE)
742         dc.SetBrush(GetBackgroundBrush());
743       else
744         dc.SetBrush(* m_brush);
745 
746       dc.DrawEllipse((long) x1, (long) y1, (long) diameter, (long) diameter);
747       break;
748     }
749     case ARROW_SINGLE_OBLIQUE:
750     {
751       break;
752     }
753     case ARROW_METAFILE:
754     {
755       if (arrow->GetMetaFile())
756       {
757         // Find point on line of centre of object, which is a half-width away
758         // from the end position
759         /*
760          *                width
761          * <-- start pos  <-----><-- positionOnLineX
762          *                _____
763          * --------------|  x  | <-- e.g. rectangular arrowhead
764          *                -----
765          */
766         double x, y;
767         GetPointOnLine(startPositionX, startPositionY,
768                    positionOnLineX, positionOnLineY,
769                    (double)(arrow->GetMetaFile()->m_width/2.0),
770                    &x, &y);
771 
772         // Calculate theta for rotating the metafile.
773         /*
774           |
775           |     o(x2, y2)   'o' represents the arrowhead.
776           |    /
777           |   /
778           |  /theta
779           | /(x1, y1)
780           |______________________
781         */
782         double theta = 0.0;
783         double x1 = startPositionX;
784         double y1 = startPositionY;
785         double x2 = positionOnLineX;
786         double y2 = positionOnLineY;
787 
788         if ((x1 == x2) && (y1 == y2))
789           theta = 0.0;
790 
791         else if ((x1 == x2) && (y1 > y2))
792           theta = (double)(3.0*myPi/2.0);
793 
794         else if ((x1 == x2) && (y2 > y1))
795           theta = (double)(myPi/2.0);
796 
797         else if ((x2 > x1) && (y2 >= y1))
798           theta = (double)atan((y2 - y1)/(x2 - x1));
799 
800         else if (x2 < x1)
801           theta = (double)(myPi + atan((y2 - y1)/(x2 - x1)));
802 
803         else if ((x2 > x1) && (y2 < y1))
804           theta = (double)(2*myPi + atan((y2 - y1)/(x2 - x1)));
805 
806         else
807         {
808           wxLogFatalError(wxT("Unknown arrowhead rotation case in lines.cc"));
809         }
810 
811         // Rotate about the centre of the object, then place
812         // the object on the line.
813         if (arrow->GetMetaFile()->GetRotateable())
814           arrow->GetMetaFile()->Rotate(0.0, 0.0, theta);
815 
816         if (m_erasing)
817         {
818           // If erasing, just draw a rectangle.
819           double minX, minY, maxX, maxY;
820           arrow->GetMetaFile()->GetBounds(&minX, &minY, &maxX, &maxY);
821           // Make erasing rectangle slightly bigger or you get droppings.
822           int extraPixels = 4;
823           dc.DrawRectangle((long)(deltaX + x + minX - (extraPixels/2.0)), (long)(deltaY + y + minY - (extraPixels/2.0)),
824                            (long)(maxX - minX + extraPixels), (long)(maxY - minY + extraPixels));
825         }
826         else
827           arrow->GetMetaFile()->Draw(dc, x+deltaX, y+deltaY);
828       }
829       break;
830     }
831     default:
832     {
833     }
834   }
835 }
836 
OnErase(wxDC & dc)837 void wxLineShape::OnErase(wxDC& dc)
838 {
839     const wxPen *old_pen = m_pen;
840     const wxBrush *old_brush = m_brush;
841     wxPen bg_pen = GetBackgroundPen();
842     wxBrush bg_brush = GetBackgroundBrush();
843     SetPen(&bg_pen);
844     SetBrush(&bg_brush);
845 
846     double bound_x, bound_y;
847     GetBoundingBoxMax(&bound_x, &bound_y);
848     if (m_font) dc.SetFont(* m_font);
849 
850     // Undraw text regions
851     for (int i = 0; i < 3; i++)
852     {
853       wxNode *node = m_regions.Item(i);
854       if (node)
855       {
856         double x, y;
857         wxShapeRegion *region = (wxShapeRegion *)node->GetData();
858         GetLabelPosition(i, &x, &y);
859         EraseRegion(dc, region, x, y);
860       }
861     }
862 
863     // Undraw line
864     dc.SetPen(GetBackgroundPen());
865     dc.SetBrush(GetBackgroundBrush());
866 
867     // Drawing over the line only seems to work if the line has a thickness
868     // of 1.
869     if (old_pen && (old_pen->GetWidth() > 1))
870     {
871       dc.DrawRectangle((long)(m_xpos - (bound_x/2.0) - 2.0), (long)(m_ypos - (bound_y/2.0) - 2.0),
872                         (long)(bound_x+4.0),  (long)(bound_y+4.0));
873     }
874     else
875     {
876       m_erasing = true;
877       GetEventHandler()->OnDraw(dc);
878       GetEventHandler()->OnEraseControlPoints(dc);
879       m_erasing = false;
880     }
881 
882     if (old_pen) SetPen(old_pen);
883     if (old_brush) SetBrush(old_brush);
884 }
885 
GetBoundingBoxMin(double * w,double * h)886 void wxLineShape::GetBoundingBoxMin(double *w, double *h)
887 {
888   double x1 = 10000;
889   double y1 = 10000;
890   double x2 = -10000;
891   double y2 = -10000;
892 
893   wxNode *node = m_lineControlPoints->GetFirst();
894   while (node)
895   {
896     wxRealPoint *point = (wxRealPoint *)node->GetData();
897 
898     if (point->x < x1) x1 = point->x;
899     if (point->y < y1) y1 = point->y;
900     if (point->x > x2) x2 = point->x;
901     if (point->y > y2) y2 = point->y;
902 
903     node = node->GetNext();
904   }
905   *w = (double)(x2 - x1);
906   *h = (double)(y2 - y1);
907 }
908 
909 /*
910  * For a node image of interest, finds the position of this arc
911  * amongst all the arcs which are attached to THIS SIDE of the node image,
912  * and the number of same.
913  */
FindNth(wxShape * image,int * nth,int * no_arcs,bool incoming)914 void wxLineShape::FindNth(wxShape *image, int *nth, int *no_arcs, bool incoming)
915 {
916   int n = -1;
917   int num = 0;
918   wxNode *node = image->GetLines().GetFirst();
919   int this_attachment;
920   if (image == m_to)
921     this_attachment = m_attachmentTo;
922   else
923     this_attachment = m_attachmentFrom;
924 
925   // Find number of lines going into/out of this particular attachment point
926   while (node)
927   {
928     wxLineShape *line = (wxLineShape *)node->GetData();
929 
930     if (line->m_from == image)
931     {
932       // This is the nth line attached to 'image'
933       if ((line == this) && !incoming)
934         n = num;
935 
936       // Increment num count if this is the same side (attachment number)
937       if (line->m_attachmentFrom == this_attachment)
938         num ++;
939     }
940 
941     if (line->m_to == image)
942     {
943       // This is the nth line attached to 'image'
944       if ((line == this) && incoming)
945         n = num;
946 
947       // Increment num count if this is the same side (attachment number)
948       if (line->m_attachmentTo == this_attachment)
949         num ++;
950     }
951 
952     node = node->GetNext();
953   }
954   *nth = n;
955   *no_arcs = num;
956 }
957 
OnDrawOutline(wxDC & dc,double WXUNUSED (x),double WXUNUSED (y),double WXUNUSED (w),double WXUNUSED (h))958 void wxLineShape::OnDrawOutline(wxDC& dc, double WXUNUSED(x), double WXUNUSED(y), double WXUNUSED(w), double WXUNUSED(h))
959 {
960   const wxPen *old_pen = m_pen;
961   const wxBrush *old_brush = m_brush;
962 
963   wxPen dottedPen(*wxBLACK, 1, wxDOT);
964   SetPen(& dottedPen);
965   SetBrush( wxTRANSPARENT_BRUSH );
966 
967   GetEventHandler()->OnDraw(dc);
968 
969   if (old_pen) SetPen(old_pen);
970   else SetPen(NULL);
971   if (old_brush) SetBrush(old_brush);
972   else SetBrush(NULL);
973 }
974 
OnMovePre(wxDC & dc,double x,double y,double old_x,double old_y,bool WXUNUSED (display))975 bool wxLineShape::OnMovePre(wxDC& dc, double x, double y, double old_x, double old_y, bool WXUNUSED(display))
976 {
977   double x_offset = x - old_x;
978   double y_offset = y - old_y;
979 
980   if (m_lineControlPoints && !(x_offset == 0.0 && y_offset == 0.0))
981   {
982     wxNode *node = m_lineControlPoints->GetFirst();
983     while (node)
984     {
985       wxRealPoint *point = (wxRealPoint *)node->GetData();
986       point->x += x_offset;
987       point->y += y_offset;
988       node = node->GetNext();
989     }
990 
991   }
992 
993   // Move temporary label rectangles if necessary
994   for (int i = 0; i < 3; i++)
995   {
996     if (m_labelObjects[i])
997     {
998       m_labelObjects[i]->Erase(dc);
999       double xp, yp, xr, yr;
1000       GetLabelPosition(i, &xp, &yp);
1001       wxNode *node = m_regions.Item(i);
1002       if (node)
1003       {
1004         wxShapeRegion *region = (wxShapeRegion *)node->GetData();
1005         region->GetPosition(&xr, &yr);
1006       }
1007       else
1008       {
1009         xr = 0.0; yr = 0.0;
1010       }
1011 
1012       m_labelObjects[i]->Move(dc, xp+xr, yp+yr);
1013     }
1014   }
1015   return true;
1016 }
1017 
OnMoveLink(wxDC & dc,bool moveControlPoints)1018 void wxLineShape::OnMoveLink(wxDC& dc, bool moveControlPoints)
1019 {
1020   if (!m_from || !m_to)
1021    return;
1022 
1023     if (m_lineControlPoints->GetCount() > 2)
1024       Initialise();
1025 
1026     // Do each end - nothing in the middle. User has to move other points
1027     // manually if necessary.
1028     double end_x, end_y;
1029     double other_end_x, other_end_y;
1030 
1031     FindLineEndPoints(&end_x, &end_y, &other_end_x, &other_end_y);
1032 
1033     wxNode *first = m_lineControlPoints->GetFirst();
1034     /* wxRealPoint *first_point = */ (wxRealPoint *)first->GetData();
1035     wxNode *last = m_lineControlPoints->GetLast();
1036     /* wxRealPoint *last_point = */ (wxRealPoint *)last->GetData();
1037 
1038 /* This is redundant, surely? Done by SetEnds.
1039     first_point->x = end_x; first_point->y = end_y;
1040     last_point->x = other_end_x; last_point->y = other_end_y;
1041 */
1042 
1043     double oldX = m_xpos;
1044     double oldY = m_ypos;
1045 
1046     SetEnds(end_x, end_y, other_end_x, other_end_y);
1047 
1048     // Do a second time, because one may depend on the other.
1049     FindLineEndPoints(&end_x, &end_y, &other_end_x, &other_end_y);
1050     SetEnds(end_x, end_y, other_end_x, other_end_y);
1051 
1052     // Try to move control points with the arc
1053     double x_offset = m_xpos - oldX;
1054     double y_offset = m_ypos - oldY;
1055 
1056 //    if (moveControlPoints && m_lineControlPoints && !(x_offset == 0.0 && y_offset == 0.0))
1057     // Only move control points if it's a self link. And only works if attachment mode is ON.
1058     if ((m_from == m_to) && (m_from->GetAttachmentMode() != ATTACHMENT_MODE_NONE) && moveControlPoints && m_lineControlPoints && !(x_offset == 0.0 && y_offset == 0.0))
1059     {
1060       wxNode *node = m_lineControlPoints->GetFirst();
1061       while (node)
1062       {
1063         if ((node != m_lineControlPoints->GetFirst()) && (node != m_lineControlPoints->GetLast()))
1064         {
1065           wxRealPoint *point = (wxRealPoint *)node->GetData();
1066           point->x += x_offset;
1067           point->y += y_offset;
1068         }
1069         node = node->GetNext();
1070       }
1071     }
1072 
1073     Move(dc, m_xpos, m_ypos);
1074 }
1075 
1076 // Finds the x, y points at the two ends of the line.
1077 // This function can be used by e.g. line-routing routines to
1078 // get the actual points on the two node images where the lines will be drawn
1079 // to/from.
FindLineEndPoints(double * fromX,double * fromY,double * toX,double * toY)1080 void wxLineShape::FindLineEndPoints(double *fromX, double *fromY, double *toX, double *toY)
1081 {
1082   if (!m_from || !m_to)
1083    return;
1084 
1085   // Do each end - nothing in the middle. User has to move other points
1086   // manually if necessary.
1087   double end_x = 0.0, end_y = 0.0;
1088   double other_end_x = 0.0, other_end_y = 0.0;
1089 
1090   wxNode *first = m_lineControlPoints->GetFirst();
1091   /* wxRealPoint *first_point = */ (wxRealPoint *)first->GetData();
1092   wxNode *last = m_lineControlPoints->GetLast();
1093   /* wxRealPoint *last_point = */ (wxRealPoint *)last->GetData();
1094 
1095   wxNode *second = first->GetNext();
1096   wxRealPoint *second_point = (wxRealPoint *)second->GetData();
1097 
1098   wxNode *second_last = last->GetPrevious();
1099   wxRealPoint *second_last_point = (wxRealPoint *)second_last->GetData();
1100 
1101   if (m_lineControlPoints->GetCount() > 2)
1102   {
1103     if (m_from->GetAttachmentMode() != ATTACHMENT_MODE_NONE)
1104     {
1105       int nth, no_arcs;
1106       FindNth(m_from, &nth, &no_arcs, false); // Not incoming
1107       m_from->GetAttachmentPosition(m_attachmentFrom, &end_x, &end_y, nth, no_arcs, this);
1108     }
1109     else
1110       (void) m_from->GetPerimeterPoint(m_from->GetX(), m_from->GetY(),
1111                                    (double)second_point->x, (double)second_point->y,
1112                                     &end_x, &end_y);
1113 
1114     if (m_to->GetAttachmentMode() != ATTACHMENT_MODE_NONE)
1115     {
1116       int nth, no_arcs;
1117       FindNth(m_to, &nth, &no_arcs, true); // Incoming
1118       m_to->GetAttachmentPosition(m_attachmentTo, &other_end_x, &other_end_y, nth, no_arcs, this);
1119     }
1120     else
1121       (void) m_to->GetPerimeterPoint(m_to->GetX(), m_to->GetY(),
1122                                 (double)second_last_point->x, (double)second_last_point->y,
1123                                 &other_end_x, &other_end_y);
1124   }
1125   else
1126   {
1127     double fromX = m_from->GetX();
1128     double fromY = m_from->GetY();
1129     double toX = m_to->GetX();
1130     double toY = m_to->GetY();
1131 
1132     if (m_from->GetAttachmentMode() != ATTACHMENT_MODE_NONE)
1133     {
1134       int nth, no_arcs;
1135       FindNth(m_from, &nth, &no_arcs, false);
1136       m_from->GetAttachmentPosition(m_attachmentFrom, &end_x, &end_y, nth, no_arcs, this);
1137       fromX = end_x;
1138       fromY = end_y;
1139     }
1140 
1141     if (m_to->GetAttachmentMode() != ATTACHMENT_MODE_NONE)
1142     {
1143       int nth, no_arcs;
1144       FindNth(m_to, &nth, &no_arcs, true);
1145       m_to->GetAttachmentPosition(m_attachmentTo, &other_end_x, &other_end_y, nth, no_arcs, this);
1146       toX = other_end_x;
1147       toY = other_end_y;
1148     }
1149 
1150     if (m_from->GetAttachmentMode() == ATTACHMENT_MODE_NONE)
1151       (void) m_from->GetPerimeterPoint(m_from->GetX(), m_from->GetY(),
1152                                   toX, toY,
1153                                   &end_x, &end_y);
1154 
1155     if (m_to->GetAttachmentMode() == ATTACHMENT_MODE_NONE)
1156       (void) m_to->GetPerimeterPoint(m_to->GetX(), m_to->GetY(),
1157                                 fromX, fromY,
1158                                 &other_end_x, &other_end_y);
1159   }
1160   *fromX = end_x;
1161   *fromY = end_y;
1162   *toX = other_end_x;
1163   *toY = other_end_y;
1164 }
1165 
OnDraw(wxDC & dc)1166 void wxLineShape::OnDraw(wxDC& dc)
1167 {
1168   if (m_lineControlPoints)
1169   {
1170     if (m_pen)
1171       dc.SetPen(* m_pen);
1172     if (m_brush)
1173       dc.SetBrush(* m_brush);
1174 
1175     int n = m_lineControlPoints->GetCount();
1176     wxPoint *points = new wxPoint[n];
1177     int i;
1178     for (i = 0; i < n; i++)
1179     {
1180         wxRealPoint* point = (wxRealPoint*) m_lineControlPoints->Item(i)->GetData();
1181         points[i].x = WXROUND(point->x);
1182         points[i].y = WXROUND(point->y);
1183     }
1184 
1185     if (m_isSpline)
1186       dc.DrawSpline(n, points);
1187     else
1188       dc.DrawLines(n, points);
1189 
1190 #ifdef __WXMSW__
1191     // For some reason, last point isn't drawn under Windows.
1192     dc.DrawPoint(points[n-1]);
1193 #endif
1194 
1195     delete[] points;
1196 
1197 
1198     // Problem with pen - if not a solid pen, does strange things
1199     // to the arrowhead. So make (get) a new pen that's solid.
1200     if (m_pen && (m_pen->GetStyle() != wxSOLID))
1201     {
1202       wxPen *solid_pen =
1203         wxThePenList->FindOrCreatePen(m_pen->GetColour(), 1, wxSOLID);
1204       if (solid_pen)
1205         dc.SetPen(* solid_pen);
1206     }
1207     DrawArrows(dc);
1208   }
1209 }
1210 
OnDrawControlPoints(wxDC & dc)1211 void wxLineShape::OnDrawControlPoints(wxDC& dc)
1212 {
1213   if (!m_drawHandles)
1214     return;
1215 
1216   // Draw temporary label rectangles if necessary
1217   for (int i = 0; i < 3; i++)
1218   {
1219     if (m_labelObjects[i])
1220       m_labelObjects[i]->Draw(dc);
1221   }
1222   wxShape::OnDrawControlPoints(dc);
1223 }
1224 
OnEraseControlPoints(wxDC & dc)1225 void wxLineShape::OnEraseControlPoints(wxDC& dc)
1226 {
1227   // Erase temporary label rectangles if necessary
1228   for (int i = 0; i < 3; i++)
1229   {
1230     if (m_labelObjects[i])
1231       m_labelObjects[i]->Erase(dc);
1232   }
1233   wxShape::OnEraseControlPoints(dc);
1234 }
1235 
OnDragLeft(bool WXUNUSED (draw),double WXUNUSED (x),double WXUNUSED (y),int WXUNUSED (keys),int WXUNUSED (attachment))1236 void wxLineShape::OnDragLeft(bool WXUNUSED(draw), double WXUNUSED(x), double WXUNUSED(y), int WXUNUSED(keys), int WXUNUSED(attachment))
1237 {
1238 }
1239 
OnBeginDragLeft(double WXUNUSED (x),double WXUNUSED (y),int WXUNUSED (keys),int WXUNUSED (attachment))1240 void wxLineShape::OnBeginDragLeft(double WXUNUSED(x), double WXUNUSED(y), int WXUNUSED(keys), int WXUNUSED(attachment))
1241 {
1242 }
1243 
OnEndDragLeft(double WXUNUSED (x),double WXUNUSED (y),int WXUNUSED (keys),int WXUNUSED (attachment))1244 void wxLineShape::OnEndDragLeft(double WXUNUSED(x), double WXUNUSED(y), int WXUNUSED(keys), int WXUNUSED(attachment))
1245 {
1246 }
1247 
1248 /*
1249 void wxLineShape::SetArrowSize(double length, double width)
1250 {
1251   arrow_length = length;
1252   arrow_width = width;
1253 }
1254 
1255 void wxLineShape::SetStartArrow(int style)
1256 {
1257   start_style = style;
1258 }
1259 
1260 void wxLineShape::SetMiddleArrow(int style)
1261 {
1262   middle_style = style;
1263 }
1264 
1265 void wxLineShape::SetEndArrow(int style)
1266 {
1267   end_style = style;
1268 }
1269 */
1270 
OnDrawContents(wxDC & dc)1271 void wxLineShape::OnDrawContents(wxDC& dc)
1272 {
1273   if (GetDisableLabel())
1274     return;
1275 
1276   for (int i = 0; i < 3; i++)
1277   {
1278     wxNode *node = m_regions.Item(i);
1279     if (node)
1280     {
1281       wxShapeRegion *region = (wxShapeRegion *)node->GetData();
1282       double x, y;
1283       GetLabelPosition(i, &x, &y);
1284       DrawRegion(dc, region, x, y);
1285     }
1286   }
1287 }
1288 
SetTo(wxShape * object)1289 void wxLineShape::SetTo(wxShape *object)
1290 {
1291   m_to = object;
1292 }
1293 
SetFrom(wxShape * object)1294 void wxLineShape::SetFrom(wxShape *object)
1295 {
1296   m_from = object;
1297 }
1298 
MakeControlPoints()1299 void wxLineShape::MakeControlPoints()
1300 {
1301   if (m_canvas && m_lineControlPoints)
1302   {
1303     wxNode *first = m_lineControlPoints->GetFirst();
1304     wxNode *last = m_lineControlPoints->GetLast();
1305     wxRealPoint *first_point = (wxRealPoint *)first->GetData();
1306     wxRealPoint *last_point = (wxRealPoint *)last->GetData();
1307 
1308     wxLineControlPoint *control = new wxLineControlPoint(m_canvas, this, CONTROL_POINT_SIZE,
1309                                                first_point->x, first_point->y,
1310                                                CONTROL_POINT_ENDPOINT_FROM);
1311     control->m_point = first_point;
1312     m_canvas->AddShape(control);
1313     m_controlPoints.Append(control);
1314 
1315 
1316     wxNode *node = first->GetNext();
1317     while (node != last)
1318     {
1319       wxRealPoint *point = (wxRealPoint *)node->GetData();
1320 
1321       control = new wxLineControlPoint(m_canvas, this, CONTROL_POINT_SIZE,
1322                                                point->x, point->y,
1323                                                CONTROL_POINT_LINE);
1324       control->m_point = point;
1325 
1326       m_canvas->AddShape(control);
1327       m_controlPoints.Append(control);
1328 
1329       node = node->GetNext();
1330     }
1331     control = new wxLineControlPoint(m_canvas, this, CONTROL_POINT_SIZE,
1332                                                last_point->x, last_point->y,
1333                                                CONTROL_POINT_ENDPOINT_TO);
1334     control->m_point = last_point;
1335     m_canvas->AddShape(control);
1336     m_controlPoints.Append(control);
1337 
1338   }
1339 
1340 }
1341 
ResetControlPoints()1342 void wxLineShape::ResetControlPoints()
1343 {
1344   if (m_canvas && m_lineControlPoints && m_controlPoints.GetCount() > 0)
1345   {
1346     wxNode *node = m_controlPoints.GetFirst();
1347     wxNode *control_node = m_lineControlPoints->GetFirst();
1348     while (node && control_node)
1349     {
1350       wxRealPoint *point = (wxRealPoint *)control_node->GetData();
1351       wxLineControlPoint *control = (wxLineControlPoint *)node->GetData();
1352       control->SetX(point->x);
1353       control->SetY(point->y);
1354 
1355       node = node->GetNext();
1356       control_node = control_node->GetNext();
1357     }
1358   }
1359 }
1360 
1361 #if wxUSE_PROLOGIO
WriteAttributes(wxExpr * clause)1362 void wxLineShape::WriteAttributes(wxExpr *clause)
1363 {
1364   wxShape::WriteAttributes(clause);
1365 
1366   if (m_from)
1367     clause->AddAttributeValue(_T("from"), m_from->GetId());
1368   if (m_to)
1369     clause->AddAttributeValue(_T("to"), m_to->GetId());
1370 
1371   if (m_attachmentTo != 0)
1372     clause->AddAttributeValue(_T("attachment_to"), (long)m_attachmentTo);
1373   if (m_attachmentFrom != 0)
1374     clause->AddAttributeValue(_T("attachment_from"), (long)m_attachmentFrom);
1375 
1376   if (m_alignmentStart != 0)
1377     clause->AddAttributeValue(_T("align_start"), (long)m_alignmentStart);
1378   if (m_alignmentEnd != 0)
1379     clause->AddAttributeValue(_T("align_end"), (long)m_alignmentEnd);
1380 
1381   clause->AddAttributeValue(_T("is_spline"), (long)m_isSpline);
1382   if (m_maintainStraightLines)
1383     clause->AddAttributeValue(_T("keep_lines_straight"), (long)m_maintainStraightLines);
1384 
1385   // Make a list of lists for the (sp)line controls
1386   wxExpr *list = new wxExpr(wxExprList);
1387   wxNode *node = m_lineControlPoints->GetFirst();
1388   while (node)
1389   {
1390     wxRealPoint *point = (wxRealPoint *)node->GetData();
1391     wxExpr *point_list = new wxExpr(wxExprList);
1392     wxExpr *x_expr = new wxExpr((double) point->x);
1393     wxExpr *y_expr = new wxExpr((double) point->y);
1394     point_list->Append(x_expr);
1395     point_list->Append(y_expr);
1396     list->Append(point_list);
1397 
1398     node = node->GetNext();
1399   }
1400   clause->AddAttributeValue(_T("controls"), list);
1401 
1402   // Write arc arrows in new OGL format, if there are any.
1403   // This is a list of lists. Each sublist comprises:
1404   // (arrowType arrowEnd xOffset arrowSize)
1405   if (m_arcArrows.GetCount() > 0)
1406   {
1407     wxExpr *arrow_list = new wxExpr(wxExprList);
1408     node = m_arcArrows.GetFirst();
1409     while (node)
1410     {
1411       wxArrowHead *head = (wxArrowHead *)node->GetData();
1412       wxExpr *head_list = new wxExpr(wxExprList);
1413       head_list->Append(new wxExpr((long)head->_GetType()));
1414       head_list->Append(new wxExpr((long)head->GetArrowEnd()));
1415       head_list->Append(new wxExpr(head->GetXOffset()));
1416       head_list->Append(new wxExpr(head->GetArrowSize()));
1417       head_list->Append(new wxExpr(wxExprString, head->GetName()));
1418       head_list->Append(new wxExpr(head->GetId()));
1419 
1420       // New members of wxArrowHead
1421       head_list->Append(new wxExpr(head->GetYOffset()));
1422       head_list->Append(new wxExpr(head->GetSpacing()));
1423 
1424       arrow_list->Append(head_list);
1425 
1426       node = node->GetNext();
1427     }
1428     clause->AddAttributeValue(_T("arrows"), arrow_list);
1429   }
1430 }
1431 
ReadAttributes(wxExpr * clause)1432 void wxLineShape::ReadAttributes(wxExpr *clause)
1433 {
1434   wxShape::ReadAttributes(clause);
1435 
1436   int iVal = (int) m_isSpline;
1437   clause->AssignAttributeValue(wxT("is_spline"), &iVal);
1438   m_isSpline = (iVal != 0);
1439 
1440   iVal = (int) m_maintainStraightLines;
1441   clause->AssignAttributeValue(wxT("keep_lines_straight"), &iVal);
1442   m_maintainStraightLines = (iVal != 0);
1443 
1444   clause->AssignAttributeValue(wxT("align_start"), &m_alignmentStart);
1445   clause->AssignAttributeValue(wxT("align_end"), &m_alignmentEnd);
1446 
1447   // Compatibility: check for no regions.
1448   if (m_regions.GetCount() == 0)
1449   {
1450     wxShapeRegion *newRegion = new wxShapeRegion;
1451     newRegion->SetName(_T("Middle"));
1452     newRegion->SetSize(150, 50);
1453     m_regions.Append((wxObject *)newRegion);
1454     if (m_text.GetCount() > 0)
1455     {
1456       newRegion->ClearText();
1457       wxNode *node = m_text.GetFirst();
1458       while (node)
1459       {
1460         wxShapeTextLine *textLine = (wxShapeTextLine *)node->GetData();
1461         wxNode *next = node->GetNext();
1462         newRegion->GetFormattedText().Append((wxObject *)textLine);
1463         delete node;
1464         node = next;
1465       }
1466     }
1467 
1468     newRegion = new wxShapeRegion;
1469     newRegion->SetName(wxT("Start"));
1470     newRegion->SetSize(150, 50);
1471     m_regions.Append((wxObject *)newRegion);
1472 
1473     newRegion = new wxShapeRegion;
1474     newRegion->SetName(wxT("End"));
1475     newRegion->SetSize(150, 50);
1476     m_regions.Append((wxObject *)newRegion);
1477   }
1478 
1479   m_attachmentTo = 0;
1480   m_attachmentFrom = 0;
1481 
1482   clause->AssignAttributeValue(wxT("attachment_to"), &m_attachmentTo);
1483   clause->AssignAttributeValue(wxT("attachment_from"), &m_attachmentFrom);
1484 
1485   wxExpr *line_list = NULL;
1486 
1487   // When image is created, there are default control points. Override
1488   // them if there are some in the file.
1489   clause->AssignAttributeValue(wxT("controls"), &line_list);
1490 
1491   if (line_list)
1492   {
1493     // Read a list of lists for the spline controls
1494     if (m_lineControlPoints)
1495     {
1496       ClearPointList(*m_lineControlPoints);
1497     }
1498     else
1499       m_lineControlPoints = new wxList;
1500 
1501     wxExpr *node = line_list->value.first;
1502 
1503     while (node)
1504     {
1505       wxExpr *xexpr = node->value.first;
1506       double x = xexpr->RealValue();
1507 
1508       wxExpr *yexpr = xexpr->next;
1509       double y = yexpr->RealValue();
1510 
1511       wxRealPoint *point = new wxRealPoint(x, y);
1512       m_lineControlPoints->Append((wxObject*) point);
1513 
1514       node = node->next;
1515     }
1516   }
1517 
1518   // Read arrow list, for new OGL code
1519   wxExpr *arrow_list = NULL;
1520 
1521   clause->AssignAttributeValue(wxT("arrows"), &arrow_list);
1522   if (arrow_list)
1523   {
1524     wxExpr *node = arrow_list->value.first;
1525 
1526     while (node)
1527     {
1528       WXTYPE arrowType = ARROW_ARROW;
1529       int arrowEnd = 0;
1530       double xOffset = 0.0;
1531       double arrowSize = 0.0;
1532       wxString arrowName;
1533       long arrowId = -1;
1534 
1535       wxExpr *type_expr = node->Nth(0);
1536       wxExpr *end_expr = node->Nth(1);
1537       wxExpr *dist_expr = node->Nth(2);
1538       wxExpr *size_expr = node->Nth(3);
1539       wxExpr *name_expr = node->Nth(4);
1540       wxExpr *id_expr = node->Nth(5);
1541 
1542       // New members of wxArrowHead
1543       wxExpr *yOffsetExpr = node->Nth(6);
1544       wxExpr *spacingExpr = node->Nth(7);
1545 
1546       if (type_expr)
1547           arrowType = (WXTYPE)type_expr->IntegerValue();
1548       if (end_expr)
1549         arrowEnd = (int)end_expr->IntegerValue();
1550       if (dist_expr)
1551         xOffset = dist_expr->RealValue();
1552       if (size_expr)
1553         arrowSize = size_expr->RealValue();
1554       if (name_expr)
1555         arrowName = name_expr->StringValue();
1556       if (id_expr)
1557         arrowId = id_expr->IntegerValue();
1558 
1559       if (arrowId == -1)
1560         arrowId = wxNewId();
1561       else
1562         wxRegisterId(arrowId);
1563 
1564       wxArrowHead *arrowHead = AddArrow(arrowType, arrowEnd, arrowSize, xOffset, arrowName, NULL, arrowId);
1565       if (yOffsetExpr)
1566         arrowHead->SetYOffset(yOffsetExpr->RealValue());
1567       if (spacingExpr)
1568         arrowHead->SetSpacing(spacingExpr->RealValue());
1569 
1570       node = node->next;
1571     }
1572   }
1573 }
1574 #endif
1575 
Copy(wxShape & copy)1576 void wxLineShape::Copy(wxShape& copy)
1577 {
1578   wxShape::Copy(copy);
1579 
1580   wxASSERT( copy.IsKindOf(CLASSINFO(wxLineShape)) );
1581 
1582   wxLineShape& lineCopy = (wxLineShape&) copy;
1583 
1584   lineCopy.m_to = m_to;
1585   lineCopy.m_from = m_from;
1586   lineCopy.m_attachmentTo = m_attachmentTo;
1587   lineCopy.m_attachmentFrom = m_attachmentFrom;
1588   lineCopy.m_isSpline = m_isSpline;
1589   lineCopy.m_alignmentStart = m_alignmentStart;
1590   lineCopy.m_alignmentEnd = m_alignmentEnd;
1591   lineCopy.m_maintainStraightLines = m_maintainStraightLines;
1592   lineCopy.m_lineOrientations.Clear();
1593 
1594   wxNode *node = m_lineOrientations.GetFirst();
1595   while (node)
1596   {
1597     lineCopy.m_lineOrientations.Append(node->GetData());
1598     node = node->GetNext();
1599   }
1600 
1601   if (lineCopy.m_lineControlPoints)
1602   {
1603     ClearPointList(*lineCopy.m_lineControlPoints);
1604     delete lineCopy.m_lineControlPoints;
1605   }
1606 
1607   lineCopy.m_lineControlPoints = new wxList;
1608 
1609   node = m_lineControlPoints->GetFirst();
1610   while (node)
1611   {
1612     wxRealPoint *point = (wxRealPoint *)node->GetData();
1613     wxRealPoint *new_point = new wxRealPoint(point->x, point->y);
1614     lineCopy.m_lineControlPoints->Append((wxObject*) new_point);
1615     node = node->GetNext();
1616   }
1617 
1618   // Copy arrows
1619   lineCopy.ClearArrowsAtPosition(-1);
1620   node = m_arcArrows.GetFirst();
1621   while (node)
1622   {
1623     wxArrowHead *arrow = (wxArrowHead *)node->GetData();
1624     lineCopy.m_arcArrows.Append(new wxArrowHead(*arrow));
1625     node = node->GetNext();
1626   }
1627 }
1628 
1629 // Override select, to create/delete temporary label-moving objects
Select(bool select,wxDC * dc)1630 void wxLineShape::Select(bool select, wxDC* dc)
1631 {
1632   wxShape::Select(select, dc);
1633   if (select)
1634   {
1635     for (int i = 0; i < 3; i++)
1636     {
1637       wxNode *node = m_regions.Item(i);
1638       if (node)
1639       {
1640         wxShapeRegion *region = (wxShapeRegion *)node->GetData();
1641         if (region->m_formattedText.GetCount() > 0)
1642         {
1643           double w, h, x, y, xx, yy;
1644           region->GetSize(&w, &h);
1645           region->GetPosition(&x, &y);
1646           GetLabelPosition(i, &xx, &yy);
1647           if (m_labelObjects[i])
1648           {
1649             m_labelObjects[i]->Select(false);
1650             m_labelObjects[i]->RemoveFromCanvas(m_canvas);
1651             delete m_labelObjects[i];
1652           }
1653           m_labelObjects[i] = OnCreateLabelShape(this, region, w, h);
1654           m_labelObjects[i]->AddToCanvas(m_canvas);
1655           m_labelObjects[i]->Show(true);
1656           if (dc)
1657             m_labelObjects[i]->Move(*dc, (double)(x + xx), (double)(y + yy));
1658           m_labelObjects[i]->Select(true, dc);
1659         }
1660       }
1661     }
1662   }
1663   else
1664   {
1665     for (int i = 0; i < 3; i++)
1666     {
1667       if (m_labelObjects[i])
1668       {
1669         m_labelObjects[i]->Select(false, dc);
1670         m_labelObjects[i]->Erase(*dc);
1671         m_labelObjects[i]->RemoveFromCanvas(m_canvas);
1672         delete m_labelObjects[i];
1673         m_labelObjects[i] = NULL;
1674       }
1675     }
1676   }
1677 }
1678 
1679 /*
1680  * Line control point
1681  *
1682  */
1683 
IMPLEMENT_DYNAMIC_CLASS(wxLineControlPoint,wxControlPoint)1684 IMPLEMENT_DYNAMIC_CLASS(wxLineControlPoint, wxControlPoint)
1685 
1686 wxLineControlPoint::wxLineControlPoint(wxShapeCanvas *theCanvas, wxShape *object, double size, double x, double y, int the_type):
1687   wxControlPoint(theCanvas, object, size, x, y, the_type)
1688 {
1689   m_xpos = x;
1690   m_ypos = y;
1691   m_type = the_type;
1692   m_point = NULL;
1693 }
1694 
~wxLineControlPoint()1695 wxLineControlPoint::~wxLineControlPoint()
1696 {
1697 }
1698 
OnDraw(wxDC & dc)1699 void wxLineControlPoint::OnDraw(wxDC& dc)
1700 {
1701   wxRectangleShape::OnDraw(dc);
1702 }
1703 
1704 // Implement movement of Line point
OnDragLeft(bool draw,double x,double y,int keys,int attachment)1705 void wxLineControlPoint::OnDragLeft(bool draw, double x, double y, int keys, int attachment)
1706 {
1707     m_shape->GetEventHandler()->OnSizingDragLeft(this, draw, x, y, keys, attachment);
1708 }
1709 
OnBeginDragLeft(double x,double y,int keys,int attachment)1710 void wxLineControlPoint::OnBeginDragLeft(double x, double y, int keys, int attachment)
1711 {
1712     m_shape->GetEventHandler()->OnSizingBeginDragLeft(this, x, y, keys, attachment);
1713 }
1714 
OnEndDragLeft(double x,double y,int keys,int attachment)1715 void wxLineControlPoint::OnEndDragLeft(double x, double y, int keys, int attachment)
1716 {
1717     m_shape->GetEventHandler()->OnSizingEndDragLeft(this, x, y, keys, attachment);
1718 }
1719 
1720 // Control points ('handles') redirect control to the actual shape, to make it easier
1721 // to override sizing behaviour.
OnSizingDragLeft(wxControlPoint * pt,bool WXUNUSED (draw),double x,double y,int WXUNUSED (keys),int WXUNUSED (attachment))1722 void wxLineShape::OnSizingDragLeft(wxControlPoint* pt, bool WXUNUSED(draw), double x, double y, int WXUNUSED(keys), int WXUNUSED(attachment))
1723 {
1724   wxLineControlPoint* lpt = (wxLineControlPoint*) pt;
1725 
1726   wxClientDC dc(GetCanvas());
1727   GetCanvas()->PrepareDC(dc);
1728 
1729   dc.SetLogicalFunction(OGLRBLF);
1730 
1731   wxPen dottedPen(*wxBLACK, 1, wxDOT);
1732   dc.SetPen(dottedPen);
1733   dc.SetBrush((* wxTRANSPARENT_BRUSH));
1734 
1735   if (lpt->m_type == CONTROL_POINT_LINE)
1736   {
1737     m_canvas->Snap(&x, &y);
1738 
1739     lpt->SetX(x); lpt->SetY(y);
1740     lpt->m_point->x = x; lpt->m_point->y = y;
1741 
1742     wxLineShape *lineShape = (wxLineShape *)this;
1743 
1744     const wxPen *old_pen = lineShape->GetPen();
1745     const wxBrush *old_brush = lineShape->GetBrush();
1746 
1747     wxPen dottedPen(*wxBLACK, 1, wxDOT);
1748     lineShape->SetPen(& dottedPen);
1749     lineShape->SetBrush(wxTRANSPARENT_BRUSH);
1750 
1751     lineShape->GetEventHandler()->OnMoveLink(dc, false);
1752 
1753     lineShape->SetPen(old_pen);
1754     lineShape->SetBrush(old_brush);
1755   }
1756 
1757   if (lpt->m_type == CONTROL_POINT_ENDPOINT_FROM || lpt->m_type == CONTROL_POINT_ENDPOINT_TO)
1758   {
1759 //    lpt->SetX(x); lpt->SetY(y);
1760   }
1761 
1762 }
1763 
OnSizingBeginDragLeft(wxControlPoint * pt,double x,double y,int WXUNUSED (keys),int WXUNUSED (attachment))1764 void wxLineShape::OnSizingBeginDragLeft(wxControlPoint* pt, double x, double y, int WXUNUSED(keys), int WXUNUSED(attachment))
1765 {
1766   wxLineControlPoint* lpt = (wxLineControlPoint*) pt;
1767 
1768   wxClientDC dc(GetCanvas());
1769   GetCanvas()->PrepareDC(dc);
1770 
1771   wxLineShape *lineShape = (wxLineShape *)this;
1772   if (lpt->m_type == CONTROL_POINT_LINE)
1773   {
1774     lpt->m_originalPos = * (lpt->m_point);
1775     m_canvas->Snap(&x, &y);
1776 
1777     this->Erase(dc);
1778 
1779     // Redraw start and end objects because we've left holes
1780     // when erasing the line
1781     lineShape->GetFrom()->OnDraw(dc);
1782     lineShape->GetFrom()->OnDrawContents(dc);
1783     lineShape->GetTo()->OnDraw(dc);
1784     lineShape->GetTo()->OnDrawContents(dc);
1785 
1786     this->SetDisableLabel(true);
1787     dc.SetLogicalFunction(OGLRBLF);
1788 
1789     lpt->m_xpos = x; lpt->m_ypos = y;
1790     lpt->m_point->x = x; lpt->m_point->y = y;
1791 
1792     const wxPen *old_pen = lineShape->GetPen();
1793     const wxBrush *old_brush = lineShape->GetBrush();
1794 
1795     wxPen dottedPen(*wxBLACK, 1, wxDOT);
1796     lineShape->SetPen(& dottedPen);
1797     lineShape->SetBrush(wxTRANSPARENT_BRUSH);
1798 
1799     lineShape->GetEventHandler()->OnMoveLink(dc, false);
1800 
1801     lineShape->SetPen(old_pen);
1802     lineShape->SetBrush(old_brush);
1803   }
1804 
1805   if (lpt->m_type == CONTROL_POINT_ENDPOINT_FROM || lpt->m_type == CONTROL_POINT_ENDPOINT_TO)
1806   {
1807     m_canvas->SetCursor(wxCursor(wxCURSOR_BULLSEYE));
1808     lpt->m_oldCursor = wxSTANDARD_CURSOR;
1809   }
1810 }
1811 
OnSizingEndDragLeft(wxControlPoint * pt,double x,double y,int WXUNUSED (keys),int WXUNUSED (attachment))1812 void wxLineShape::OnSizingEndDragLeft(wxControlPoint* pt, double x, double y, int WXUNUSED(keys), int WXUNUSED(attachment))
1813 {
1814   wxLineControlPoint* lpt = (wxLineControlPoint*) pt;
1815 
1816   wxClientDC dc(GetCanvas());
1817   GetCanvas()->PrepareDC(dc);
1818 
1819   this->SetDisableLabel(false);
1820   wxLineShape *lineShape = (wxLineShape *)this;
1821 
1822   if (lpt->m_type == CONTROL_POINT_LINE)
1823   {
1824     m_canvas->Snap(&x, &y);
1825 
1826     wxRealPoint pt = wxRealPoint(x, y);
1827 
1828     // Move the control point back to where it was;
1829     // MoveControlPoint will move it to the new position
1830     // if it decides it wants. We only moved the position
1831     // during user feedback so we could redraw the line
1832     // as it changed shape.
1833     lpt->m_xpos = lpt->m_originalPos.x; lpt->m_ypos = lpt->m_originalPos.y;
1834     lpt->m_point->x = lpt->m_originalPos.x; lpt->m_point->y = lpt->m_originalPos.y;
1835 
1836     OnMoveMiddleControlPoint(dc, lpt, pt);
1837   }
1838   if (lpt->m_type == CONTROL_POINT_ENDPOINT_FROM)
1839   {
1840     if (lpt->m_oldCursor)
1841       m_canvas->SetCursor(* lpt->m_oldCursor);
1842 
1843 //    this->Erase(dc);
1844 
1845 //    lpt->m_xpos = x; lpt->m_ypos = y;
1846 
1847     if (lineShape->GetFrom())
1848     {
1849       lineShape->GetFrom()->MoveLineToNewAttachment(dc, lineShape, x, y);
1850     }
1851   }
1852   if (lpt->m_type == CONTROL_POINT_ENDPOINT_TO)
1853   {
1854     if (lpt->m_oldCursor)
1855       m_canvas->SetCursor(* lpt->m_oldCursor);
1856 
1857 //    lpt->m_xpos = x; lpt->m_ypos = y;
1858 
1859     if (lineShape->GetTo())
1860     {
1861       lineShape->GetTo()->MoveLineToNewAttachment(dc, lineShape, x, y);
1862     }
1863   }
1864 
1865   // Needed?
1866 #if 0
1867   int i = 0;
1868   for (i = 0; i < lineShape->GetLineControlPoints()->GetCount(); i++)
1869     if (((wxRealPoint *)(lineShape->GetLineControlPoints()->Item(i)->GetData())) == lpt->m_point)
1870       break;
1871 
1872   // N.B. in OnMoveControlPoint, an event handler in Hardy could have deselected
1873   // the line and therefore deleted 'this'. -> GPF, intermittently.
1874   // So assume at this point that we've been blown away.
1875 
1876   lineShape->OnMoveControlPoint(i+1, x, y);
1877 #endif
1878 }
1879 
1880 // This is called only when a non-end control point is moved.
OnMoveMiddleControlPoint(wxDC & dc,wxLineControlPoint * lpt,const wxRealPoint & pt)1881 bool wxLineShape::OnMoveMiddleControlPoint(wxDC& dc, wxLineControlPoint* lpt, const wxRealPoint& pt)
1882 {
1883     lpt->m_xpos = pt.x; lpt->m_ypos = pt.y;
1884     lpt->m_point->x = pt.x; lpt->m_point->y = pt.y;
1885 
1886     GetEventHandler()->OnMoveLink(dc);
1887 
1888     return true;
1889 }
1890 
1891 // Implement movement of endpoint to a new attachment
1892 // OBSOLETE: done by dragging with the left button.
1893 
1894 #if 0
1895 void wxLineControlPoint::OnDragRight(bool draw, double x, double y, int keys, int attachment)
1896 {
1897   if (m_type == CONTROL_POINT_ENDPOINT_FROM || m_type == CONTROL_POINT_ENDPOINT_TO)
1898   {
1899     m_xpos = x; m_ypos = y;
1900   }
1901 }
1902 
1903 void wxLineControlPoint::OnBeginDragRight(double x, double y, int keys, int attachment)
1904 {
1905   wxClientDC dc(GetCanvas());
1906   GetCanvas()->PrepareDC(dc);
1907 
1908   wxLineShape *lineShape = (wxLineShape *)m_shape;
1909   if (m_type == CONTROL_POINT_ENDPOINT_FROM || m_type == CONTROL_POINT_ENDPOINT_TO)
1910   {
1911     Erase(dc);
1912     lineShape->GetEventHandler()->OnDraw(dc);
1913     if (m_type == CONTROL_POINT_ENDPOINT_FROM)
1914     {
1915       lineShape->GetFrom()->GetEventHandler()->OnDraw(dc);
1916       lineShape->GetFrom()->GetEventHandler()->OnDrawContents(dc);
1917     }
1918     else
1919     {
1920       lineShape->GetTo()->GetEventHandler()->OnDraw(dc);
1921       lineShape->GetTo()->GetEventHandler()->OnDrawContents(dc);
1922     }
1923     m_canvas->SetCursor(wxCursor(wxCURSOR_BULLSEYE));
1924     m_oldCursor = wxSTANDARD_CURSOR;
1925   }
1926 }
1927 
1928 void wxLineControlPoint::OnEndDragRight(double x, double y, int keys, int attachment)
1929 {
1930   wxClientDC dc(GetCanvas());
1931   GetCanvas()->PrepareDC(dc);
1932 
1933   wxLineShape *lineShape = (wxLineShape *)m_shape;
1934   if (m_type == CONTROL_POINT_ENDPOINT_FROM)
1935   {
1936     if (m_oldCursor)
1937       m_canvas->SetCursor(m_oldCursor);
1938 
1939     m_xpos = x; m_ypos = y;
1940 
1941     if (lineShape->GetFrom())
1942     {
1943       lineShape->GetFrom()->EraseLinks(dc);
1944 
1945       int new_attachment;
1946       double distance;
1947 
1948       if (lineShape->GetFrom()->HitTest(x, y, &new_attachment, &distance))
1949         lineShape->SetAttachments(new_attachment, lineShape->GetAttachmentTo());
1950 
1951       lineShape->GetFrom()->MoveLinks(dc);
1952     }
1953   }
1954   if (m_type == CONTROL_POINT_ENDPOINT_TO)
1955   {
1956     if (m_oldCursor)
1957       m_canvas->SetCursor(m_oldCursor);
1958     m_shape->Erase(dc);
1959 
1960     m_xpos = x; m_ypos = y;
1961 
1962     if (lineShape->GetTo())
1963     {
1964       lineShape->GetTo()->EraseLinks(dc);
1965 
1966       int new_attachment;
1967       double distance;
1968       if (lineShape->GetTo()->HitTest(x, y, &new_attachment, &distance))
1969         lineShape->SetAttachments(lineShape->GetAttachmentFrom(), new_attachment);
1970 
1971       lineShape->GetTo()->MoveLinks(dc);
1972     }
1973   }
1974   int i = 0;
1975   for (i = 0; i < lineShape->GetLineControlPoints()->GetCount(); i++)
1976     if (((wxRealPoint *)(lineShape->GetLineControlPoints()->Item(i)->GetData())) == m_point)
1977       break;
1978   lineShape->OnMoveControlPoint(i+1, x, y);
1979   if (!m_canvas->GetQuickEditMode()) m_canvas->Redraw(dc);
1980 }
1981 #endif
1982 
1983 /*
1984  * Get the point on the given line (x1, y1) (x2, y2)
1985  * distance 'length' along from the end,
1986  * returned values in x and y
1987  */
1988 
GetPointOnLine(double x1,double y1,double x2,double y2,double length,double * x,double * y)1989 void GetPointOnLine(double x1, double y1, double x2, double y2,
1990                     double length, double *x, double *y)
1991 {
1992   double l = (double)sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1));
1993 
1994   if (l < 0.01)
1995     l = (double) 0.01;
1996 
1997   double i_bar = (x2 - x1)/l;
1998   double j_bar = (y2 - y1)/l;
1999 
2000   *x = (- length*i_bar) + x2;
2001   *y = (- length*j_bar) + y2;
2002 }
2003 
AddArrow(WXTYPE type,int end,double size,double xOffset,const wxString & name,wxPseudoMetaFile * mf,long arrowId)2004 wxArrowHead *wxLineShape::AddArrow(WXTYPE type, int end, double size, double xOffset,
2005     const wxString& name, wxPseudoMetaFile *mf, long arrowId)
2006 {
2007   wxArrowHead *arrow = new wxArrowHead(type, end, size, xOffset, name, mf, arrowId);
2008   m_arcArrows.Append(arrow);
2009   return arrow;
2010 }
2011 
2012 /*
2013  * Add arrowhead at a particular position in the arrowhead list.
2014  */
AddArrowOrdered(wxArrowHead * arrow,wxList & referenceList,int end)2015 bool wxLineShape::AddArrowOrdered(wxArrowHead *arrow, wxList& referenceList, int end)
2016 {
2017   wxNode *refNode = referenceList.GetFirst();
2018   wxNode *currNode = m_arcArrows.GetFirst();
2019   wxString targetName(arrow->GetName());
2020   if (!refNode) return false;
2021 
2022   // First check whether we need to insert in front of list,
2023   // because this arrowhead is the first in the reference
2024   // list and should therefore be first in the current list.
2025   wxArrowHead *refArrow = (wxArrowHead *)refNode->GetData();
2026   if (refArrow->GetName() == targetName)
2027   {
2028     m_arcArrows.Insert(arrow);
2029     return true;
2030   }
2031 
2032   wxArrowHead *currArrow = (wxArrowHead *)currNode->GetData();
2033   while (refNode && currNode)
2034   {
2035     refArrow = (wxArrowHead *)refNode->GetData();
2036 
2037     // Matching: advance current arrow pointer
2038     if ((currArrow->GetArrowEnd() == end) &&
2039         (currArrow->GetName() == refArrow->GetName()))
2040     {
2041       currNode = currNode->GetNext(); // Could be NULL now
2042       if (currNode)
2043         currArrow = (wxArrowHead *)currNode->GetData();
2044     }
2045 
2046     // Check if we're at the correct position in the
2047     // reference list
2048     if (targetName == refArrow->GetName())
2049     {
2050       if (currNode)
2051         m_arcArrows.Insert(currNode, arrow);
2052       else
2053         m_arcArrows.Append(arrow);
2054       return true;
2055     }
2056     refNode = refNode->GetNext();
2057   }
2058   m_arcArrows.Append(arrow);
2059   return true;
2060 }
2061 
ClearArrowsAtPosition(int end)2062 void wxLineShape::ClearArrowsAtPosition(int end)
2063 {
2064   wxNode *node = m_arcArrows.GetFirst();
2065   while (node)
2066   {
2067     wxArrowHead *arrow = (wxArrowHead *)node->GetData();
2068     wxNode *next = node->GetNext();
2069     switch (end)
2070     {
2071       case -1:
2072       {
2073         delete arrow;
2074         delete node;
2075         break;
2076       }
2077       case ARROW_POSITION_START:
2078       {
2079         if (arrow->GetArrowEnd() == ARROW_POSITION_START)
2080         {
2081           delete arrow;
2082           delete node;
2083         }
2084         break;
2085       }
2086       case ARROW_POSITION_END:
2087       {
2088         if (arrow->GetArrowEnd() == ARROW_POSITION_END)
2089         {
2090           delete arrow;
2091           delete node;
2092         }
2093         break;
2094       }
2095       case ARROW_POSITION_MIDDLE:
2096       {
2097         if (arrow->GetArrowEnd() == ARROW_POSITION_MIDDLE)
2098         {
2099           delete arrow;
2100           delete node;
2101         }
2102         break;
2103       }
2104     }
2105     node = next;
2106   }
2107 }
2108 
ClearArrow(const wxString & name)2109 bool wxLineShape::ClearArrow(const wxString& name)
2110 {
2111   wxNode *node = m_arcArrows.GetFirst();
2112   while (node)
2113   {
2114     wxArrowHead *arrow = (wxArrowHead *)node->GetData();
2115     if (arrow->GetName() == name)
2116     {
2117       delete arrow;
2118       delete node;
2119       return true;
2120     }
2121     node = node->GetNext();
2122   }
2123   return false;
2124 }
2125 
2126 /*
2127  * Finds an arrowhead at the given position (if -1, any position)
2128  *
2129  */
2130 
FindArrowHead(int position,const wxString & name)2131 wxArrowHead *wxLineShape::FindArrowHead(int position, const wxString& name)
2132 {
2133   wxNode *node = m_arcArrows.GetFirst();
2134   while (node)
2135   {
2136     wxArrowHead *arrow = (wxArrowHead *)node->GetData();
2137     if (((position == -1) || (position == arrow->GetArrowEnd())) &&
2138         (arrow->GetName() == name))
2139       return arrow;
2140     node = node->GetNext();
2141   }
2142   return NULL;
2143 }
2144 
FindArrowHead(long arrowId)2145 wxArrowHead *wxLineShape::FindArrowHead(long arrowId)
2146 {
2147   wxNode *node = m_arcArrows.GetFirst();
2148   while (node)
2149   {
2150     wxArrowHead *arrow = (wxArrowHead *)node->GetData();
2151     if (arrowId == arrow->GetId())
2152       return arrow;
2153     node = node->GetNext();
2154   }
2155   return NULL;
2156 }
2157 
2158 /*
2159  * Deletes an arrowhead at the given position (if -1, any position)
2160  *
2161  */
2162 
DeleteArrowHead(int position,const wxString & name)2163 bool wxLineShape::DeleteArrowHead(int position, const wxString& name)
2164 {
2165   wxNode *node = m_arcArrows.GetFirst();
2166   while (node)
2167   {
2168     wxArrowHead *arrow = (wxArrowHead *)node->GetData();
2169     if (((position == -1) || (position == arrow->GetArrowEnd())) &&
2170         (arrow->GetName() == name))
2171     {
2172       delete arrow;
2173       delete node;
2174       return true;
2175     }
2176     node = node->GetNext();
2177   }
2178   return false;
2179 }
2180 
2181 // Overloaded DeleteArrowHead: pass arrowhead id.
DeleteArrowHead(long id)2182 bool wxLineShape::DeleteArrowHead(long id)
2183 {
2184   wxNode *node = m_arcArrows.GetFirst();
2185   while (node)
2186   {
2187     wxArrowHead *arrow = (wxArrowHead *)node->GetData();
2188     if (arrow->GetId() == id)
2189     {
2190       delete arrow;
2191       delete node;
2192       return true;
2193     }
2194     node = node->GetNext();
2195   }
2196   return false;
2197 }
2198 
2199 /*
2200  * Calculate the minimum width a line
2201  * occupies, for the purposes of drawing lines in tools.
2202  *
2203  */
2204 
FindMinimumWidth()2205 double wxLineShape::FindMinimumWidth()
2206 {
2207   double minWidth = 0.0;
2208   wxNode *node = m_arcArrows.GetFirst();
2209   while (node)
2210   {
2211     wxArrowHead *arrowHead = (wxArrowHead *)node->GetData();
2212     minWidth += arrowHead->GetSize();
2213     if (node->GetNext())
2214       minWidth += arrowHead->GetSpacing();
2215 
2216     node = node->GetNext();
2217   }
2218   // We have ABSOLUTE minimum now. So
2219   // scale it to give it reasonable aesthetics
2220   // when drawing with line.
2221   if (minWidth > 0.0)
2222     minWidth = (double)(minWidth * 1.4);
2223   else
2224     minWidth = 20.0;
2225 
2226   SetEnds(0.0, 0.0, minWidth, 0.0);
2227   Initialise();
2228 
2229   return minWidth;
2230 }
2231 
2232 // Find which position we're talking about at this (x, y).
2233 // Returns ARROW_POSITION_START, ARROW_POSITION_MIDDLE, ARROW_POSITION_END
FindLinePosition(double x,double y)2234 int wxLineShape::FindLinePosition(double x, double y)
2235 {
2236   double startX, startY, endX, endY;
2237   GetEnds(&startX, &startY, &endX, &endY);
2238 
2239   // Find distances from centre, start and end. The smallest wins.
2240   double centreDistance = (double)(sqrt((x - m_xpos)*(x - m_xpos) + (y - m_ypos)*(y - m_ypos)));
2241   double startDistance = (double)(sqrt((x - startX)*(x - startX) + (y - startY)*(y - startY)));
2242   double endDistance = (double)(sqrt((x - endX)*(x - endX) + (y - endY)*(y - endY)));
2243 
2244   if (centreDistance < startDistance && centreDistance < endDistance)
2245     return ARROW_POSITION_MIDDLE;
2246   else if (startDistance < endDistance)
2247     return ARROW_POSITION_START;
2248   else
2249     return ARROW_POSITION_END;
2250 }
2251 
2252 // Set alignment flags
SetAlignmentOrientation(bool isEnd,bool isHoriz)2253 void wxLineShape::SetAlignmentOrientation(bool isEnd, bool isHoriz)
2254 {
2255   if (isEnd)
2256   {
2257     if (isHoriz && ((m_alignmentEnd & LINE_ALIGNMENT_HORIZ) != LINE_ALIGNMENT_HORIZ))
2258       m_alignmentEnd |= LINE_ALIGNMENT_HORIZ;
2259     else if (!isHoriz && ((m_alignmentEnd & LINE_ALIGNMENT_HORIZ) == LINE_ALIGNMENT_HORIZ))
2260       m_alignmentEnd -= LINE_ALIGNMENT_HORIZ;
2261   }
2262   else
2263   {
2264     if (isHoriz && ((m_alignmentStart & LINE_ALIGNMENT_HORIZ) != LINE_ALIGNMENT_HORIZ))
2265       m_alignmentStart |= LINE_ALIGNMENT_HORIZ;
2266     else if (!isHoriz && ((m_alignmentStart & LINE_ALIGNMENT_HORIZ) == LINE_ALIGNMENT_HORIZ))
2267       m_alignmentStart -= LINE_ALIGNMENT_HORIZ;
2268   }
2269 }
2270 
SetAlignmentType(bool isEnd,int alignType)2271 void wxLineShape::SetAlignmentType(bool isEnd, int alignType)
2272 {
2273   if (isEnd)
2274   {
2275     if (alignType == LINE_ALIGNMENT_TO_NEXT_HANDLE)
2276     {
2277       if ((m_alignmentEnd & LINE_ALIGNMENT_TO_NEXT_HANDLE) != LINE_ALIGNMENT_TO_NEXT_HANDLE)
2278         m_alignmentEnd |= LINE_ALIGNMENT_TO_NEXT_HANDLE;
2279     }
2280     else if ((m_alignmentEnd & LINE_ALIGNMENT_TO_NEXT_HANDLE) == LINE_ALIGNMENT_TO_NEXT_HANDLE)
2281       m_alignmentEnd -= LINE_ALIGNMENT_TO_NEXT_HANDLE;
2282   }
2283   else
2284   {
2285     if (alignType == LINE_ALIGNMENT_TO_NEXT_HANDLE)
2286     {
2287       if ((m_alignmentStart & LINE_ALIGNMENT_TO_NEXT_HANDLE) != LINE_ALIGNMENT_TO_NEXT_HANDLE)
2288         m_alignmentStart |= LINE_ALIGNMENT_TO_NEXT_HANDLE;
2289     }
2290     else if ((m_alignmentStart & LINE_ALIGNMENT_TO_NEXT_HANDLE) == LINE_ALIGNMENT_TO_NEXT_HANDLE)
2291       m_alignmentStart -= LINE_ALIGNMENT_TO_NEXT_HANDLE;
2292   }
2293 }
2294 
GetAlignmentOrientation(bool isEnd)2295 bool wxLineShape::GetAlignmentOrientation(bool isEnd)
2296 {
2297   if (isEnd)
2298     return ((m_alignmentEnd & LINE_ALIGNMENT_HORIZ) == LINE_ALIGNMENT_HORIZ);
2299   else
2300     return ((m_alignmentStart & LINE_ALIGNMENT_HORIZ) == LINE_ALIGNMENT_HORIZ);
2301 }
2302 
GetAlignmentType(bool isEnd)2303 int wxLineShape::GetAlignmentType(bool isEnd)
2304 {
2305   if (isEnd)
2306     return (m_alignmentEnd & LINE_ALIGNMENT_TO_NEXT_HANDLE);
2307   else
2308     return (m_alignmentStart & LINE_ALIGNMENT_TO_NEXT_HANDLE);
2309 }
2310 
GetNextControlPoint(wxShape * nodeObject)2311 wxRealPoint *wxLineShape::GetNextControlPoint(wxShape *nodeObject)
2312 {
2313   int n = m_lineControlPoints->GetCount();
2314   int nn;
2315   if (m_to == nodeObject)
2316   {
2317     // Must be END of line, so we want (n - 1)th control point.
2318     // But indexing ends at n-1, so subtract 2.
2319     nn = n - 2;
2320   }
2321   else nn = 1;
2322   wxNode *node = m_lineControlPoints->Item(nn);
2323   if (node)
2324   {
2325     return (wxRealPoint *)node->GetData();
2326   }
2327   else
2328     return NULL;
2329 }
2330 
2331 /*
2332  * Arrowhead
2333  *
2334  */
2335 
IMPLEMENT_DYNAMIC_CLASS(wxArrowHead,wxObject)2336 IMPLEMENT_DYNAMIC_CLASS(wxArrowHead, wxObject)
2337 
2338 wxArrowHead::wxArrowHead(WXTYPE type, int end, double size, double dist, const wxString& name,
2339                      wxPseudoMetaFile *mf, long arrowId)
2340 {
2341   m_arrowType = type; m_arrowEnd = end; m_arrowSize = size;
2342   m_xOffset = dist;
2343   m_yOffset = 0.0;
2344   m_spacing = 5.0;
2345 
2346   m_arrowName = name;
2347   m_metaFile = mf;
2348   m_id = arrowId;
2349   if (m_id == -1)
2350     m_id = wxNewId();
2351 }
2352 
wxArrowHead(wxArrowHead & toCopy)2353 wxArrowHead::wxArrowHead(wxArrowHead& toCopy):wxObject()
2354 {
2355   m_arrowType = toCopy.m_arrowType; m_arrowEnd = toCopy.GetArrowEnd();
2356   m_arrowSize = toCopy.m_arrowSize;
2357   m_xOffset = toCopy.m_xOffset;
2358   m_yOffset = toCopy.m_yOffset;
2359   m_spacing = toCopy.m_spacing;
2360   m_arrowName = toCopy.m_arrowName ;
2361   if (toCopy.m_metaFile)
2362     m_metaFile = new wxPseudoMetaFile(*(toCopy.m_metaFile));
2363   else
2364     m_metaFile = NULL;
2365   m_id = wxNewId();
2366 }
2367 
~wxArrowHead()2368 wxArrowHead::~wxArrowHead()
2369 {
2370   if (m_metaFile) delete m_metaFile;
2371 }
2372 
SetSize(double size)2373 void wxArrowHead::SetSize(double size)
2374 {
2375   m_arrowSize = size;
2376   if ((m_arrowType == ARROW_METAFILE) && m_metaFile)
2377   {
2378     double oldWidth = m_metaFile->m_width;
2379     if (oldWidth == 0.0)
2380       return;
2381 
2382     double scale = (double)(size/oldWidth);
2383     if (scale != 1.0)
2384       m_metaFile->Scale(scale, scale);
2385   }
2386 }
2387 
2388 // Can override this to create a different class of label shape
OnCreateLabelShape(wxLineShape * parent,wxShapeRegion * region,double w,double h)2389 wxLabelShape* wxLineShape::OnCreateLabelShape(wxLineShape *parent, wxShapeRegion *region, double w, double h)
2390 {
2391     return new wxLabelShape(parent, region, w, h);
2392 }
2393 
2394 /*
2395  * Label object
2396  *
2397  */
2398 
IMPLEMENT_DYNAMIC_CLASS(wxLabelShape,wxRectangleShape)2399 IMPLEMENT_DYNAMIC_CLASS(wxLabelShape, wxRectangleShape)
2400 
2401 wxLabelShape::wxLabelShape(wxLineShape *parent, wxShapeRegion *region, double w, double h):wxRectangleShape(w, h)
2402 {
2403   m_lineShape = parent;
2404   m_shapeRegion = region;
2405   SetPen(wxThePenList->FindOrCreatePen(*wxBLACK, 1, wxDOT));
2406 }
2407 
~wxLabelShape()2408 wxLabelShape::~wxLabelShape()
2409 {
2410 }
2411 
OnDraw(wxDC & dc)2412 void wxLabelShape::OnDraw(wxDC& dc)
2413 {
2414   if (m_lineShape && !m_lineShape->GetDrawHandles())
2415     return;
2416 
2417     double x1 = (double)(m_xpos - m_width/2.0);
2418     double y1 = (double)(m_ypos - m_height/2.0);
2419 
2420     if (m_pen)
2421     {
2422       if (m_pen->GetWidth() == 0)
2423         dc.SetPen(* g_oglTransparentPen);
2424       else
2425         dc.SetPen(* m_pen);
2426     }
2427     dc.SetBrush(* wxTRANSPARENT_BRUSH);
2428 
2429     if (m_cornerRadius > 0.0)
2430       dc.DrawRoundedRectangle(WXROUND(x1), WXROUND(y1), WXROUND(m_width), WXROUND(m_height), m_cornerRadius);
2431     else
2432       dc.DrawRectangle(WXROUND(x1), WXROUND(y1), WXROUND(m_width), WXROUND(m_height));
2433 }
2434 
OnDrawContents(wxDC & WXUNUSED (dc))2435 void wxLabelShape::OnDrawContents(wxDC& WXUNUSED(dc))
2436 {
2437 }
2438 
OnDragLeft(bool draw,double x,double y,int keys,int attachment)2439 void wxLabelShape::OnDragLeft(bool draw, double x, double y, int keys, int attachment)
2440 {
2441   wxRectangleShape::OnDragLeft(draw, x, y, keys, attachment);
2442 }
2443 
OnBeginDragLeft(double x,double y,int keys,int attachment)2444 void wxLabelShape::OnBeginDragLeft(double x, double y, int keys, int attachment)
2445 {
2446   wxRectangleShape::OnBeginDragLeft(x, y, keys, attachment);
2447 }
2448 
OnEndDragLeft(double x,double y,int keys,int attachment)2449 void wxLabelShape::OnEndDragLeft(double x, double y, int keys, int attachment)
2450 {
2451   wxRectangleShape::OnEndDragLeft(x, y, keys, attachment);
2452 }
2453 
OnMovePre(wxDC & dc,double x,double y,double old_x,double old_y,bool display)2454 bool wxLabelShape::OnMovePre(wxDC& dc, double x, double y, double old_x, double old_y, bool display)
2455 {
2456     return m_lineShape->OnLabelMovePre(dc, this, x, y, old_x, old_y, display);
2457 }
2458 
OnLabelMovePre(wxDC & dc,wxLabelShape * labelShape,double x,double y,double WXUNUSED (old_x),double WXUNUSED (old_y),bool WXUNUSED (display))2459 bool wxLineShape::OnLabelMovePre(wxDC& dc, wxLabelShape* labelShape, double x, double y, double WXUNUSED(old_x), double WXUNUSED(old_y), bool WXUNUSED(display))
2460 {
2461   labelShape->m_shapeRegion->SetSize(labelShape->GetWidth(), labelShape->GetHeight());
2462 
2463   // Find position in line's region list
2464   int i = 0;
2465   wxNode *node = GetRegions().GetFirst();
2466   while (node)
2467   {
2468     if (labelShape->m_shapeRegion == (wxShapeRegion *)node->GetData())
2469       node = NULL;
2470     else
2471     {
2472       node = node->GetNext();
2473       i ++;
2474     }
2475   }
2476   double xx, yy;
2477   GetLabelPosition(i, &xx, &yy);
2478   // Set the region's offset, relative to the default position for
2479   // each region.
2480   labelShape->m_shapeRegion->SetPosition((double)(x - xx), (double)(y - yy));
2481 
2482   labelShape->SetX(x);
2483   labelShape->SetY(y);
2484 
2485   // Need to reformat to fit region.
2486   if (labelShape->m_shapeRegion->GetText())
2487   {
2488 
2489     wxString s(labelShape->m_shapeRegion->GetText());
2490     labelShape->FormatText(dc, s, i);
2491     DrawRegion(dc, labelShape->m_shapeRegion, xx, yy);
2492   }
2493   return true;
2494 }
2495 
2496 // Divert left and right clicks to line object
OnLeftClick(double x,double y,int keys,int attachment)2497 void wxLabelShape::OnLeftClick(double x, double y, int keys, int attachment)
2498 {
2499   m_lineShape->GetEventHandler()->OnLeftClick(x, y, keys, attachment);
2500 }
2501 
OnRightClick(double x,double y,int keys,int attachment)2502 void wxLabelShape::OnRightClick(double x, double y, int keys, int attachment)
2503 {
2504   m_lineShape->GetEventHandler()->OnRightClick(x, y, keys, attachment);
2505 }
2506