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