]> git.saurik.com Git - wxWidgets.git/blob - utils/ogl/src/basic2.cpp
Corrected link error for missing wxRegTipProvider::GetTip by giving it
[wxWidgets.git] / utils / ogl / src / basic2.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: basic2.cpp
3 // Purpose: Basic OGL classes (2)
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 "basicp.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 #include <wx/wxexpr.h>
28
29 #if wxUSE_IOSTREAMH
30 #include <iostream.h>
31 #else
32 #include <iostream>
33 #endif
34
35 #include <stdio.h>
36 #include <ctype.h>
37 #include <math.h>
38
39 #include "basic.h"
40 #include "basicp.h"
41 #include "composit.h"
42 #include "lines.h"
43 #include "canvas.h"
44 #include "divided.h"
45 #include "misc.h"
46
47 // Control point types
48 // Rectangle and most other shapes
49 #define CONTROL_POINT_VERTICAL 1
50 #define CONTROL_POINT_HORIZONTAL 2
51 #define CONTROL_POINT_DIAGONAL 3
52
53 // Line
54 #define CONTROL_POINT_ENDPOINT_TO 4
55 #define CONTROL_POINT_ENDPOINT_FROM 5
56 #define CONTROL_POINT_LINE 6
57
58 // Two stage construction: need to call Create
59 IMPLEMENT_DYNAMIC_CLASS(wxPolygonShape, wxShape)
60
61 wxPolygonShape::wxPolygonShape()
62 {
63 m_points = NULL;
64 m_originalPoints = NULL;
65 }
66
67 void wxPolygonShape::Create(wxList *the_points)
68 {
69 ClearPoints();
70
71 m_originalPoints = the_points;
72
73 // Duplicate the list of points
74 m_points = new wxList;
75
76 wxNode *node = the_points->First();
77 while (node)
78 {
79 wxRealPoint *point = (wxRealPoint *)node->Data();
80 wxRealPoint *new_point = new wxRealPoint(point->x, point->y);
81 m_points->Append((wxObject*) new_point);
82 node = node->Next();
83 }
84 CalculateBoundingBox();
85 m_originalWidth = m_boundWidth;
86 m_originalHeight = m_boundHeight;
87 SetDefaultRegionSize();
88 }
89
90 wxPolygonShape::~wxPolygonShape()
91 {
92 ClearPoints();
93 }
94
95 void wxPolygonShape::ClearPoints()
96 {
97 if (m_points)
98 {
99 wxNode *node = m_points->First();
100 while (node)
101 {
102 wxRealPoint *point = (wxRealPoint *)node->Data();
103 delete point;
104 delete node;
105 node = m_points->First();
106 }
107 delete m_points;
108 m_points = NULL;
109 }
110 if (m_originalPoints)
111 {
112 wxNode *node = m_originalPoints->First();
113 while (node)
114 {
115 wxRealPoint *point = (wxRealPoint *)node->Data();
116 delete point;
117 delete node;
118 node = m_originalPoints->First();
119 }
120 delete m_originalPoints;
121 m_originalPoints = NULL;
122 }
123 }
124
125
126 // Width and height. Centre of object is centre of box.
127 void wxPolygonShape::GetBoundingBoxMin(double *width, double *height)
128 {
129 *width = m_boundWidth;
130 *height = m_boundHeight;
131 }
132
133 void wxPolygonShape::CalculateBoundingBox()
134 {
135 // Calculate bounding box at construction (and presumably resize) time
136 double left = 10000;
137 double right = -10000;
138 double top = 10000;
139 double bottom = -10000;
140
141 wxNode *node = m_points->First();
142 while (node)
143 {
144 wxRealPoint *point = (wxRealPoint *)node->Data();
145 if (point->x < left) left = point->x;
146 if (point->x > right) right = point->x;
147
148 if (point->y < top) top = point->y;
149 if (point->y > bottom) bottom = point->y;
150
151 node = node->Next();
152 }
153 m_boundWidth = right - left;
154 m_boundHeight = bottom - top;
155 }
156
157 // Recalculates the centre of the polygon, and
158 // readjusts the point offsets accordingly.
159 // Necessary since the centre of the polygon
160 // is expected to be the real centre of the bounding
161 // box.
162 void wxPolygonShape::CalculatePolygonCentre()
163 {
164 double left = 10000;
165 double right = -10000;
166 double top = 10000;
167 double bottom = -10000;
168
169 wxNode *node = m_points->First();
170 while (node)
171 {
172 wxRealPoint *point = (wxRealPoint *)node->Data();
173 if (point->x < left) left = point->x;
174 if (point->x > right) right = point->x;
175
176 if (point->y < top) top = point->y;
177 if (point->y > bottom) bottom = point->y;
178
179 node = node->Next();
180 }
181 double bwidth = right - left;
182 double bheight = bottom - top;
183
184 double newCentreX = (double)(left + (bwidth/2.0));
185 double newCentreY = (double)(top + (bheight/2.0));
186
187 node = m_points->First();
188 while (node)
189 {
190 wxRealPoint *point = (wxRealPoint *)node->Data();
191 point->x -= newCentreX;
192 point->y -= newCentreY;
193 node = node->Next();
194 }
195 m_xpos += newCentreX;
196 m_ypos += newCentreY;
197 }
198
199 bool PolylineHitTest(double n, double xvec[], double yvec[],
200 double x1, double y1, double x2, double y2)
201 {
202 bool isAHit = FALSE;
203 int i;
204 double lastx = xvec[0];
205 double lasty = yvec[0];
206
207 double min_ratio = 1.0;
208 double line_ratio;
209 double other_ratio;
210
211 // char buf[300];
212 for (i = 1; i < n; i++)
213 {
214 oglCheckLineIntersection(x1, y1, x2, y2, lastx, lasty, xvec[i], yvec[i],
215 &line_ratio, &other_ratio);
216 if (line_ratio != 1.0)
217 isAHit = TRUE;
218 // sprintf(buf, "Line ratio = %.2f, other ratio = %.2f\n", line_ratio, other_ratio);
219 // ClipsErrorFunction(buf);
220 lastx = xvec[i];
221 lasty = yvec[i];
222
223 if (line_ratio < min_ratio)
224 min_ratio = line_ratio;
225 }
226
227 // Do last (implicit) line if last and first doubles are not identical
228 if (!(xvec[0] == lastx && yvec[0] == lasty))
229 {
230 oglCheckLineIntersection(x1, y1, x2, y2, lastx, lasty, xvec[0], yvec[0],
231 &line_ratio, &other_ratio);
232 if (line_ratio != 1.0)
233 isAHit = TRUE;
234 // sprintf(buf, "Line ratio = %.2f, other ratio = %.2f\n", line_ratio, other_ratio);
235 // ClipsErrorFunction(buf);
236
237 if (line_ratio < min_ratio)
238 min_ratio = line_ratio;
239 }
240 // ClipsErrorFunction("\n");
241 return isAHit;
242 }
243
244 bool wxPolygonShape::HitTest(double x, double y, int *attachment, double *distance)
245 {
246 // Imagine four lines radiating from this point. If all of these lines hit the polygon,
247 // we're inside it, otherwise we're not. Obviously we'd need more radiating lines
248 // to be sure of correct results for very strange (concave) shapes.
249 double endPointsX[4];
250 double endPointsY[4];
251 // North
252 endPointsX[0] = x;
253 endPointsY[0] = (double)(y - 1000.0);
254 // East
255 endPointsX[1] = (double)(x + 1000.0);
256 endPointsY[1] = y;
257 // South
258 endPointsX[2] = x;
259 endPointsY[2] = (double)(y + 1000.0);
260 // West
261 endPointsX[3] = (double)(x - 1000.0);
262 endPointsY[3] = y;
263
264 // Store polygon points in an array
265 int np = m_points->Number();
266 double *xpoints = new double[np];
267 double *ypoints = new double[np];
268 wxNode *node = m_points->First();
269 int i = 0;
270 while (node)
271 {
272 wxRealPoint *point = (wxRealPoint *)node->Data();
273 xpoints[i] = point->x + m_xpos;
274 ypoints[i] = point->y + m_ypos;
275 node = node->Next();
276 i ++;
277 }
278
279 // We assume it's inside the polygon UNLESS one or more
280 // lines don't hit the outline.
281 bool isContained = TRUE;
282
283 int noPoints = 4;
284 for (i = 0; i < noPoints; i++)
285 {
286 if (!PolylineHitTest(np, xpoints, ypoints, x, y, endPointsX[i], endPointsY[i]))
287 isContained = FALSE;
288 }
289 /*
290 if (isContained)
291 ClipsErrorFunction("It's a hit!\n");
292 else
293 ClipsErrorFunction("No hit.\n");
294 */
295 delete[] xpoints;
296 delete[] ypoints;
297
298 if (!isContained)
299 return FALSE;
300
301 int nearest_attachment = 0;
302
303 // If a hit, check the attachment points within the object.
304 int n = GetNumberOfAttachments();
305 double nearest = 999999.0;
306
307 for (i = 0; i < n; i++)
308 {
309 double xp, yp;
310 if (GetAttachmentPositionEdge(i, &xp, &yp))
311 {
312 double l = (double)sqrt(((xp - x) * (xp - x)) +
313 ((yp - y) * (yp - y)));
314 if (l < nearest)
315 {
316 nearest = l;
317 nearest_attachment = i;
318 }
319 }
320 }
321 *attachment = nearest_attachment;
322 *distance = nearest;
323 return TRUE;
324 }
325
326 // Really need to be able to reset the shape! Otherwise, if the
327 // points ever go to zero, we've lost it, and can't resize.
328 void wxPolygonShape::SetSize(double new_width, double new_height, bool recursive)
329 {
330 SetAttachmentSize(new_width, new_height);
331
332 // Multiply all points by proportion of new size to old size
333 double x_proportion = (double)(fabs(new_width/m_originalWidth));
334 double y_proportion = (double)(fabs(new_height/m_originalHeight));
335
336 wxNode *node = m_points->First();
337 wxNode *original_node = m_originalPoints->First();
338 while (node && original_node)
339 {
340 wxRealPoint *point = (wxRealPoint *)node->Data();
341 wxRealPoint *original_point = (wxRealPoint *)original_node->Data();
342
343 point->x = (original_point->x * x_proportion);
344 point->y = (original_point->y * y_proportion);
345
346 node = node->Next();
347 original_node = original_node->Next();
348 }
349
350 // CalculateBoundingBox();
351 m_boundWidth = (double)fabs(new_width);
352 m_boundHeight = (double)fabs(new_height);
353 SetDefaultRegionSize();
354 }
355
356 // Make the original points the same as the working points
357 void wxPolygonShape::UpdateOriginalPoints()
358 {
359 if (!m_originalPoints) m_originalPoints = new wxList;
360 wxNode *original_node = m_originalPoints->First();
361 while (original_node)
362 {
363 wxNode *next_node = original_node->Next();
364 wxRealPoint *original_point = (wxRealPoint *)original_node->Data();
365 delete original_point;
366 delete original_node;
367
368 original_node = next_node;
369 }
370
371 wxNode *node = m_points->First();
372 while (node)
373 {
374 wxRealPoint *point = (wxRealPoint *)node->Data();
375 wxRealPoint *original_point = new wxRealPoint(point->x, point->y);
376 m_originalPoints->Append((wxObject*) original_point);
377
378 node = node->Next();
379 }
380 CalculateBoundingBox();
381 m_originalWidth = m_boundWidth;
382 m_originalHeight = m_boundHeight;
383 }
384
385 void wxPolygonShape::AddPolygonPoint(int pos)
386 {
387 wxNode *node = m_points->Nth(pos);
388 if (!node) node = m_points->First();
389 wxRealPoint *firstPoint = (wxRealPoint *)node->Data();
390
391 wxNode *node2 = m_points->Nth(pos + 1);
392 if (!node2) node2 = m_points->First();
393 wxRealPoint *secondPoint = (wxRealPoint *)node2->Data();
394
395 double x = (double)((secondPoint->x - firstPoint->x)/2.0 + firstPoint->x);
396 double y = (double)((secondPoint->y - firstPoint->y)/2.0 + firstPoint->y);
397 wxRealPoint *point = new wxRealPoint(x, y);
398
399 if (pos >= (m_points->Number() - 1))
400 m_points->Append((wxObject*) point);
401 else
402 m_points->Insert(node2, (wxObject*) point);
403
404 UpdateOriginalPoints();
405
406 if (m_selected)
407 {
408 DeleteControlPoints();
409 MakeControlPoints();
410 }
411 }
412
413 void wxPolygonShape::DeletePolygonPoint(int pos)
414 {
415 wxNode *node = m_points->Nth(pos);
416 if (node)
417 {
418 wxRealPoint *point = (wxRealPoint *)node->Data();
419 delete point;
420 delete node;
421 UpdateOriginalPoints();
422 if (m_selected)
423 {
424 DeleteControlPoints();
425 MakeControlPoints();
426 }
427 }
428 }
429
430 // Assume (x1, y1) is centre of box (most generally, line end at box)
431 bool wxPolygonShape::GetPerimeterPoint(double x1, double y1,
432 double x2, double y2,
433 double *x3, double *y3)
434 {
435 int n = m_points->Number();
436
437 // First check for situation where the line is vertical,
438 // and we would want to connect to a point on that vertical --
439 // oglFindEndForPolyline can't cope with this (the arrow
440 // gets drawn to the wrong place).
441 if ((m_attachmentMode == ATTACHMENT_MODE_NONE) && (x1 == x2))
442 {
443 // Look for the point we'd be connecting to. This is
444 // a heuristic...
445 wxNode *node = m_points->First();
446 while (node)
447 {
448 wxRealPoint *point = (wxRealPoint *)node->Data();
449 if (point->x == 0.0)
450 {
451 if ((y2 > y1) && (point->y > 0.0))
452 {
453 *x3 = point->x + m_xpos;
454 *y3 = point->y + m_ypos;
455 return TRUE;
456 }
457 else if ((y2 < y1) && (point->y < 0.0))
458 {
459 *x3 = point->x + m_xpos;
460 *y3 = point->y + m_ypos;
461 return TRUE;
462 }
463 }
464 node = node->Next();
465 }
466 }
467
468 double *xpoints = new double[n];
469 double *ypoints = new double[n];
470
471 wxNode *node = m_points->First();
472 int i = 0;
473 while (node)
474 {
475 wxRealPoint *point = (wxRealPoint *)node->Data();
476 xpoints[i] = point->x + m_xpos;
477 ypoints[i] = point->y + m_ypos;
478 node = node->Next();
479 i ++;
480 }
481
482 oglFindEndForPolyline(n, xpoints, ypoints,
483 x1, y1, x2, y2, x3, y3);
484
485 delete[] xpoints;
486 delete[] ypoints;
487
488 return TRUE;
489 }
490
491 void wxPolygonShape::OnDraw(wxDC& dc)
492 {
493 int n = m_points->Number();
494 wxPoint *intPoints = new wxPoint[n];
495 int i;
496 for (i = 0; i < n; i++)
497 {
498 wxRealPoint* point = (wxRealPoint*) m_points->Nth(i)->Data();
499 intPoints[i].x = WXROUND(point->x);
500 intPoints[i].y = WXROUND(point->y);
501 }
502
503 if (m_shadowMode != SHADOW_NONE)
504 {
505 if (m_shadowBrush)
506 dc.SetBrush(* m_shadowBrush);
507 dc.SetPen(* g_oglTransparentPen);
508
509 dc.DrawPolygon(n, intPoints, WXROUND(m_xpos + m_shadowOffsetX), WXROUND(m_ypos + m_shadowOffsetY));
510 }
511
512 if (m_pen)
513 {
514 if (m_pen->GetWidth() == 0)
515 dc.SetPen(* g_oglTransparentPen);
516 else
517 dc.SetPen(* m_pen);
518 }
519 if (m_brush)
520 dc.SetBrush(* m_brush);
521 dc.DrawPolygon(n, intPoints, WXROUND(m_xpos), WXROUND(m_ypos));
522
523 delete[] intPoints;
524 }
525
526 void wxPolygonShape::OnDrawOutline(wxDC& dc, double x, double y, double w, double h)
527 {
528 dc.SetBrush(* wxTRANSPARENT_BRUSH);
529 // Multiply all points by proportion of new size to old size
530 double x_proportion = (double)(fabs(w/m_originalWidth));
531 double y_proportion = (double)(fabs(h/m_originalHeight));
532
533 int n = m_originalPoints->Number();
534 wxPoint *intPoints = new wxPoint[n];
535 int i;
536 for (i = 0; i < n; i++)
537 {
538 wxRealPoint* point = (wxRealPoint*) m_originalPoints->Nth(i)->Data();
539 intPoints[i].x = WXROUND(x_proportion * point->x);
540 intPoints[i].y = WXROUND(y_proportion * point->y);
541 }
542 dc.DrawPolygon(n, intPoints, WXROUND(x), WXROUND(y));
543 delete[] intPoints;
544 }
545
546 // Make as many control points as there are vertices.
547 void wxPolygonShape::MakeControlPoints()
548 {
549 wxNode *node = m_points->First();
550 while (node)
551 {
552 wxRealPoint *point = (wxRealPoint *)node->Data();
553 wxPolygonControlPoint *control = new wxPolygonControlPoint(m_canvas, this, CONTROL_POINT_SIZE,
554 point, point->x, point->y);
555 m_canvas->AddShape(control);
556 m_controlPoints.Append(control);
557 node = node->Next();
558 }
559 }
560
561 void wxPolygonShape::ResetControlPoints()
562 {
563 wxNode *node = m_points->First();
564 wxNode *controlPointNode = m_controlPoints.First();
565 while (node && controlPointNode)
566 {
567 wxRealPoint *point = (wxRealPoint *)node->Data();
568 wxPolygonControlPoint *controlPoint = (wxPolygonControlPoint *)controlPointNode->Data();
569
570 controlPoint->m_xoffset = point->x;
571 controlPoint->m_yoffset = point->y;
572 controlPoint->m_polygonVertex = point;
573
574 node = node->Next();
575 controlPointNode = controlPointNode->Next();
576 }
577 }
578
579
580 #ifdef PROLOGIO
581 void wxPolygonShape::WriteAttributes(wxExpr *clause)
582 {
583 wxShape::WriteAttributes(clause);
584
585 clause->AddAttributeValue("x", m_xpos);
586 clause->AddAttributeValue("y", m_ypos);
587
588 // Make a list of lists for the coordinates
589 wxExpr *list = new wxExpr(wxExprList);
590 wxNode *node = m_points->First();
591 while (node)
592 {
593 wxRealPoint *point = (wxRealPoint *)node->Data();
594 wxExpr *point_list = new wxExpr(wxExprList);
595 wxExpr *x_expr = new wxExpr((double)point->x);
596 wxExpr *y_expr = new wxExpr((double)point->y);
597
598 point_list->Append(x_expr);
599 point_list->Append(y_expr);
600 list->Append(point_list);
601
602 node = node->Next();
603 }
604 clause->AddAttributeValue("points", list);
605
606 // Save the original (unscaled) points
607 list = new wxExpr(wxExprList);
608 node = m_originalPoints->First();
609 while (node)
610 {
611 wxRealPoint *point = (wxRealPoint *)node->Data();
612 wxExpr *point_list = new wxExpr(wxExprList);
613 wxExpr *x_expr = new wxExpr((double) point->x);
614 wxExpr *y_expr = new wxExpr((double) point->y);
615 point_list->Append(x_expr);
616 point_list->Append(y_expr);
617 list->Append(point_list);
618
619 node = node->Next();
620 }
621 clause->AddAttributeValue("m_originalPoints", list);
622 }
623
624 void wxPolygonShape::ReadAttributes(wxExpr *clause)
625 {
626 wxShape::ReadAttributes(clause);
627
628 // Read a list of lists
629 m_points = new wxList;
630 m_originalPoints = new wxList;
631
632 wxExpr *points_list = NULL;
633 clause->AssignAttributeValue("points", &points_list);
634
635 // If no points_list, don't crash!! Assume a diamond instead.
636 double the_height = 100.0;
637 double the_width = 100.0;
638 if (!points_list)
639 {
640 wxRealPoint *point = new wxRealPoint(0.0, (-the_height/2));
641 m_points->Append((wxObject*) point);
642
643 point = new wxRealPoint((the_width/2), 0.0);
644 m_points->Append((wxObject*) point);
645
646 point = new wxRealPoint(0.0, (the_height/2));
647 m_points->Append((wxObject*) point);
648
649 point = new wxRealPoint((-the_width/2), 0.0);
650 m_points->Append((wxObject*) point);
651
652 point = new wxRealPoint(0.0, (-the_height/2));
653 m_points->Append((wxObject*) point);
654 }
655 else
656 {
657 wxExpr *node = points_list->value.first;
658
659 while (node)
660 {
661 wxExpr *xexpr = node->value.first;
662 long x = xexpr->IntegerValue();
663
664 wxExpr *yexpr = xexpr->next;
665 long y = yexpr->IntegerValue();
666
667 wxRealPoint *point = new wxRealPoint((double)x, (double)y);
668 m_points->Append((wxObject*) point);
669
670 node = node->next;
671 }
672 }
673
674 points_list = NULL;
675 clause->AssignAttributeValue("m_originalPoints", &points_list);
676
677 // If no points_list, don't crash!! Assume a diamond instead.
678 if (!points_list)
679 {
680 wxRealPoint *point = new wxRealPoint(0.0, (-the_height/2));
681 m_originalPoints->Append((wxObject*) point);
682
683 point = new wxRealPoint((the_width/2), 0.0);
684 m_originalPoints->Append((wxObject*) point);
685
686 point = new wxRealPoint(0.0, (the_height/2));
687 m_originalPoints->Append((wxObject*) point);
688
689 point = new wxRealPoint((-the_width/2), 0.0);
690 m_originalPoints->Append((wxObject*) point);
691
692 point = new wxRealPoint(0.0, (-the_height/2));
693 m_originalPoints->Append((wxObject*) point);
694
695 m_originalWidth = the_width;
696 m_originalHeight = the_height;
697 }
698 else
699 {
700 wxExpr *node = points_list->value.first;
701 double min_x = 1000;
702 double min_y = 1000;
703 double max_x = -1000;
704 double max_y = -1000;
705 while (node)
706 {
707 wxExpr *xexpr = node->value.first;
708 long x = xexpr->IntegerValue();
709
710 wxExpr *yexpr = xexpr->next;
711 long y = yexpr->IntegerValue();
712
713 wxRealPoint *point = new wxRealPoint((double)x, (double)y);
714 m_originalPoints->Append((wxObject*) point);
715
716 if (x < min_x)
717 min_x = (double)x;
718 if (y < min_y)
719 min_y = (double)y;
720 if (x > max_x)
721 max_x = (double)x;
722 if (y > max_y)
723 max_y = (double)y;
724
725 node = node->next;
726 }
727 m_originalWidth = max_x - min_x;
728 m_originalHeight = max_y - min_y;
729 }
730
731 CalculateBoundingBox();
732 }
733 #endif
734
735 void wxPolygonShape::Copy(wxShape& copy)
736 {
737 wxShape::Copy(copy);
738
739 wxASSERT( copy.IsKindOf(CLASSINFO(wxPolygonShape)) );
740
741 wxPolygonShape& polyCopy = (wxPolygonShape&) copy;
742
743 polyCopy.ClearPoints();
744
745 polyCopy.m_points = new wxList;
746 polyCopy.m_originalPoints = new wxList;
747
748 wxNode *node = m_points->First();
749 while (node)
750 {
751 wxRealPoint *point = (wxRealPoint *)node->Data();
752 wxRealPoint *new_point = new wxRealPoint(point->x, point->y);
753 polyCopy.m_points->Append((wxObject*) new_point);
754 node = node->Next();
755 }
756 node = m_originalPoints->First();
757 while (node)
758 {
759 wxRealPoint *point = (wxRealPoint *)node->Data();
760 wxRealPoint *new_point = new wxRealPoint(point->x, point->y);
761 polyCopy.m_originalPoints->Append((wxObject*) new_point);
762 node = node->Next();
763 }
764 polyCopy.m_boundWidth = m_boundWidth;
765 polyCopy.m_boundHeight = m_boundHeight;
766 polyCopy.m_originalWidth = m_originalWidth;
767 polyCopy.m_originalHeight = m_originalHeight;
768 }
769
770 int wxPolygonShape::GetNumberOfAttachments() const
771 {
772 int maxN = (m_points ? (m_points->Number() - 1) : 0);
773 wxNode *node = m_attachmentPoints.First();
774 while (node)
775 {
776 wxAttachmentPoint *point = (wxAttachmentPoint *)node->Data();
777 if (point->m_id > maxN)
778 maxN = point->m_id;
779 node = node->Next();
780 }
781 return maxN+1;;
782 }
783
784 bool wxPolygonShape::GetAttachmentPosition(int attachment, double *x, double *y,
785 int nth, int no_arcs, wxLineShape *line)
786 {
787 if ((m_attachmentMode == ATTACHMENT_MODE_EDGE) && m_points && attachment < m_points->Number())
788 {
789 wxRealPoint *point = (wxRealPoint *)m_points->Nth(attachment)->Data();
790 *x = point->x + m_xpos;
791 *y = point->y + m_ypos;
792 return TRUE;
793 }
794 else
795 { return wxShape::GetAttachmentPosition(attachment, x, y, nth, no_arcs, line); }
796 }
797
798 bool wxPolygonShape::AttachmentIsValid(int attachment)
799 {
800 if (!m_points)
801 return FALSE;
802
803 if ((attachment >= 0) && (attachment < m_points->Number()))
804 return TRUE;
805
806 wxNode *node = m_attachmentPoints.First();
807 while (node)
808 {
809 wxAttachmentPoint *point = (wxAttachmentPoint *)node->Data();
810 if (point->m_id == attachment)
811 return TRUE;
812 node = node->Next();
813 }
814 return FALSE;
815 }
816
817 // Rotate about the given axis by the given amount in radians
818 void wxPolygonShape::Rotate(double x, double y, double theta)
819 {
820 double actualTheta = theta-m_rotation;
821
822 // Rotate attachment points
823 double sinTheta = (double)sin(actualTheta);
824 double cosTheta = (double)cos(actualTheta);
825 wxNode *node = m_attachmentPoints.First();
826 while (node)
827 {
828 wxAttachmentPoint *point = (wxAttachmentPoint *)node->Data();
829 double x1 = point->m_x;
830 double y1 = point->m_y;
831 point->m_x = x1*cosTheta - y1*sinTheta + x*(1.0 - cosTheta) + y*sinTheta;
832 point->m_y = x1*sinTheta + y1*cosTheta + y*(1.0 - cosTheta) + x*sinTheta;
833 node = node->Next();
834 }
835
836 node = m_points->First();
837 while (node)
838 {
839 wxRealPoint *point = (wxRealPoint *)node->Data();
840 double x1 = point->x;
841 double y1 = point->y;
842 point->x = x1*cosTheta - y1*sinTheta + x*(1.0 - cosTheta) + y*sinTheta;
843 point->y = x1*sinTheta + y1*cosTheta + y*(1.0 - cosTheta) + x*sinTheta;
844 node = node->Next();
845 }
846 node = m_originalPoints->First();
847 while (node)
848 {
849 wxRealPoint *point = (wxRealPoint *)node->Data();
850 double x1 = point->x;
851 double y1 = point->y;
852 point->x = x1*cosTheta - y1*sinTheta + x*(1.0 - cosTheta) + y*sinTheta;
853 point->y = x1*sinTheta + y1*cosTheta + y*(1.0 - cosTheta) + x*sinTheta;
854 node = node->Next();
855 }
856
857 m_rotation = theta;
858
859 CalculatePolygonCentre();
860 CalculateBoundingBox();
861 ResetControlPoints();
862 }
863
864 // Rectangle object
865
866 IMPLEMENT_DYNAMIC_CLASS(wxRectangleShape, wxShape)
867
868 wxRectangleShape::wxRectangleShape(double w, double h)
869 {
870 m_width = w; m_height = h; m_cornerRadius = 0.0;
871 SetDefaultRegionSize();
872 }
873
874 void wxRectangleShape::OnDraw(wxDC& dc)
875 {
876 double x1 = (double)(m_xpos - m_width/2.0);
877 double y1 = (double)(m_ypos - m_height/2.0);
878
879 if (m_shadowMode != SHADOW_NONE)
880 {
881 if (m_shadowBrush)
882 dc.SetBrush(* m_shadowBrush);
883 dc.SetPen(* g_oglTransparentPen);
884
885 if (m_cornerRadius != 0.0)
886 dc.DrawRoundedRectangle(WXROUND(x1 + m_shadowOffsetX), WXROUND(y1 + m_shadowOffsetY),
887 WXROUND(m_width), WXROUND(m_height), m_cornerRadius);
888 else
889 dc.DrawRectangle(WXROUND(x1 + m_shadowOffsetX), WXROUND(y1 + m_shadowOffsetY), WXROUND(m_width), WXROUND(m_height));
890 }
891
892 if (m_pen)
893 {
894 if (m_pen->GetWidth() == 0)
895 dc.SetPen(* g_oglTransparentPen);
896 else
897 dc.SetPen(* m_pen);
898 }
899 if (m_brush)
900 dc.SetBrush(* m_brush);
901
902 if (m_cornerRadius != 0.0)
903 dc.DrawRoundedRectangle(WXROUND(x1), WXROUND(y1), WXROUND(m_width), WXROUND(m_height), m_cornerRadius);
904 else
905 dc.DrawRectangle(WXROUND(x1), WXROUND(y1), WXROUND(m_width), WXROUND(m_height));
906 }
907
908 void wxRectangleShape::GetBoundingBoxMin(double *the_width, double *the_height)
909 {
910 *the_width = m_width;
911 *the_height = m_height;
912 }
913
914 void wxRectangleShape::SetSize(double x, double y, bool recursive)
915 {
916 SetAttachmentSize(x, y);
917 m_width = (double)wxMax(x, 1.0);
918 m_height = (double)wxMax(y, 1.0);
919 SetDefaultRegionSize();
920 }
921
922 void wxRectangleShape::SetCornerRadius(double rad)
923 {
924 m_cornerRadius = rad;
925 }
926
927 // Assume (x1, y1) is centre of box (most generally, line end at box)
928 bool wxRectangleShape::GetPerimeterPoint(double x1, double y1,
929 double x2, double y2,
930 double *x3, double *y3)
931 {
932 double bound_x, bound_y;
933 GetBoundingBoxMax(&bound_x, &bound_y);
934 oglFindEndForBox(bound_x, bound_y, m_xpos, m_ypos, x2, y2, x3, y3);
935
936 return TRUE;
937 }
938
939 #ifdef PROLOGIO
940 void wxRectangleShape::WriteAttributes(wxExpr *clause)
941 {
942 wxShape::WriteAttributes(clause);
943 clause->AddAttributeValue("x", m_xpos);
944 clause->AddAttributeValue("y", m_ypos);
945
946 clause->AddAttributeValue("width", m_width);
947 clause->AddAttributeValue("height", m_height);
948 if (m_cornerRadius != 0.0)
949 clause->AddAttributeValue("corner", m_cornerRadius);
950 }
951
952 void wxRectangleShape::ReadAttributes(wxExpr *clause)
953 {
954 wxShape::ReadAttributes(clause);
955 clause->AssignAttributeValue("width", &m_width);
956 clause->AssignAttributeValue("height", &m_height);
957 clause->AssignAttributeValue("corner", &m_cornerRadius);
958
959 // In case we're reading an old file, set the region's size
960 if (m_regions.Number() == 1)
961 {
962 wxShapeRegion *region = (wxShapeRegion *)m_regions.First()->Data();
963 region->SetSize(m_width, m_height);
964 }
965 }
966 #endif
967
968 void wxRectangleShape::Copy(wxShape& copy)
969 {
970 wxShape::Copy(copy);
971
972 wxASSERT( copy.IsKindOf(CLASSINFO(wxRectangleShape)) );
973
974 wxRectangleShape& rectCopy = (wxRectangleShape&) copy;
975 rectCopy.m_width = m_width;
976 rectCopy.m_height = m_height;
977 rectCopy.m_cornerRadius = m_cornerRadius;
978 }
979
980 int wxRectangleShape::GetNumberOfAttachments() const
981 {
982 return wxShape::GetNumberOfAttachments();
983 }
984
985
986 // There are 4 attachment points on a rectangle - 0 = top, 1 = right, 2 = bottom,
987 // 3 = left.
988 bool wxRectangleShape::GetAttachmentPosition(int attachment, double *x, double *y,
989 int nth, int no_arcs, wxLineShape *line)
990 {
991 return wxShape::GetAttachmentPosition(attachment, x, y, nth, no_arcs, line);
992 }
993
994 // Text object (no box)
995
996 IMPLEMENT_DYNAMIC_CLASS(wxTextShape, wxRectangleShape)
997
998 wxTextShape::wxTextShape(double width, double height):
999 wxRectangleShape(width, height)
1000 {
1001 }
1002
1003 void wxTextShape::OnDraw(wxDC& dc)
1004 {
1005 }
1006
1007 void wxTextShape::Copy(wxShape& copy)
1008 {
1009 wxRectangleShape::Copy(copy);
1010 }
1011
1012 #ifdef PROLOGIO
1013 void wxTextShape::WriteAttributes(wxExpr *clause)
1014 {
1015 wxRectangleShape::WriteAttributes(clause);
1016 }
1017 #endif
1018
1019 // Ellipse object
1020
1021 IMPLEMENT_DYNAMIC_CLASS(wxEllipseShape, wxShape)
1022
1023 wxEllipseShape::wxEllipseShape(double w, double h)
1024 {
1025 m_width = w; m_height = h;
1026 SetDefaultRegionSize();
1027 }
1028
1029 void wxEllipseShape::GetBoundingBoxMin(double *w, double *h)
1030 {
1031 *w = m_width; *h = m_height;
1032 }
1033
1034 bool wxEllipseShape::GetPerimeterPoint(double x1, double y1,
1035 double x2, double y2,
1036 double *x3, double *y3)
1037 {
1038 double bound_x, bound_y;
1039 GetBoundingBoxMax(&bound_x, &bound_y);
1040
1041 // oglFindEndForBox(bound_x, bound_y, m_xpos, m_ypos, x2, y2, x3, y3);
1042 oglDrawArcToEllipse(m_xpos, m_ypos, bound_x, bound_y, x2, y2, x1, y1, x3, y3);
1043
1044 return TRUE;
1045 }
1046
1047 void wxEllipseShape::OnDraw(wxDC& dc)
1048 {
1049 if (m_shadowMode != SHADOW_NONE)
1050 {
1051 if (m_shadowBrush)
1052 dc.SetBrush(* m_shadowBrush);
1053 dc.SetPen(* g_oglTransparentPen);
1054 dc.DrawEllipse((long) ((m_xpos - GetWidth()/2) + m_shadowOffsetX),
1055 (long) ((m_ypos - GetHeight()/2) + m_shadowOffsetY),
1056 (long) GetWidth(), (long) GetHeight());
1057 }
1058
1059 if (m_pen)
1060 {
1061 if (m_pen->GetWidth() == 0)
1062 dc.SetPen(* g_oglTransparentPen);
1063 else
1064 dc.SetPen(* m_pen);
1065 }
1066 if (m_brush)
1067 dc.SetBrush(* m_brush);
1068 dc.DrawEllipse((long) (m_xpos - GetWidth()/2), (long) (m_ypos - GetHeight()/2), (long) GetWidth(), (long) GetHeight());
1069 }
1070
1071 void wxEllipseShape::SetSize(double x, double y, bool recursive)
1072 {
1073 SetAttachmentSize(x, y);
1074 m_width = x;
1075 m_height = y;
1076 SetDefaultRegionSize();
1077 }
1078
1079 #ifdef PROLOGIO
1080 void wxEllipseShape::WriteAttributes(wxExpr *clause)
1081 {
1082 wxShape::WriteAttributes(clause);
1083 clause->AddAttributeValue("x", m_xpos);
1084 clause->AddAttributeValue("y", m_ypos);
1085
1086 clause->AddAttributeValue("width", m_width);
1087 clause->AddAttributeValue("height", m_height);
1088 }
1089
1090 void wxEllipseShape::ReadAttributes(wxExpr *clause)
1091 {
1092 wxShape::ReadAttributes(clause);
1093 clause->AssignAttributeValue("width", &m_width);
1094 clause->AssignAttributeValue("height", &m_height);
1095
1096 // In case we're reading an old file, set the region's size
1097 if (m_regions.Number() == 1)
1098 {
1099 wxShapeRegion *region = (wxShapeRegion *)m_regions.First()->Data();
1100 region->SetSize(m_width, m_height);
1101 }
1102 }
1103 #endif
1104
1105 void wxEllipseShape::Copy(wxShape& copy)
1106 {
1107 wxShape::Copy(copy);
1108
1109 wxASSERT( copy.IsKindOf(CLASSINFO(wxEllipseShape)) );
1110
1111 wxEllipseShape& ellipseCopy = (wxEllipseShape&) copy;
1112
1113 ellipseCopy.m_width = m_width;
1114 ellipseCopy.m_height = m_height;
1115 }
1116
1117 int wxEllipseShape::GetNumberOfAttachments() const
1118 {
1119 return wxShape::GetNumberOfAttachments();
1120 }
1121
1122 // There are 4 attachment points on an ellipse - 0 = top, 1 = right, 2 = bottom,
1123 // 3 = left.
1124 bool wxEllipseShape::GetAttachmentPosition(int attachment, double *x, double *y,
1125 int nth, int no_arcs, wxLineShape *line)
1126 {
1127 if (m_attachmentMode == ATTACHMENT_MODE_BRANCHING)
1128 return wxShape::GetAttachmentPosition(attachment, x, y, nth, no_arcs, line);
1129
1130 if (m_attachmentMode != ATTACHMENT_MODE_NONE)
1131 {
1132 double top = (double)(m_ypos + m_height/2.0);
1133 double bottom = (double)(m_ypos - m_height/2.0);
1134 double left = (double)(m_xpos - m_width/2.0);
1135 double right = (double)(m_xpos + m_width/2.0);
1136
1137 int physicalAttachment = LogicalToPhysicalAttachment(attachment);
1138
1139 switch (physicalAttachment)
1140 {
1141 case 0:
1142 {
1143 if (m_spaceAttachments)
1144 *x = left + (nth + 1)*m_width/(no_arcs + 1);
1145 else *x = m_xpos;
1146 *y = top;
1147 // We now have the point on the bounding box: but get the point on the ellipse
1148 // by imagining a vertical line from (*x, m_ypos - m_height- 500) to (*x, m_ypos) intersecting
1149 // the ellipse.
1150 oglDrawArcToEllipse(m_xpos, m_ypos, m_width, m_height, *x, (double)(m_ypos-m_height-500), *x, m_ypos, x, y);
1151 break;
1152 }
1153 case 1:
1154 {
1155 *x = right;
1156 if (m_spaceAttachments)
1157 *y = bottom + (nth + 1)*m_height/(no_arcs + 1);
1158 else *y = m_ypos;
1159 oglDrawArcToEllipse(m_xpos, m_ypos, m_width, m_height, (double)(m_xpos+m_width+500), *y, m_xpos, *y, x, y);
1160 break;
1161 }
1162 case 2:
1163 {
1164 if (m_spaceAttachments)
1165 *x = left + (nth + 1)*m_width/(no_arcs + 1);
1166 else *x = m_xpos;
1167 *y = bottom;
1168 oglDrawArcToEllipse(m_xpos, m_ypos, m_width, m_height, *x, (double)(m_ypos+m_height+500), *x, m_ypos, x, y);
1169 break;
1170 }
1171 case 3:
1172 {
1173 *x = left;
1174 if (m_spaceAttachments)
1175 *y = bottom + (nth + 1)*m_height/(no_arcs + 1);
1176 else *y = m_ypos;
1177 oglDrawArcToEllipse(m_xpos, m_ypos, m_width, m_height, (double)(m_xpos-m_width-500), *y, m_xpos, *y, x, y);
1178 break;
1179 }
1180 default:
1181 {
1182 return wxShape::GetAttachmentPosition(attachment, x, y, nth, no_arcs, line);
1183 break;
1184 }
1185 }
1186 return TRUE;
1187 }
1188 else
1189 { *x = m_xpos; *y = m_ypos; return TRUE; }
1190 }
1191
1192
1193 // Circle object
1194 IMPLEMENT_DYNAMIC_CLASS(wxCircleShape, wxEllipseShape)
1195
1196 wxCircleShape::wxCircleShape(double diameter):wxEllipseShape(diameter, diameter)
1197 {
1198 SetMaintainAspectRatio(TRUE);
1199 }
1200
1201 void wxCircleShape::Copy(wxShape& copy)
1202 {
1203 wxEllipseShape::Copy(copy);
1204 }
1205
1206 bool wxCircleShape::GetPerimeterPoint(double x1, double y1,
1207 double x2, double y2,
1208 double *x3, double *y3)
1209 {
1210 oglFindEndForCircle(m_width/2,
1211 m_xpos, m_ypos, // Centre of circle
1212 x2, y2, // Other end of line
1213 x3, y3);
1214
1215 return TRUE;
1216 }
1217
1218 // Control points
1219
1220 double wxControlPoint::sm_controlPointDragStartX = 0.0;
1221 double wxControlPoint::sm_controlPointDragStartY = 0.0;
1222 double wxControlPoint::sm_controlPointDragStartWidth = 0.0;
1223 double wxControlPoint::sm_controlPointDragStartHeight = 0.0;
1224 double wxControlPoint::sm_controlPointDragEndWidth = 0.0;
1225 double wxControlPoint::sm_controlPointDragEndHeight = 0.0;
1226 double wxControlPoint::sm_controlPointDragPosX = 0.0;
1227 double wxControlPoint::sm_controlPointDragPosY = 0.0;
1228
1229 IMPLEMENT_DYNAMIC_CLASS(wxControlPoint, wxRectangleShape)
1230
1231 wxControlPoint::wxControlPoint(wxShapeCanvas *theCanvas, wxShape *object, double size, double the_xoffset, double the_yoffset, int the_type):wxRectangleShape(size, size)
1232 {
1233 m_canvas = theCanvas;
1234 m_shape = object;
1235 m_xoffset = the_xoffset;
1236 m_yoffset = the_yoffset;
1237 m_type = the_type;
1238 SetPen(g_oglBlackForegroundPen);
1239 SetBrush(wxBLACK_BRUSH);
1240 m_oldCursor = NULL;
1241 m_visible = TRUE;
1242 m_eraseObject = TRUE;
1243 }
1244
1245 wxControlPoint::~wxControlPoint()
1246 {
1247 }
1248
1249 // Don't even attempt to draw any text - waste of time!
1250 void wxControlPoint::OnDrawContents(wxDC& dc)
1251 {
1252 }
1253
1254 void wxControlPoint::OnDraw(wxDC& dc)
1255 {
1256 m_xpos = m_shape->GetX() + m_xoffset;
1257 m_ypos = m_shape->GetY() + m_yoffset;
1258 wxRectangleShape::OnDraw(dc);
1259 }
1260
1261 void wxControlPoint::OnErase(wxDC& dc)
1262 {
1263 wxRectangleShape::OnErase(dc);
1264 }
1265
1266 // Implement resizing of canvas object
1267 void wxControlPoint::OnDragLeft(bool draw, double x, double y, int keys, int attachment)
1268 {
1269 m_shape->GetEventHandler()->OnSizingDragLeft(this, draw, x, y, keys, attachment);
1270 }
1271
1272 void wxControlPoint::OnBeginDragLeft(double x, double y, int keys, int attachment)
1273 {
1274 m_shape->GetEventHandler()->OnSizingBeginDragLeft(this, x, y, keys, attachment);
1275 }
1276
1277 void wxControlPoint::OnEndDragLeft(double x, double y, int keys, int attachment)
1278 {
1279 m_shape->GetEventHandler()->OnSizingEndDragLeft(this, x, y, keys, attachment);
1280 }
1281
1282 int wxControlPoint::GetNumberOfAttachments() const
1283 {
1284 return 1;
1285 }
1286
1287 bool wxControlPoint::GetAttachmentPosition(int attachment, double *x, double *y,
1288 int nth, int no_arcs, wxLineShape *line)
1289 {
1290 *x = m_xpos; *y = m_ypos;
1291 return TRUE;
1292 }
1293
1294 // Control points ('handles') redirect control to the actual shape, to make it easier
1295 // to override sizing behaviour.
1296 void wxShape::OnSizingDragLeft(wxControlPoint* pt, bool draw, double x, double y, int keys, int attachment)
1297 {
1298 double bound_x;
1299 double bound_y;
1300 this->GetBoundingBoxMin(&bound_x, &bound_y);
1301
1302 wxClientDC dc(GetCanvas());
1303 GetCanvas()->PrepareDC(dc);
1304
1305 dc.SetLogicalFunction(wxXOR);
1306
1307 wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
1308 dc.SetPen(dottedPen);
1309 dc.SetBrush((* wxTRANSPARENT_BRUSH));
1310
1311 if (this->GetCentreResize())
1312 {
1313 // Maintain the same centre point.
1314 double new_width = (double)(2.0*fabs(x - this->GetX()));
1315 double new_height = (double)(2.0*fabs(y - this->GetY()));
1316
1317 // Constrain sizing according to what control point you're dragging
1318 if (pt->m_type == CONTROL_POINT_HORIZONTAL)
1319 {
1320 if (GetMaintainAspectRatio())
1321 {
1322 new_height = bound_y*(new_width/bound_x);
1323 }
1324 else
1325 new_height = bound_y;
1326 }
1327 else if (pt->m_type == CONTROL_POINT_VERTICAL)
1328 {
1329 if (GetMaintainAspectRatio())
1330 {
1331 new_width = bound_x*(new_height/bound_y);
1332 }
1333 else
1334 new_width = bound_x;
1335 }
1336 else if (pt->m_type == CONTROL_POINT_DIAGONAL && (keys & KEY_SHIFT))
1337 new_height = bound_y*(new_width/bound_x);
1338
1339 if (this->GetFixedWidth())
1340 new_width = bound_x;
1341
1342 if (this->GetFixedHeight())
1343 new_height = bound_y;
1344
1345 pt->sm_controlPointDragEndWidth = new_width;
1346 pt->sm_controlPointDragEndHeight = new_height;
1347
1348 this->GetEventHandler()->OnDrawOutline(dc, this->GetX(), this->GetY(),
1349 new_width, new_height);
1350 }
1351 else
1352 {
1353 // Don't maintain the same centre point!
1354 double newX1 = wxMin(pt->sm_controlPointDragStartX, x);
1355 double newY1 = wxMin(pt->sm_controlPointDragStartY, y);
1356 double newX2 = wxMax(pt->sm_controlPointDragStartX, x);
1357 double newY2 = wxMax(pt->sm_controlPointDragStartY, y);
1358 if (pt->m_type == CONTROL_POINT_HORIZONTAL)
1359 {
1360 newY1 = pt->sm_controlPointDragStartY;
1361 newY2 = newY1 + pt->sm_controlPointDragStartHeight;
1362 }
1363 else if (pt->m_type == CONTROL_POINT_VERTICAL)
1364 {
1365 newX1 = pt->sm_controlPointDragStartX;
1366 newX2 = newX1 + pt->sm_controlPointDragStartWidth;
1367 }
1368 else if (pt->m_type == CONTROL_POINT_DIAGONAL && ((keys & KEY_SHIFT) || GetMaintainAspectRatio()))
1369 {
1370 double newH = (double)((newX2 - newX1)*(pt->sm_controlPointDragStartHeight/pt->sm_controlPointDragStartWidth));
1371 if (GetY() > pt->sm_controlPointDragStartY)
1372 newY2 = (double)(newY1 + newH);
1373 else
1374 newY1 = (double)(newY2 - newH);
1375 }
1376 double newWidth = (double)(newX2 - newX1);
1377 double newHeight = (double)(newY2 - newY1);
1378
1379 if (pt->m_type == CONTROL_POINT_VERTICAL && GetMaintainAspectRatio())
1380 {
1381 newWidth = bound_x * (newHeight/bound_y) ;
1382 }
1383
1384 if (pt->m_type == CONTROL_POINT_HORIZONTAL && GetMaintainAspectRatio())
1385 {
1386 newHeight = bound_y * (newWidth/bound_x) ;
1387 }
1388
1389 pt->sm_controlPointDragPosX = (double)(newX1 + (newWidth/2.0));
1390 pt->sm_controlPointDragPosY = (double)(newY1 + (newHeight/2.0));
1391 if (this->GetFixedWidth())
1392 newWidth = bound_x;
1393
1394 if (this->GetFixedHeight())
1395 newHeight = bound_y;
1396
1397 pt->sm_controlPointDragEndWidth = newWidth;
1398 pt->sm_controlPointDragEndHeight = newHeight;
1399 this->GetEventHandler()->OnDrawOutline(dc, pt->sm_controlPointDragPosX, pt->sm_controlPointDragPosY, newWidth, newHeight);
1400 }
1401 }
1402
1403 void wxShape::OnSizingBeginDragLeft(wxControlPoint* pt, double x, double y, int keys, int attachment)
1404 {
1405 m_canvas->CaptureMouse();
1406
1407 wxClientDC dc(GetCanvas());
1408 GetCanvas()->PrepareDC(dc);
1409 /*
1410 if (pt->m_eraseObject)
1411 this->Erase(dc);
1412 */
1413
1414 dc.SetLogicalFunction(wxXOR);
1415
1416 double bound_x;
1417 double bound_y;
1418 this->GetBoundingBoxMin(&bound_x, &bound_y);
1419
1420 // Choose the 'opposite corner' of the object as the stationary
1421 // point in case this is non-centring resizing.
1422 if (pt->GetX() < this->GetX())
1423 pt->sm_controlPointDragStartX = (double)(this->GetX() + (bound_x/2.0));
1424 else
1425 pt->sm_controlPointDragStartX = (double)(this->GetX() - (bound_x/2.0));
1426
1427 if (pt->GetY() < this->GetY())
1428 pt->sm_controlPointDragStartY = (double)(this->GetY() + (bound_y/2.0));
1429 else
1430 pt->sm_controlPointDragStartY = (double)(this->GetY() - (bound_y/2.0));
1431
1432 if (pt->m_type == CONTROL_POINT_HORIZONTAL)
1433 pt->sm_controlPointDragStartY = (double)(this->GetY() - (bound_y/2.0));
1434 else if (pt->m_type == CONTROL_POINT_VERTICAL)
1435 pt->sm_controlPointDragStartX = (double)(this->GetX() - (bound_x/2.0));
1436
1437 // We may require the old width and height.
1438 pt->sm_controlPointDragStartWidth = bound_x;
1439 pt->sm_controlPointDragStartHeight = bound_y;
1440
1441 wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
1442 dc.SetPen(dottedPen);
1443 dc.SetBrush((* wxTRANSPARENT_BRUSH));
1444
1445 if (this->GetCentreResize())
1446 {
1447 double new_width = (double)(2.0*fabs(x - this->GetX()));
1448 double new_height = (double)(2.0*fabs(y - this->GetY()));
1449
1450 // Constrain sizing according to what control point you're dragging
1451 if (pt->m_type == CONTROL_POINT_HORIZONTAL)
1452 {
1453 if (GetMaintainAspectRatio())
1454 {
1455 new_height = bound_y*(new_width/bound_x);
1456 }
1457 else
1458 new_height = bound_y;
1459 }
1460 else if (pt->m_type == CONTROL_POINT_VERTICAL)
1461 {
1462 if (GetMaintainAspectRatio())
1463 {
1464 new_width = bound_x*(new_height/bound_y);
1465 }
1466 else
1467 new_width = bound_x;
1468 }
1469 else if (pt->m_type == CONTROL_POINT_DIAGONAL && (keys & KEY_SHIFT))
1470 new_height = bound_y*(new_width/bound_x);
1471
1472 if (this->GetFixedWidth())
1473 new_width = bound_x;
1474
1475 if (this->GetFixedHeight())
1476 new_height = bound_y;
1477
1478 pt->sm_controlPointDragEndWidth = new_width;
1479 pt->sm_controlPointDragEndHeight = new_height;
1480 this->GetEventHandler()->OnDrawOutline(dc, this->GetX(), this->GetY(),
1481 new_width, new_height);
1482 }
1483 else
1484 {
1485 // Don't maintain the same centre point!
1486 double newX1 = wxMin(pt->sm_controlPointDragStartX, x);
1487 double newY1 = wxMin(pt->sm_controlPointDragStartY, y);
1488 double newX2 = wxMax(pt->sm_controlPointDragStartX, x);
1489 double newY2 = wxMax(pt->sm_controlPointDragStartY, y);
1490 if (pt->m_type == CONTROL_POINT_HORIZONTAL)
1491 {
1492 newY1 = pt->sm_controlPointDragStartY;
1493 newY2 = newY1 + pt->sm_controlPointDragStartHeight;
1494 }
1495 else if (pt->m_type == CONTROL_POINT_VERTICAL)
1496 {
1497 newX1 = pt->sm_controlPointDragStartX;
1498 newX2 = newX1 + pt->sm_controlPointDragStartWidth;
1499 }
1500 else if (pt->m_type == CONTROL_POINT_DIAGONAL && ((keys & KEY_SHIFT) || GetMaintainAspectRatio()))
1501 {
1502 double newH = (double)((newX2 - newX1)*(pt->sm_controlPointDragStartHeight/pt->sm_controlPointDragStartWidth));
1503 if (pt->GetY() > pt->sm_controlPointDragStartY)
1504 newY2 = (double)(newY1 + newH);
1505 else
1506 newY1 = (double)(newY2 - newH);
1507 }
1508 double newWidth = (double)(newX2 - newX1);
1509 double newHeight = (double)(newY2 - newY1);
1510
1511 if (pt->m_type == CONTROL_POINT_VERTICAL && GetMaintainAspectRatio())
1512 {
1513 newWidth = bound_x * (newHeight/bound_y) ;
1514 }
1515
1516 if (pt->m_type == CONTROL_POINT_HORIZONTAL && GetMaintainAspectRatio())
1517 {
1518 newHeight = bound_y * (newWidth/bound_x) ;
1519 }
1520
1521 pt->sm_controlPointDragPosX = (double)(newX1 + (newWidth/2.0));
1522 pt->sm_controlPointDragPosY = (double)(newY1 + (newHeight/2.0));
1523 if (this->GetFixedWidth())
1524 newWidth = bound_x;
1525
1526 if (this->GetFixedHeight())
1527 newHeight = bound_y;
1528
1529 pt->sm_controlPointDragEndWidth = newWidth;
1530 pt->sm_controlPointDragEndHeight = newHeight;
1531 this->GetEventHandler()->OnDrawOutline(dc, pt->sm_controlPointDragPosX, pt->sm_controlPointDragPosY, newWidth, newHeight);
1532 }
1533 }
1534
1535 void wxShape::OnSizingEndDragLeft(wxControlPoint* pt, double x, double y, int keys, int attachment)
1536 {
1537 wxClientDC dc(GetCanvas());
1538 GetCanvas()->PrepareDC(dc);
1539
1540 m_canvas->ReleaseMouse();
1541 dc.SetLogicalFunction(wxCOPY);
1542 this->Recompute();
1543 this->ResetControlPoints();
1544
1545 this->Erase(dc);
1546 /*
1547 if (!pt->m_eraseObject)
1548 this->Show(FALSE);
1549 */
1550
1551 this->SetSize(pt->sm_controlPointDragEndWidth, pt->sm_controlPointDragEndHeight);
1552
1553 // The next operation could destroy this control point (it does for label objects,
1554 // via formatting the text), so save all values we're going to use, or
1555 // we'll be accessing garbage.
1556 wxShape *theObject = this;
1557 wxShapeCanvas *theCanvas = m_canvas;
1558 bool eraseIt = pt->m_eraseObject;
1559
1560 if (theObject->GetCentreResize())
1561 theObject->Move(dc, theObject->GetX(), theObject->GetY());
1562 else
1563 theObject->Move(dc, pt->sm_controlPointDragPosX, pt->sm_controlPointDragPosY);
1564
1565 /*
1566 if (!eraseIt)
1567 theObject->Show(TRUE);
1568 */
1569
1570 // Recursively redraw links if we have a composite.
1571 if (theObject->GetChildren().Number() > 0)
1572 theObject->DrawLinks(dc, -1, TRUE);
1573
1574 double width, height;
1575 theObject->GetBoundingBoxMax(&width, &height);
1576 theObject->GetEventHandler()->OnEndSize(width, height);
1577
1578 if (!theCanvas->GetQuickEditMode() && eraseIt) theCanvas->Redraw(dc);
1579 }
1580
1581
1582
1583 // Polygon control points
1584
1585 IMPLEMENT_DYNAMIC_CLASS(wxPolygonControlPoint, wxControlPoint)
1586
1587 wxPolygonControlPoint::wxPolygonControlPoint(wxShapeCanvas *theCanvas, wxShape *object, double size,
1588 wxRealPoint *vertex, double the_xoffset, double the_yoffset):
1589 wxControlPoint(theCanvas, object, size, the_xoffset, the_yoffset, 0)
1590 {
1591 m_polygonVertex = vertex;
1592 m_originalDistance = 0.0;
1593 }
1594
1595 wxPolygonControlPoint::~wxPolygonControlPoint()
1596 {
1597 }
1598
1599 // Calculate what new size would be, at end of resize
1600 void wxPolygonControlPoint::CalculateNewSize(double x, double y)
1601 {
1602 double bound_x;
1603 double bound_y;
1604 GetShape()->GetBoundingBoxMin(&bound_x, &bound_y);
1605
1606 double dist = (double)sqrt((x - m_shape->GetX())*(x - m_shape->GetX()) +
1607 (y - m_shape->GetY())*(y - m_shape->GetY()));
1608
1609 m_newSize.x = (double)(dist/this->m_originalDistance)*this->m_originalSize.x;
1610 m_newSize.y = (double)(dist/this->m_originalDistance)*this->m_originalSize.y;
1611 }
1612
1613
1614 // Implement resizing polygon or moving the vertex.
1615 void wxPolygonControlPoint::OnDragLeft(bool draw, double x, double y, int keys, int attachment)
1616 {
1617 m_shape->GetEventHandler()->OnSizingDragLeft(this, draw, x, y, keys, attachment);
1618 }
1619
1620 void wxPolygonControlPoint::OnBeginDragLeft(double x, double y, int keys, int attachment)
1621 {
1622 m_shape->GetEventHandler()->OnSizingBeginDragLeft(this, x, y, keys, attachment);
1623 }
1624
1625 void wxPolygonControlPoint::OnEndDragLeft(double x, double y, int keys, int attachment)
1626 {
1627 m_shape->GetEventHandler()->OnSizingEndDragLeft(this, x, y, keys, attachment);
1628 }
1629
1630 // Control points ('handles') redirect control to the actual shape, to make it easier
1631 // to override sizing behaviour.
1632 void wxPolygonShape::OnSizingDragLeft(wxControlPoint* pt, bool draw, double x, double y, int keys, int attachment)
1633 {
1634 wxPolygonControlPoint* ppt = (wxPolygonControlPoint*) pt;
1635
1636 wxClientDC dc(GetCanvas());
1637 GetCanvas()->PrepareDC(dc);
1638
1639 dc.SetLogicalFunction(wxXOR);
1640
1641 wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
1642 dc.SetPen(dottedPen);
1643 dc.SetBrush((* wxTRANSPARENT_BRUSH));
1644
1645 if (0) // keys & KEY_CTRL)
1646 {
1647 // TODO: mend this code. Currently we rely on altering the
1648 // actual points, but we should assume we're not, as per
1649 // the normal sizing case.
1650 m_canvas->Snap(&x, &y);
1651
1652 // Move point
1653 ppt->m_polygonVertex->x = x - this->GetX();
1654 ppt->m_polygonVertex->y = y - this->GetY();
1655 ppt->SetX(x);
1656 ppt->SetY(y);
1657 ((wxPolygonShape *)this)->CalculateBoundingBox();
1658 ((wxPolygonShape *)this)->CalculatePolygonCentre();
1659 }
1660 else
1661 {
1662 ppt->CalculateNewSize(x, y);
1663 }
1664
1665 this->GetEventHandler()->OnDrawOutline(dc, this->GetX(), this->GetY(),
1666 ppt->GetNewSize().x, ppt->GetNewSize().y);
1667 }
1668
1669 void wxPolygonShape::OnSizingBeginDragLeft(wxControlPoint* pt, double x, double y, int keys, int attachment)
1670 {
1671 wxPolygonControlPoint* ppt = (wxPolygonControlPoint*) pt;
1672
1673 wxClientDC dc(GetCanvas());
1674 GetCanvas()->PrepareDC(dc);
1675
1676 this->Erase(dc);
1677
1678 dc.SetLogicalFunction(wxXOR);
1679
1680 double bound_x;
1681 double bound_y;
1682 this->GetBoundingBoxMin(&bound_x, &bound_y);
1683
1684 double dist = (double)sqrt((x - this->GetX())*(x - this->GetX()) +
1685 (y - this->GetY())*(y - this->GetY()));
1686 ppt->m_originalDistance = dist;
1687 ppt->m_originalSize.x = bound_x;
1688 ppt->m_originalSize.y = bound_y;
1689
1690 if (ppt->m_originalDistance == 0.0) ppt->m_originalDistance = (double) 0.0001;
1691
1692 wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
1693 dc.SetPen(dottedPen);
1694 dc.SetBrush((* wxTRANSPARENT_BRUSH));
1695
1696 if (0) // keys & KEY_CTRL)
1697 {
1698 // TODO: mend this code. Currently we rely on altering the
1699 // actual points, but we should assume we're not, as per
1700 // the normal sizing case.
1701 m_canvas->Snap(&x, &y);
1702
1703 // Move point
1704 ppt->m_polygonVertex->x = x - this->GetX();
1705 ppt->m_polygonVertex->y = y - this->GetY();
1706 ppt->SetX(x);
1707 ppt->SetY(y);
1708 ((wxPolygonShape *)this)->CalculateBoundingBox();
1709 ((wxPolygonShape *)this)->CalculatePolygonCentre();
1710 }
1711 else
1712 {
1713 ppt->CalculateNewSize(x, y);
1714 }
1715
1716 this->GetEventHandler()->OnDrawOutline(dc, this->GetX(), this->GetY(),
1717 ppt->GetNewSize().x, ppt->GetNewSize().y);
1718
1719 m_canvas->CaptureMouse();
1720 }
1721
1722 void wxPolygonShape::OnSizingEndDragLeft(wxControlPoint* pt, double x, double y, int keys, int attachment)
1723 {
1724 wxPolygonControlPoint* ppt = (wxPolygonControlPoint*) pt;
1725
1726 wxClientDC dc(GetCanvas());
1727 GetCanvas()->PrepareDC(dc);
1728
1729 m_canvas->ReleaseMouse();
1730 dc.SetLogicalFunction(wxCOPY);
1731
1732 // If we're changing shape, must reset the original points
1733 if (keys & KEY_CTRL)
1734 {
1735 ((wxPolygonShape *)this)->CalculateBoundingBox();
1736 ((wxPolygonShape *)this)->UpdateOriginalPoints();
1737 }
1738 else
1739 {
1740 SetSize(ppt->GetNewSize().x, ppt->GetNewSize().y);
1741 }
1742
1743 ((wxPolygonShape *)this)->CalculateBoundingBox();
1744 ((wxPolygonShape *)this)->CalculatePolygonCentre();
1745
1746 this->Recompute();
1747 this->ResetControlPoints();
1748 this->Move(dc, this->GetX(), this->GetY());
1749 if (!m_canvas->GetQuickEditMode()) m_canvas->Redraw(dc);
1750 }
1751
1752 /*
1753 * Object region
1754 *
1755 */
1756 IMPLEMENT_DYNAMIC_CLASS(wxShapeRegion, wxObject)
1757
1758 wxShapeRegion::wxShapeRegion()
1759 {
1760 m_regionText = "";
1761 m_font = g_oglNormalFont;
1762 m_minHeight = 5.0;
1763 m_minWidth = 5.0;
1764 m_width = 0.0;
1765 m_height = 0.0;
1766 m_x = 0.0;
1767 m_y = 0.0;
1768
1769 m_regionProportionX = -1.0;
1770 m_regionProportionY = -1.0;
1771 m_formatMode = FORMAT_CENTRE_HORIZ | FORMAT_CENTRE_VERT;
1772 m_regionName = "";
1773 m_textColour = "BLACK";
1774 m_penColour = "BLACK";
1775 m_penStyle = wxSOLID;
1776 m_actualColourObject = NULL;
1777 m_actualPenObject = NULL;
1778 }
1779
1780 wxShapeRegion::wxShapeRegion(wxShapeRegion& region)
1781 {
1782 m_regionText = region.m_regionText;
1783 m_regionName = region.m_regionName;
1784 m_textColour = region.m_textColour;
1785
1786 m_font = region.m_font;
1787 m_minHeight = region.m_minHeight;
1788 m_minWidth = region.m_minWidth;
1789 m_width = region.m_width;
1790 m_height = region.m_height;
1791 m_x = region.m_x;
1792 m_y = region.m_y;
1793
1794 m_regionProportionX = region.m_regionProportionX;
1795 m_regionProportionY = region.m_regionProportionY;
1796 m_formatMode = region.m_formatMode;
1797 m_actualColourObject = NULL;
1798 m_actualPenObject = NULL;
1799 m_penStyle = region.m_penStyle;
1800 m_penColour = region.m_penColour;
1801
1802 ClearText();
1803 wxNode *node = region.m_formattedText.First();
1804 while (node)
1805 {
1806 wxShapeTextLine *line = (wxShapeTextLine *)node->Data();
1807 wxShapeTextLine *new_line =
1808 new wxShapeTextLine(line->GetX(), line->GetY(), line->GetText());
1809 m_formattedText.Append(new_line);
1810 node = node->Next();
1811 }
1812 }
1813
1814 wxShapeRegion::~wxShapeRegion()
1815 {
1816 ClearText();
1817 }
1818
1819 void wxShapeRegion::ClearText()
1820 {
1821 wxNode *node = m_formattedText.First();
1822 while (node)
1823 {
1824 wxShapeTextLine *line = (wxShapeTextLine *)node->Data();
1825 wxNode *next = node->Next();
1826 delete line;
1827 delete node;
1828 node = next;
1829 }
1830 }
1831
1832 void wxShapeRegion::SetFont(wxFont *f)
1833 {
1834 m_font = f;
1835 }
1836
1837 void wxShapeRegion::SetMinSize(double w, double h)
1838 {
1839 m_minWidth = w;
1840 m_minHeight = h;
1841 }
1842
1843 void wxShapeRegion::SetSize(double w, double h)
1844 {
1845 m_width = w;
1846 m_height = h;
1847 }
1848
1849 void wxShapeRegion::SetPosition(double xp, double yp)
1850 {
1851 m_x = xp;
1852 m_y = yp;
1853 }
1854
1855 void wxShapeRegion::SetProportions(double xp, double yp)
1856 {
1857 m_regionProportionX = xp;
1858 m_regionProportionY = yp;
1859 }
1860
1861 void wxShapeRegion::SetFormatMode(int mode)
1862 {
1863 m_formatMode = mode;
1864 }
1865
1866 void wxShapeRegion::SetColour(const wxString& col)
1867 {
1868 m_textColour = col;
1869 m_actualColourObject = NULL;
1870 }
1871
1872 wxColour *wxShapeRegion::GetActualColourObject()
1873 {
1874 if (!m_actualColourObject)
1875 m_actualColourObject = wxTheColourDatabase->FindColour(GetColour());
1876 if (!m_actualColourObject)
1877 m_actualColourObject = wxBLACK;
1878 return m_actualColourObject;
1879 }
1880
1881 void wxShapeRegion::SetPenColour(const wxString& col)
1882 {
1883 m_penColour = col;
1884 m_actualPenObject = NULL;
1885 }
1886
1887 // Returns NULL if the pen is invisible
1888 // (different to pen being transparent; indicates that
1889 // region boundary should not be drawn.)
1890 wxPen *wxShapeRegion::GetActualPen()
1891 {
1892 if (m_actualPenObject)
1893 return m_actualPenObject;
1894
1895 if (!m_penColour) return NULL;
1896 if (m_penColour == "Invisible")
1897 return NULL;
1898 m_actualPenObject = wxThePenList->FindOrCreatePen(m_penColour, 1, m_penStyle);
1899 return m_actualPenObject;
1900 }
1901
1902