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