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