]> git.saurik.com Git - wxWidgets.git/blame - utils/ogl/src/composit.cpp
small fixes
[wxWidgets.git] / utils / ogl / src / composit.cpp
CommitLineData
0fc1a713
JS
1/////////////////////////////////////////////////////////////////////////////
2// Name: composit.cpp
3// Purpose: Composite OGL class
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 "composit.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
JS
28
29#include "basic.h"
30#include "basicp.h"
31#include "constrnt.h"
32#include "composit.h"
33#include "misc.h"
34#include "canvas.h"
35
36// Sometimes, objects need to access the whole database to
37// construct themselves.
38wxExprDatabase *GlobalwxExprDatabase = NULL;
39
40// Popup menu for editing divisions
41wxMenu *oglPopupDivisionMenu = NULL;
42
43/*
44 * Division control point
45 */
a097c93d 46
0fc1a713
JS
47class wxDivisionControlPoint: public wxControlPoint
48{
49 DECLARE_DYNAMIC_CLASS(wxDivisionControlPoint)
50 public:
51 wxDivisionControlPoint() {}
42cfaf8c 52 wxDivisionControlPoint(wxShapeCanvas *the_canvas, wxShape *object, double size, double the_xoffset, double the_yoffset, int the_type);
0fc1a713
JS
53 ~wxDivisionControlPoint();
54
42cfaf8c
JS
55 void OnDragLeft(bool draw, double x, double y, int keys=0, int attachment = 0);
56 void OnBeginDragLeft(double x, double y, int keys=0, int attachment = 0);
57 void OnEndDragLeft(double x, double y, int keys=0, int attachment = 0);
0fc1a713
JS
58};
59
60IMPLEMENT_DYNAMIC_CLASS(wxDivisionControlPoint, wxControlPoint)
61
62/*
63 * Composite object
64 *
65 */
66
67IMPLEMENT_DYNAMIC_CLASS(wxCompositeShape, wxRectangleShape)
68
69wxCompositeShape::wxCompositeShape(): wxRectangleShape(10.0, 10.0)
70{
71// selectable = FALSE;
72 m_oldX = m_xpos;
73 m_oldY = m_ypos;
74}
75
76wxCompositeShape::~wxCompositeShape()
77{
78 wxNode *node = m_constraints.First();
79 while (node)
80 {
6f5f3ca0 81 wxOGLConstraint *constraint = (wxOGLConstraint *)node->Data();
0fc1a713
JS
82 delete constraint;
83 node = node->Next();
84 }
85 node = m_children.First();
86 while (node)
87 {
88 wxShape *object = (wxShape *)node->Data();
89 wxNode *next = node->Next();
90 object->Unlink();
91 delete object;
92 node = next;
93 }
94}
95
96void wxCompositeShape::OnDraw(wxDC& dc)
97{
42cfaf8c
JS
98 double x1 = (double)(m_xpos - m_width/2.0);
99 double y1 = (double)(m_ypos - m_height/2.0);
0fc1a713
JS
100
101 if (m_shadowMode != SHADOW_NONE)
102 {
103 if (m_shadowBrush)
c0ed460c
JS
104 dc.SetBrush(* m_shadowBrush);
105 dc.SetPen(* g_oglTransparentPen);
0fc1a713
JS
106
107 if (m_cornerRadius != 0.0)
42cfaf8c
JS
108 dc.DrawRoundedRectangle(WXROUND(x1 + m_shadowOffsetX), WXROUND(y1 + m_shadowOffsetY),
109 WXROUND(m_width), WXROUND(m_height), m_cornerRadius);
0fc1a713 110 else
42cfaf8c 111 dc.DrawRectangle(WXROUND(x1 + m_shadowOffsetX), WXROUND(y1 + m_shadowOffsetY), WXROUND(m_width), WXROUND(m_height));
0fc1a713
JS
112 }
113}
114
115void wxCompositeShape::OnDrawContents(wxDC& dc)
116{
117 wxNode *node = m_children.First();
118 while (node)
119 {
120 wxShape *object = (wxShape *)node->Data();
121 object->Draw(dc);
122 object->DrawLinks(dc);
123 node = node->Next();
124 }
125 wxShape::OnDrawContents(dc);
126}
127
42cfaf8c 128bool wxCompositeShape::OnMovePre(wxDC& dc, double x, double y, double oldx, double oldy, bool display)
0fc1a713 129{
42cfaf8c
JS
130 double diffX = x - oldx;
131 double diffY = y - oldy;
0fc1a713
JS
132 wxNode *node = m_children.First();
133 while (node)
134 {
135 wxShape *object = (wxShape *)node->Data();
136
137 object->Erase(dc);
138 object->Move(dc, object->GetX() + diffX, object->GetY() + diffY, display);
139
140 node = node->Next();
141 }
142 return TRUE;
143}
144
145void wxCompositeShape::OnErase(wxDC& dc)
146{
147 wxRectangleShape::OnErase(dc);
148 wxNode *node = m_children.First();
149 while (node)
150 {
151 wxShape *object = (wxShape *)node->Data();
152 object->Erase(dc);
153 node = node->Next();
154 }
155}
156
42cfaf8c
JS
157static double objectStartX = 0.0;
158static double objectStartY = 0.0;
0fc1a713 159
42cfaf8c 160void wxCompositeShape::OnDragLeft(bool draw, double x, double y, int keys, int attachment)
0fc1a713 161{
42cfaf8c
JS
162 double xx = x;
163 double yy = y;
0fc1a713 164 m_canvas->Snap(&xx, &yy);
42cfaf8c
JS
165 double offsetX = xx - objectStartX;
166 double offsetY = yy - objectStartY;
0fc1a713
JS
167
168 wxClientDC dc(GetCanvas());
169 GetCanvas()->PrepareDC(dc);
170
a097c93d 171 dc.SetLogicalFunction(OGLRBLF);
0fc1a713
JS
172 wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
173 dc.SetPen(dottedPen);
174 dc.SetBrush((* wxTRANSPARENT_BRUSH));
175
176 GetEventHandler()->OnDrawOutline(dc, GetX() + offsetX, GetY() + offsetY, GetWidth(), GetHeight());
177// wxShape::OnDragLeft(draw, x, y, keys, attachment);
178}
179
42cfaf8c 180void wxCompositeShape::OnBeginDragLeft(double x, double y, int keys, int attachment)
0fc1a713
JS
181{
182 objectStartX = x;
183 objectStartY = y;
184
185 wxClientDC dc(GetCanvas());
186 GetCanvas()->PrepareDC(dc);
187
188 Erase(dc);
189
a097c93d 190 dc.SetLogicalFunction(OGLRBLF);
0fc1a713
JS
191
192 wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
193 dc.SetPen(dottedPen);
194 dc.SetBrush((* wxTRANSPARENT_BRUSH));
195 m_canvas->CaptureMouse();
196
42cfaf8c
JS
197 double xx = x;
198 double yy = y;
0fc1a713 199 m_canvas->Snap(&xx, &yy);
42cfaf8c
JS
200 double offsetX = xx - objectStartX;
201 double offsetY = yy - objectStartY;
0fc1a713
JS
202
203 GetEventHandler()->OnDrawOutline(dc, GetX() + offsetX, GetY() + offsetY, GetWidth(), GetHeight());
204
205// wxShape::OnBeginDragLeft(x, y, keys, attachment);
206}
207
42cfaf8c 208void wxCompositeShape::OnEndDragLeft(double x, double y, int keys, int attachment)
0fc1a713
JS
209{
210// wxShape::OnEndDragLeft(x, y, keys, attachment);
211
212 wxClientDC dc(GetCanvas());
213 GetCanvas()->PrepareDC(dc);
214
215 m_canvas->ReleaseMouse();
216
217 if (!m_draggable)
218 {
219 if (m_parent) m_parent->GetEventHandler()->OnEndDragLeft(x, y, keys, 0);
220 return;
221 }
222
223 dc.SetLogicalFunction(wxCOPY);
42cfaf8c
JS
224 double xx = x;
225 double yy = y;
0fc1a713 226 m_canvas->Snap(&xx, &yy);
42cfaf8c
JS
227 double offsetX = xx - objectStartX;
228 double offsetY = yy - objectStartY;
0fc1a713
JS
229
230 Move(dc, GetX() + offsetX, GetY() + offsetY);
a097c93d 231
0fc1a713
JS
232 if (m_canvas && !m_canvas->GetQuickEditMode()) m_canvas->Redraw(dc);
233}
234
42cfaf8c 235void wxCompositeShape::OnRightClick(double x, double y, int keys, int attachment)
0fc1a713
JS
236{
237 // If we get a ctrl-right click, this means send the message to
238 // the division, so we can invoke a user interface for dealing with regions.
239 if (keys & KEY_CTRL)
240 {
241 wxNode *node = m_divisions.First();
242 while (node)
243 {
244 wxDivisionShape *division = (wxDivisionShape *)node->Data();
245 wxNode *next = node->Next();
246 int attach = 0;
42cfaf8c 247 double dist = 0.0;
0fc1a713
JS
248 if (division->HitTest(x, y, &attach, &dist))
249 {
250 division->GetEventHandler()->OnRightClick(x, y, keys, attach);
251 node = NULL;
252 }
253 if (node)
254 node = next;
255 }
256 }
257}
258
42cfaf8c 259void wxCompositeShape::SetSize(double w, double h, bool recursive)
0fc1a713
JS
260{
261 SetAttachmentSize(w, h);
262
42cfaf8c
JS
263 double xScale = (double)(w/(wxMax(1.0, GetWidth())));
264 double yScale = (double)(h/(wxMax(1.0, GetHeight())));
0fc1a713
JS
265
266 m_width = w;
267 m_height = h;
268
269 if (!recursive) return;
270
271 wxNode *node = m_children.First();
272
273 wxClientDC dc(GetCanvas());
274 GetCanvas()->PrepareDC(dc);
275
42cfaf8c 276 double xBound, yBound;
0fc1a713
JS
277 while (node)
278 {
279 wxShape *object = (wxShape *)node->Data();
280
281 // Scale the position first
42cfaf8c
JS
282 double newX = (double)(((object->GetX() - GetX())*xScale) + GetX());
283 double newY = (double)(((object->GetY() - GetY())*yScale) + GetY());
0fc1a713
JS
284 object->Show(FALSE);
285 object->Move(dc, newX, newY);
286 object->Show(TRUE);
a097c93d 287
0fc1a713
JS
288 // Now set the scaled size
289 object->GetBoundingBoxMin(&xBound, &yBound);
290 object->SetSize(object->GetFixedWidth() ? xBound : xScale*xBound,
291 object->GetFixedHeight() ? yBound : yScale*yBound);
292
293 node = node->Next();
294 }
295 SetDefaultRegionSize();
296}
297
298void wxCompositeShape::AddChild(wxShape *child, wxShape *addAfter)
299{
300 m_children.Append(child);
301 child->SetParent(this);
302 if (m_canvas)
303 {
304 // Ensure we add at the right position
305 if (addAfter)
306 child->RemoveFromCanvas(m_canvas);
307 child->AddToCanvas(m_canvas, addAfter);
308 }
309}
310
311void wxCompositeShape::RemoveChild(wxShape *child)
312{
313 m_children.DeleteObject(child);
314 m_divisions.DeleteObject(child);
315 RemoveChildFromConstraints(child);
316 child->SetParent(NULL);
317}
318
319void wxCompositeShape::DeleteConstraintsInvolvingChild(wxShape *child)
320{
321 wxNode *node = m_constraints.First();
322 while (node)
323 {
6f5f3ca0 324 wxOGLConstraint *constraint = (wxOGLConstraint *)node->Data();
0fc1a713
JS
325 wxNode *nextNode = node->Next();
326
327 if ((constraint->m_constrainingObject == child) ||
328 constraint->m_constrainedObjects.Member(child))
329 {
330 delete constraint;
331 delete node;
332 }
333 node = nextNode;
334 }
335}
336
337void wxCompositeShape::RemoveChildFromConstraints(wxShape *child)
338{
339 wxNode *node = m_constraints.First();
340 while (node)
341 {
6f5f3ca0 342 wxOGLConstraint *constraint = (wxOGLConstraint *)node->Data();
0fc1a713
JS
343 wxNode *nextNode = node->Next();
344
345 if (constraint->m_constrainedObjects.Member(child))
346 constraint->m_constrainedObjects.DeleteObject(child);
347 if (constraint->m_constrainingObject == child)
348 constraint->m_constrainingObject = NULL;
349
350 // Delete the constraint if no participants left
351 if (!constraint->m_constrainingObject)
352 {
353 delete constraint;
354 delete node;
355 }
356
357 node = nextNode;
358 }
359}
360
2bb0cd28 361void wxCompositeShape::Copy(wxShape& copy)
0fc1a713
JS
362{
363 wxRectangleShape::Copy(copy);
364
2bb0cd28
JS
365 wxASSERT( copy.IsKindOf(CLASSINFO(wxCompositeShape)) ) ;
366
367 wxCompositeShape& compositeCopy = (wxCompositeShape&) copy;
368
369 // Associate old and new copies for compositeCopying constraints and division geometry
42cfaf8c 370 oglObjectCopyMapping.Append((long)this, &compositeCopy);
0fc1a713
JS
371
372 // Copy the children
373 wxNode *node = m_children.First();
374 while (node)
375 {
376 wxShape *object = (wxShape *)node->Data();
2bb0cd28 377 wxShape *newObject = object->CreateNewCopy(FALSE, FALSE);
0fc1a713
JS
378 if (newObject->GetId() == 0)
379 newObject->SetId(NewId());
a097c93d 380
2bb0cd28
JS
381 newObject->SetParent(&compositeCopy);
382 compositeCopy.m_children.Append(newObject);
0fc1a713
JS
383
384 // Some m_children may be divisions
385 if (m_divisions.Member(object))
2bb0cd28 386 compositeCopy.m_divisions.Append(newObject);
0fc1a713 387
42cfaf8c 388 oglObjectCopyMapping.Append((long)object, newObject);
0fc1a713
JS
389
390 node = node->Next();
391 }
392
393 // Copy the constraints
394 node = m_constraints.First();
395 while (node)
396 {
6f5f3ca0 397 wxOGLConstraint *constraint = (wxOGLConstraint *)node->Data();
0fc1a713 398
42cfaf8c 399 wxShape *newConstraining = (wxShape *)(oglObjectCopyMapping.Find((long)constraint->m_constrainingObject)->Data());
0fc1a713
JS
400
401 wxList newConstrainedList;
402 wxNode *node2 = constraint->m_constrainedObjects.First();
403 while (node2)
404 {
405 wxShape *constrainedObject = (wxShape *)node2->Data();
42cfaf8c 406 wxShape *newConstrained = (wxShape *)(oglObjectCopyMapping.Find((long)constrainedObject)->Data());
0fc1a713 407 newConstrainedList.Append(newConstrained);
a097c93d 408 node2 = node2->Next();
0fc1a713
JS
409 }
410
6f5f3ca0 411 wxOGLConstraint *newConstraint = new wxOGLConstraint(constraint->m_constraintType, newConstraining,
0fc1a713
JS
412 newConstrainedList);
413 newConstraint->m_constraintId = constraint->m_constraintId;
414 if (constraint->m_constraintName)
415 {
416 newConstraint->m_constraintName = constraint->m_constraintName;
417 }
418 newConstraint->SetSpacing(constraint->m_xSpacing, constraint->m_ySpacing);
2bb0cd28 419 compositeCopy.m_constraints.Append(newConstraint);
0fc1a713
JS
420
421 node = node->Next();
422 }
423
2bb0cd28 424 // Now compositeCopy the division geometry
0fc1a713
JS
425 node = m_divisions.First();
426 while (node)
427 {
428 wxDivisionShape *division = (wxDivisionShape *)node->Data();
42cfaf8c 429 wxNode *node1 = oglObjectCopyMapping.Find((long)division);
0fc1a713
JS
430 wxNode *leftNode = NULL;
431 wxNode *topNode = NULL;
432 wxNode *rightNode = NULL;
433 wxNode *bottomNode = NULL;
434 if (division->GetLeftSide())
42cfaf8c 435 leftNode = oglObjectCopyMapping.Find((long)division->GetLeftSide());
0fc1a713 436 if (division->GetTopSide())
42cfaf8c 437 topNode = oglObjectCopyMapping.Find((long)division->GetTopSide());
0fc1a713 438 if (division->GetRightSide())
42cfaf8c 439 rightNode = oglObjectCopyMapping.Find((long)division->GetRightSide());
0fc1a713 440 if (division->GetBottomSide())
42cfaf8c 441 bottomNode = oglObjectCopyMapping.Find((long)division->GetBottomSide());
0fc1a713
JS
442 if (node1)
443 {
444 wxDivisionShape *newDivision = (wxDivisionShape *)node1->Data();
445 if (leftNode)
446 newDivision->SetLeftSide((wxDivisionShape *)leftNode->Data());
447 if (topNode)
448 newDivision->SetTopSide((wxDivisionShape *)topNode->Data());
449 if (rightNode)
450 newDivision->SetRightSide((wxDivisionShape *)rightNode->Data());
451 if (bottomNode)
452 newDivision->SetBottomSide((wxDivisionShape *)bottomNode->Data());
453 }
454 node = node->Next();
455 }
456}
457
6f5f3ca0 458wxOGLConstraint *wxCompositeShape::AddConstraint(wxOGLConstraint *constraint)
0fc1a713
JS
459{
460 m_constraints.Append(constraint);
461 if (constraint->m_constraintId == 0)
462 constraint->m_constraintId = NewId();
463 return constraint;
464}
465
6f5f3ca0 466wxOGLConstraint *wxCompositeShape::AddConstraint(int type, wxShape *constraining, wxList& constrained)
0fc1a713 467{
6f5f3ca0 468 wxOGLConstraint *constraint = new wxOGLConstraint(type, constraining, constrained);
0fc1a713
JS
469 if (constraint->m_constraintId == 0)
470 constraint->m_constraintId = NewId();
471 m_constraints.Append(constraint);
472 return constraint;
473}
474
6f5f3ca0 475wxOGLConstraint *wxCompositeShape::AddConstraint(int type, wxShape *constraining, wxShape *constrained)
0fc1a713
JS
476{
477 wxList l;
478 l.Append(constrained);
6f5f3ca0 479 wxOGLConstraint *constraint = new wxOGLConstraint(type, constraining, l);
0fc1a713
JS
480 if (constraint->m_constraintId == 0)
481 constraint->m_constraintId = NewId();
482 m_constraints.Append(constraint);
483 return constraint;
484}
485
6f5f3ca0 486wxOGLConstraint *wxCompositeShape::FindConstraint(long cId, wxCompositeShape **actualComposite)
0fc1a713
JS
487{
488 wxNode *node = m_constraints.First();
489 while (node)
490 {
6f5f3ca0 491 wxOGLConstraint *constraint = (wxOGLConstraint *)node->Data();
0fc1a713
JS
492 if (constraint->m_constraintId == cId)
493 {
494 if (actualComposite)
495 *actualComposite = this;
496 return constraint;
497 }
498 node = node->Next();
499 }
500 // If not found, try children.
501 node = m_children.First();
502 while (node)
503 {
504 wxShape *child = (wxShape *)node->Data();
505 if (child->IsKindOf(CLASSINFO(wxCompositeShape)))
506 {
6f5f3ca0 507 wxOGLConstraint *constraint = ((wxCompositeShape *)child)->FindConstraint(cId, actualComposite);
0fc1a713
JS
508 if (constraint)
509 {
510 if (actualComposite)
511 *actualComposite = (wxCompositeShape *)child;
512 return constraint;
513 }
514 }
515 node = node->Next();
516 }
517 return NULL;
518}
519
6f5f3ca0 520void wxCompositeShape::DeleteConstraint(wxOGLConstraint *constraint)
0fc1a713
JS
521{
522 m_constraints.DeleteObject(constraint);
523 delete constraint;
524}
525
526void wxCompositeShape::CalculateSize()
527{
42cfaf8c
JS
528 double maxX = (double) -999999.9;
529 double maxY = (double) -999999.9;
530 double minX = (double) 999999.9;
531 double minY = (double) 999999.9;
0fc1a713 532
42cfaf8c 533 double w, h;
0fc1a713
JS
534 wxNode *node = m_children.First();
535 while (node)
536 {
537 wxShape *object = (wxShape *)node->Data();
538
539 // Recalculate size of composite objects because may not conform
540 // to size it was set to - depends on the children.
541 object->CalculateSize();
542
543 object->GetBoundingBoxMax(&w, &h);
544 if ((object->GetX() + (w/2.0)) > maxX)
42cfaf8c 545 maxX = (double)(object->GetX() + (w/2.0));
0fc1a713 546 if ((object->GetX() - (w/2.0)) < minX)
42cfaf8c 547 minX = (double)(object->GetX() - (w/2.0));
0fc1a713 548 if ((object->GetY() + (h/2.0)) > maxY)
42cfaf8c 549 maxY = (double)(object->GetY() + (h/2.0));
0fc1a713 550 if ((object->GetY() - (h/2.0)) < minY)
42cfaf8c 551 minY = (double)(object->GetY() - (h/2.0));
0fc1a713
JS
552
553 node = node->Next();
554 }
555 m_width = maxX - minX;
556 m_height = maxY - minY;
42cfaf8c
JS
557 m_xpos = (double)(m_width/2.0 + minX);
558 m_ypos = (double)(m_height/2.0 + minY);
0fc1a713
JS
559}
560
561bool wxCompositeShape::Recompute()
562{
563 int noIterations = 0;
564 bool changed = TRUE;
565 while (changed && (noIterations < 500))
566 {
567 changed = Constrain();
568 noIterations ++;
569 }
570/*
571#ifdef wx_x
572 if (changed)
573 cerr << "Warning: constraint algorithm failed after 500 iterations.\n";
574#endif
575*/
576 return (!changed);
577}
578
579bool wxCompositeShape::Constrain()
580{
581 CalculateSize();
582
583 bool changed = FALSE;
584 wxNode *node = m_children.First();
585 while (node)
586 {
587 wxShape *object = (wxShape *)node->Data();
588 if (object->Constrain())
589 changed = TRUE;
590 node = node->Next();
591 }
592
593 node = m_constraints.First();
594 while (node)
595 {
6f5f3ca0 596 wxOGLConstraint *constraint = (wxOGLConstraint *)node->Data();
0fc1a713
JS
597 if (constraint->Evaluate()) changed = TRUE;
598 node = node->Next();
599 }
600 return changed;
601}
602
603#ifdef PROLOGIO
6f5f3ca0 604void wxCompositeShape::WriteAttributes(wxExpr *clause)
0fc1a713 605{
6f5f3ca0 606 wxRectangleShape::WriteAttributes(clause);
0fc1a713
JS
607
608// clause->AddAttributeValue("selectable", (long)selectable);
609
610 // Output constraints as constraint1 = (...), constraint2 = (...), etc.
611 int constraintNo = 1;
612 char m_constraintNameBuf[20];
613 wxNode *node = m_constraints.First();
614 while (node)
615 {
6f5f3ca0 616 wxOGLConstraint *constraint = (wxOGLConstraint *)node->Data();
0fc1a713
JS
617 sprintf(m_constraintNameBuf, "constraint%d", constraintNo);
618
619 // Each constraint is stored in the form
620 // (type name id xspacing yspacing m_constrainingObjectId constrainedObjectIdList)
f93ce4da 621 wxExpr *constraintExpr = new wxExpr(wxExprList);
0fc1a713 622 constraintExpr->Append(new wxExpr((long)constraint->m_constraintType));
f93ce4da 623 constraintExpr->Append(new wxExpr(wxExprString, constraint->m_constraintName));
0fc1a713
JS
624 constraintExpr->Append(new wxExpr(constraint->m_constraintId));
625 constraintExpr->Append(new wxExpr(constraint->m_xSpacing));
626 constraintExpr->Append(new wxExpr(constraint->m_ySpacing));
627 constraintExpr->Append(new wxExpr(constraint->m_constrainingObject->GetId()));
628
f93ce4da 629 wxExpr *objectList = new wxExpr(wxExprList);
0fc1a713
JS
630 wxNode *node1 = constraint->m_constrainedObjects.First();
631 while (node1)
632 {
633 wxShape *obj = (wxShape *)node1->Data();
634 objectList->Append(new wxExpr(obj->GetId()));
635 node1 = node1->Next();
636 }
637 constraintExpr->Append(objectList);
638
639 clause->AddAttributeValue(m_constraintNameBuf, constraintExpr);
640
641 node = node->Next();
642 constraintNo ++;
643 }
644
645 // Write the ids of all the child images
f93ce4da 646 wxExpr *childrenExpr = new wxExpr(wxExprList);
0fc1a713
JS
647 node = m_children.First();
648 while (node)
649 {
650 wxShape *child = (wxShape *)node->Data();
651 childrenExpr->Append(new wxExpr(child->GetId()));
652 node = node->Next();
653 }
654 clause->AddAttributeValue("children", childrenExpr);
655
656 // Write the ids of all the division images
657 if (m_divisions.Number() > 0)
658 {
f93ce4da 659 wxExpr *divisionsExpr = new wxExpr(wxExprList);
0fc1a713
JS
660 node = m_divisions.First();
661 while (node)
662 {
663 wxShape *child = (wxShape *)node->Data();
664 divisionsExpr->Append(new wxExpr(child->GetId()));
665 node = node->Next();
666 }
667 clause->AddAttributeValue("divisions", divisionsExpr);
668 }
669}
670
671// Problem. Child images are always written AFTER the parent
672// so as to be able to link up to parent. So we may not be able
673// to find the constraint participants until we've read everything
674// in. Need to have another pass for composites.
6f5f3ca0 675void wxCompositeShape::ReadAttributes(wxExpr *clause)
0fc1a713 676{
6f5f3ca0 677 wxRectangleShape::ReadAttributes(clause);
0fc1a713
JS
678
679// clause->GetAttributeValue("selectable", selectable);
680}
681
682void wxCompositeShape::ReadConstraints(wxExpr *clause, wxExprDatabase *database)
683{
684 // Constraints are output as constraint1 = (...), constraint2 = (...), etc.
685 int constraintNo = 1;
686 char m_constraintNameBuf[20];
687 bool haveConstraints = TRUE;
688
689 while (haveConstraints)
690 {
691 sprintf(m_constraintNameBuf, "constraint%d", constraintNo);
692 wxExpr *constraintExpr = NULL;
693 clause->GetAttributeValue(m_constraintNameBuf, &constraintExpr);
694 if (!constraintExpr)
695 {
696 haveConstraints = FALSE;
697 break;
698 }
699 int cType = 0;
42cfaf8c
JS
700 double cXSpacing = 0.0;
701 double cYSpacing = 0.0;
0fc1a713
JS
702 wxString cName("");
703 long cId = 0;
704 wxShape *m_constrainingObject = NULL;
705 wxList m_constrainedObjects;
706
707 // Each constraint is stored in the form
708 // (type name id xspacing yspacing m_constrainingObjectId constrainedObjectIdList)
709
710 wxExpr *typeExpr = constraintExpr->Nth(0);
711 wxExpr *nameExpr = constraintExpr->Nth(1);
712 wxExpr *idExpr = constraintExpr->Nth(2);
713 wxExpr *xExpr = constraintExpr->Nth(3);
714 wxExpr *yExpr = constraintExpr->Nth(4);
715 wxExpr *constrainingExpr = constraintExpr->Nth(5);
716 wxExpr *constrainedExpr = constraintExpr->Nth(6);
717
718 cType = (int)typeExpr->IntegerValue();
719 cXSpacing = xExpr->RealValue();
720 cYSpacing = yExpr->RealValue();
721 cName = nameExpr->StringValue();
722 cId = idExpr->IntegerValue();
723
724 wxExpr *objExpr1 = database->HashFind("node_image", constrainingExpr->IntegerValue());
725 if (objExpr1 && objExpr1->GetClientData())
726 m_constrainingObject = (wxShape *)objExpr1->GetClientData();
727 else
728 wxFatalError("Couldn't find constraining image of composite.", "Object graphics error");
a097c93d 729
0fc1a713
JS
730 int i = 0;
731 wxExpr *currentIdExpr = constrainedExpr->Nth(i);
732 while (currentIdExpr)
733 {
734 long currentId = currentIdExpr->IntegerValue();
735 wxExpr *objExpr2 = database->HashFind("node_image", currentId);
736 if (objExpr2 && objExpr2->GetClientData())
737 {
738 m_constrainedObjects.Append((wxShape *)objExpr2->GetClientData());
739 }
740 else
741 {
742 wxFatalError("Couldn't find constrained image of composite.", "Object graphics error");
743 }
744
745 i ++;
746 currentIdExpr = constrainedExpr->Nth(i);
747 }
6f5f3ca0 748 wxOGLConstraint *newConstraint = AddConstraint(cType, m_constrainingObject, m_constrainedObjects);
0fc1a713
JS
749 newConstraint->SetSpacing(cXSpacing, cYSpacing);
750 newConstraint->m_constraintId = cId;
751 newConstraint->m_constraintName = (const char*) cName;
752 constraintNo ++;
753 }
754}
755#endif
756
757// Make this composite into a container by creating one wxDivisionShape
758void wxCompositeShape::MakeContainer()
759{
760 wxDivisionShape *division = OnCreateDivision();
761 m_divisions.Append(division);
762 AddChild(division);
763
764 division->SetSize(m_width, m_height);
765
766 wxClientDC dc(GetCanvas());
767 GetCanvas()->PrepareDC(dc);
768
769 division->Move(dc, GetX(), GetY());
770 Recompute();
771 division->Show(TRUE);
772}
773
774wxDivisionShape *wxCompositeShape::OnCreateDivision()
775{
776 return new wxDivisionShape;
777}
778
779wxShape *wxCompositeShape::FindContainerImage()
780{
781 wxNode *node = m_children.First();
782 while (node)
783 {
784 wxShape *child = (wxShape *)node->Data();
785 if (!m_divisions.Member(child))
786 return child;
787 node = node->Next();
788 }
789 return NULL;
790}
791
792// Returns TRUE if division is a descendant of this container
793bool wxCompositeShape::ContainsDivision(wxDivisionShape *division)
794{
795 if (m_divisions.Member(division))
796 return TRUE;
797 wxNode *node = m_children.First();
798 while (node)
799 {
800 wxShape *child = (wxShape *)node->Data();
801 if (child->IsKindOf(CLASSINFO(wxCompositeShape)))
802 {
803 bool ans = ((wxCompositeShape *)child)->ContainsDivision(division);
804 if (ans)
805 return TRUE;
806 }
807 node = node->Next();
808 }
809 return FALSE;
810}
811
812/*
813 * Division object
814 *
815 */
816
817IMPLEMENT_DYNAMIC_CLASS(wxDivisionShape, wxCompositeShape)
818
819wxDivisionShape::wxDivisionShape()
820{
821 SetSensitivityFilter(OP_CLICK_LEFT | OP_CLICK_RIGHT | OP_DRAG_RIGHT);
822 SetCentreResize(FALSE);
823 SetAttachmentMode(TRUE);
824 m_leftSide = NULL;
825 m_rightSide = NULL;
826 m_topSide = NULL;
827 m_bottomSide = NULL;
828 m_handleSide = DIVISION_SIDE_NONE;
829 m_leftSidePen = wxBLACK_PEN;
830 m_topSidePen = wxBLACK_PEN;
831 m_leftSideColour = "BLACK";
832 m_topSideColour = "BLACK";
833 m_leftSideStyle = "Solid";
834 m_topSideStyle = "Solid";
835 ClearRegions();
836}
837
838wxDivisionShape::~wxDivisionShape()
839{
840}
841
842void wxDivisionShape::OnDraw(wxDC& dc)
843{
c0ed460c 844 dc.SetBrush(* wxTRANSPARENT_BRUSH);
0fc1a713
JS
845 dc.SetBackgroundMode(wxTRANSPARENT);
846
42cfaf8c
JS
847 double x1 = (double)(GetX() - (GetWidth()/2.0));
848 double y1 = (double)(GetY() - (GetHeight()/2.0));
849 double x2 = (double)(GetX() + (GetWidth()/2.0));
850 double y2 = (double)(GetY() + (GetHeight()/2.0));
0fc1a713
JS
851
852 // Should subtract 1 pixel if drawing under Windows
853#ifdef __WXMSW__
42cfaf8c 854 y2 -= (double)1.0;
0fc1a713
JS
855#endif
856
857 if (m_leftSide)
858 {
c0ed460c 859 dc.SetPen(* m_leftSidePen);
42cfaf8c 860 dc.DrawLine(WXROUND(x1), WXROUND(y2), WXROUND(x1), WXROUND(y1));
0fc1a713
JS
861 }
862 if (m_topSide)
863 {
c0ed460c 864 dc.SetPen(* m_topSidePen);
42cfaf8c 865 dc.DrawLine(WXROUND(x1), WXROUND(y1), WXROUND(x2), WXROUND(y1));
0fc1a713
JS
866 }
867
868 // For testing purposes, draw a rectangle so we know
869 // how big the division is.
c0ed460c 870// SetBrush(* wxCYAN_BRUSH);
0fc1a713
JS
871// wxRectangleShape::OnDraw(dc);
872}
873
874void wxDivisionShape::OnDrawContents(wxDC& dc)
875{
876 wxCompositeShape::OnDrawContents(dc);
877}
878
42cfaf8c 879bool wxDivisionShape::OnMovePre(wxDC& dc, double x, double y, double oldx, double oldy, bool display)
0fc1a713 880{
42cfaf8c
JS
881 double diffX = x - oldx;
882 double diffY = y - oldy;
0fc1a713
JS
883 wxNode *node = m_children.First();
884 while (node)
885 {
886 wxShape *object = (wxShape *)node->Data();
887 object->Erase(dc);
888 object->Move(dc, object->GetX() + diffX, object->GetY() + diffY, display);
889 node = node->Next();
890 }
891 return TRUE;
892}
893
42cfaf8c 894void wxDivisionShape::OnDragLeft(bool draw, double x, double y, int keys, int attachment)
0fc1a713
JS
895{
896 if ((m_sensitivity & OP_DRAG_LEFT) != OP_DRAG_LEFT)
897 {
898 attachment = 0;
42cfaf8c 899 double dist;
0fc1a713
JS
900 if (m_parent)
901 {
902 m_parent->HitTest(x, y, &attachment, &dist);
903 m_parent->GetEventHandler()->OnDragLeft(draw, x, y, keys, attachment);
904 }
905 return;
906 }
907 wxShape::OnDragLeft(draw, x, y, keys, attachment);
908}
909
42cfaf8c 910void wxDivisionShape::OnBeginDragLeft(double x, double y, int keys, int attachment)
0fc1a713
JS
911{
912 if ((m_sensitivity & OP_DRAG_LEFT) != OP_DRAG_LEFT)
913 {
914 attachment = 0;
42cfaf8c 915 double dist;
0fc1a713
JS
916 if (m_parent)
917 {
918 m_parent->HitTest(x, y, &attachment, &dist);
919 m_parent->GetEventHandler()->OnBeginDragLeft(x, y, keys, attachment);
920 }
921 return;
922 }
923
924 wxShape::OnBeginDragLeft(x, y, keys, attachment);
925}
926
42cfaf8c 927void wxDivisionShape::OnEndDragLeft(double x, double y, int keys, int attachment)
0fc1a713
JS
928{
929 m_canvas->ReleaseMouse();
930 if ((m_sensitivity & OP_DRAG_LEFT) != OP_DRAG_LEFT)
931 {
932 attachment = 0;
42cfaf8c 933 double dist;
0fc1a713
JS
934 if (m_parent)
935 {
936 m_parent->HitTest(x, y, &attachment, &dist);
937 m_parent->GetEventHandler()->OnEndDragLeft(x, y, keys, attachment);
938 }
939 return;
940 }
a097c93d 941
0fc1a713
JS
942 wxClientDC dc(GetCanvas());
943 GetCanvas()->PrepareDC(dc);
944
945 dc.SetLogicalFunction(wxCOPY);
946
947 m_canvas->Snap(&m_xpos, &m_ypos);
948 GetEventHandler()->OnMovePre(dc, x, y, m_oldX, m_oldY);
949
950 ResetControlPoints();
951 Draw(dc);
952 MoveLinks(dc);
953 GetEventHandler()->OnDrawControlPoints(dc);
954
955 if (m_canvas && !m_canvas->GetQuickEditMode()) m_canvas->Redraw(dc);
956}
957
42cfaf8c 958void wxDivisionShape::SetSize(double w, double h, bool recursive)
0fc1a713
JS
959{
960 m_width = w;
961 m_height = h;
962 wxRectangleShape::SetSize(w, h, recursive);
963}
964
965void wxDivisionShape::CalculateSize()
966{
967}
968
2bb0cd28 969void wxDivisionShape::Copy(wxShape& copy)
0fc1a713
JS
970{
971 wxCompositeShape::Copy(copy);
972
2bb0cd28 973 wxASSERT( copy.IsKindOf(CLASSINFO(wxDivisionShape)) ) ;
0fc1a713 974
2bb0cd28 975 wxDivisionShape& divisionCopy = (wxDivisionShape&) copy;
0fc1a713 976
2bb0cd28
JS
977 divisionCopy.m_leftSideStyle = m_leftSideStyle;
978 divisionCopy.m_topSideStyle = m_topSideStyle;
979 divisionCopy.m_leftSideColour = m_leftSideColour;
980 divisionCopy.m_topSideColour = m_topSideColour;
0fc1a713 981
2bb0cd28
JS
982 divisionCopy.m_leftSidePen = m_leftSidePen;
983 divisionCopy.m_topSidePen = m_topSidePen;
984 divisionCopy.m_handleSide = m_handleSide;
985
986 // Division geometry copying is handled at the wxCompositeShape level.
0fc1a713
JS
987}
988
989#ifdef PROLOGIO
6f5f3ca0 990void wxDivisionShape::WriteAttributes(wxExpr *clause)
0fc1a713 991{
6f5f3ca0 992 wxCompositeShape::WriteAttributes(clause);
0fc1a713
JS
993
994 if (m_leftSide)
995 clause->AddAttributeValue("left_side", (long)m_leftSide->GetId());
996 if (m_topSide)
997 clause->AddAttributeValue("top_side", (long)m_topSide->GetId());
998 if (m_rightSide)
999 clause->AddAttributeValue("right_side", (long)m_rightSide->GetId());
1000 if (m_bottomSide)
1001 clause->AddAttributeValue("bottom_side", (long)m_bottomSide->GetId());
1002
1003 clause->AddAttributeValue("handle_side", (long)m_handleSide);
1004 clause->AddAttributeValueString("left_colour", m_leftSideColour);
1005 clause->AddAttributeValueString("top_colour", m_topSideColour);
1006 clause->AddAttributeValueString("left_style", m_leftSideStyle);
1007 clause->AddAttributeValueString("top_style", m_topSideStyle);
1008}
1009
6f5f3ca0 1010void wxDivisionShape::ReadAttributes(wxExpr *clause)
0fc1a713 1011{
6f5f3ca0 1012 wxCompositeShape::ReadAttributes(clause);
0fc1a713
JS
1013
1014 clause->GetAttributeValue("handle_side", m_handleSide);
1015 clause->GetAttributeValue("left_colour", m_leftSideColour);
1016 clause->GetAttributeValue("top_colour", m_topSideColour);
1017 clause->GetAttributeValue("left_style", m_leftSideStyle);
1018 clause->GetAttributeValue("top_style", m_topSideStyle);
1019}
1020#endif
1021
1022// Experimental
42cfaf8c 1023void wxDivisionShape::OnRightClick(double x, double y, int keys, int attachment)
0fc1a713
JS
1024{
1025 if (keys & KEY_CTRL)
1026 {
1027 PopupMenu(x, y);
1028 }
1029/*
1030 else if (keys & KEY_SHIFT)
1031 {
1032 if (m_leftSide || m_topSide || m_rightSide || m_bottomSide)
1033 {
1034 if (Selected())
1035 {
1036 Select(FALSE);
1037 GetParent()->Draw(dc);
1038 }
1039 else
1040 Select(TRUE);
1041 }
1042 }
1043*/
1044 else
1045 {
1046 attachment = 0;
42cfaf8c 1047 double dist;
0fc1a713
JS
1048 if (m_parent)
1049 {
1050 m_parent->HitTest(x, y, &attachment, &dist);
1051 m_parent->GetEventHandler()->OnRightClick(x, y, keys, attachment);
1052 }
1053 return;
1054 }
1055}
1056
1057
1058// Divide wxHORIZONTALly or wxVERTICALly
1059bool wxDivisionShape::Divide(int direction)
1060{
1061 // Calculate existing top-left, bottom-right
42cfaf8c
JS
1062 double x1 = (double)(GetX() - (GetWidth()/2.0));
1063 double y1 = (double)(GetY() - (GetHeight()/2.0));
0fc1a713 1064 wxCompositeShape *compositeParent = (wxCompositeShape *)GetParent();
42cfaf8c
JS
1065 double oldWidth = GetWidth();
1066 double oldHeight = GetHeight();
0fc1a713
JS
1067 if (Selected())
1068 Select(FALSE);
1069
1070 wxClientDC dc(GetCanvas());
1071 GetCanvas()->PrepareDC(dc);
1072
1073 if (direction == wxVERTICAL)
1074 {
1075 // Dividing vertically means notionally putting a horizontal line through it.
1076 // Break existing piece into two.
42cfaf8c
JS
1077 double newXPos1 = GetX();
1078 double newYPos1 = (double)(y1 + (GetHeight()/4.0));
1079 double newXPos2 = GetX();
1080 double newYPos2 = (double)(y1 + (3.0*GetHeight()/4.0));
0fc1a713
JS
1081 wxDivisionShape *newDivision = compositeParent->OnCreateDivision();
1082 newDivision->Show(TRUE);
a097c93d 1083
0fc1a713
JS
1084 Erase(dc);
1085
1086 // Anything adjoining the bottom of this division now adjoins the
1087 // bottom of the new division.
1088 wxNode *node = compositeParent->GetDivisions().First();
1089 while (node)
1090 {
1091 wxDivisionShape *obj = (wxDivisionShape *)node->Data();
1092 if (obj->GetTopSide() == this)
1093 obj->SetTopSide(newDivision);
1094 node = node->Next();
1095 }
1096 newDivision->SetTopSide(this);
1097 newDivision->SetBottomSide(m_bottomSide);
1098 newDivision->SetLeftSide(m_leftSide);
1099 newDivision->SetRightSide(m_rightSide);
1100 m_bottomSide = newDivision;
1101
1102 compositeParent->GetDivisions().Append(newDivision);
1103
1104 // CHANGE: Need to insert this division at start of divisions in the object
1105 // list, because e.g.:
1106 // 1) Add division
1107 // 2) Add contained object
1108 // 3) Add division
1109 // Division is now receiving mouse events _before_ the contained object,
1110 // because it was added last (on top of all others)
1111
a097c93d 1112 // Add after the image that visualizes the container
0fc1a713
JS
1113 compositeParent->AddChild(newDivision, compositeParent->FindContainerImage());
1114
1115 m_handleSide = DIVISION_SIDE_BOTTOM;
1116 newDivision->SetHandleSide(DIVISION_SIDE_TOP);
1117
42cfaf8c 1118 SetSize(oldWidth, (double)(oldHeight/2.0));
0fc1a713
JS
1119 Move(dc, newXPos1, newYPos1);
1120
42cfaf8c 1121 newDivision->SetSize(oldWidth, (double)(oldHeight/2.0));
0fc1a713
JS
1122 newDivision->Move(dc, newXPos2, newYPos2);
1123 }
1124 else
1125 {
1126 // Dividing horizontally means notionally putting a vertical line through it.
1127 // Break existing piece into two.
42cfaf8c
JS
1128 double newXPos1 = (double)(x1 + (GetWidth()/4.0));
1129 double newYPos1 = GetY();
1130 double newXPos2 = (double)(x1 + (3.0*GetWidth()/4.0));
1131 double newYPos2 = GetY();
0fc1a713
JS
1132 wxDivisionShape *newDivision = compositeParent->OnCreateDivision();
1133 newDivision->Show(TRUE);
a097c93d 1134
0fc1a713
JS
1135 Erase(dc);
1136
1137 // Anything adjoining the left of this division now adjoins the
1138 // left of the new division.
1139 wxNode *node = compositeParent->GetDivisions().First();
1140 while (node)
1141 {
1142 wxDivisionShape *obj = (wxDivisionShape *)node->Data();
1143 if (obj->GetLeftSide() == this)
1144 obj->SetLeftSide(newDivision);
1145 node = node->Next();
1146 }
1147 newDivision->SetTopSide(m_topSide);
1148 newDivision->SetBottomSide(m_bottomSide);
1149 newDivision->SetLeftSide(this);
1150 newDivision->SetRightSide(m_rightSide);
1151 m_rightSide = newDivision;
1152
1153 compositeParent->GetDivisions().Append(newDivision);
1154 compositeParent->AddChild(newDivision, compositeParent->FindContainerImage());
1155
1156 m_handleSide = DIVISION_SIDE_RIGHT;
1157 newDivision->SetHandleSide(DIVISION_SIDE_LEFT);
1158
42cfaf8c 1159 SetSize((double)(oldWidth/2.0), oldHeight);
0fc1a713
JS
1160 Move(dc, newXPos1, newYPos1);
1161
42cfaf8c 1162 newDivision->SetSize((double)(oldWidth/2.0), oldHeight);
0fc1a713
JS
1163 newDivision->Move(dc, newXPos2, newYPos2);
1164 }
1165 if (compositeParent->Selected())
1166 {
1167 compositeParent->DeleteControlPoints(& dc);
1168 compositeParent->MakeControlPoints();
1169 compositeParent->MakeMandatoryControlPoints();
1170 }
1171 compositeParent->Draw(dc);
1172 return TRUE;
1173}
1174
1175// Make one control point for every visible line
1176void wxDivisionShape::MakeControlPoints()
1177{
1178 MakeMandatoryControlPoints();
1179}
1180
1181void wxDivisionShape::MakeMandatoryControlPoints()
1182{
42cfaf8c 1183 double maxX, maxY;
0fc1a713
JS
1184
1185 GetBoundingBoxMax(&maxX, &maxY);
42cfaf8c 1186 double x, y;
0fc1a713
JS
1187 int direction;
1188/*
1189 if (m_leftSide)
1190 {
42cfaf8c 1191 x = (double)(-maxX/2.0);
0fc1a713
JS
1192 y = 0.0;
1193 wxDivisionControlPoint *control = new wxDivisionControlPoint(m_canvas, this, CONTROL_POINT_SIZE, x, y,
1194 CONTROL_POINT_HORIZONTAL);
1195 m_canvas->AddShape(control);
1196 m_controlPoints.Append(control);
1197 }
1198 if (m_topSide)
1199 {
1200 x = 0.0;
42cfaf8c 1201 y = (double)(-maxY/2.0);
0fc1a713
JS
1202 wxDivisionControlPoint *control = new wxDivisionControlPoint(m_canvas, this, CONTROL_POINT_SIZE, x, y,
1203 CONTROL_POINT_VERTICAL);
1204 m_canvas->AddShape(control);
1205 m_controlPoints.Append(control);
1206 }
1207*/
1208 switch (m_handleSide)
1209 {
1210 case DIVISION_SIDE_LEFT:
1211 {
42cfaf8c 1212 x = (double)(-maxX/2.0);
0fc1a713
JS
1213 y = 0.0;
1214 direction = CONTROL_POINT_HORIZONTAL;
1215 break;
1216 }
1217 case DIVISION_SIDE_TOP:
1218 {
1219 x = 0.0;
42cfaf8c 1220 y = (double)(-maxY/2.0);
0fc1a713
JS
1221 direction = CONTROL_POINT_VERTICAL;
1222 break;
1223 }
1224 case DIVISION_SIDE_RIGHT:
1225 {
42cfaf8c 1226 x = (double)(maxX/2.0);
0fc1a713
JS
1227 y = 0.0;
1228 direction = CONTROL_POINT_HORIZONTAL;
1229 break;
1230 }
1231 case DIVISION_SIDE_BOTTOM:
1232 {
1233 x = 0.0;
42cfaf8c 1234 y = (double)(maxY/2.0);
0fc1a713
JS
1235 direction = CONTROL_POINT_VERTICAL;
1236 break;
1237 }
1238 default:
1239 break;
1240 }
1241 if (m_handleSide != DIVISION_SIDE_NONE)
1242 {
1243 wxDivisionControlPoint *control = new wxDivisionControlPoint(m_canvas, this, CONTROL_POINT_SIZE, x, y,
1244 direction);
1245 m_canvas->AddShape(control);
1246 m_controlPoints.Append(control);
1247 }
1248}
1249
1250void wxDivisionShape::ResetControlPoints()
1251{
1252 ResetMandatoryControlPoints();
1253}
1254
1255void wxDivisionShape::ResetMandatoryControlPoints()
1256{
1257 if (m_controlPoints.Number() < 1)
1258 return;
1259
42cfaf8c 1260 double maxX, maxY;
0fc1a713
JS
1261
1262 GetBoundingBoxMax(&maxX, &maxY);
1263/*
1264 wxNode *node = m_controlPoints.First();
1265 while (node)
1266 {
1267 wxDivisionControlPoint *control = (wxDivisionControlPoint *)node->Data();
1268 if (control->type == CONTROL_POINT_HORIZONTAL)
1269 {
42cfaf8c 1270 control->xoffset = (double)(-maxX/2.0); control->m_yoffset = 0.0;
0fc1a713
JS
1271 }
1272 else if (control->type == CONTROL_POINT_VERTICAL)
1273 {
42cfaf8c 1274 control->xoffset = 0.0; control->m_yoffset = (double)(-maxY/2.0);
0fc1a713
JS
1275 }
1276 node = node->Next();
1277 }
1278*/
1279 wxNode *node = m_controlPoints.First();
1280 if ((m_handleSide == DIVISION_SIDE_LEFT) && node)
1281 {
1282 wxDivisionControlPoint *control = (wxDivisionControlPoint *)node->Data();
42cfaf8c 1283 control->m_xoffset = (double)(-maxX/2.0); control->m_yoffset = 0.0;
0fc1a713
JS
1284 }
1285
1286 if ((m_handleSide == DIVISION_SIDE_TOP) && node)
1287 {
1288 wxDivisionControlPoint *control = (wxDivisionControlPoint *)node->Data();
42cfaf8c 1289 control->m_xoffset = 0.0; control->m_yoffset = (double)(-maxY/2.0);
0fc1a713
JS
1290 }
1291
1292 if ((m_handleSide == DIVISION_SIDE_RIGHT) && node)
1293 {
1294 wxDivisionControlPoint *control = (wxDivisionControlPoint *)node->Data();
42cfaf8c 1295 control->m_xoffset = (double)(maxX/2.0); control->m_yoffset = 0.0;
0fc1a713
JS
1296 }
1297
1298 if ((m_handleSide == DIVISION_SIDE_BOTTOM) && node)
1299 {
1300 wxDivisionControlPoint *control = (wxDivisionControlPoint *)node->Data();
42cfaf8c 1301 control->m_xoffset = 0.0; control->m_yoffset = (double)(maxY/2.0);
0fc1a713
JS
1302 }
1303}
1304
1305// Adjust a side, returning FALSE if it's not physically possible.
42cfaf8c 1306bool wxDivisionShape::AdjustLeft(double left, bool test)
0fc1a713 1307{
42cfaf8c 1308 double x2 = (double)(GetX() + (GetWidth()/2.0));
0fc1a713
JS
1309
1310 if (left >= x2)
1311 return FALSE;
1312 if (test)
1313 return TRUE;
1314
42cfaf8c
JS
1315 double newW = x2 - left;
1316 double newX = (double)(left + newW/2.0);
0fc1a713
JS
1317 SetSize(newW, GetHeight());
1318
1319 wxClientDC dc(GetCanvas());
1320 GetCanvas()->PrepareDC(dc);
1321
1322 Move(dc, newX, GetY());
a097c93d 1323
0fc1a713
JS
1324 return TRUE;
1325}
1326
42cfaf8c 1327bool wxDivisionShape::AdjustTop(double top, bool test)
0fc1a713 1328{
42cfaf8c 1329 double y2 = (double)(GetY() + (GetHeight()/2.0));
0fc1a713
JS
1330
1331 if (top >= y2)
1332 return FALSE;
1333 if (test)
1334 return TRUE;
1335
42cfaf8c
JS
1336 double newH = y2 - top;
1337 double newY = (double)(top + newH/2.0);
0fc1a713
JS
1338 SetSize(GetWidth(), newH);
1339
1340 wxClientDC dc(GetCanvas());
1341 GetCanvas()->PrepareDC(dc);
1342
1343 Move(dc, GetX(), newY);
a097c93d 1344
0fc1a713
JS
1345 return TRUE;
1346}
1347
42cfaf8c 1348bool wxDivisionShape::AdjustRight(double right, bool test)
0fc1a713 1349{
42cfaf8c 1350 double x1 = (double)(GetX() - (GetWidth()/2.0));
0fc1a713
JS
1351
1352 if (right <= x1)
1353 return FALSE;
1354 if (test)
1355 return TRUE;
1356
42cfaf8c
JS
1357 double newW = right - x1;
1358 double newX = (double)(x1 + newW/2.0);
0fc1a713
JS
1359 SetSize(newW, GetHeight());
1360
1361 wxClientDC dc(GetCanvas());
1362 GetCanvas()->PrepareDC(dc);
1363
1364 Move(dc, newX, GetY());
a097c93d 1365
0fc1a713
JS
1366 return TRUE;
1367}
1368
42cfaf8c 1369bool wxDivisionShape::AdjustBottom(double bottom, bool test)
0fc1a713 1370{
42cfaf8c 1371 double y1 = (double)(GetY() - (GetHeight()/2.0));
0fc1a713
JS
1372
1373 if (bottom <= y1)
1374 return FALSE;
1375 if (test)
1376 return TRUE;
1377
42cfaf8c
JS
1378 double newH = bottom - y1;
1379 double newY = (double)(y1 + newH/2.0);
0fc1a713
JS
1380 SetSize(GetWidth(), newH);
1381
1382 wxClientDC dc(GetCanvas());
1383 GetCanvas()->PrepareDC(dc);
1384
1385 Move(dc, GetX(), newY);
a097c93d 1386
0fc1a713
JS
1387 return TRUE;
1388}
1389
42cfaf8c 1390wxDivisionControlPoint::wxDivisionControlPoint(wxShapeCanvas *the_canvas, wxShape *object, double size, double the_xoffset, double the_yoffset, int the_type):
0fc1a713
JS
1391 wxControlPoint(the_canvas, object, size, the_xoffset, the_yoffset, the_type)
1392{
1393 SetEraseObject(FALSE);
1394}
1395
1396wxDivisionControlPoint::~wxDivisionControlPoint()
1397{
1398}
1399
42cfaf8c
JS
1400static double originalX = 0.0;
1401static double originalY = 0.0;
1402static double originalW = 0.0;
1403static double originalH = 0.0;
0fc1a713
JS
1404
1405// Implement resizing of canvas object
42cfaf8c 1406void wxDivisionControlPoint::OnDragLeft(bool draw, double x, double y, int keys, int attachment)
0fc1a713
JS
1407{
1408 wxControlPoint::OnDragLeft(draw, x, y, keys, attachment);
1409}
1410
42cfaf8c 1411void wxDivisionControlPoint::OnBeginDragLeft(double x, double y, int keys, int attachment)
0fc1a713
JS
1412{
1413 wxDivisionShape *division = (wxDivisionShape *)m_shape;
1414 originalX = division->GetX();
1415 originalY = division->GetY();
1416 originalW = division->GetWidth();
1417 originalH = division->GetHeight();
a097c93d 1418
0fc1a713
JS
1419 wxControlPoint::OnBeginDragLeft(x, y, keys, attachment);
1420}
1421
42cfaf8c 1422void wxDivisionControlPoint::OnEndDragLeft(double x, double y, int keys, int attachment)
0fc1a713
JS
1423{
1424 wxControlPoint::OnEndDragLeft(x, y, keys, attachment);
1425
1426 wxClientDC dc(GetCanvas());
1427 GetCanvas()->PrepareDC(dc);
1428
1429 wxDivisionShape *division = (wxDivisionShape *)m_shape;
1430 wxCompositeShape *divisionParent = (wxCompositeShape *)division->GetParent();
1431
1432 // Need to check it's within the bounds of the parent composite.
42cfaf8c
JS
1433 double x1 = (double)(divisionParent->GetX() - (divisionParent->GetWidth()/2.0));
1434 double y1 = (double)(divisionParent->GetY() - (divisionParent->GetHeight()/2.0));
1435 double x2 = (double)(divisionParent->GetX() + (divisionParent->GetWidth()/2.0));
1436 double y2 = (double)(divisionParent->GetY() + (divisionParent->GetHeight()/2.0));
0fc1a713
JS
1437
1438 // Need to check it has not made the division zero or negative width/height
42cfaf8c
JS
1439 double dx1 = (double)(division->GetX() - (division->GetWidth()/2.0));
1440 double dy1 = (double)(division->GetY() - (division->GetHeight()/2.0));
1441 double dx2 = (double)(division->GetX() + (division->GetWidth()/2.0));
1442 double dy2 = (double)(division->GetY() + (division->GetHeight()/2.0));
0fc1a713
JS
1443
1444 bool success = TRUE;
1445 switch (division->GetHandleSide())
1446 {
1447 case DIVISION_SIDE_LEFT:
1448 {
1449 if ((x <= x1) || (x >= x2) || (x >= dx2))
1450 success = FALSE;
1451 // Try it out first...
1452 else if (!division->ResizeAdjoining(DIVISION_SIDE_LEFT, x, TRUE))
1453 success = FALSE;
1454 else
1455 division->ResizeAdjoining(DIVISION_SIDE_LEFT, x, FALSE);
a097c93d 1456
0fc1a713
JS
1457 break;
1458 }
1459 case DIVISION_SIDE_TOP:
1460 {
1461 if ((y <= y1) || (y >= y2) || (y >= dy2))
1462 success = FALSE;
1463 else if (!division->ResizeAdjoining(DIVISION_SIDE_TOP, y, TRUE))
1464 success = FALSE;
1465 else
1466 division->ResizeAdjoining(DIVISION_SIDE_TOP, y, FALSE);
a097c93d 1467
0fc1a713
JS
1468 break;
1469 }
1470 case DIVISION_SIDE_RIGHT:
1471 {
1472 if ((x <= x1) || (x >= x2) || (x <= dx1))
1473 success = FALSE;
1474 else if (!division->ResizeAdjoining(DIVISION_SIDE_RIGHT, x, TRUE))
1475 success = FALSE;
1476 else
1477 division->ResizeAdjoining(DIVISION_SIDE_RIGHT, x, FALSE);
a097c93d 1478
0fc1a713
JS
1479 break;
1480 }
1481 case DIVISION_SIDE_BOTTOM:
1482 {
1483 if ((y <= y1) || (y >= y2) || (y <= dy1))
1484 success = FALSE;
1485 else if (!division->ResizeAdjoining(DIVISION_SIDE_BOTTOM, y, TRUE))
1486 success = FALSE;
1487 else
1488 division->ResizeAdjoining(DIVISION_SIDE_BOTTOM, y, FALSE);
a097c93d 1489
0fc1a713
JS
1490 break;
1491 }
1492 }
1493 if (!success)
1494 {
1495 division->SetSize(originalW, originalH);
1496 division->Move(dc, originalX, originalY);
1497 }
1498 divisionParent->Draw(dc);
1499 division->GetEventHandler()->OnDrawControlPoints(dc);
1500}
1501
1502/* Resize adjoining divisions.
1503 *
1504 Behaviour should be as follows:
1505 If right edge moves, find all objects whose left edge
1506 adjoins this object, and move left edge accordingly.
1507 If left..., move ... right.
1508 If top..., move ... bottom.
1509 If bottom..., move top.
1510 If size goes to zero or end position is other side of start position,
1511 resize to original size and return.
1512 */
42cfaf8c 1513bool wxDivisionShape::ResizeAdjoining(int side, double newPos, bool test)
0fc1a713
JS
1514{
1515 wxCompositeShape *divisionParent = (wxCompositeShape *)GetParent();
1516 wxNode *node = divisionParent->GetDivisions().First();
1517 while (node)
1518 {
1519 wxDivisionShape *division = (wxDivisionShape *)node->Data();
1520 switch (side)
1521 {
1522 case DIVISION_SIDE_LEFT:
1523 {
1524 if (division->m_rightSide == this)
1525 {
1526 bool success = division->AdjustRight(newPos, test);
1527 if (!success && test)
1528 return FALSE;
1529 }
1530 break;
1531 }
1532 case DIVISION_SIDE_TOP:
1533 {
1534 if (division->m_bottomSide == this)
1535 {
1536 bool success = division->AdjustBottom(newPos, test);
1537 if (!success && test)
1538 return FALSE;
1539 }
1540 break;
1541 }
1542 case DIVISION_SIDE_RIGHT:
1543 {
1544 if (division->m_leftSide == this)
1545 {
1546 bool success = division->AdjustLeft(newPos, test);
1547 if (!success && test)
1548 return FALSE;
1549 }
1550 break;
1551 }
1552 case DIVISION_SIDE_BOTTOM:
1553 {
1554 if (division->m_topSide == this)
1555 {
1556 bool success = division->AdjustTop(newPos, test);
1557 if (!success && test)
1558 return FALSE;
1559 }
1560 break;
1561 }
1562 default:
1563 break;
1564 }
1565 node = node->Next();
1566 }
a097c93d 1567
0fc1a713
JS
1568 return TRUE;
1569}
1570
1571/*
1572 * Popup menu for editing divisions
1573 *
1574 */
a097c93d 1575
0fc1a713
JS
1576void oglGraphicsDivisionMenuProc(wxMenu& menu, wxCommandEvent& event)
1577{
1578 wxDivisionShape *division = (wxDivisionShape *)menu.GetClientData();
1579 switch (event.GetInt())
1580 {
1581 case DIVISION_MENU_SPLIT_HORIZONTALLY:
1582 {
1583 division->Divide(wxHORIZONTAL);
1584 break;
1585 }
1586 case DIVISION_MENU_SPLIT_VERTICALLY:
1587 {
1588 division->Divide(wxVERTICAL);
1589 break;
1590 }
1591 case DIVISION_MENU_EDIT_LEFT_EDGE:
1592 {
1593 division->EditEdge(DIVISION_SIDE_LEFT);
1594 break;
1595 }
1596 case DIVISION_MENU_EDIT_TOP_EDGE:
1597 {
1598 division->EditEdge(DIVISION_SIDE_TOP);
1599 break;
1600 }
1601 default:
1602 break;
1603 }
1604}
1605
1606void wxDivisionShape::EditEdge(int side)
1607{
1608 wxMessageBox("EditEdge() not implemented", "OGL", wxOK);
1609
1610#if 0
1611 wxBeginBusyCursor();
1612
1613 wxPen *currentPen = NULL;
1614 char **pColour = NULL;
1615 char **pStyle = NULL;
1616 if (side == DIVISION_SIDE_LEFT)
1617 {
1618 currentPen = m_leftSidePen;
1619 pColour = &m_leftSideColour;
1620 pStyle = &m_leftSideStyle;
1621 }
1622 else
1623 {
1624 currentPen = m_topSidePen;
1625 pColour = &m_topSideColour;
1626 pStyle = &m_topSideStyle;
1627 }
1628
1629 GraphicsForm *form = new GraphicsForm("Containers");
1630 int lineWidth = currentPen->GetWidth();
a097c93d 1631
0fc1a713
JS
1632 form->Add(wxMakeFormShort("Width", &lineWidth, wxFORM_DEFAULT, NULL, NULL, wxVERTICAL,
1633 150));
1634 form->Add(wxMakeFormString("Colour", pColour, wxFORM_CHOICE,
1635 new wxList(wxMakeConstraintStrings(
1636 "BLACK" ,
1637 "BLUE" ,
1638 "BROWN" ,
1639 "CORAL" ,
1640 "CYAN" ,
1641 "DARK GREY" ,
1642 "DARK GREEN" ,
1643 "DIM GREY" ,
1644 "GREY" ,
1645 "GREEN" ,
1646 "LIGHT BLUE" ,
1647 "LIGHT GREY" ,
1648 "MAGENTA" ,
1649 "MAROON" ,
1650 "NAVY" ,
1651 "ORANGE" ,
1652 "PURPLE" ,
1653 "RED" ,
1654 "TURQUOISE" ,
1655 "VIOLET" ,
1656 "WHITE" ,
1657 "YELLOW" ,
1658 NULL),
1659 NULL), NULL, wxVERTICAL, 150));
1660 form->Add(wxMakeFormString("Style", pStyle, wxFORM_CHOICE,
1661 new wxList(wxMakeConstraintStrings(
1662 "Solid" ,
1663 "Short Dash" ,
1664 "Long Dash" ,
1665 "Dot" ,
1666 "Dot Dash" ,
1667 NULL),
1668 NULL), NULL, wxVERTICAL, 100));
1669
1670 wxDialogBox *dialog = new wxDialogBox(m_canvas->GetParent(), "Division properties", 10, 10, 500, 500);
1671 if (GraphicsLabelFont)
1672 dialog->SetLabelFont(GraphicsLabelFont);
1673 if (GraphicsButtonFont)
1674 dialog->SetButtonFont(GraphicsButtonFont);
1675
1676 form->AssociatePanel(dialog);
1677 form->dialog = dialog;
1678
1679 dialog->Fit();
1680 dialog->Centre(wxBOTH);
1681
1682 wxEndBusyCursor();
1683 dialog->Show(TRUE);
1684
1685 int lineStyle = wxSOLID;
1686 if (*pStyle)
1687 {
1688 if (strcmp(*pStyle, "Solid") == 0)
1689 lineStyle = wxSOLID;
1690 else if (strcmp(*pStyle, "Dot") == 0)
1691 lineStyle = wxDOT;
1692 else if (strcmp(*pStyle, "Short Dash") == 0)
1693 lineStyle = wxSHORT_DASH;
1694 else if (strcmp(*pStyle, "Long Dash") == 0)
1695 lineStyle = wxLONG_DASH;
1696 else if (strcmp(*pStyle, "Dot Dash") == 0)
1697 lineStyle = wxDOT_DASH;
1698 }
a097c93d 1699
0fc1a713
JS
1700 wxPen *newPen = wxThePenList->FindOrCreatePen(*pColour, lineWidth, lineStyle);
1701 if (!pen)
1702 pen = wxBLACK_PEN;
1703 if (side == DIVISION_SIDE_LEFT)
1704 m_leftSidePen = newPen;
1705 else
1706 m_topSidePen = newPen;
1707
1708 // Need to draw whole image again
1709 wxCompositeShape *compositeParent = (wxCompositeShape *)GetParent();
1710 compositeParent->Draw(dc);
1711#endif
1712}
1713
1714// Popup menu
42cfaf8c 1715void wxDivisionShape::PopupMenu(double x, double y)
0fc1a713 1716{
3dd4e4e0 1717 oglPopupDivisionMenu->SetClientData((void *)this);
0fc1a713
JS
1718 if (m_leftSide)
1719 oglPopupDivisionMenu->Enable(DIVISION_MENU_EDIT_LEFT_EDGE, TRUE);
1720 else
1721 oglPopupDivisionMenu->Enable(DIVISION_MENU_EDIT_LEFT_EDGE, FALSE);
1722 if (m_topSide)
1723 oglPopupDivisionMenu->Enable(DIVISION_MENU_EDIT_TOP_EDGE, TRUE);
1724 else
1725 oglPopupDivisionMenu->Enable(DIVISION_MENU_EDIT_TOP_EDGE, FALSE);
a097c93d 1726
0fc1a713
JS
1727 int x1, y1;
1728 m_canvas->ViewStart(&x1, &y1);
1729
1730 int unit_x, unit_y;
1731 m_canvas->GetScrollPixelsPerUnit(&unit_x, &unit_y);
1732
1733 wxClientDC dc(GetCanvas());
1734 GetCanvas()->PrepareDC(dc);
1735
ea57084d
JS
1736 int mouse_x = (int)(dc.LogicalToDeviceX((long)(x - x1*unit_x)));
1737 int mouse_y = (int)(dc.LogicalToDeviceY((long)(y - y1*unit_y)));
0fc1a713
JS
1738
1739 m_canvas->PopupMenu(oglPopupDivisionMenu, mouse_x, mouse_y);
1740}
1741
1742void wxDivisionShape::SetLeftSideColour(const wxString& colour)
1743{
1744 m_leftSideColour = colour;
1745}
1746
1747void wxDivisionShape::SetTopSideColour(const wxString& colour)
1748{
1749 m_topSideColour = colour;
1750}
1751
1752void wxDivisionShape::SetLeftSideStyle(const wxString& style)
1753{
1754 m_leftSideStyle = style;
1755}
1756
1757void wxDivisionShape::SetTopSideStyle(const wxString& style)
1758{
1759 m_topSideStyle = style;
1760}
1761