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