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