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