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