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