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