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