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