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