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