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