]> git.saurik.com Git - wxWidgets.git/blame - utils/ogl/src/basic.cpp
Made wxNO_DEFAULT set the "no" button as the default, according to the docs
[wxWidgets.git] / utils / ogl / src / basic.cpp
CommitLineData
0fc1a713
JS
1/////////////////////////////////////////////////////////////////////////////
2// Name: basic.cpp
3// Purpose: Basic OGL classes
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 "basic.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
0fc1a713 27#include <wx/wxexpr.h>
0fc1a713 28
47d67540 29#if wxUSE_IOSTREAMH
0fc1a713
JS
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
58IMPLEMENT_DYNAMIC_CLASS(wxShapeTextLine, wxObject)
59IMPLEMENT_DYNAMIC_CLASS(wxAttachmentPoint, wxObject)
60
42cfaf8c 61wxShapeTextLine::wxShapeTextLine(double the_x, double the_y, const wxString& the_line)
0fc1a713
JS
62{
63 m_x = the_x; m_y = the_y; m_line = the_line;
64}
65
66wxShapeTextLine::~wxShapeTextLine()
67{
68}
69
70IMPLEMENT_ABSTRACT_CLASS(wxShapeEvtHandler, wxObject)
71
72wxShapeEvtHandler::wxShapeEvtHandler(wxShapeEvtHandler *prev, wxShape *shape)
73{
74 m_previousHandler = prev;
75 m_handlerShape = shape;
76}
77
78wxShapeEvtHandler::~wxShapeEvtHandler()
79{
80}
81
2bb0cd28
JS
82// Creates a copy of this event handler.
83wxShapeEvtHandler* wxShapeEvtHandler::CreateNewCopy()
84{
85 wxShapeEvtHandler* newObject = (wxShapeEvtHandler*) GetClassInfo()->CreateObject();
86
87 wxASSERT( (newObject != NULL) );
88 wxASSERT( (newObject->IsKindOf(CLASSINFO(wxShapeEvtHandler))) );
89
90 newObject->m_previousHandler = newObject;
91
92 CopyData(*newObject);
93
94 return newObject;
95}
96
97
0fc1a713
JS
98void wxShapeEvtHandler::OnDelete()
99{
100 if (this != GetShape())
101 delete this;
102}
103
104void wxShapeEvtHandler::OnDraw(wxDC& dc)
105{
106 if (m_previousHandler)
107 m_previousHandler->OnDraw(dc);
108}
109
110void wxShapeEvtHandler::OnMoveLinks(wxDC& dc)
111{
112 if (m_previousHandler)
113 m_previousHandler->OnMoveLinks(dc);
114}
115
116void wxShapeEvtHandler::OnMoveLink(wxDC& dc, bool moveControlPoints)
117{
118 if (m_previousHandler)
119 m_previousHandler->OnMoveLink(dc, moveControlPoints);
120}
121
122void wxShapeEvtHandler::OnDrawContents(wxDC& dc)
123{
124 if (m_previousHandler)
125 m_previousHandler->OnDrawContents(dc);
126}
127
f97c9854
JS
128void wxShapeEvtHandler::OnDrawBranches(wxDC& dc, bool erase)
129{
130 if (m_previousHandler)
131 m_previousHandler->OnDrawBranches(dc, erase);
132}
133
42cfaf8c 134void wxShapeEvtHandler::OnSize(double x, double y)
0fc1a713
JS
135{
136 if (m_previousHandler)
137 m_previousHandler->OnSize(x, y);
138}
139
42cfaf8c 140bool wxShapeEvtHandler::OnMovePre(wxDC& dc, double x, double y, double old_x, double old_y, bool display)
0fc1a713
JS
141{
142 if (m_previousHandler)
143 return m_previousHandler->OnMovePre(dc, x, y, old_x, old_y, display);
144 else
145 return TRUE;
146}
147
42cfaf8c 148void wxShapeEvtHandler::OnMovePost(wxDC& dc, double x, double y, double old_x, double old_y, bool display)
0fc1a713
JS
149{
150 if (m_previousHandler)
151 m_previousHandler->OnMovePost(dc, x, y, old_x, old_y, display);
152}
153
154void wxShapeEvtHandler::OnErase(wxDC& dc)
155{
156 if (m_previousHandler)
157 m_previousHandler->OnErase(dc);
158}
159
160void wxShapeEvtHandler::OnEraseContents(wxDC& dc)
161{
162 if (m_previousHandler)
163 m_previousHandler->OnEraseContents(dc);
164}
165
166void wxShapeEvtHandler::OnHighlight(wxDC& dc)
167{
168 if (m_previousHandler)
169 m_previousHandler->OnHighlight(dc);
170}
171
42cfaf8c 172void wxShapeEvtHandler::OnLeftClick(double x, double y, int keys, int attachment)
0fc1a713
JS
173{
174 if (m_previousHandler)
175 m_previousHandler->OnLeftClick(x, y, keys, attachment);
176}
177
dfad0599
JS
178void wxShapeEvtHandler::OnLeftDoubleClick(double x, double y, int keys, int attachment)
179{
180 if (m_previousHandler)
181 m_previousHandler->OnLeftDoubleClick(x, y, keys, attachment);
182}
183
42cfaf8c 184void wxShapeEvtHandler::OnRightClick(double x, double y, int keys, int attachment)
0fc1a713
JS
185{
186 if (m_previousHandler)
187 m_previousHandler->OnRightClick(x, y, keys, attachment);
188}
189
42cfaf8c 190void wxShapeEvtHandler::OnDragLeft(bool draw, double x, double y, int keys, int attachment)
0fc1a713
JS
191{
192 if (m_previousHandler)
193 m_previousHandler->OnDragLeft(draw, x, y, keys, attachment);
194}
195
42cfaf8c 196void wxShapeEvtHandler::OnBeginDragLeft(double x, double y, int keys, int attachment)
0fc1a713
JS
197{
198 if (m_previousHandler)
199 m_previousHandler->OnBeginDragLeft(x, y, keys, attachment);
200}
201
42cfaf8c 202void wxShapeEvtHandler::OnEndDragLeft(double x, double y, int keys, int attachment)
0fc1a713
JS
203{
204 if (m_previousHandler)
205 m_previousHandler->OnEndDragLeft(x, y, keys, attachment);
206}
207
42cfaf8c 208void wxShapeEvtHandler::OnDragRight(bool draw, double x, double y, int keys, int attachment)
0fc1a713
JS
209{
210 if (m_previousHandler)
211 m_previousHandler->OnDragRight(draw, x, y, keys, attachment);
212}
213
42cfaf8c 214void wxShapeEvtHandler::OnBeginDragRight(double x, double y, int keys, int attachment)
0fc1a713
JS
215{
216 if (m_previousHandler)
217 m_previousHandler->OnBeginDragRight(x, y, keys, attachment);
218}
219
42cfaf8c 220void wxShapeEvtHandler::OnEndDragRight(double x, double y, int keys, int attachment)
0fc1a713
JS
221{
222 if (m_previousHandler)
223 m_previousHandler->OnEndDragRight(x, y, keys, attachment);
224}
225
2bb0cd28
JS
226// Control points ('handles') redirect control to the actual shape, to make it easier
227// to override sizing behaviour.
42cfaf8c 228void wxShapeEvtHandler::OnSizingDragLeft(wxControlPoint* pt, bool draw, double x, double y, int keys, int attachment)
2bb0cd28
JS
229{
230 if (m_previousHandler)
231 m_previousHandler->OnSizingDragLeft(pt, draw, x, y, keys, attachment);
232}
233
42cfaf8c 234void wxShapeEvtHandler::OnSizingBeginDragLeft(wxControlPoint* pt, double x, double y, int keys, int attachment)
2bb0cd28
JS
235{
236 if (m_previousHandler)
237 m_previousHandler->OnSizingBeginDragLeft(pt, x, y, keys, attachment);
238}
239
42cfaf8c 240void wxShapeEvtHandler::OnSizingEndDragLeft(wxControlPoint* pt, double x, double y, int keys, int attachment)
2bb0cd28
JS
241{
242 if (m_previousHandler)
243 m_previousHandler->OnSizingEndDragLeft(pt, x, y, keys, attachment);
244}
245
42cfaf8c 246void wxShapeEvtHandler::OnDrawOutline(wxDC& dc, double x, double y, double w, double h)
0fc1a713
JS
247{
248 if (m_previousHandler)
249 m_previousHandler->OnDrawOutline(dc, x, y, w, h);
250}
251
252void wxShapeEvtHandler::OnDrawControlPoints(wxDC& dc)
253{
254 if (m_previousHandler)
255 m_previousHandler->OnDrawControlPoints(dc);
256}
257
258void wxShapeEvtHandler::OnEraseControlPoints(wxDC& dc)
259{
260 if (m_previousHandler)
261 m_previousHandler->OnEraseControlPoints(dc);
262}
263
42cfaf8c
JS
264// Can override this to prevent or intercept line reordering.
265void wxShapeEvtHandler::OnChangeAttachment(int attachment, wxLineShape* line, wxList& ordering)
266{
267 if (m_previousHandler)
268 m_previousHandler->OnChangeAttachment(attachment, line, ordering);
269}
270
0fc1a713
JS
271IMPLEMENT_ABSTRACT_CLASS(wxShape, wxShapeEvtHandler)
272
273wxShape::wxShape(wxShapeCanvas *can)
274{
275 m_eventHandler = this;
2bb0cd28 276 SetShape(this);
0fc1a713
JS
277 m_id = 0;
278 m_formatted = FALSE;
279 m_canvas = can;
280 m_xpos = 0.0; m_ypos = 0.0;
42cfaf8c 281 m_pen = g_oglBlackPen;
0fc1a713
JS
282 m_brush = wxWHITE_BRUSH;
283 m_font = g_oglNormalFont;
284 m_textColour = wxBLACK;
285 m_textColourName = "BLACK";
286 m_visible = FALSE;
287 m_clientData = NULL;
288 m_selected = FALSE;
f97c9854 289 m_attachmentMode = ATTACHMENT_MODE_NONE;
0fc1a713
JS
290 m_spaceAttachments = TRUE;
291 m_disableLabel = FALSE;
292 m_fixedWidth = FALSE;
293 m_fixedHeight = FALSE;
294 m_drawHandles = TRUE;
295 m_sensitivity = OP_ALL;
296 m_draggable = TRUE;
297 m_parent = NULL;
298 m_formatMode = FORMAT_CENTRE_HORIZ | FORMAT_CENTRE_VERT;
299 m_shadowMode = SHADOW_NONE;
ea57084d
JS
300 m_shadowOffsetX = 6;
301 m_shadowOffsetY = 6;
0fc1a713 302 m_shadowBrush = wxBLACK_BRUSH;
ea57084d
JS
303 m_textMarginX = 5;
304 m_textMarginY = 5;
0fc1a713
JS
305 m_regionName = "0";
306 m_centreResize = TRUE;
f93ce4da 307 m_maintainAspectRatio = FALSE;
0fc1a713
JS
308 m_highlighted = FALSE;
309 m_rotation = 0.0;
f97c9854
JS
310 m_branchNeckLength = 10;
311 m_branchStemLength = 10;
312 m_branchSpacing = 10;
313 m_branchStyle = BRANCHING_ATTACHMENT_NORMAL;
0fc1a713
JS
314
315 // Set up a default region. Much of the above will be put into
316 // the region eventually (the duplication is for compatibility)
317 wxShapeRegion *region = new wxShapeRegion;
318 m_regions.Append(region);
319 region->SetName("0");
320 region->SetFont(g_oglNormalFont);
321 region->SetFormatMode(FORMAT_CENTRE_HORIZ | FORMAT_CENTRE_VERT);
322 region->SetColour("BLACK");
323}
324
325wxShape::~wxShape()
326{
327 if (m_parent)
328 m_parent->GetChildren().DeleteObject(this);
329
330 ClearText();
331 ClearRegions();
332 ClearAttachments();
333
334 if (m_canvas)
335 m_canvas->RemoveShape(this);
336
337 GetEventHandler()->OnDelete();
338}
339
340void wxShape::SetHighlight(bool hi, bool recurse)
341{
342 m_highlighted = hi;
343 if (recurse)
344 {
345 wxNode *node = m_children.First();
346 while (node)
347 {
348 wxShape *child = (wxShape *)node->Data();
349 child->SetHighlight(hi, recurse);
350 node = node->Next();
351 }
352 }
353}
354
355void wxShape::SetSensitivityFilter(int sens, bool recursive)
356{
357 if (sens & OP_DRAG_LEFT)
358 m_draggable = TRUE;
359 else
360 m_draggable = FALSE;
361
362 m_sensitivity = sens;
363 if (recursive)
364 {
365 wxNode *node = m_children.First();
366 while (node)
367 {
368 wxShape *obj = (wxShape *)node->Data();
369 obj->SetSensitivityFilter(sens, TRUE);
370 node = node->Next();
371 }
372 }
373}
374
375void wxShape::SetDraggable(bool drag, bool recursive)
376{
377 m_draggable = drag;
378 if (m_draggable)
379 m_sensitivity |= OP_DRAG_LEFT;
380 else
381 if (m_sensitivity & OP_DRAG_LEFT)
382 m_sensitivity = m_sensitivity - OP_DRAG_LEFT;
383
384 if (recursive)
385 {
386 wxNode *node = m_children.First();
387 while (node)
388 {
389 wxShape *obj = (wxShape *)node->Data();
390 obj->SetDraggable(drag, TRUE);
391 node = node->Next();
392 }
393 }
394}
395
396void wxShape::SetDrawHandles(bool drawH)
397{
398 m_drawHandles = drawH;
399 wxNode *node = m_children.First();
400 while (node)
401 {
402 wxShape *obj = (wxShape *)node->Data();
403 obj->SetDrawHandles(drawH);
404 node = node->Next();
405 }
406}
407
408void wxShape::SetShadowMode(int mode, bool redraw)
409{
410 if (redraw && GetCanvas())
411 {
412 wxClientDC dc(GetCanvas());
413 GetCanvas()->PrepareDC(dc);
414 Erase(dc);
415
416 m_shadowMode = mode;
417
418 Draw(dc);
419 }
420 else
421 {
422 m_shadowMode = mode;
423 }
424}
425
426void wxShape::SetCanvas(wxShapeCanvas *theCanvas)
427{
428 m_canvas = theCanvas;
429 wxNode *node = m_children.First();
430 while (node)
431 {
432 wxShape *child = (wxShape *)node->Data();
433 child->SetCanvas(theCanvas);
434 node = node->Next();
435 }
436}
437
438void wxShape::AddToCanvas(wxShapeCanvas *theCanvas, wxShape *addAfter)
439{
440 theCanvas->AddShape(this, addAfter);
441 wxNode *node = m_children.First();
442 wxShape *lastImage = this;
443 while (node)
444 {
445 wxShape *object = (wxShape *)node->Data();
446 object->AddToCanvas(theCanvas, lastImage);
447 lastImage = object;
448
449 node = node->Next();
450 }
451}
452
453// Insert at front of canvas
454void wxShape::InsertInCanvas(wxShapeCanvas *theCanvas)
455{
456 theCanvas->InsertShape(this);
457 wxNode *node = m_children.First();
458 wxShape *lastImage = this;
459 while (node)
460 {
461 wxShape *object = (wxShape *)node->Data();
462 object->AddToCanvas(theCanvas, lastImage);
463 lastImage = object;
464
465 node = node->Next();
466 }
467}
468
469void wxShape::RemoveFromCanvas(wxShapeCanvas *theCanvas)
470{
471 if (Selected())
472 Select(FALSE);
473 theCanvas->RemoveShape(this);
474 wxNode *node = m_children.First();
475 while (node)
476 {
477 wxShape *object = (wxShape *)node->Data();
478 object->RemoveFromCanvas(theCanvas);
479
480 node = node->Next();
481 }
482}
483
484void wxShape::ClearAttachments()
485{
486 wxNode *node = m_attachmentPoints.First();
487 while (node)
488 {
489 wxAttachmentPoint *point = (wxAttachmentPoint *)node->Data();
490 delete point;
491 node = node->Next();
492 }
493 m_attachmentPoints.Clear();
494}
495
496void wxShape::ClearText(int regionId)
497{
498 if (regionId == 0)
499 {
500 m_text.DeleteContents(TRUE);
501 m_text.Clear();
502 m_text.DeleteContents(FALSE);
503 }
504 wxNode *node = m_regions.Nth(regionId);
505 if (!node)
506 return;
507 wxShapeRegion *region = (wxShapeRegion *)node->Data();
508 region->ClearText();
509}
510
511void wxShape::ClearRegions()
512{
513 wxNode *node = m_regions.First();
514 while (node)
515 {
516 wxShapeRegion *region = (wxShapeRegion *)node->Data();
517 wxNode *next = node->Next();
518 delete region;
519 delete node;
520 node = next;
521 }
522}
523
524void wxShape::AddRegion(wxShapeRegion *region)
525{
526 m_regions.Append(region);
527}
528
529void wxShape::SetDefaultRegionSize()
530{
531 wxNode *node = m_regions.First();
532 if (!node) return;
533 wxShapeRegion *region = (wxShapeRegion *)node->Data();
42cfaf8c 534 double w, h;
0fc1a713
JS
535 GetBoundingBoxMin(&w, &h);
536 region->SetSize(w, h);
537}
538
42cfaf8c 539bool wxShape::HitTest(double x, double y, int *attachment, double *distance)
0fc1a713
JS
540{
541// if (!sensitive)
542// return FALSE;
543
42cfaf8c 544 double width = 0.0, height = 0.0;
0fc1a713
JS
545 GetBoundingBoxMin(&width, &height);
546 if (fabs(width) < 4.0) width = 4.0;
547 if (fabs(height) < 4.0) height = 4.0;
548
42cfaf8c 549 width += (double)4.0; height += (double)4.0; // Allowance for inaccurate mousing
0fc1a713 550
42cfaf8c
JS
551 double left = (double)(m_xpos - (width/2.0));
552 double top = (double)(m_ypos - (height/2.0));
553 double right = (double)(m_xpos + (width/2.0));
554 double bottom = (double)(m_ypos + (height/2.0));
0fc1a713
JS
555
556 int nearest_attachment = 0;
557
0fc1a713
JS
558 // If within the bounding box, check the attachment points
559 // within the object.
560
561 if (x >= left && x <= right && y >= top && y <= bottom)
562 {
563 int n = GetNumberOfAttachments();
42cfaf8c 564 double nearest = 999999.0;
0fc1a713 565
f97c9854
JS
566 // GetAttachmentPosition[Edge] takes a logical attachment position,
567 // i.e. if it's rotated through 90%, position 0 is East-facing.
568
0fc1a713
JS
569 for (int i = 0; i < n; i++)
570 {
42cfaf8c 571 double xp, yp;
f97c9854 572 if (GetAttachmentPositionEdge(i, &xp, &yp))
0fc1a713 573 {
42cfaf8c 574 double l = (double)sqrt(((xp - x) * (xp - x)) +
0fc1a713
JS
575 ((yp - y) * (yp - y)));
576
577 if (l < nearest)
578 {
579 nearest = l;
580 nearest_attachment = i;
581 }
582 }
583 }
584 *attachment = nearest_attachment;
585 *distance = nearest;
586 return TRUE;
587 }
588 else return FALSE;
589}
590
591// Format a text string according to the region size, adding
592// strings with positions to region text list
593
594static bool GraphicsInSizeToContents = FALSE; // Infinite recursion elimination
595void wxShape::FormatText(wxDC& dc, const wxString& s, int i)
596{
42cfaf8c 597 double w, h;
0fc1a713
JS
598 ClearText(i);
599
600 if (m_regions.Number() < 1)
601 return;
602 wxNode *node = m_regions.Nth(i);
603 if (!node)
604 return;
605
606 wxShapeRegion *region = (wxShapeRegion *)node->Data();
607 region->SetText(s);
c0ed460c 608 dc.SetFont(* region->GetFont());
0fc1a713
JS
609
610 region->GetSize(&w, &h);
611
42cfaf8c
JS
612 wxStringList *stringList = oglFormatText(dc, s, (w-5), (h-5), region->GetFormatMode());
613 node = stringList->First();
0fc1a713
JS
614 while (node)
615 {
616 char *s = (char *)node->Data();
617 wxShapeTextLine *line = new wxShapeTextLine(0.0, 0.0, s);
618 region->GetFormattedText().Append((wxObject *)line);
42cfaf8c 619 node = node->Next();
0fc1a713 620 }
42cfaf8c
JS
621 delete stringList;
622 double actualW = w;
623 double actualH = h;
0fc1a713
JS
624 // Don't try to resize an object with more than one image (this case should be dealt
625 // with by overriden handlers)
626 if ((region->GetFormatMode() & FORMAT_SIZE_TO_CONTENTS) &&
627 (region->GetFormattedText().Number() > 0) &&
628 (m_regions.Number() == 1) && !GraphicsInSizeToContents)
629 {
42cfaf8c 630 oglGetCentredTextExtent(dc, &(region->GetFormattedText()), m_xpos, m_ypos, w, h, &actualW, &actualH);
0fc1a713
JS
631 if ((actualW+m_textMarginX != w ) || (actualH+m_textMarginY != h))
632 {
633 // If we are a descendant of a composite, must make sure the composite gets
634 // resized properly
635 wxShape *topAncestor = GetTopAncestor();
636
637 if (topAncestor != this)
638 {
639 // Make sure we don't recurse infinitely
640 GraphicsInSizeToContents = TRUE;
641
642 wxCompositeShape *composite = (wxCompositeShape *)topAncestor;
643 composite->Erase(dc);
644 SetSize(actualW+m_textMarginX, actualH+m_textMarginY);
645 Move(dc, m_xpos, m_ypos);
646 composite->CalculateSize();
647 if (composite->Selected())
648 {
649 composite->DeleteControlPoints(& dc);
650 composite->MakeControlPoints();
651 composite->MakeMandatoryControlPoints();
652 }
653 // Where infinite recursion might happen if we didn't stop it
654 composite->Draw(dc);
655
656 GraphicsInSizeToContents = FALSE;
657 }
658 else
659 {
660 Erase(dc);
661 SetSize(actualW+m_textMarginX, actualH+m_textMarginY);
662 Move(dc, m_xpos, m_ypos);
663 }
664 SetSize(actualW+m_textMarginX, actualH+m_textMarginY);
665 Move(dc, m_xpos, m_ypos);
666 EraseContents(dc);
667 }
668 }
42cfaf8c 669 oglCentreText(dc, &(region->GetFormattedText()), m_xpos, m_ypos, actualW, actualH, region->GetFormatMode());
0fc1a713
JS
670 m_formatted = TRUE;
671}
672
673void wxShape::Recentre(wxDC& dc)
674{
42cfaf8c 675 double w, h;
0fc1a713
JS
676 GetBoundingBoxMin(&w, &h);
677
678 int noRegions = m_regions.Number();
679 for (int i = 0; i < noRegions; i++)
680 {
681 wxNode *node = m_regions.Nth(i);
682 if (node)
683 {
684 wxShapeRegion *region = (wxShapeRegion *)node->Data();
42cfaf8c 685 oglCentreText(dc, &(region->GetFormattedText()), m_xpos, m_ypos, w, h, region->GetFormatMode());
0fc1a713
JS
686 }
687 }
688}
689
42cfaf8c
JS
690bool wxShape::GetPerimeterPoint(double x1, double y1,
691 double x2, double y2,
692 double *x3, double *y3)
0fc1a713
JS
693{
694 return FALSE;
695}
696
697void wxShape::SetPen(wxPen *the_pen)
698{
699 m_pen = the_pen;
700}
701
702void wxShape::SetBrush(wxBrush *the_brush)
703{
704 m_brush = the_brush;
705}
706
707// Get the top-most (non-division) ancestor, or self
708wxShape *wxShape::GetTopAncestor()
709{
710 if (!GetParent())
711 return this;
712
713 if (GetParent()->IsKindOf(CLASSINFO(wxDivisionShape)))
714 return this;
715 else return GetParent()->GetTopAncestor();
716}
717
718/*
719 * Region functions
720 *
721 */
722void wxShape::SetFont(wxFont *the_font, int regionId)
723{
724 m_font = the_font;
725 wxNode *node = m_regions.Nth(regionId);
726 if (!node)
727 return;
728 wxShapeRegion *region = (wxShapeRegion *)node->Data();
729 region->SetFont(the_font);
730}
731
732wxFont *wxShape::GetFont(int n) const
733{
734 wxNode *node = m_regions.Nth(n);
735 if (!node)
736 return NULL;
737 wxShapeRegion *region = (wxShapeRegion *)node->Data();
738 return region->GetFont();
739}
740
741void wxShape::SetFormatMode(int mode, int regionId)
742{
743 wxNode *node = m_regions.Nth(regionId);
744 if (!node)
745 return;
746 wxShapeRegion *region = (wxShapeRegion *)node->Data();
747 region->SetFormatMode(mode);
748}
749
750int wxShape::GetFormatMode(int regionId) const
751{
752 wxNode *node = m_regions.Nth(regionId);
753 if (!node)
754 return 0;
755 wxShapeRegion *region = (wxShapeRegion *)node->Data();
756 return region->GetFormatMode();
757}
758
759void wxShape::SetTextColour(const wxString& the_colour, int regionId)
760{
761 wxColour *wxcolour = wxTheColourDatabase->FindColour(the_colour);
762 m_textColour = wxcolour;
763 m_textColourName = the_colour;
764
765 wxNode *node = m_regions.Nth(regionId);
766 if (!node)
767 return;
768 wxShapeRegion *region = (wxShapeRegion *)node->Data();
769 region->SetColour(the_colour);
770}
771
772wxString wxShape::GetTextColour(int regionId) const
773{
774 wxNode *node = m_regions.Nth(regionId);
775 if (!node)
776 return wxString("");
777 wxShapeRegion *region = (wxShapeRegion *)node->Data();
778 return region->GetColour();
779}
780
781void wxShape::SetRegionName(const wxString& name, int regionId)
782{
783 wxNode *node = m_regions.Nth(regionId);
784 if (!node)
785 return;
786 wxShapeRegion *region = (wxShapeRegion *)node->Data();
787 region->SetName(name);
788}
789
790wxString wxShape::GetRegionName(int regionId)
791{
792 wxNode *node = m_regions.Nth(regionId);
793 if (!node)
794 return wxString("");
795 wxShapeRegion *region = (wxShapeRegion *)node->Data();
796 return region->GetName();
797}
798
799int wxShape::GetRegionId(const wxString& name)
800{
801 wxNode *node = m_regions.First();
802 int i = 0;
803 while (node)
804 {
805 wxShapeRegion *region = (wxShapeRegion *)node->Data();
806 if (region->GetName() == name)
807 return i;
808 node = node->Next();
809 i ++;
810 }
811 return -1;
812}
813
814// Name all m_regions in all subimages recursively.
815void wxShape::NameRegions(const wxString& parentName)
816{
817 int n = GetNumberOfTextRegions();
818 char buf[100];
819 for (int i = 0; i < n; i++)
820 {
821 if (parentName.Length() > 0)
822 sprintf(buf, "%s.%d", (const char*) parentName, i);
823 else
824 sprintf(buf, "%d", i);
825 SetRegionName(buf, i);
826 }
827 wxNode *node = m_children.First();
828 int j = 0;
829 while (node)
830 {
831 wxShape *child = (wxShape *)node->Data();
832 if (parentName.Length() > 0)
833 sprintf(buf, "%s.%d", (const char*) parentName, j);
834 else
835 sprintf(buf, "%d", j);
836 child->NameRegions(buf);
837 node = node->Next();
838 j ++;
839 }
840}
841
842// Get a region by name, possibly looking recursively into composites.
843wxShape *wxShape::FindRegion(const wxString& name, int *regionId)
844{
845 int id = GetRegionId(name);
846 if (id > -1)
847 {
848 *regionId = id;
849 return this;
850 }
851
852 wxNode *node = m_children.First();
853 while (node)
854 {
855 wxShape *child = (wxShape *)node->Data();
856 wxShape *actualImage = child->FindRegion(name, regionId);
857 if (actualImage)
858 return actualImage;
859 node = node->Next();
860 }
861 return NULL;
862}
863
864// Finds all region names for this image (composite or simple).
865// Supply empty string list.
866void wxShape::FindRegionNames(wxStringList& list)
867{
868 int n = GetNumberOfTextRegions();
869 for (int i = 0; i < n; i++)
870 {
871 wxString name(GetRegionName(i));
872 list.Add((const char*) name);
873 }
874
875 wxNode *node = m_children.First();
876 while (node)
877 {
878 wxShape *child = (wxShape *)node->Data();
879 child->FindRegionNames(list);
880 node = node->Next();
881 }
882}
883
884void wxShape::AssignNewIds()
885{
34138703
JS
886// if (m_id == 0)
887 m_id = NewId();
0fc1a713
JS
888 wxNode *node = m_children.First();
889 while (node)
890 {
891 wxShape *child = (wxShape *)node->Data();
892 child->AssignNewIds();
893 node = node->Next();
894 }
895}
896
897void wxShape::OnDraw(wxDC& dc)
898{
899}
900
901void wxShape::OnMoveLinks(wxDC& dc)
902{
903 // Want to set the ends of all attached links
904 // to point to/from this object
905
906 wxNode *current = m_lines.First();
907 while (current)
908 {
909 wxLineShape *line = (wxLineShape *)current->Data();
910 line->GetEventHandler()->OnMoveLink(dc);
911 current = current->Next();
912 }
913}
914
915
916void wxShape::OnDrawContents(wxDC& dc)
917{
42cfaf8c 918 double bound_x, bound_y;
0fc1a713
JS
919 GetBoundingBoxMin(&bound_x, &bound_y);
920 if (m_regions.Number() < 1) return;
921
c0ed460c 922 if (m_pen) dc.SetPen(* m_pen);
0fc1a713
JS
923
924 wxShapeRegion *region = (wxShapeRegion *)m_regions.First()->Data();
c0ed460c 925 if (region->GetFont()) dc.SetFont(* region->GetFont());
0fc1a713
JS
926
927 dc.SetTextForeground(* (region->GetActualColourObject()));
928 dc.SetBackgroundMode(wxTRANSPARENT);
929 if (!m_formatted)
930 {
42cfaf8c 931 oglCentreText(dc, &(region->GetFormattedText()), m_xpos, m_ypos, bound_x, bound_y, region->GetFormatMode());
0fc1a713
JS
932 m_formatted = TRUE;
933 }
934 if (!GetDisableLabel())
935 {
42cfaf8c 936 oglDrawFormattedText(dc, &(region->GetFormattedText()), m_xpos, m_ypos, bound_x, bound_y, region->GetFormatMode());
0fc1a713
JS
937 }
938}
939
940void wxShape::DrawContents(wxDC& dc)
941{
942 GetEventHandler()->OnDrawContents(dc);
943}
944
42cfaf8c 945void wxShape::OnSize(double x, double y)
0fc1a713
JS
946{
947}
948
42cfaf8c 949bool wxShape::OnMovePre(wxDC& dc, double x, double y, double old_x, double old_y, bool display)
0fc1a713
JS
950{
951 return TRUE;
952}
953
42cfaf8c 954void wxShape::OnMovePost(wxDC& dc, double x, double y, double old_x, double old_y, bool display)
0fc1a713
JS
955{
956}
957
958void wxShape::OnErase(wxDC& dc)
959{
960 if (!m_visible)
961 return;
962
963 // Erase links
964 wxNode *current = m_lines.First();
965 while (current)
966 {
967 wxLineShape *line = (wxLineShape *)current->Data();
968 line->GetEventHandler()->OnErase(dc);
969 current = current->Next();
970 }
971 GetEventHandler()->OnEraseContents(dc);
972}
973
974void wxShape::OnEraseContents(wxDC& dc)
975{
976 if (!m_visible)
977 return;
978
42cfaf8c
JS
979 double maxX, maxY, minX, minY;
980 double xp = GetX();
981 double yp = GetY();
0fc1a713
JS
982 GetBoundingBoxMin(&minX, &minY);
983 GetBoundingBoxMax(&maxX, &maxY);
42cfaf8c
JS
984 double topLeftX = (double)(xp - (maxX / 2.0) - 2.0);
985 double topLeftY = (double)(yp - (maxY / 2.0) - 2.0);
0fc1a713
JS
986
987 int penWidth = 0;
988 if (m_pen)
989 penWidth = m_pen->GetWidth();
990
c0ed460c
JS
991 dc.SetPen(* g_oglWhiteBackgroundPen);
992 dc.SetBrush(* g_oglWhiteBackgroundBrush);
42cfaf8c
JS
993 dc.DrawRectangle(WXROUND(topLeftX - penWidth), WXROUND(topLeftY - penWidth),
994 WXROUND(maxX + penWidth*2.0 + 4.0), WXROUND(maxY + penWidth*2.0 + 4.0));
0fc1a713
JS
995}
996
997void wxShape::EraseLinks(wxDC& dc, int attachment, bool recurse)
998{
999 if (!m_visible)
1000 return;
1001
1002 wxNode *current = m_lines.First();
1003 while (current)
1004 {
1005 wxLineShape *line = (wxLineShape *)current->Data();
1006 if (attachment == -1 || ((line->GetTo() == this && line->GetAttachmentTo() == attachment) ||
1007 (line->GetFrom() == this && line->GetAttachmentFrom() == attachment)))
1008 line->GetEventHandler()->OnErase(dc);
1009 current = current->Next();
1010 }
1011 if (recurse)
1012 {
1013 wxNode *node = m_children.First();
1014 while (node)
1015 {
1016 wxShape *child = (wxShape *)node->Data();
1017 child->EraseLinks(dc, attachment, recurse);
1018 node = node->Next();
1019 }
1020 }
1021}
1022
1023void wxShape::DrawLinks(wxDC& dc, int attachment, bool recurse)
1024{
1025 if (!m_visible)
1026 return;
1027
1028 wxNode *current = m_lines.First();
1029 while (current)
1030 {
1031 wxLineShape *line = (wxLineShape *)current->Data();
1032 if (attachment == -1 ||
1033 (line->GetTo() == this && line->GetAttachmentTo() == attachment) ||
1034 (line->GetFrom() == this && line->GetAttachmentFrom() == attachment))
1035 line->Draw(dc);
1036 current = current->Next();
1037 }
1038 if (recurse)
1039 {
1040 wxNode *node = m_children.First();
1041 while (node)
1042 {
1043 wxShape *child = (wxShape *)node->Data();
1044 child->DrawLinks(dc, attachment, recurse);
1045 node = node->Next();
1046 }
1047 }
1048}
1049
42cfaf8c
JS
1050// Returns TRUE if pt1 <= pt2 in the sense that one point comes before another on an
1051// edge of the shape.
1052// attachmentPoint is the attachment point (= side) in question.
1053
1054// This is the default, rectangular implementation.
1055bool wxShape::AttachmentSortTest(int attachmentPoint, const wxRealPoint& pt1, const wxRealPoint& pt2)
1056{
f97c9854
JS
1057 int physicalAttachment = LogicalToPhysicalAttachment(attachmentPoint);
1058 switch (physicalAttachment)
42cfaf8c
JS
1059 {
1060 case 0:
1061 case 2:
1062 {
1063 return (pt1.x <= pt2.x) ;
1064 break;
1065 }
1066 case 1:
1067 case 3:
1068 {
1069 return (pt1.y <= pt2.y) ;
1070 break;
1071 }
1072 }
1073
1074 return FALSE;
1075}
1076
2e5ed787 1077bool wxShape::MoveLineToNewAttachment(wxDC& dc, wxLineShape *to_move,
42cfaf8c 1078 double x, double y)
0fc1a713 1079{
f97c9854 1080 if (GetAttachmentMode() == ATTACHMENT_MODE_NONE)
2e5ed787 1081 return FALSE;
42cfaf8c
JS
1082
1083 int newAttachment, oldAttachment;
1084 double distance;
0fc1a713
JS
1085
1086 // Is (x, y) on this object? If so, find the new attachment point
1087 // the user has moved the point to
42cfaf8c 1088 bool hit = HitTest(x, y, &newAttachment, &distance);
0fc1a713 1089 if (!hit)
2e5ed787 1090 return FALSE;
0fc1a713
JS
1091
1092 EraseLinks(dc);
1093
0fc1a713 1094 if (to_move->GetTo() == this)
42cfaf8c 1095 oldAttachment = to_move->GetAttachmentTo();
0fc1a713 1096 else
42cfaf8c
JS
1097 oldAttachment = to_move->GetAttachmentFrom();
1098
1099 // The links in a new ordering.
1100 wxList newOrdering;
1101
1102 // First, add all links to the new list.
1103 wxNode *node = m_lines.First();
1104 while (node)
1105 {
1106 newOrdering.Append(node->Data());
1107 node = node->Next();
1108 }
0fc1a713
JS
1109
1110 // Delete the line object from the list of links; we're going to move
1111 // it to another position in the list
42cfaf8c 1112 newOrdering.DeleteObject(to_move);
0fc1a713 1113
42cfaf8c
JS
1114 double old_x = (double) -99999.9;
1115 double old_y = (double) -99999.9;
0fc1a713 1116
42cfaf8c 1117 node = newOrdering.First();
0fc1a713
JS
1118 bool found = FALSE;
1119
42cfaf8c 1120 while (!found && node)
0fc1a713
JS
1121 {
1122 wxLineShape *line = (wxLineShape *)node->Data();
42cfaf8c
JS
1123 if ((line->GetTo() == this && oldAttachment == line->GetAttachmentTo()) ||
1124 (line->GetFrom() == this && oldAttachment == line->GetAttachmentFrom()))
0fc1a713 1125 {
42cfaf8c
JS
1126 double startX, startY, endX, endY;
1127 double xp, yp;
0fc1a713
JS
1128 line->GetEnds(&startX, &startY, &endX, &endY);
1129 if (line->GetTo() == this)
1130 {
0fc1a713
JS
1131 xp = endX;
1132 yp = endY;
42cfaf8c
JS
1133 } else
1134 {
1135 xp = startX;
1136 yp = startY;
0fc1a713
JS
1137 }
1138
42cfaf8c
JS
1139 wxRealPoint thisPoint(xp, yp);
1140 wxRealPoint lastPoint(old_x, old_y);
1141 wxRealPoint newPoint(x, y);
1142
1143 if (AttachmentSortTest(newAttachment, newPoint, thisPoint) && AttachmentSortTest(newAttachment, lastPoint, newPoint))
0fc1a713 1144 {
42cfaf8c
JS
1145 found = TRUE;
1146 newOrdering.Insert(node, to_move);
0fc1a713 1147 }
42cfaf8c 1148
0fc1a713
JS
1149 old_x = xp;
1150 old_y = yp;
1151 }
1152 node = node->Next();
1153 }
42cfaf8c 1154
0fc1a713 1155 if (!found)
42cfaf8c
JS
1156 newOrdering.Append(to_move);
1157
1158 GetEventHandler()->OnChangeAttachment(newAttachment, to_move, newOrdering);
2e5ed787
JS
1159
1160 return TRUE;
42cfaf8c
JS
1161}
1162
1163void wxShape::OnChangeAttachment(int attachment, wxLineShape* line, wxList& ordering)
1164{
1165 if (line->GetTo() == this)
1166 line->SetAttachmentTo(attachment);
1167 else
1168 line->SetAttachmentFrom(attachment);
1169
1170 ApplyAttachmentOrdering(ordering);
1171
1172 wxClientDC dc(GetCanvas());
1173 GetCanvas()->PrepareDC(dc);
1174
1175 MoveLinks(dc);
1176
1177 if (!GetCanvas()->GetQuickEditMode()) GetCanvas()->Redraw(dc);
1178}
1179
1180// Reorders the lines according to the given list.
1181void wxShape::ApplyAttachmentOrdering(wxList& linesToSort)
1182{
1183 // This is a temporary store of all the lines.
1184 wxList linesStore;
1185
1186 wxNode *node = m_lines.First();
1187 while (node)
1188 {
1189 wxLineShape *line = (wxLineShape *)node->Data();
1190 linesStore.Append(line);
1191 node = node->Next();;
1192 }
1193
1194 m_lines.Clear();
1195
1196 node = linesToSort.First();
1197 while (node)
1198 {
1199 wxLineShape *line = (wxLineShape *)node->Data();
1200 if (linesStore.Member(line))
1201 {
1202 // Done this one
1203 linesStore.DeleteObject(line);
1204 m_lines.Append(line);
1205 }
1206 node = node->Next();
1207 }
1208
1209 // Now add any lines that haven't been listed in linesToSort.
1210 node = linesStore.First();
1211 while (node)
1212 {
1213 wxLineShape *line = (wxLineShape *)node->Data();
1214 m_lines.Append(line);
1215 node = node->Next();
1216 }
0fc1a713
JS
1217}
1218
1219// Reorders the lines coming into the node image at this attachment
1220// position, in the order in which they appear in linesToSort.
1221// Any remaining lines not in the list will be added to the end.
1222void wxShape::SortLines(int attachment, wxList& linesToSort)
1223{
1224 // This is a temporary store of all the lines at this attachment
1225 // point. We'll tick them off as we've processed them.
1226 wxList linesAtThisAttachment;
1227
1228 wxNode *node = m_lines.First();
1229 while (node)
1230 {
1231 wxLineShape *line = (wxLineShape *)node->Data();
1232 wxNode *next = node->Next();
1233 if ((line->GetTo() == this && line->GetAttachmentTo() == attachment) ||
1234 (line->GetFrom() == this && line->GetAttachmentFrom() == attachment))
1235 {
1236 linesAtThisAttachment.Append(line);
1237 delete node;
1238 node = next;
1239 }
1240 else node = node->Next();
1241 }
1242
1243 node = linesToSort.First();
1244 while (node)
1245 {
1246 wxLineShape *line = (wxLineShape *)node->Data();
1247 if (linesAtThisAttachment.Member(line))
1248 {
1249 // Done this one
1250 linesAtThisAttachment.DeleteObject(line);
1251 m_lines.Append(line);
1252 }
1253 node = node->Next();
1254 }
1255
1256 // Now add any lines that haven't been listed in linesToSort.
1257 node = linesAtThisAttachment.First();
1258 while (node)
1259 {
1260 wxLineShape *line = (wxLineShape *)node->Data();
1261 m_lines.Append(line);
1262 node = node->Next();
1263 }
1264}
1265
1266void wxShape::OnHighlight(wxDC& dc)
1267{
1268}
1269
42cfaf8c 1270void wxShape::OnLeftClick(double x, double y, int keys, int attachment)
0fc1a713
JS
1271{
1272 if ((m_sensitivity & OP_CLICK_LEFT) != OP_CLICK_LEFT)
1273 {
1274 attachment = 0;
42cfaf8c 1275 double dist;
0fc1a713
JS
1276 if (m_parent)
1277 {
1278 m_parent->HitTest(x, y, &attachment, &dist);
1279 m_parent->GetEventHandler()->OnLeftClick(x, y, keys, attachment);
1280 }
1281 return;
1282 }
1283}
1284
42cfaf8c 1285void wxShape::OnRightClick(double x, double y, int keys, int attachment)
0fc1a713
JS
1286{
1287 if ((m_sensitivity & OP_CLICK_RIGHT) != OP_CLICK_RIGHT)
1288 {
1289 attachment = 0;
42cfaf8c 1290 double dist;
0fc1a713
JS
1291 if (m_parent)
1292 {
1293 m_parent->HitTest(x, y, &attachment, &dist);
1294 m_parent->GetEventHandler()->OnRightClick(x, y, keys, attachment);
1295 }
1296 return;
1297 }
1298}
1299
42cfaf8c
JS
1300double DragOffsetX = 0.0;
1301double DragOffsetY = 0.0;
0fc1a713 1302
42cfaf8c 1303void wxShape::OnDragLeft(bool draw, double x, double y, int keys, int attachment)
0fc1a713
JS
1304{
1305 if ((m_sensitivity & OP_DRAG_LEFT) != OP_DRAG_LEFT)
1306 {
1307 attachment = 0;
42cfaf8c 1308 double dist;
0fc1a713
JS
1309 if (m_parent)
1310 {
1311 m_parent->HitTest(x, y, &attachment, &dist);
1312 m_parent->GetEventHandler()->OnDragLeft(draw, x, y, keys, attachment);
1313 }
1314 return;
1315 }
1316
1317 wxClientDC dc(GetCanvas());
1318 GetCanvas()->PrepareDC(dc);
1319
1320 dc.SetLogicalFunction(wxXOR);
1321
1322 wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
1323 dc.SetPen(dottedPen);
1324 dc.SetBrush(* wxTRANSPARENT_BRUSH);
1325
42cfaf8c 1326 double xx, yy;
0fc1a713
JS
1327 xx = x + DragOffsetX;
1328 yy = y + DragOffsetY;
1329
1330 m_canvas->Snap(&xx, &yy);
1331// m_xpos = xx; m_ypos = yy;
42cfaf8c 1332 double w, h;
0fc1a713
JS
1333 GetBoundingBoxMax(&w, &h);
1334 GetEventHandler()->OnDrawOutline(dc, xx, yy, w, h);
1335}
1336
42cfaf8c 1337void wxShape::OnBeginDragLeft(double x, double y, int keys, int attachment)
0fc1a713
JS
1338{
1339 if ((m_sensitivity & OP_DRAG_LEFT) != OP_DRAG_LEFT)
1340 {
1341 attachment = 0;
42cfaf8c 1342 double dist;
0fc1a713
JS
1343 if (m_parent)
1344 {
1345 m_parent->HitTest(x, y, &attachment, &dist);
1346 m_parent->GetEventHandler()->OnBeginDragLeft(x, y, keys, attachment);
1347 }
1348 return;
1349 }
1350
1351 DragOffsetX = m_xpos - x;
1352 DragOffsetY = m_ypos - y;
1353
1354 wxClientDC dc(GetCanvas());
1355 GetCanvas()->PrepareDC(dc);
1356
42cfaf8c
JS
1357 // New policy: don't erase shape until end of drag.
1358// Erase(dc);
1359
1360 double xx, yy;
0fc1a713
JS
1361 xx = x + DragOffsetX;
1362 yy = y + DragOffsetY;
1363 m_canvas->Snap(&xx, &yy);
1364// m_xpos = xx; m_ypos = yy;
1365 dc.SetLogicalFunction(wxXOR);
1366
1367 wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
1368 dc.SetPen(dottedPen);
1369 dc.SetBrush((* wxTRANSPARENT_BRUSH));
1370
42cfaf8c 1371 double w, h;
0fc1a713
JS
1372 GetBoundingBoxMax(&w, &h);
1373 GetEventHandler()->OnDrawOutline(dc, xx, yy, w, h);
1374 m_canvas->CaptureMouse();
1375}
1376
42cfaf8c 1377void wxShape::OnEndDragLeft(double x, double y, int keys, int attachment)
0fc1a713
JS
1378{
1379 m_canvas->ReleaseMouse();
1380 if ((m_sensitivity & OP_DRAG_LEFT) != OP_DRAG_LEFT)
1381 {
1382 attachment = 0;
42cfaf8c 1383 double dist;
0fc1a713
JS
1384 if (m_parent)
1385 {
1386 m_parent->HitTest(x, y, &attachment, &dist);
1387 m_parent->GetEventHandler()->OnEndDragLeft(x, y, keys, attachment);
1388 }
1389 return;
1390 }
1391
1392 wxClientDC dc(GetCanvas());
1393 GetCanvas()->PrepareDC(dc);
1394
1395 dc.SetLogicalFunction(wxCOPY);
1396
42cfaf8c
JS
1397 double xx = x + DragOffsetX;
1398 double yy = y + DragOffsetY;
0fc1a713
JS
1399 m_canvas->Snap(&xx, &yy);
1400// canvas->Snap(&m_xpos, &m_ypos);
42cfaf8c
JS
1401
1402 // New policy: erase shape at end of drag.
1403 Erase(dc);
1404
0fc1a713
JS
1405 Move(dc, xx, yy);
1406 if (m_canvas && !m_canvas->GetQuickEditMode()) m_canvas->Redraw(dc);
1407}
1408
42cfaf8c 1409void wxShape::OnDragRight(bool draw, double x, double y, int keys, int attachment)
0fc1a713
JS
1410{
1411 if ((m_sensitivity & OP_DRAG_RIGHT) != OP_DRAG_RIGHT)
1412 {
1413 attachment = 0;
42cfaf8c 1414 double dist;
0fc1a713
JS
1415 if (m_parent)
1416 {
1417 m_parent->HitTest(x, y, &attachment, &dist);
1418 m_parent->GetEventHandler()->OnDragRight(draw, x, y, keys, attachment);
1419 }
1420 return;
1421 }
1422}
1423
42cfaf8c 1424void wxShape::OnBeginDragRight(double x, double y, int keys, int attachment)
0fc1a713
JS
1425{
1426 if ((m_sensitivity & OP_DRAG_RIGHT) != OP_DRAG_RIGHT)
1427 {
1428 attachment = 0;
42cfaf8c 1429 double dist;
0fc1a713
JS
1430 if (m_parent)
1431 {
1432 m_parent->HitTest(x, y, &attachment, &dist);
1433 m_parent->GetEventHandler()->OnBeginDragRight(x, y, keys, attachment);
1434 }
1435 return;
1436 }
1437}
1438
42cfaf8c 1439void wxShape::OnEndDragRight(double x, double y, int keys, int attachment)
0fc1a713
JS
1440{
1441 if ((m_sensitivity & OP_DRAG_RIGHT) != OP_DRAG_RIGHT)
1442 {
1443 attachment = 0;
42cfaf8c 1444 double dist;
0fc1a713
JS
1445 if (m_parent)
1446 {
1447 m_parent->HitTest(x, y, &attachment, &dist);
1448 m_parent->GetEventHandler()->OnEndDragRight(x, y, keys, attachment);
1449 }
1450 return;
1451 }
1452}
1453
42cfaf8c 1454void wxShape::OnDrawOutline(wxDC& dc, double x, double y, double w, double h)
0fc1a713 1455{
42cfaf8c
JS
1456 double top_left_x = (double)(x - w/2.0);
1457 double top_left_y = (double)(y - h/2.0);
1458 double top_right_x = (double)(top_left_x + w);
1459 double top_right_y = (double)top_left_y;
1460 double bottom_left_x = (double)top_left_x;
1461 double bottom_left_y = (double)(top_left_y + h);
1462 double bottom_right_x = (double)top_right_x;
1463 double bottom_right_y = (double)bottom_left_y;
0fc1a713
JS
1464
1465 wxPoint points[5];
42cfaf8c
JS
1466 points[0].x = WXROUND(top_left_x); points[0].y = WXROUND(top_left_y);
1467 points[1].x = WXROUND(top_right_x); points[1].y = WXROUND(top_right_y);
1468 points[2].x = WXROUND(bottom_right_x); points[2].y = WXROUND(bottom_right_y);
1469 points[3].x = WXROUND(bottom_left_x); points[3].y = WXROUND(bottom_left_y);
1470 points[4].x = WXROUND(top_left_x); points[4].y = WXROUND(top_left_y);
0fc1a713
JS
1471
1472 dc.DrawLines(5, points);
1473}
1474
1475void wxShape::Attach(wxShapeCanvas *can)
1476{
1477 m_canvas = can;
1478}
1479
1480void wxShape::Detach()
1481{
1482 m_canvas = NULL;
1483}
1484
42cfaf8c 1485void wxShape::Move(wxDC& dc, double x, double y, bool display)
0fc1a713 1486{
42cfaf8c
JS
1487 double old_x = m_xpos;
1488 double old_y = m_ypos;
0fc1a713 1489
0fc1a713
JS
1490 if (!GetEventHandler()->OnMovePre(dc, x, y, old_x, old_y, display))
1491 {
2e5ed787
JS
1492// m_xpos = old_x;
1493// m_ypos = old_y;
0fc1a713
JS
1494 return;
1495 }
1496
2e5ed787
JS
1497 m_xpos = x; m_ypos = y;
1498
0fc1a713
JS
1499 ResetControlPoints();
1500
1501 if (display)
1502 Draw(dc);
1503
1504 MoveLinks(dc);
1505
1506 GetEventHandler()->OnMovePost(dc, x, y, old_x, old_y, display);
1507}
1508
1509void wxShape::MoveLinks(wxDC& dc)
1510{
1511 GetEventHandler()->OnMoveLinks(dc);
1512}
1513
1514
1515void wxShape::Draw(wxDC& dc)
1516{
1517 if (m_visible)
1518 {
1519 GetEventHandler()->OnDraw(dc);
1520 GetEventHandler()->OnDrawContents(dc);
1521 GetEventHandler()->OnDrawControlPoints(dc);
f97c9854 1522 GetEventHandler()->OnDrawBranches(dc);
0fc1a713
JS
1523 }
1524}
1525
1526void wxShape::Flash()
1527{
1528 if (GetCanvas())
1529 {
1530 wxClientDC dc(GetCanvas());
1531 GetCanvas()->PrepareDC(dc);
1532
1533 dc.SetLogicalFunction(wxXOR);
1534 Draw(dc);
1535 dc.SetLogicalFunction(wxCOPY);
1536 Draw(dc);
1537 }
1538}
1539
1540void wxShape::Show(bool show)
1541{
1542 m_visible = show;
1543 wxNode *node = m_children.First();
1544 while (node)
1545 {
1546 wxShape *image = (wxShape *)node->Data();
1547 image->Show(show);
1548 node = node->Next();
1549 }
1550}
1551
1552void wxShape::Erase(wxDC& dc)
1553{
1554 GetEventHandler()->OnErase(dc);
1555 GetEventHandler()->OnEraseControlPoints(dc);
f97c9854 1556 GetEventHandler()->OnDrawBranches(dc, TRUE);
0fc1a713
JS
1557}
1558
1559void wxShape::EraseContents(wxDC& dc)
1560{
1561 GetEventHandler()->OnEraseContents(dc);
1562}
1563
1564void wxShape::AddText(const wxString& string)
1565{
1566 wxNode *node = m_regions.First();
1567 if (!node)
1568 return;
1569 wxShapeRegion *region = (wxShapeRegion *)node->Data();
1570 region->ClearText();
1571 wxShapeTextLine *new_line =
1572 new wxShapeTextLine(0.0, 0.0, string);
1573 region->GetFormattedText().Append(new_line);
1574
1575 m_formatted = FALSE;
1576}
1577
42cfaf8c 1578void wxShape::SetSize(double x, double y, bool recursive)
0fc1a713
JS
1579{
1580 SetAttachmentSize(x, y);
1581 SetDefaultRegionSize();
1582}
1583
42cfaf8c 1584void wxShape::SetAttachmentSize(double w, double h)
0fc1a713 1585{
42cfaf8c
JS
1586 double scaleX;
1587 double scaleY;
1588 double width, height;
0fc1a713
JS
1589 GetBoundingBoxMin(&width, &height);
1590 if (width == 0.0)
1591 scaleX = 1.0;
1592 else scaleX = w/width;
1593 if (height == 0.0)
1594 scaleY = 1.0;
1595 else scaleY = h/height;
1596
1597 wxNode *node = m_attachmentPoints.First();
1598 while (node)
1599 {
1600 wxAttachmentPoint *point = (wxAttachmentPoint *)node->Data();
42cfaf8c
JS
1601 point->m_x = (double)(point->m_x * scaleX);
1602 point->m_y = (double)(point->m_y * scaleY);
0fc1a713
JS
1603 node = node->Next();
1604 }
1605}
1606
1607// Add line FROM this object
1608void wxShape::AddLine(wxLineShape *line, wxShape *other,
42cfaf8c
JS
1609 int attachFrom, int attachTo,
1610 // The line ordering
1611 int positionFrom, int positionTo)
0fc1a713 1612{
f93ce4da
JS
1613 if (positionFrom == -1)
1614 {
1615 if (!m_lines.Member(line))
1616 m_lines.Append(line);
1617 }
1618 else
1619 {
1620 // Don't preserve old ordering if we have new ordering instructions
1621 m_lines.DeleteObject(line);
1622 if (positionFrom < m_lines.Number())
1623 {
1624 wxNode* node = m_lines.Nth(positionFrom);
1625 m_lines.Insert(node, line);
1626 }
1627 else
1628 m_lines.Append(line);
1629 }
1630
1631 if (positionTo == -1)
1632 {
1633 if (!other->m_lines.Member(line))
1634 other->m_lines.Append(line);
1635 }
1636 else
1637 {
1638 // Don't preserve old ordering if we have new ordering instructions
1639 other->m_lines.DeleteObject(line);
1640 if (positionTo < other->m_lines.Number())
1641 {
1642 wxNode* node = other->m_lines.Nth(positionTo);
1643 other->m_lines.Insert(node, line);
1644 }
1645 else
1646 other->m_lines.Append(line);
1647 }
1648#if 0
1649 // Wrong: doesn't preserve ordering of shape already linked
42cfaf8c
JS
1650 m_lines.DeleteObject(line);
1651 other->m_lines.DeleteObject(line);
1652
1653 if (positionFrom == -1)
1654 m_lines.Append(line);
1655 else
1656 {
1657 if (positionFrom < m_lines.Number())
1658 {
1659 wxNode* node = m_lines.Nth(positionFrom);
1660 m_lines.Insert(node, line);
1661 }
1662 else
1663 m_lines.Append(line);
1664 }
0fc1a713 1665
42cfaf8c
JS
1666 if (positionTo == -1)
1667 other->m_lines.Append(line);
1668 else
1669 {
1670 if (positionTo < other->m_lines.Number())
1671 {
1672 wxNode* node = other->m_lines.Nth(positionTo);
1673 other->m_lines.Insert(node, line);
1674 }
1675 else
1676 other->m_lines.Append(line);
1677 }
f93ce4da 1678#endif
0fc1a713 1679
42cfaf8c
JS
1680 line->SetFrom(this);
1681 line->SetTo(other);
1682 line->SetAttachments(attachFrom, attachTo);
0fc1a713
JS
1683}
1684
1685void wxShape::RemoveLine(wxLineShape *line)
1686{
1687 if (line->GetFrom() == this)
1688 line->GetTo()->m_lines.DeleteObject(line);
1689 else
1690 line->GetFrom()->m_lines.DeleteObject(line);
1691
1692 m_lines.DeleteObject(line);
1693}
1694
1695#ifdef PROLOGIO
6f5f3ca0 1696void wxShape::WriteAttributes(wxExpr *clause)
0fc1a713
JS
1697{
1698 clause->AddAttributeValueString("type", GetClassInfo()->GetClassName());
1699 clause->AddAttributeValue("id", m_id);
1700
1701 if (m_pen)
1702 {
1703 int penWidth = m_pen->GetWidth();
1704 int penStyle = m_pen->GetStyle();
1705 if (penWidth != 1)
1706 clause->AddAttributeValue("pen_width", (long)penWidth);
1707 if (penStyle != wxSOLID)
1708 clause->AddAttributeValue("pen_style", (long)penStyle);
1709
1710 wxString penColour = wxTheColourDatabase->FindName(m_pen->GetColour());
34138703
JS
1711 if (penColour == "")
1712 {
1713 wxString hex(oglColourToHex(m_pen->GetColour()));
1714 hex = wxString("#") + hex;
1715 clause->AddAttributeValueString("pen_colour", hex);
1716 }
1717 else if (penColour != "BLACK")
0fc1a713
JS
1718 clause->AddAttributeValueString("pen_colour", penColour);
1719 }
1720
1721 if (m_brush)
1722 {
1723 wxString brushColour = wxTheColourDatabase->FindName(m_brush->GetColour());
1724
34138703
JS
1725 if (brushColour == "")
1726 {
1727 wxString hex(oglColourToHex(m_brush->GetColour()));
1728 hex = wxString("#") + hex;
1729 clause->AddAttributeValueString("brush_colour", hex);
1730 }
1731 else if (brushColour != "WHITE")
0fc1a713
JS
1732 clause->AddAttributeValueString("brush_colour", brushColour);
1733
1734 if (m_brush->GetStyle() != wxSOLID)
1735 clause->AddAttributeValue("brush_style", (long)m_brush->GetStyle());
1736 }
1737
1738 // Output line ids
1739
1740 int n_lines = m_lines.Number();
1741 if (n_lines > 0)
1742 {
f93ce4da 1743 wxExpr *list = new wxExpr(wxExprList);
0fc1a713
JS
1744 wxNode *node = m_lines.First();
1745 while (node)
1746 {
1747 wxShape *line = (wxShape *)node->Data();
1748 wxExpr *id_expr = new wxExpr(line->GetId());
1749 list->Append(id_expr);
1750 node = node->Next();
1751 }
1752 clause->AddAttributeValue("arcs", list);
1753 }
1754
1755 // Miscellaneous members
1756 if (m_attachmentMode != 0)
1757 clause->AddAttributeValue("use_attachments", (long)m_attachmentMode);
1758 if (m_sensitivity != OP_ALL)
1759 clause->AddAttributeValue("sensitivity", (long)m_sensitivity);
1760 if (!m_spaceAttachments)
1761 clause->AddAttributeValue("space_attachments", (long)m_spaceAttachments);
1762 if (m_fixedWidth)
1763 clause->AddAttributeValue("fixed_width", (long)m_fixedWidth);
1764 if (m_fixedHeight)
1765 clause->AddAttributeValue("fixed_height", (long)m_fixedHeight);
1766 if (m_shadowMode != SHADOW_NONE)
1767 clause->AddAttributeValue("shadow_mode", (long)m_shadowMode);
1768 if (m_centreResize != TRUE)
1769 clause->AddAttributeValue("centre_resize", (long)0);
f93ce4da 1770 clause->AddAttributeValue("maintain_aspect_ratio", (long) m_maintainAspectRatio);
0fc1a713
JS
1771 if (m_highlighted != FALSE)
1772 clause->AddAttributeValue("hilite", (long)m_highlighted);
1773
1774 if (m_parent) // For composite objects
1775 clause->AddAttributeValue("parent", (long)m_parent->GetId());
1776
1777 if (m_rotation != 0.0)
1778 clause->AddAttributeValue("rotation", m_rotation);
1779
f97c9854
JS
1780 if (!this->IsKindOf(CLASSINFO(wxLineShape)))
1781 {
1782 clause->AddAttributeValue("neck_length", (long) m_branchNeckLength);
1783 clause->AddAttributeValue("stem_length", (long) m_branchStemLength);
1784 clause->AddAttributeValue("branch_spacing", (long) m_branchSpacing);
1785 clause->AddAttributeValue("branch_style", (long) m_branchStyle);
1786 }
1787
0fc1a713
JS
1788 // Write user-defined attachment points, if any
1789 if (m_attachmentPoints.Number() > 0)
1790 {
f93ce4da 1791 wxExpr *attachmentList = new wxExpr(wxExprList);
0fc1a713
JS
1792 wxNode *node = m_attachmentPoints.First();
1793 while (node)
1794 {
1795 wxAttachmentPoint *point = (wxAttachmentPoint *)node->Data();
f93ce4da 1796 wxExpr *pointExpr = new wxExpr(wxExprList);
0fc1a713
JS
1797 pointExpr->Append(new wxExpr((long)point->m_id));
1798 pointExpr->Append(new wxExpr(point->m_x));
1799 pointExpr->Append(new wxExpr(point->m_y));
1800 attachmentList->Append(pointExpr);
1801 node = node->Next();
1802 }
1803 clause->AddAttributeValue("user_attachments", attachmentList);
1804 }
1805
1806 // Write text regions
1807 WriteRegions(clause);
1808}
1809
1810void wxShape::WriteRegions(wxExpr *clause)
1811{
1812 // Output regions as region1 = (...), region2 = (...), etc
1813 // and formatted text as text1 = (...), text2 = (...) etc.
1814 int regionNo = 1;
1815 char regionNameBuf[20];
1816 char textNameBuf[20];
1817 wxNode *node = m_regions.First();
1818 while (node)
1819 {
1820 wxShapeRegion *region = (wxShapeRegion *)node->Data();
1821 sprintf(regionNameBuf, "region%d", regionNo);
1822 sprintf(textNameBuf, "text%d", regionNo);
1823
1824 // Original text and region attributes:
1825 // region1 = (regionName regionText x y width height minWidth minHeight proportionX proportionY
1826 // formatMode fontSize fontFamily fontStyle fontWeight textColour)
f93ce4da 1827 wxExpr *regionExpr = new wxExpr(wxExprList);
3dd4e4e0
JS
1828 regionExpr->Append(new wxExpr(wxExprString, region->m_regionName));
1829 regionExpr->Append(new wxExpr(wxExprString, region->m_regionText));
0fc1a713
JS
1830
1831 regionExpr->Append(new wxExpr(region->m_x));
1832 regionExpr->Append(new wxExpr(region->m_y));
1833 regionExpr->Append(new wxExpr(region->GetWidth()));
1834 regionExpr->Append(new wxExpr(region->GetHeight()));
1835
1836 regionExpr->Append(new wxExpr(region->m_minWidth));
1837 regionExpr->Append(new wxExpr(region->m_minHeight));
1838 regionExpr->Append(new wxExpr(region->m_regionProportionX));
1839 regionExpr->Append(new wxExpr(region->m_regionProportionY));
1840
1841 regionExpr->Append(new wxExpr((long)region->m_formatMode));
1842
1843 regionExpr->Append(new wxExpr((long)(region->m_font ? region->m_font->GetPointSize() : 10)));
1844 regionExpr->Append(new wxExpr((long)(region->m_font ? region->m_font->GetFamily() : wxDEFAULT)));
1845 regionExpr->Append(new wxExpr((long)(region->m_font ? region->m_font->GetStyle() : wxDEFAULT)));
1846 regionExpr->Append(new wxExpr((long)(region->m_font ? region->m_font->GetWeight() : wxNORMAL)));
3dd4e4e0 1847 regionExpr->Append(new wxExpr(wxExprString, region->m_textColour));
0fc1a713
JS
1848
1849 // New members for pen colour/style
3dd4e4e0 1850 regionExpr->Append(new wxExpr(wxExprString, region->m_penColour));
0fc1a713
JS
1851 regionExpr->Append(new wxExpr((long)region->m_penStyle));
1852
1853 // Formatted text:
1854 // text1 = ((x y string) (x y string) ...)
f93ce4da 1855 wxExpr *textExpr = new wxExpr(wxExprList);
0fc1a713
JS
1856
1857 wxNode *textNode = region->m_formattedText.First();
1858 while (textNode)
1859 {
1860 wxShapeTextLine *line = (wxShapeTextLine *)textNode->Data();
f93ce4da 1861 wxExpr *list2 = new wxExpr(wxExprList);
0fc1a713
JS
1862 list2->Append(new wxExpr(line->GetX()));
1863 list2->Append(new wxExpr(line->GetY()));
f93ce4da 1864 list2->Append(new wxExpr(wxExprString, line->GetText()));
0fc1a713
JS
1865 textExpr->Append(list2);
1866 textNode = textNode->Next();
1867 }
1868
1869 // Now add both attributes to the clause
1870 clause->AddAttributeValue(regionNameBuf, regionExpr);
1871 clause->AddAttributeValue(textNameBuf, textExpr);
1872
1873 node = node->Next();
1874 regionNo ++;
1875 }
1876}
1877
6f5f3ca0 1878void wxShape::ReadAttributes(wxExpr *clause)
0fc1a713
JS
1879{
1880 clause->GetAttributeValue("id", m_id);
1881 RegisterId(m_id);
1882
1883 clause->GetAttributeValue("x", m_xpos);
1884 clause->GetAttributeValue("y", m_ypos);
1885
1886 // Input text strings (FOR COMPATIBILITY WITH OLD FILES ONLY. SEE REGION CODE BELOW.)
1887 ClearText();
1888 wxExpr *strings = clause->AttributeValue("text");
f93ce4da 1889 if (strings && strings->Type() == wxExprList)
0fc1a713
JS
1890 {
1891 m_formatted = TRUE; // Assume text is formatted unless we prove otherwise
1892 wxExpr *node = strings->value.first;
1893 while (node)
1894 {
1895 wxExpr *string_expr = node;
42cfaf8c
JS
1896 double the_x = 0.0;
1897 double the_y = 0.0;
0fc1a713
JS
1898 wxString the_string("");
1899
1900 // string_expr can either be a string, or a list of
1901 // 3 elements: x, y, and string.
f93ce4da 1902 if (string_expr->Type() == wxExprString)
0fc1a713
JS
1903 {
1904 the_string = string_expr->StringValue();
1905 m_formatted = FALSE;
1906 }
f93ce4da 1907 else if (string_expr->Type() == wxExprList)
0fc1a713
JS
1908 {
1909 wxExpr *first = string_expr->value.first;
ea57084d
JS
1910 wxExpr *second = first ? first->next : (wxExpr*) NULL;
1911 wxExpr *third = second ? second->next : (wxExpr*) NULL;
0fc1a713
JS
1912
1913 if (first && second && third &&
f93ce4da
JS
1914 (first->Type() == wxExprReal || first->Type() == wxExprInteger) &&
1915 (second->Type() == wxExprReal || second->Type() == wxExprInteger) &&
1916 third->Type() == wxExprString)
0fc1a713 1917 {
f93ce4da 1918 if (first->Type() == wxExprReal)
0fc1a713 1919 the_x = first->RealValue();
42cfaf8c 1920 else the_x = (double)first->IntegerValue();
0fc1a713 1921
f93ce4da 1922 if (second->Type() == wxExprReal)
0fc1a713 1923 the_y = second->RealValue();
42cfaf8c 1924 else the_y = (double)second->IntegerValue();
0fc1a713
JS
1925
1926 the_string = third->StringValue();
1927 }
1928 }
1929 wxShapeTextLine *line =
1930 new wxShapeTextLine(the_x, the_y, (char*) (const char*) the_string);
1931 m_text.Append(line);
1932
1933 node = node->next;
1934 }
1935 }
1936
1937 wxString pen_string = "";
1938 wxString brush_string = "";
1939 int pen_width = 1;
1940 int pen_style = wxSOLID;
1941 int brush_style = wxSOLID;
f97c9854 1942 m_attachmentMode = ATTACHMENT_MODE_NONE;
0fc1a713
JS
1943
1944 clause->GetAttributeValue("pen_colour", pen_string);
1945 clause->GetAttributeValue("text_colour", m_textColourName);
1946
1947 SetTextColour(m_textColourName);
1948
1949 clause->GetAttributeValue("region_name", m_regionName);
1950
1951 clause->GetAttributeValue("brush_colour", brush_string);
1952 clause->GetAttributeValue("pen_width", pen_width);
1953 clause->GetAttributeValue("pen_style", pen_style);
1954 clause->GetAttributeValue("brush_style", brush_style);
1955
1956 int iVal = (int) m_attachmentMode;
1957 clause->GetAttributeValue("use_attachments", iVal);
f97c9854 1958 m_attachmentMode = iVal;
0fc1a713
JS
1959
1960 clause->GetAttributeValue("sensitivity", m_sensitivity);
1961
1962 iVal = (int) m_spaceAttachments;
1963 clause->GetAttributeValue("space_attachments", iVal);
1964 m_spaceAttachments = (iVal != 0);
1965
1966 iVal = (int) m_fixedWidth;
1967 clause->GetAttributeValue("fixed_width", iVal);
1968 m_fixedWidth = (iVal != 0);
1969
1970 iVal = (int) m_fixedHeight;
1971 clause->GetAttributeValue("fixed_height", iVal);
1972 m_fixedHeight = (iVal != 0);
1973
1974 clause->GetAttributeValue("format_mode", m_formatMode);
1975 clause->GetAttributeValue("shadow_mode", m_shadowMode);
1976
f97c9854
JS
1977 iVal = m_branchNeckLength;
1978 clause->GetAttributeValue("neck_length", iVal);
1979 m_branchNeckLength = iVal;
1980
1981 iVal = m_branchStemLength;
1982 clause->GetAttributeValue("stem_length", iVal);
1983 m_branchStemLength = iVal;
1984
1985 iVal = m_branchSpacing;
1986 clause->GetAttributeValue("branch_spacing", iVal);
1987 m_branchSpacing = iVal;
1988
1989 clause->GetAttributeValue("branch_style", m_branchStyle);
1990
0fc1a713
JS
1991 iVal = (int) m_centreResize;
1992 clause->GetAttributeValue("centre_resize", iVal);
1993 m_centreResize = (iVal != 0);
1994
f93ce4da
JS
1995 iVal = (int) m_maintainAspectRatio;
1996 clause->GetAttributeValue("maintain_aspect_ratio", iVal);
1997 m_maintainAspectRatio = (iVal != 0);
1998
0fc1a713
JS
1999 iVal = (int) m_highlighted;
2000 clause->GetAttributeValue("hilite", iVal);
2001 m_highlighted = (iVal != 0);
2002
2003 clause->GetAttributeValue("rotation", m_rotation);
2004
2005 if (pen_string == "")
2006 pen_string = "BLACK";
2007 if (brush_string == "")
2008 brush_string = "WHITE";
2009
dbda9e86 2010 if (pen_string.GetChar(0) == '#')
34138703
JS
2011 {
2012 wxColour col(oglHexToColour(pen_string.After('#')));
2013 m_pen = wxThePenList->FindOrCreatePen(col, pen_width, pen_style);
2014 }
2015 else
2016 m_pen = wxThePenList->FindOrCreatePen(pen_string, pen_width, pen_style);
2017
0fc1a713
JS
2018 if (!m_pen)
2019 m_pen = wxBLACK_PEN;
2020
dbda9e86 2021 if (brush_string.GetChar(0) == '#')
34138703
JS
2022 {
2023 wxColour col(oglHexToColour(brush_string.After('#')));
2024 m_brush = wxTheBrushList->FindOrCreateBrush(col, brush_style);
2025 }
2026 else
2027 m_brush = wxTheBrushList->FindOrCreateBrush(brush_string, brush_style);
2028
0fc1a713
JS
2029 if (!m_brush)
2030 m_brush = wxWHITE_BRUSH;
2031
2032 int point_size = 10;
2033 clause->GetAttributeValue("point_size", point_size);
42cfaf8c 2034 SetFont(oglMatchFont(point_size));
0fc1a713
JS
2035
2036 // Read user-defined attachment points, if any
2037 wxExpr *attachmentList = clause->AttributeValue("user_attachments");
2038 if (attachmentList)
2039 {
2040 wxExpr *pointExpr = attachmentList->GetFirst();
2041 while (pointExpr)
2042 {
2043 wxExpr *idExpr = pointExpr->Nth(0);
2044 wxExpr *xExpr = pointExpr->Nth(1);
2045 wxExpr *yExpr = pointExpr->Nth(2);
2046 if (idExpr && xExpr && yExpr)
2047 {
2048 wxAttachmentPoint *point = new wxAttachmentPoint;
2049 point->m_id = (int)idExpr->IntegerValue();
2050 point->m_x = xExpr->RealValue();
2051 point->m_y = yExpr->RealValue();
2052 m_attachmentPoints.Append((wxObject *)point);
2053 }
2054 pointExpr = pointExpr->GetNext();
2055 }
2056 }
2057
2058 // Read text regions
2059 ReadRegions(clause);
2060}
2061
2062void wxShape::ReadRegions(wxExpr *clause)
2063{
2064 ClearRegions();
2065
2066 // region1 = (regionName regionText x y width height minWidth minHeight proportionX proportionY
2067 // formatMode fontSize fontFamily fontStyle fontWeight textColour)
2068 int regionNo = 1;
2069 char regionNameBuf[20];
2070 char textNameBuf[20];
2071
2072 wxExpr *regionExpr = NULL;
2073 wxExpr *textExpr = NULL;
2074 sprintf(regionNameBuf, "region%d", regionNo);
2075 sprintf(textNameBuf, "text%d", regionNo);
2076
2077 m_formatted = TRUE; // Assume text is formatted unless we prove otherwise
2078
ea57084d 2079 while ((regionExpr = clause->AttributeValue(regionNameBuf)))
0fc1a713
JS
2080 {
2081 /*
2082 * Get the region information
2083 *
2084 */
2085
2086 wxString regionName("");
2087 wxString regionText("");
42cfaf8c
JS
2088 double x = 0.0;
2089 double y = 0.0;
2090 double width = 0.0;
2091 double height = 0.0;
2092 double minWidth = 5.0;
2093 double minHeight = 5.0;
2094 double m_regionProportionX = -1.0;
2095 double m_regionProportionY = -1.0;
0fc1a713
JS
2096 int formatMode = FORMAT_NONE;
2097 int fontSize = 10;
2098 int fontFamily = wxSWISS;
2099 int fontStyle = wxNORMAL;
2100 int fontWeight = wxNORMAL;
2101 wxString regionTextColour("");
2102 wxString penColour("");
2103 int penStyle = wxSOLID;
2104
f93ce4da 2105 if (regionExpr->Type() == wxExprList)
0fc1a713
JS
2106 {
2107 wxExpr *nameExpr = regionExpr->Nth(0);
2108 wxExpr *textExpr = regionExpr->Nth(1);
2109 wxExpr *xExpr = regionExpr->Nth(2);
2110 wxExpr *yExpr = regionExpr->Nth(3);
2111 wxExpr *widthExpr = regionExpr->Nth(4);
2112 wxExpr *heightExpr = regionExpr->Nth(5);
2113 wxExpr *minWidthExpr = regionExpr->Nth(6);
2114 wxExpr *minHeightExpr = regionExpr->Nth(7);
2115 wxExpr *propXExpr = regionExpr->Nth(8);
2116 wxExpr *propYExpr = regionExpr->Nth(9);
2117 wxExpr *formatExpr = regionExpr->Nth(10);
2118 wxExpr *sizeExpr = regionExpr->Nth(11);
2119 wxExpr *familyExpr = regionExpr->Nth(12);
2120 wxExpr *styleExpr = regionExpr->Nth(13);
2121 wxExpr *weightExpr = regionExpr->Nth(14);
2122 wxExpr *colourExpr = regionExpr->Nth(15);
2123 wxExpr *penColourExpr = regionExpr->Nth(16);
2124 wxExpr *penStyleExpr = regionExpr->Nth(17);
2125
2126 regionName = nameExpr->StringValue();
2127 regionText = textExpr->StringValue();
2128
2129 x = xExpr->RealValue();
2130 y = yExpr->RealValue();
2131
2132 width = widthExpr->RealValue();
2133 height = heightExpr->RealValue();
2134
2135 minWidth = minWidthExpr->RealValue();
2136 minHeight = minHeightExpr->RealValue();
2137
2138 m_regionProportionX = propXExpr->RealValue();
2139 m_regionProportionY = propYExpr->RealValue();
2140
2bb0cd28 2141 formatMode = (int) formatExpr->IntegerValue();
0fc1a713
JS
2142 fontSize = (int)sizeExpr->IntegerValue();
2143 fontFamily = (int)familyExpr->IntegerValue();
2144 fontStyle = (int)styleExpr->IntegerValue();
2145 fontWeight = (int)weightExpr->IntegerValue();
2146
2147 if (colourExpr)
2148 {
2149 regionTextColour = colourExpr->StringValue();
2150 }
2151 else
2152 regionTextColour = "BLACK";
2153
2154 if (penColourExpr)
2155 penColour = penColourExpr->StringValue();
2156 if (penStyleExpr)
2157 penStyle = (int)penStyleExpr->IntegerValue();
2158 }
2159 wxFont *font = wxTheFontList->FindOrCreateFont(fontSize, fontFamily, fontStyle, fontWeight);
2160
2161 wxShapeRegion *region = new wxShapeRegion;
2162 region->SetProportions(m_regionProportionX, m_regionProportionY);
2163 region->SetFont(font);
2164 region->SetSize(width, height);
2165 region->SetPosition(x, y);
2166 region->SetMinSize(minWidth, minHeight);
2167 region->SetFormatMode(formatMode);
2168 region->SetPenStyle(penStyle);
2169 if (penColour != "")
2170 region->SetPenColour(penColour);
2171
2172 region->m_textColour = regionTextColour;
2173 region->m_regionText = regionText;
2174 region->m_regionName = regionName;
2175
2176 m_regions.Append(region);
2177
2178 /*
2179 * Get the formatted text strings
2180 *
2181 */
2182 textExpr = clause->AttributeValue(textNameBuf);
f93ce4da 2183 if (textExpr && (textExpr->Type() == wxExprList))
0fc1a713
JS
2184 {
2185 wxExpr *node = textExpr->value.first;
2186 while (node)
2187 {
2188 wxExpr *string_expr = node;
42cfaf8c
JS
2189 double the_x = 0.0;
2190 double the_y = 0.0;
0fc1a713
JS
2191 wxString the_string("");
2192
2193 // string_expr can either be a string, or a list of
2194 // 3 elements: x, y, and string.
f93ce4da 2195 if (string_expr->Type() == wxExprString)
0fc1a713
JS
2196 {
2197 the_string = string_expr->StringValue();
2198 m_formatted = FALSE;
2199 }
f93ce4da 2200 else if (string_expr->Type() == wxExprList)
0fc1a713
JS
2201 {
2202 wxExpr *first = string_expr->value.first;
ea57084d
JS
2203 wxExpr *second = first ? first->next : (wxExpr*) NULL;
2204 wxExpr *third = second ? second->next : (wxExpr*) NULL;
0fc1a713
JS
2205
2206 if (first && second && third &&
f93ce4da
JS
2207 (first->Type() == wxExprReal || first->Type() == wxExprInteger) &&
2208 (second->Type() == wxExprReal || second->Type() == wxExprInteger) &&
2209 third->Type() == wxExprString)
0fc1a713 2210 {
f93ce4da 2211 if (first->Type() == wxExprReal)
0fc1a713 2212 the_x = first->RealValue();
42cfaf8c 2213 else the_x = (double)first->IntegerValue();
0fc1a713 2214
f93ce4da 2215 if (second->Type() == wxExprReal)
0fc1a713 2216 the_y = second->RealValue();
42cfaf8c 2217 else the_y = (double)second->IntegerValue();
0fc1a713
JS
2218
2219 the_string = third->StringValue();
2220 }
2221 }
2222 if (the_string)
2223 {
2224 wxShapeTextLine *line =
2225 new wxShapeTextLine(the_x, the_y, (char*) (const char*) the_string);
2226 region->m_formattedText.Append(line);
2227 }
2228 node = node->next;
2229 }
2230 }
2231
2232 regionNo ++;
2233 sprintf(regionNameBuf, "region%d", regionNo);
2234 sprintf(textNameBuf, "text%d", regionNo);
2235 }
2236
2237 // Compatibility: check for no regions (old file).
2238 // Lines and divided rectangles must deal with this compatibility
2239 // theirselves. Composites _may_ not have any regions anyway.
2240 if ((m_regions.Number() == 0) &&
2241 !this->IsKindOf(CLASSINFO(wxLineShape)) && !this->IsKindOf(CLASSINFO(wxDividedShape)) &&
2242 !this->IsKindOf(CLASSINFO(wxCompositeShape)))
2243 {
2244 wxShapeRegion *newRegion = new wxShapeRegion;
2245 newRegion->SetName("0");
2246 m_regions.Append((wxObject *)newRegion);
2247 if (m_text.Number() > 0)
2248 {
2249 newRegion->ClearText();
2250 wxNode *node = m_text.First();
2251 while (node)
2252 {
2253 wxShapeTextLine *textLine = (wxShapeTextLine *)node->Data();
2254 wxNode *next = node->Next();
2255 newRegion->GetFormattedText().Append((wxObject *)textLine);
2256 delete node;
2257 node = next;
2258 }
2259 }
2260 }
2261}
2262
2263#endif
2264
2265void wxShape::Copy(wxShape& copy)
2266{
e8435fa3 2267 copy.m_id = m_id;
0fc1a713
JS
2268 copy.m_xpos = m_xpos;
2269 copy.m_ypos = m_ypos;
2270 copy.m_pen = m_pen;
2271 copy.m_brush = m_brush;
2272 copy.m_textColour = m_textColour;
2273 copy.m_centreResize = m_centreResize;
f93ce4da 2274 copy.m_maintainAspectRatio = m_maintainAspectRatio;
0fc1a713
JS
2275 copy.m_attachmentMode = m_attachmentMode;
2276 copy.m_spaceAttachments = m_spaceAttachments;
2277 copy.m_highlighted = m_highlighted;
2278 copy.m_rotation = m_rotation;
2279 copy.m_textColourName = m_textColourName;
2280 copy.m_regionName = m_regionName;
2281
2282 copy.m_sensitivity = m_sensitivity;
2283 copy.m_draggable = m_draggable;
2284 copy.m_fixedWidth = m_fixedWidth;
2285 copy.m_fixedHeight = m_fixedHeight;
2286 copy.m_formatMode = m_formatMode;
2287 copy.m_drawHandles = m_drawHandles;
2288
2289 copy.m_visible = m_visible;
2290 copy.m_shadowMode = m_shadowMode;
2291 copy.m_shadowOffsetX = m_shadowOffsetX;
2292 copy.m_shadowOffsetY = m_shadowOffsetY;
2293 copy.m_shadowBrush = m_shadowBrush;
2294
f97c9854
JS
2295 copy.m_branchNeckLength = m_branchNeckLength;
2296 copy.m_branchStemLength = m_branchStemLength;
2297 copy.m_branchSpacing = m_branchSpacing;
2298
0fc1a713
JS
2299 // Copy text regions
2300 copy.ClearRegions();
2301 wxNode *node = m_regions.First();
2302 while (node)
2303 {
2304 wxShapeRegion *region = (wxShapeRegion *)node->Data();
2305 wxShapeRegion *newRegion = new wxShapeRegion(*region);
2306 copy.m_regions.Append(newRegion);
2307 node = node->Next();
2308 }
2309
2310 // Copy attachments
2311 copy.ClearAttachments();
2312 node = m_attachmentPoints.First();
2313 while (node)
2314 {
2315 wxAttachmentPoint *point = (wxAttachmentPoint *)node->Data();
2316 wxAttachmentPoint *newPoint = new wxAttachmentPoint;
2317 newPoint->m_id = point->m_id;
2318 newPoint->m_x = point->m_x;
2319 newPoint->m_y = point->m_y;
2320 copy.m_attachmentPoints.Append((wxObject *)newPoint);
2321 node = node->Next();
2322 }
42cfaf8c
JS
2323
2324 // Copy lines
2325 copy.m_lines.Clear();
2326 node = m_lines.First();
2327 while (node)
2328 {
2329 wxLineShape* line = (wxLineShape*) node->Data();
2330 copy.m_lines.Append(line);
2331 node = node->Next();
2332 }
0fc1a713
JS
2333}
2334
2335// Create and return a new, fully copied object.
2bb0cd28 2336wxShape *wxShape::CreateNewCopy(bool resetMapping, bool recompute)
0fc1a713 2337{
2bb0cd28 2338 if (resetMapping)
42cfaf8c 2339 oglObjectCopyMapping.Clear();
2bb0cd28
JS
2340
2341 wxShape* newObject = (wxShape*) GetClassInfo()->CreateObject();
2342
2343 wxASSERT( (newObject != NULL) );
2344 wxASSERT( (newObject->IsKindOf(CLASSINFO(wxShape))) );
2345
2346 Copy(*newObject);
2347
2348 if (GetEventHandler() != this)
2349 {
2350 wxShapeEvtHandler* newHandler = GetEventHandler()->CreateNewCopy();
2351 newObject->SetEventHandler(newHandler);
5de76427
JS
2352 newObject->SetPreviousHandler(NULL);
2353 newHandler->SetPreviousHandler(newObject);
2bb0cd28
JS
2354 newHandler->SetShape(newObject);
2355 }
2356
2357 if (recompute)
2358 newObject->Recompute();
0fc1a713
JS
2359 return newObject;
2360}
2361
2bb0cd28
JS
2362// Does the copying for this object, including copying event
2363// handler data if any. Calls the virtual Copy function.
2364void wxShape::CopyWithHandler(wxShape& copy)
2365{
2366 Copy(copy);
2367
2368 if (GetEventHandler() != this)
2369 {
2370 wxASSERT( copy.GetEventHandler() != NULL );
2371 wxASSERT( copy.GetEventHandler() != (&copy) );
2372 wxASSERT( GetEventHandler()->GetClassInfo() == copy.GetEventHandler()->GetClassInfo() );
2373 GetEventHandler()->CopyData(* (copy.GetEventHandler()));
2374 }
2375}
2376
2377
0fc1a713
JS
2378// Default - make 6 control points
2379void wxShape::MakeControlPoints()
2380{
42cfaf8c 2381 double maxX, maxY, minX, minY;
0fc1a713
JS
2382
2383 GetBoundingBoxMax(&maxX, &maxY);
2384 GetBoundingBoxMin(&minX, &minY);
2385
42cfaf8c
JS
2386 double widthMin = (double)(minX + CONTROL_POINT_SIZE + 2);
2387 double heightMin = (double)(minY + CONTROL_POINT_SIZE + 2);
0fc1a713
JS
2388
2389 // Offsets from main object
42cfaf8c
JS
2390 double top = (double)(- (heightMin / 2.0));
2391 double bottom = (double)(heightMin / 2.0 + (maxY - minY));
2392 double left = (double)(- (widthMin / 2.0));
2393 double right = (double)(widthMin / 2.0 + (maxX - minX));
0fc1a713
JS
2394
2395 wxControlPoint *control = new wxControlPoint(m_canvas, this, CONTROL_POINT_SIZE, left, top,
2396 CONTROL_POINT_DIAGONAL);
2397 m_canvas->AddShape(control);
2398 m_controlPoints.Append(control);
2399
2400 control = new wxControlPoint(m_canvas, this, CONTROL_POINT_SIZE, 0, top,
2401 CONTROL_POINT_VERTICAL);
2402 m_canvas->AddShape(control);
2403 m_controlPoints.Append(control);
2404
2405 control = new wxControlPoint(m_canvas, this, CONTROL_POINT_SIZE, right, top,
2406 CONTROL_POINT_DIAGONAL);
2407 m_canvas->AddShape(control);
2408 m_controlPoints.Append(control);
2409
2410 control = new wxControlPoint(m_canvas, this, CONTROL_POINT_SIZE, right, 0,
2411 CONTROL_POINT_HORIZONTAL);
2412 m_canvas->AddShape(control);
2413 m_controlPoints.Append(control);
2414
2415 control = new wxControlPoint(m_canvas, this, CONTROL_POINT_SIZE, right, bottom,
2416 CONTROL_POINT_DIAGONAL);
2417 m_canvas->AddShape(control);
2418 m_controlPoints.Append(control);
2419
2420 control = new wxControlPoint(m_canvas, this, CONTROL_POINT_SIZE, 0, bottom,
2421 CONTROL_POINT_VERTICAL);
2422 m_canvas->AddShape(control);
2423 m_controlPoints.Append(control);
2424
2425 control = new wxControlPoint(m_canvas, this, CONTROL_POINT_SIZE, left, bottom,
2426 CONTROL_POINT_DIAGONAL);
2427 m_canvas->AddShape(control);
2428 m_controlPoints.Append(control);
2429
2430 control = new wxControlPoint(m_canvas, this, CONTROL_POINT_SIZE, left, 0,
2431 CONTROL_POINT_HORIZONTAL);
2432 m_canvas->AddShape(control);
2433 m_controlPoints.Append(control);
2434
2435}
2436
2437void wxShape::MakeMandatoryControlPoints()
2438{
2439 wxNode *node = m_children.First();
2440 while (node)
2441 {
2442 wxShape *child = (wxShape *)node->Data();
2443 child->MakeMandatoryControlPoints();
2444 node = node->Next();
2445 }
2446}
2447
2448void wxShape::ResetMandatoryControlPoints()
2449{
2450 wxNode *node = m_children.First();
2451 while (node)
2452 {
2453 wxShape *child = (wxShape *)node->Data();
2454 child->ResetMandatoryControlPoints();
2455 node = node->Next();
2456 }
2457}
2458
2459void wxShape::ResetControlPoints()
2460{
2461 ResetMandatoryControlPoints();
2462
2463 if (m_controlPoints.Number() < 1)
2464 return;
2465
42cfaf8c 2466 double maxX, maxY, minX, minY;
0fc1a713
JS
2467
2468 GetBoundingBoxMax(&maxX, &maxY);
2469 GetBoundingBoxMin(&minX, &minY);
2470
42cfaf8c
JS
2471 double widthMin = (double)(minX + CONTROL_POINT_SIZE + 2);
2472 double heightMin = (double)(minY + CONTROL_POINT_SIZE + 2);
0fc1a713
JS
2473
2474 // Offsets from main object
42cfaf8c
JS
2475 double top = (double)(- (heightMin / 2.0));
2476 double bottom = (double)(heightMin / 2.0 + (maxY - minY));
2477 double left = (double)(- (widthMin / 2.0));
2478 double right = (double)(widthMin / 2.0 + (maxX - minX));
0fc1a713
JS
2479
2480 wxNode *node = m_controlPoints.First();
2481 wxControlPoint *control = (wxControlPoint *)node->Data();
2482 control->m_xoffset = left; control->m_yoffset = top;
2483
2484 node = node->Next(); control = (wxControlPoint *)node->Data();
2485 control->m_xoffset = 0; control->m_yoffset = top;
2486
2487 node = node->Next(); control = (wxControlPoint *)node->Data();
2488 control->m_xoffset = right; control->m_yoffset = top;
2489
2490 node = node->Next(); control = (wxControlPoint *)node->Data();
2491 control->m_xoffset = right; control->m_yoffset = 0;
2492
2493 node = node->Next(); control = (wxControlPoint *)node->Data();
2494 control->m_xoffset = right; control->m_yoffset = bottom;
2495
2496 node = node->Next(); control = (wxControlPoint *)node->Data();
2497 control->m_xoffset = 0; control->m_yoffset = bottom;
2498
2499 node = node->Next(); control = (wxControlPoint *)node->Data();
2500 control->m_xoffset = left; control->m_yoffset = bottom;
2501
2502 node = node->Next(); control = (wxControlPoint *)node->Data();
2503 control->m_xoffset = left; control->m_yoffset = 0;
2504}
2505
2506void wxShape::DeleteControlPoints(wxDC *dc)
2507{
2508 wxNode *node = m_controlPoints.First();
2509 while (node)
2510 {
2511 wxControlPoint *control = (wxControlPoint *)node->Data();
2512 if (dc)
2513 control->GetEventHandler()->OnErase(*dc);
2514 m_canvas->RemoveShape(control);
2515 delete control;
2516 delete node;
2517 node = m_controlPoints.First();
2518 }
2519 // Children of divisions are contained objects,
2520 // so stop here
2521 if (!IsKindOf(CLASSINFO(wxDivisionShape)))
2522 {
2523 node = m_children.First();
2524 while (node)
2525 {
2526 wxShape *child = (wxShape *)node->Data();
2527 child->DeleteControlPoints(dc);
2528 node = node->Next();
2529 }
2530 }
2531}
2532
2533void wxShape::OnDrawControlPoints(wxDC& dc)
2534{
2535 if (!m_drawHandles)
2536 return;
2537
c0ed460c
JS
2538 dc.SetBrush(* wxBLACK_BRUSH);
2539 dc.SetPen(* wxBLACK_PEN);
0fc1a713
JS
2540
2541 wxNode *node = m_controlPoints.First();
2542 while (node)
2543 {
2544 wxControlPoint *control = (wxControlPoint *)node->Data();
2545 control->Draw(dc);
2546 node = node->Next();
2547 }
2548 // Children of divisions are contained objects,
2549 // so stop here.
2550 // This test bypasses the type facility for speed
2551 // (critical when drawing)
2552 if (!IsKindOf(CLASSINFO(wxDivisionShape)))
2553 {
2554 node = m_children.First();
2555 while (node)
2556 {
2557 wxShape *child = (wxShape *)node->Data();
2558 child->GetEventHandler()->OnDrawControlPoints(dc);
2559 node = node->Next();
2560 }
2561 }
2562}
2563
2564void wxShape::OnEraseControlPoints(wxDC& dc)
2565{
2566 wxNode *node = m_controlPoints.First();
2567 while (node)
2568 {
2569 wxControlPoint *control = (wxControlPoint *)node->Data();
2570 control->Erase(dc);
2571 node = node->Next();
2572 }
2573 if (!IsKindOf(CLASSINFO(wxDivisionShape)))
2574 {
2575 node = m_children.First();
2576 while (node)
2577 {
2578 wxShape *child = (wxShape *)node->Data();
2579 child->GetEventHandler()->OnEraseControlPoints(dc);
2580 node = node->Next();
2581 }
2582 }
2583}
2584
2585void wxShape::Select(bool select, wxDC* dc)
2586{
2587 m_selected = select;
2588 if (select)
2589 {
2590 MakeControlPoints();
2591 // Children of divisions are contained objects,
2592 // so stop here
2593 if (!IsKindOf(CLASSINFO(wxDivisionShape)))
2594 {
2595 wxNode *node = m_children.First();
2596 while (node)
2597 {
2598 wxShape *child = (wxShape *)node->Data();
2599 child->MakeMandatoryControlPoints();
2600 node = node->Next();
2601 }
2602 }
2603 if (dc)
2604 GetEventHandler()->OnDrawControlPoints(*dc);
2605 }
2606 if (!select)
2607 {
2608 DeleteControlPoints(dc);
2609 if (!IsKindOf(CLASSINFO(wxDivisionShape)))
2610 {
2611 wxNode *node = m_children.First();
2612 while (node)
2613 {
2614 wxShape *child = (wxShape *)node->Data();
2615 child->DeleteControlPoints(dc);
2616 node = node->Next();
2617 }
2618 }
2619 }
2620}
2621
2622bool wxShape::Selected() const
2623{
2624 return m_selected;
2625}
2626
2627bool wxShape::AncestorSelected() const
2628{
2629 if (m_selected) return TRUE;
2630 if (!GetParent())
2631 return FALSE;
2632 else
2633 return GetParent()->AncestorSelected();
2634}
2635
6f5f3ca0 2636int wxShape::GetNumberOfAttachments() const
0fc1a713
JS
2637{
2638 // Should return the MAXIMUM attachment point id here,
2639 // so higher-level functions can iterate through all attachments,
2640 // even if they're not contiguous.
2641 if (m_attachmentPoints.Number() == 0)
2642 return 4;
2643 else
2644 {
2645 int maxN = 3;
2646 wxNode *node = m_attachmentPoints.First();
2647 while (node)
2648 {
2649 wxAttachmentPoint *point = (wxAttachmentPoint *)node->Data();
2650 if (point->m_id > maxN)
2651 maxN = point->m_id;
2652 node = node->Next();
2653 }
2654 return maxN+1;;
2655 }
2656}
2657
6f5f3ca0 2658bool wxShape::AttachmentIsValid(int attachment) const
0fc1a713 2659{
f93ce4da
JS
2660 if (m_attachmentPoints.Number() == 0)
2661 {
2662 return ((attachment >= 0) && (attachment < 4)) ;
2663 }
0fc1a713
JS
2664
2665 wxNode *node = m_attachmentPoints.First();
2666 while (node)
2667 {
2668 wxAttachmentPoint *point = (wxAttachmentPoint *)node->Data();
2669 if (point->m_id == attachment)
2670 return TRUE;
2671 node = node->Next();
2672 }
2673 return FALSE;
2674}
2675
42cfaf8c 2676bool wxShape::GetAttachmentPosition(int attachment, double *x, double *y,
0fc1a713
JS
2677 int nth, int no_arcs, wxLineShape *line)
2678{
f97c9854 2679 if (m_attachmentMode == ATTACHMENT_MODE_NONE)
0fc1a713 2680 {
f93ce4da 2681 *x = m_xpos; *y = m_ypos;
0fc1a713 2682 return TRUE;
0fc1a713 2683 }
f97c9854
JS
2684 else if (m_attachmentMode == ATTACHMENT_MODE_BRANCHING)
2685 {
2686 wxRealPoint pt, stemPt;
2687 GetBranchingAttachmentPoint(attachment, nth, pt, stemPt);
2688 *x = pt.x;
2689 *y = pt.y;
2690 return TRUE;
2691 }
2692 else if (m_attachmentMode == ATTACHMENT_MODE_EDGE)
f93ce4da
JS
2693 {
2694 if (m_attachmentPoints.Number() > 0)
2695 {
2696 wxNode *node = m_attachmentPoints.First();
2697 while (node)
2698 {
2699 wxAttachmentPoint *point = (wxAttachmentPoint *)node->Data();
2700 if (point->m_id == attachment)
2701 {
2702 *x = (double)(m_xpos + point->m_x);
2703 *y = (double)(m_ypos + point->m_y);
2704 return TRUE;
2705 }
2706 node = node->Next();
2707 }
2708 *x = m_xpos; *y = m_ypos;
2709 return FALSE;
2710 }
2711 else
2712 {
2713 // Assume is rectangular
2714 double w, h;
2715 GetBoundingBoxMax(&w, &h);
2716 double top = (double)(m_ypos + h/2.0);
2717 double bottom = (double)(m_ypos - h/2.0);
2718 double left = (double)(m_xpos - w/2.0);
2719 double right = (double)(m_xpos + w/2.0);
2720
2721 bool isEnd = (line && line->IsEnd(this));
2722
f97c9854
JS
2723 int physicalAttachment = LogicalToPhysicalAttachment(attachment);
2724
f93ce4da 2725 // Simplified code
f97c9854 2726 switch (physicalAttachment)
f93ce4da
JS
2727 {
2728 case 0:
2729 {
2730 wxRealPoint pt = CalcSimpleAttachment(wxRealPoint(left, bottom), wxRealPoint(right, bottom),
2731 nth, no_arcs, line);
2732
2733 *x = pt.x; *y = pt.y;
2734 break;
2735 }
2736 case 1:
2737 {
2738 wxRealPoint pt = CalcSimpleAttachment(wxRealPoint(right, bottom), wxRealPoint(right, top),
2739 nth, no_arcs, line);
2740
2741 *x = pt.x; *y = pt.y;
2742 break;
2743 }
2744 case 2:
2745 {
2746 wxRealPoint pt = CalcSimpleAttachment(wxRealPoint(left, top), wxRealPoint(right, top),
2747 nth, no_arcs, line);
2748
2749 *x = pt.x; *y = pt.y;
2750 break;
2751 }
2752 case 3:
2753 {
2754 wxRealPoint pt = CalcSimpleAttachment(wxRealPoint(left, bottom), wxRealPoint(left, top),
2755 nth, no_arcs, line);
2756
2757 *x = pt.x; *y = pt.y;
2758 break;
2759 }
2760 default:
2761 {
f97c9854 2762 return FALSE;
f93ce4da
JS
2763 break;
2764 }
2765 }
2766 return TRUE;
2767 }
2768 }
0fc1a713 2769 return FALSE;
0fc1a713
JS
2770}
2771
42cfaf8c 2772void wxShape::GetBoundingBoxMax(double *w, double *h)
0fc1a713 2773{
42cfaf8c 2774 double ww, hh;
0fc1a713
JS
2775 GetBoundingBoxMin(&ww, &hh);
2776 if (m_shadowMode != SHADOW_NONE)
2777 {
2778 ww += m_shadowOffsetX;
2779 hh += m_shadowOffsetY;
2780 }
2781 *w = ww;
2782 *h = hh;
2783}
2784
2785// Returns TRUE if image is a descendant of this composite
2786bool wxShape::HasDescendant(wxShape *image)
2787{
2788 if (image == this)
2789 return TRUE;
2790 wxNode *node = m_children.First();
2791 while (node)
2792 {
2793 wxShape *child = (wxShape *)node->Data();
2794 bool ans = child->HasDescendant(image);
2795 if (ans)
2796 return TRUE;
2797 node = node->Next();
2798 }
2799 return FALSE;
2800}
2801
2802// Clears points from a list of wxRealPoints, and clears list
2803void wxShape::ClearPointList(wxList& list)
2804{
2805 wxNode* node = list.First();
2806 while (node)
2807 {
2808 wxRealPoint* pt = (wxRealPoint*) node->Data();
2809 delete pt;
2810
2811 node = node->Next();
2812 }
2813 list.Clear();
2814}
2815
42cfaf8c
JS
2816// Assuming the attachment lies along a vertical or horizontal line,
2817// calculate the position on that point.
2818wxRealPoint wxShape::CalcSimpleAttachment(const wxRealPoint& pt1, const wxRealPoint& pt2,
2819 int nth, int noArcs, wxLineShape* line)
2820{
2821 bool isEnd = (line && line->IsEnd(this));
2822
2823 // Are we horizontal or vertical?
2824 bool isHorizontal = (oglRoughlyEqual(pt1.y, pt2.y) == TRUE);
2825
2826 double x, y;
2827
2828 if (isHorizontal)
2829 {
2830 wxRealPoint firstPoint, secondPoint;
2831 if (pt1.x > pt2.x)
2832 {
2833 firstPoint = pt2;
2834 secondPoint = pt1;
2835 }
2836 else
2837 {
2838 firstPoint = pt1;
2839 secondPoint = pt2;
2840 }
2841
2842 if (m_spaceAttachments)
2843 {
2844 if (line && (line->GetAlignmentType(isEnd) == LINE_ALIGNMENT_TO_NEXT_HANDLE))
2845 {
2846 // Align line according to the next handle along
2847 wxRealPoint *point = line->GetNextControlPoint(this);
2848 if (point->x < firstPoint.x)
2849 x = firstPoint.x;
2850 else if (point->x > secondPoint.x)
2851 x = secondPoint.x;
2852 else
2853 x = point->x;
2854 }
2855 else
2856 x = firstPoint.x + (nth + 1)*(secondPoint.x - firstPoint.x)/(noArcs + 1);
2857 }
2858 else x = (secondPoint.x - firstPoint.x)/2.0; // Midpoint
2859
2860 y = pt1.y;
2861 }
2862 else
2863 {
2864 wxASSERT( oglRoughlyEqual(pt1.x, pt2.x) == TRUE );
2865
2866 wxRealPoint firstPoint, secondPoint;
2867 if (pt1.y > pt2.y)
2868 {
2869 firstPoint = pt2;
2870 secondPoint = pt1;
2871 }
2872 else
2873 {
2874 firstPoint = pt1;
2875 secondPoint = pt2;
2876 }
2877
2878 if (m_spaceAttachments)
2879 {
2880 if (line && (line->GetAlignmentType(isEnd) == LINE_ALIGNMENT_TO_NEXT_HANDLE))
2881 {
2882 // Align line according to the next handle along
2883 wxRealPoint *point = line->GetNextControlPoint(this);
2884 if (point->y < firstPoint.y)
2885 y = firstPoint.y;
2886 else if (point->y > secondPoint.y)
2887 y = secondPoint.y;
2888 else
2889 y = point->y;
2890 }
2891 else
2892 y = firstPoint.y + (nth + 1)*(secondPoint.y - firstPoint.y)/(noArcs + 1);
2893 }
2894 else y = (secondPoint.y - firstPoint.y)/2.0; // Midpoint
2895
2896 x = pt1.x;
2897 }
2898
2899 return wxRealPoint(x, y);
2900}
2901
2902// Return the zero-based position in m_lines of line.
2903int wxShape::GetLinePosition(wxLineShape* line)
2904{
2905 int i = 0;
2906 for (i = 0; i < m_lines.Number(); i++)
2907 if ((wxLineShape*) (m_lines.Nth(i)->Data()) == line)
2908 return i;
2909
2910 return 0;
2911}
2912
f97c9854
JS
2913//
2914// |________|
2915// | <- root
2916// | <- neck
2917// shoulder1 ->---------<- shoulder2
2918// | | | | |
2919// <- branching attachment point N-1
2920
2921// This function gets information about where branching connections go.
2922// Returns FALSE if there are no lines at this attachment.
2923bool wxShape::GetBranchingAttachmentInfo(int attachment, wxRealPoint& root, wxRealPoint& neck,
2924 wxRealPoint& shoulder1, wxRealPoint& shoulder2)
2925{
2926 int physicalAttachment = LogicalToPhysicalAttachment(attachment);
2927
2928 // Number of lines at this attachment.
2929 int lineCount = GetAttachmentLineCount(attachment);
2930
2931 if (lineCount == 0)
2932 return FALSE;
2933
2934 int totalBranchLength = m_branchSpacing * (lineCount - 1);
2935
2936 root = GetBranchingAttachmentRoot(attachment);
2937
2938 // Assume that we have attachment points 0 to 3: top, right, bottom, left.
2939 switch (physicalAttachment)
2940 {
2941 case 0:
2942 {
2943 neck.x = GetX();
2944 neck.y = root.y - m_branchNeckLength;
2945
2946 shoulder1.x = root.x - (totalBranchLength/2.0) ;
2947 shoulder2.x = root.x + (totalBranchLength/2.0) ;
2948
2949 shoulder1.y = neck.y;
2950 shoulder2.y = neck.y;
2951 break;
2952 }
2953 case 1:
2954 {
2955 neck.x = root.x + m_branchNeckLength;
2956 neck.y = root.y;
2957
2958 shoulder1.x = neck.x ;
2959 shoulder2.x = neck.x ;
2960
2961 shoulder1.y = neck.y - (totalBranchLength/2.0) ;
2962 shoulder2.y = neck.y + (totalBranchLength/2.0) ;
2963 break;
2964 }
2965 case 2:
2966 {
2967 neck.x = GetX();
2968 neck.y = root.y + m_branchNeckLength;
2969
2970 shoulder1.x = root.x - (totalBranchLength/2.0) ;
2971 shoulder2.x = root.x + (totalBranchLength/2.0) ;
2972
2973 shoulder1.y = neck.y;
2974 shoulder2.y = neck.y;
2975 break;
2976 }
2977 case 3:
2978 {
2979 neck.x = root.x - m_branchNeckLength;
2980 neck.y = root.y ;
2981
2982 shoulder1.x = neck.x ;
2983 shoulder2.x = neck.x ;
2984
2985 shoulder1.y = neck.y - (totalBranchLength/2.0) ;
2986 shoulder2.y = neck.y + (totalBranchLength/2.0) ;
2987 break;
2988 }
2989 default:
2990 {
2991 wxFAIL_MSG( "Unrecognised attachment point in GetBranchingAttachmentInfo." );
2992 break;
2993 }
2994 }
2995 return TRUE;
2996}
2997
2998// n is the number of the adjoining line, from 0 to N-1 where N is the number of lines
2999// at this attachment point.
3000// Get the attachment point where the arc joins the stem, and also the point where the
3001// the stem meets the shoulder.
3002bool wxShape::GetBranchingAttachmentPoint(int attachment, int n, wxRealPoint& pt, wxRealPoint& stemPt)
3003{
3004 int physicalAttachment = LogicalToPhysicalAttachment(attachment);
3005
3006 wxRealPoint root, neck, shoulder1, shoulder2;
3007 GetBranchingAttachmentInfo(attachment, root, neck, shoulder1, shoulder2);
3008
3009 // Assume that we have attachment points 0 to 3: top, right, bottom, left.
3010 switch (physicalAttachment)
3011 {
3012 case 0:
3013 {
3014 pt.y = neck.y - m_branchStemLength;
3015 pt.x = shoulder1.x + n*m_branchSpacing;
3016
3017 stemPt.x = pt.x;
3018 stemPt.y = neck.y;
3019 break;
3020 }
3021 case 2:
3022 {
3023 pt.y = neck.y + m_branchStemLength;
3024 pt.x = shoulder1.x + n*m_branchSpacing;
3025
3026 stemPt.x = pt.x;
3027 stemPt.y = neck.y;
3028 break;
3029 }
3030 case 1:
3031 {
3032 pt.x = neck.x + m_branchStemLength;
3033 pt.y = shoulder1.y + n*m_branchSpacing;
3034
3035 stemPt.x = neck.x;
3036 stemPt.y = pt.y;
3037 break;
3038 }
3039 case 3:
3040 {
3041 pt.x = neck.x - m_branchStemLength;
3042 pt.y = shoulder1.y + n*m_branchSpacing;
3043
3044 stemPt.x = neck.x;
3045 stemPt.y = pt.y;
3046 break;
3047 }
3048 default:
3049 {
3050 wxFAIL_MSG( "Unrecognised attachment point in GetBranchingAttachmentPoint." );
3051 break;
3052 }
3053 }
3054
3055 return TRUE;
3056}
3057
3058// Get the number of lines at this attachment position.
3059int wxShape::GetAttachmentLineCount(int attachment) const
3060{
3061 int count = 0;
3062 wxNode* node = m_lines.First();
3063 while (node)
3064 {
3065 wxLineShape* lineShape = (wxLineShape*) node->Data();
3066 if ((lineShape->GetFrom() == this) && (lineShape->GetAttachmentFrom() == attachment))
3067 count ++;
3068 else if ((lineShape->GetTo() == this) && (lineShape->GetAttachmentTo() == attachment))
3069 count ++;
3070
3071 node = node->Next();
3072 }
3073 return count;
3074}
3075
3076// This function gets the root point at the given attachment.
3077wxRealPoint wxShape::GetBranchingAttachmentRoot(int attachment)
3078{
3079 int physicalAttachment = LogicalToPhysicalAttachment(attachment);
3080
3081 wxRealPoint root;
3082
3083 double width, height;
3084 GetBoundingBoxMax(& width, & height);
3085
3086 // Assume that we have attachment points 0 to 3: top, right, bottom, left.
3087 switch (physicalAttachment)
3088 {
3089 case 0:
3090 {
3091 root.x = GetX() ;
3092 root.y = GetY() - height/2.0;
3093 break;
3094 }
3095 case 1:
3096 {
3097 root.x = GetX() + width/2.0;
3098 root.y = GetY() ;
3099 break;
3100 }
3101 case 2:
3102 {
3103 root.x = GetX() ;
3104 root.y = GetY() + height/2.0;
3105 break;
3106 }
3107 case 3:
3108 {
3109 root.x = GetX() - width/2.0;
3110 root.y = GetY() ;
3111 break;
3112 }
3113 default:
3114 {
3115 wxFAIL_MSG( "Unrecognised attachment point in GetBranchingAttachmentRoot." );
3116 break;
3117 }
3118 }
3119 return root;
3120}
3121
3122// Draw or erase the branches (not the actual arcs though)
3123void wxShape::OnDrawBranches(wxDC& dc, int attachment, bool erase)
3124{
3125 int count = GetAttachmentLineCount(attachment);
3126 if (count == 0)
3127 return;
3128
3129 wxRealPoint root, neck, shoulder1, shoulder2;
3130 GetBranchingAttachmentInfo(attachment, root, neck, shoulder1, shoulder2);
3131
3132 if (erase)
3133 {
3134 dc.SetPen(*wxWHITE_PEN);
3135 dc.SetBrush(*wxWHITE_BRUSH);
3136 }
3137 else
3138 {
3139 dc.SetPen(*wxBLACK_PEN);
3140 dc.SetBrush(*wxBLACK_BRUSH);
3141 }
3142
3143 // Draw neck
3144 dc.DrawLine((long) root.x, (long) root.y, (long) neck.x, (long) neck.y);
3145
3146 if (count > 1)
3147 {
3148 // Draw shoulder-to-shoulder line
3149 dc.DrawLine((long) shoulder1.x, (long) shoulder1.y, (long) shoulder2.x, (long) shoulder2.y);
3150 }
3151 // Draw all the little branches
3152 int i;
3153 for (i = 0; i < count; i++)
3154 {
3155 wxRealPoint pt, stemPt;
3156 GetBranchingAttachmentPoint(attachment, i, pt, stemPt);
3157 dc.DrawLine((long) stemPt.x, (long) stemPt.y, (long) pt.x, (long) pt.y);
3158
dfad0599 3159 if ((GetBranchStyle() & BRANCHING_ATTACHMENT_BLOB) && (count > 1))
f97c9854
JS
3160 {
3161 long blobSize=6;
3162// dc.DrawEllipse((long) (stemPt.x + 0.5 - (blobSize/2.0)), (long) (stemPt.y + 0.5 - (blobSize/2.0)), blobSize, blobSize);
3163 dc.DrawEllipse((long) (stemPt.x - (blobSize/2.0)), (long) (stemPt.y - (blobSize/2.0)), blobSize, blobSize);
3164 }
3165 }
3166}
3167
3168// Draw or erase the branches (not the actual arcs though)
3169void wxShape::OnDrawBranches(wxDC& dc, bool erase)
3170{
3171 if (m_attachmentMode != ATTACHMENT_MODE_BRANCHING)
3172 return;
3173
3174 int count = GetNumberOfAttachments();
3175 int i;
3176 for (i = 0; i < count; i++)
3177 OnDrawBranches(dc, i, erase);
3178}
3179
3180// Only get the attachment position at the _edge_ of the shape, ignoring
3181// branching mode. This is used e.g. to indicate the edge of interest, not the point
3182// on the attachment branch.
3183bool wxShape::GetAttachmentPositionEdge(int attachment, double *x, double *y,
3184 int nth, int no_arcs, wxLineShape *line)
3185{
3186 int oldMode = m_attachmentMode;
3187
3188 // Calculate as if to edge, not branch
3189 if (m_attachmentMode == ATTACHMENT_MODE_BRANCHING)
3190 m_attachmentMode = ATTACHMENT_MODE_EDGE;
3191 bool success = GetAttachmentPosition(attachment, x, y, nth, no_arcs, line);
3192 m_attachmentMode = oldMode;
3193
3194 return success;
3195}
3196
3197// Rotate the standard attachment point from physical (0 is always North)
3198// to logical (0 -> 1 if rotated by 90 degrees)
3199int wxShape::PhysicalToLogicalAttachment(int physicalAttachment) const
3200{
3201 const double pi = 3.1415926535897932384626433832795 ;
3202 int i;
3203 if (oglRoughlyEqual(GetRotation(), 0.0))
3204 {
3205 i = physicalAttachment;
3206 }
3207 else if (oglRoughlyEqual(GetRotation(), (pi/2.0)))
3208 {
3209 i = physicalAttachment - 1;
3210 }
3211 else if (oglRoughlyEqual(GetRotation(), pi))
3212 {
3213 i = physicalAttachment - 2;
3214 }
3215 else if (oglRoughlyEqual(GetRotation(), (3.0*pi/2.0)))
3216 {
3217 i = physicalAttachment - 3;
3218 }
3219 else
3220 // Can't handle -- assume the same.
3221 return physicalAttachment;
3222
3223 if (i < 0)
3224 i += 4;
3225
3226 return i;
3227}
3228
3229// Rotate the standard attachment point from logical
3230// to physical (0 is always North)
3231int wxShape::LogicalToPhysicalAttachment(int logicalAttachment) const
3232{
3233 const double pi = 3.1415926535897932384626433832795 ;
3234 int i;
3235 if (oglRoughlyEqual(GetRotation(), 0.0))
3236 {
3237 i = logicalAttachment;
3238 }
3239 else if (oglRoughlyEqual(GetRotation(), (pi/2.0)))
3240 {
3241 i = logicalAttachment + 1;
3242 }
3243 else if (oglRoughlyEqual(GetRotation(), pi))
3244 {
3245 i = logicalAttachment + 2;
3246 }
3247 else if (oglRoughlyEqual(GetRotation(), (3.0*pi/2.0)))
3248 {
3249 i = logicalAttachment + 3;
3250 }
3251 else
3252 // Can't handle -- assume the same.
3253 return logicalAttachment;
3254
3255 if (i > 3)
3256 i -= 4;
3257
3258 return i;
3259}
3260
3261void wxShape::Rotate(double WXUNUSED(x), double WXUNUSED(y), double theta)
3262{
3263 const double pi = 3.1415926535897932384626433832795 ;
3264 m_rotation = theta;
3265 if (m_rotation < 0.0)
3266 {
3267 m_rotation += 2*pi;
3268 }
3269 else if (m_rotation > 2*pi)
3270 {
3271 m_rotation -= 2*pi;
3272 }
3273}
3274