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