]> git.saurik.com Git - wxWidgets.git/blob - utils/ogl/src/divided.cpp
Added revamped Object Graphics Library (for node/arc diagrams).
[wxWidgets.git] / utils / ogl / src / divided.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: divided.cpp
3 // Purpose: wxDividedShape 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 "divided.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 #ifdef PROLOGIO
28 #include <wx/wxexpr.h>
29 #endif
30
31 #include "basic.h"
32 #include "basicp.h"
33 #include "canvas.h"
34 #include "divided.h"
35 #include "lines.h"
36 #include "misc.h"
37
38 class wxDividedShapeControlPoint: public wxControlPoint
39 {
40 DECLARE_DYNAMIC_CLASS(wxDividedShapeControlPoint)
41 private:
42 int regionId;
43 public:
44 wxDividedShapeControlPoint() { regionId = 0; }
45 wxDividedShapeControlPoint(wxShapeCanvas *the_canvas, wxShape *object, int region,
46 float size, float the_xoffset, float the_yoffset, int the_type);
47 ~wxDividedShapeControlPoint();
48
49 void OnDragLeft(bool draw, float x, float y, int keys=0, int attachment = 0);
50 void OnBeginDragLeft(float x, float y, int keys=0, int attachment = 0);
51 void OnEndDragLeft(float x, float y, int keys=0, int attachment = 0);
52 };
53
54 IMPLEMENT_DYNAMIC_CLASS(wxDividedShapeControlPoint, wxControlPoint)
55
56 /*
57 * Divided object
58 *
59 */
60
61 IMPLEMENT_DYNAMIC_CLASS(wxDividedShape, wxRectangleShape)
62
63 wxDividedShape::wxDividedShape(float w, float h): wxRectangleShape(w, h)
64 {
65 ClearRegions();
66 }
67
68 wxDividedShape::~wxDividedShape()
69 {
70 }
71
72 void wxDividedShape::OnDraw(wxDC& dc)
73 {
74 wxRectangleShape::OnDraw(dc);
75 }
76
77 void wxDividedShape::OnDrawContents(wxDC& dc)
78 {
79 float defaultProportion = (float)(GetRegions().Number() > 0 ? (1.0/((float)(GetRegions().Number()))) : 0.0);
80 float currentY = (float)(m_ypos - (m_height / 2.0));
81 float maxY = (float)(m_ypos + (m_height / 2.0));
82
83 float leftX = (float)(m_xpos - (m_width / 2.0));
84 float rightX = (float)(m_xpos + (m_width / 2.0));
85
86 if (m_pen) dc.SetPen(m_pen);
87
88 if (m_textColour) dc.SetTextForeground(* m_textColour);
89
90 #ifdef __WXMSW__
91 // For efficiency, don't do this under X - doesn't make
92 // any visible difference for our purposes.
93 if (m_brush)
94 dc.SetTextBackground(m_brush->GetColour());
95 #endif
96 /*
97 if (!formatted)
98 {
99 FormatRegionText();
100 formatted = TRUE;
101 }
102 */
103 if (GetDisableLabel()) return;
104
105 float xMargin = 2;
106 float yMargin = 2;
107 dc.SetBackgroundMode(wxTRANSPARENT);
108
109 wxNode *node = GetRegions().First();
110 while (node)
111 {
112 wxShapeRegion *region = (wxShapeRegion *)node->Data();
113 dc.SetFont(region->GetFont());
114 dc.SetTextForeground(* region->GetActualColourObject());
115
116 float proportion =
117 region->m_regionProportionY < 0.0 ? defaultProportion : region->m_regionProportionY;
118
119 float y = currentY + m_height*proportion;
120 float actualY = maxY < y ? maxY : y;
121
122 float centreX = m_xpos;
123 float centreY = (float)(currentY + (actualY - currentY)/2.0);
124
125 DrawFormattedText(dc, &region->m_formattedText,
126 (float)(centreX), (float)(centreY), (float)(m_width-2*xMargin), (float)(actualY - currentY - 2*yMargin),
127 region->m_formatMode);
128 if ((y <= maxY) && (node->Next()))
129 {
130 wxPen *regionPen = region->GetActualPen();
131 if (regionPen)
132 {
133 dc.SetPen(regionPen);
134 dc.DrawLine(leftX, y, rightX, y);
135 }
136 }
137
138 currentY = actualY;
139
140 node = node->Next();
141 }
142 }
143
144 void wxDividedShape::SetSize(float w, float h, bool recursive)
145 {
146 SetAttachmentSize(w, h);
147 m_width = w;
148 m_height = h;
149 SetRegionSizes();
150 }
151
152 void wxDividedShape::SetRegionSizes()
153 {
154 if (GetRegions().Number() == 0)
155 return;
156
157 float defaultProportion = (float)(GetRegions().Number() > 0 ? (1.0/((float)(GetRegions().Number()))) : 0.0);
158 float currentY = (float)(m_ypos - (m_height / 2.0));
159 float maxY = (float)(m_ypos + (m_height / 2.0));
160
161 // float leftX = (float)(m_xpos - (m_width / 2.0));
162 // float rightX = (float)(m_xpos + (m_width / 2.0));
163
164 wxNode *node = GetRegions().First();
165 while (node)
166 {
167 wxShapeRegion *region = (wxShapeRegion *)node->Data();
168 float proportion =
169 region->m_regionProportionY <= 0.0 ? defaultProportion : region->m_regionProportionY;
170
171 float sizeY = (float)proportion*m_height;
172 float y = currentY + sizeY;
173 float actualY = maxY < y ? maxY : y;
174
175 float centreY = (float)(currentY + (actualY - currentY)/2.0);
176
177 region->SetSize(m_width, sizeY);
178 region->SetPosition(0.0, (float)(centreY - m_ypos));
179 currentY = actualY;
180 node = node->Next();
181 }
182 }
183
184 // Attachment points correspond to regions in the divided box
185 bool wxDividedShape::GetAttachmentPosition(int attachment, float *x, float *y, int nth, int no_arcs,
186 wxLineShape *line)
187 {
188 int totalNumberAttachments = (GetRegions().Number() * 2) + 2;
189 if (!GetAttachmentMode() || (attachment >= totalNumberAttachments))
190 {
191 return wxShape::GetAttachmentPosition(attachment, x, y, nth, no_arcs);
192 }
193
194 int n = GetRegions().Number();
195 bool isEnd = (line && line->IsEnd(this));
196
197 float left = (float)(m_xpos - m_width/2.0);
198 float right = (float)(m_xpos + m_width/2.0);
199 float top = (float)(m_ypos - m_height/2.0);
200 float bottom = (float)(m_ypos + m_height/2.0);
201
202 // Zero is top, n+1 is bottom.
203 if (attachment == 0)
204 {
205 *y = top;
206 if (m_spaceAttachments)
207 {
208 if (line && (line->GetAlignmentType(isEnd) == LINE_ALIGNMENT_TO_NEXT_HANDLE))
209 {
210 // Align line according to the next handle along
211 wxRealPoint *point = line->GetNextControlPoint(this);
212 if (point->x < left)
213 *x = left;
214 else if (point->x > right)
215 *x = right;
216 else
217 *x = point->x;
218 }
219 else
220 *x = left + (nth + 1)*m_width/(no_arcs + 1);
221 }
222 else
223 *x = m_xpos;
224 }
225 else if (attachment == (n+1))
226 {
227 *y = bottom;
228 if (m_spaceAttachments)
229 {
230 if (line && (line->GetAlignmentType(isEnd) == LINE_ALIGNMENT_TO_NEXT_HANDLE))
231 {
232 // Align line according to the next handle along
233 wxRealPoint *point = line->GetNextControlPoint(this);
234 if (point->x < left)
235 *x = left;
236 else if (point->x > right)
237 *x = right;
238 else
239 *x = point->x;
240 }
241 else
242 *x = left + (nth + 1)*m_width/(no_arcs + 1);
243 }
244 else
245 *x = m_xpos;
246 }
247 // Left or right.
248 else
249 {
250 int i = 0;
251 bool isLeft = FALSE;
252 if (attachment < (n+1))
253 {
254 i = attachment-1;
255 isLeft = FALSE;
256 }
257 else
258 {
259 i = (totalNumberAttachments - attachment - 1);
260 isLeft = TRUE;
261 }
262 wxNode *node = GetRegions().Nth(i);
263 if (node)
264 {
265 wxShapeRegion *region = (wxShapeRegion *)node->Data();
266
267 if (isLeft)
268 *x = left;
269 else
270 *x = right;
271
272 // Calculate top and bottom of region
273 top = (float)((m_ypos + region->m_y) - (region->m_height/2.0));
274 bottom = (float)((m_ypos + region->m_y) + (region->m_height/2.0));
275
276 // Assuming we can trust the absolute size and
277 // position of these regions...
278 if (m_spaceAttachments)
279 {
280 if (line && (line->GetAlignmentType(isEnd) == LINE_ALIGNMENT_TO_NEXT_HANDLE))
281 {
282 // Align line according to the next handle along
283 wxRealPoint *point = line->GetNextControlPoint(this);
284 if (point->y < bottom)
285 *y = bottom;
286 else if (point->y > top)
287 *y = top;
288 else
289 *y = point->y;
290 }
291 else
292 // *y = (float)(((m_ypos + region->m_y) - (region->m_height/2.0)) + (nth + 1)*region->m_height/(no_arcs+1));
293 *y = (float)(top + (nth + 1)*region->m_height/(no_arcs+1));
294 }
295 else
296 *y = (float)(m_ypos + region->m_y);
297 }
298 else
299 {
300 *x = m_xpos;
301 *y = m_ypos;
302 return FALSE;
303 }
304 }
305 return TRUE;
306 }
307
308 int wxDividedShape::GetNumberOfAttachments()
309 {
310 // There are two attachments for each region (left and right),
311 // plus one on the top and one on the bottom.
312 int n = (GetRegions().Number() * 2) + 2;
313
314 int maxN = n - 1;
315 wxNode *node = m_attachmentPoints.First();
316 while (node)
317 {
318 wxAttachmentPoint *point = (wxAttachmentPoint *)node->Data();
319 if (point->m_id > maxN)
320 maxN = point->m_id;
321 node = node->Next();
322 }
323 return maxN + 1;
324 }
325
326 bool wxDividedShape::AttachmentIsValid(int attachment)
327 {
328 int totalNumberAttachments = (GetRegions().Number() * 2) + 2;
329 if (attachment >= totalNumberAttachments)
330 {
331 return wxShape::AttachmentIsValid(attachment);
332 }
333 else if (attachment >= 0)
334 return TRUE;
335 else
336 return FALSE;
337 }
338
339 void wxDividedShape::Copy(wxDividedShape& copy)
340 {
341 wxRectangleShape::Copy(copy);
342 }
343
344 wxShape *wxDividedShape::PrivateCopy()
345 {
346 wxDividedShape *obj = new wxDividedShape(m_width, m_height);
347 Copy(*obj);
348 return obj;
349 }
350
351 // Region operations
352
353 void wxDividedShape::MakeControlPoints()
354 {
355 wxRectangleShape::MakeControlPoints();
356
357 MakeMandatoryControlPoints();
358 }
359
360 void wxDividedShape::MakeMandatoryControlPoints()
361 {
362 float currentY = (float)(GetY() - (m_height / 2.0));
363 float maxY = (float)(GetY() + (m_height / 2.0));
364
365 wxNode *node = GetRegions().First();
366 int i = 0;
367 while (node)
368 {
369 wxShapeRegion *region = (wxShapeRegion *)node->Data();
370
371 float proportion = region->m_regionProportionY;
372
373 float y = currentY + m_height*proportion;
374 float actualY = (float)(maxY < y ? maxY : y);
375
376 if (node->Next())
377 {
378 wxDividedShapeControlPoint *controlPoint =
379 new wxDividedShapeControlPoint(m_canvas, this, i, CONTROL_POINT_SIZE, 0.0, (float)(actualY - GetY()), 0);
380 m_canvas->AddShape(controlPoint);
381 m_controlPoints.Append(controlPoint);
382 }
383 currentY = actualY;
384 i ++;
385 node = node->Next();
386 }
387 }
388
389 void wxDividedShape::ResetControlPoints()
390 {
391 // May only have the region handles, (n - 1) of them.
392 if (m_controlPoints.Number() > (GetRegions().Number() - 1))
393 wxRectangleShape::ResetControlPoints();
394
395 ResetMandatoryControlPoints();
396 }
397
398 void wxDividedShape::ResetMandatoryControlPoints()
399 {
400 float currentY = (float)(GetY() - (m_height / 2.0));
401 float maxY = (float)(GetY() + (m_height / 2.0));
402
403 wxNode *node = m_controlPoints.First();
404 int i = 0;
405 while (node)
406 {
407 wxControlPoint *controlPoint = (wxControlPoint *)node->Data();
408 if (controlPoint->IsKindOf(CLASSINFO(wxDividedShapeControlPoint)))
409 {
410 wxNode *node1 = GetRegions().Nth(i);
411 wxShapeRegion *region = (wxShapeRegion *)node1->Data();
412
413 float proportion = region->m_regionProportionY;
414
415 float y = currentY + m_height*proportion;
416 float actualY = (float)(maxY < y ? maxY : y);
417
418 controlPoint->m_xoffset = 0.0;
419 controlPoint->m_yoffset = (float)(actualY - GetY());
420 currentY = actualY;
421 i ++;
422 }
423 node = node->Next();
424 }
425 }
426
427 #ifdef PROLOGIO
428 void wxDividedShape::WritePrologAttributes(wxExpr *clause)
429 {
430 wxRectangleShape::WritePrologAttributes(clause);
431 }
432
433 void wxDividedShape::ReadPrologAttributes(wxExpr *clause)
434 {
435 wxRectangleShape::ReadPrologAttributes(clause);
436 }
437 #endif
438
439 /*
440 * Edit the division colour/style
441 *
442 */
443
444 void wxDividedShape::EditRegions()
445 {
446 wxMessageBox("EditRegions() is unimplemented.", "OGL", wxOK);
447
448 // TODO
449 #if 0
450 if (GetRegions().Number() < 2)
451 return;
452
453 wxBeginBusyCursor();
454
455 GraphicsForm *form = new GraphicsForm("Divided nodes");
456 // Need an array to store all the style strings,
457 // since they need to be converted to integers
458 char **styleStrings = new char *[GetRegions().Number()];
459 for (int j = 0; j < GetRegions().Number(); j++)
460 styleStrings[j] = NULL;
461
462 int i = 0;
463 wxNode *node = GetRegions().First();
464 while (node && node->Next())
465 {
466 wxShapeRegion *region = (wxShapeRegion *)node->Data();
467 char buf[50];
468 sprintf(buf, "Region %d", (i+1));
469 form->Add(wxMakeFormMessage(buf));
470 form->Add(wxMakeFormNewLine());
471
472 form->Add(wxMakeFormString("Colour", &region->penColour, wxFORM_CHOICE,
473 new wxList(wxMakeConstraintStrings(
474 "Invisible" ,
475 "BLACK" ,
476 "BLUE" ,
477 "BROWN" ,
478 "CORAL" ,
479 "CYAN" ,
480 "DARK GREY" ,
481 "DARK GREEN" ,
482 "DIM GREY" ,
483 "GREY" ,
484 "GREEN" ,
485 "LIGHT BLUE" ,
486 "LIGHT GREY" ,
487 "MAGENTA" ,
488 "MAROON" ,
489 "NAVY" ,
490 "ORANGE" ,
491 "PURPLE" ,
492 "RED" ,
493 "TURQUOISE" ,
494 "VIOLET" ,
495 "WHITE" ,
496 "YELLOW" ,
497 NULL),
498 NULL), NULL, wxVERTICAL, 150));
499
500 char *styleString = NULL;
501 switch (region->penStyle)
502 {
503 case wxSHORT_DASH:
504 styleString = "Short Dash";
505 break;
506 case wxLONG_DASH:
507 styleString = "Long Dash";
508 break;
509 case wxDOT:
510 styleString = "Dot";
511 break;
512 case wxDOT_DASH:
513 styleString = "Dot Dash";
514 break;
515 case wxSOLID:
516 default:
517 styleString = "Solid";
518 break;
519 }
520 styleStrings[i] = copystring(styleString);
521 form->Add(wxMakeFormString("Style", &(styleStrings[i]), wxFORM_CHOICE,
522 new wxList(wxMakeConstraintStrings(
523 "Solid" ,
524 "Short Dash" ,
525 "Long Dash" ,
526 "Dot" ,
527 "Dot Dash" ,
528 NULL),
529 NULL), NULL, wxVERTICAL, 100));
530 node = node->Next();
531 i ++;
532 if (node && node->Next())
533 form->Add(wxMakeFormNewLine());
534 }
535 wxDialogBox *dialog = new wxDialogBox(m_canvas->GetParent(), "Divided object properties", 10, 10, 500, 500);
536 if (GraphicsLabelFont)
537 dialog->SetLabelFont(GraphicsLabelFont);
538 if (GraphicsButtonFont)
539 dialog->SetButtonFont(GraphicsButtonFont);
540 form->AssociatePanel(dialog);
541 form->dialog = dialog;
542
543 dialog->Fit();
544 dialog->Centre(wxBOTH);
545
546 wxEndBusyCursor();
547
548 dialog->Show(TRUE);
549
550 node = GetRegions().First();
551 i = 0;
552 while (node)
553 {
554 wxShapeRegion *region = (wxShapeRegion *)node->Data();
555
556 if (styleStrings[i])
557 {
558 if (strcmp(styleStrings[i], "Solid") == 0)
559 region->penStyle = wxSOLID;
560 else if (strcmp(styleStrings[i], "Dot") == 0)
561 region->penStyle = wxDOT;
562 else if (strcmp(styleStrings[i], "Short Dash") == 0)
563 region->penStyle = wxSHORT_DASH;
564 else if (strcmp(styleStrings[i], "Long Dash") == 0)
565 region->penStyle = wxLONG_DASH;
566 else if (strcmp(styleStrings[i], "Dot Dash") == 0)
567 region->penStyle = wxDOT_DASH;
568 delete[] styleStrings[i];
569 }
570 region->m_actualPenObject = NULL;
571 node = node->Next();
572 i ++;
573 }
574 delete[] styleStrings;
575 Draw(dc);
576 #endif
577 }
578
579 void wxDividedShape::OnRightClick(float x, float y, int keys, int attachment)
580 {
581 if (keys & KEY_CTRL)
582 {
583 EditRegions();
584 }
585 else
586 {
587 wxRectangleShape::OnRightClick(x, y, keys, attachment);
588 }
589 }
590
591 wxDividedShapeControlPoint::wxDividedShapeControlPoint(wxShapeCanvas *the_canvas, wxShape *object,
592 int region, float size, float the_m_xoffset, float the_m_yoffset, int the_type):
593 wxControlPoint(the_canvas, object, size, the_m_xoffset, the_m_yoffset, the_type)
594 {
595 regionId = region;
596 }
597
598 wxDividedShapeControlPoint::~wxDividedShapeControlPoint()
599 {
600 }
601
602 // Implement resizing of divided object division
603 void wxDividedShapeControlPoint::OnDragLeft(bool draw, float x, float y, int keys, int attachment)
604 {
605 wxClientDC dc(GetCanvas());
606 GetCanvas()->PrepareDC(dc);
607
608 dc.SetLogicalFunction(wxXOR);
609 wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
610 dc.SetPen(dottedPen);
611 dc.SetBrush((* wxTRANSPARENT_BRUSH));
612
613 wxDividedShape *dividedObject = (wxDividedShape *)m_shape;
614 float x1 = (float)(dividedObject->GetX() - (dividedObject->GetWidth()/2.0));
615 float y1 = y;
616 float x2 = (float)(dividedObject->GetX() + (dividedObject->GetWidth()/2.0));
617 float y2 = y;
618 dc.DrawLine(x1, y1, x2, y2);
619 }
620
621 void wxDividedShapeControlPoint::OnBeginDragLeft(float x, float y, int keys, int attachment)
622 {
623 wxClientDC dc(GetCanvas());
624 GetCanvas()->PrepareDC(dc);
625
626 wxDividedShape *dividedObject = (wxDividedShape *)m_shape;
627 dc.SetLogicalFunction(wxXOR);
628 wxPen dottedPen(wxColour(0, 0, 0), 1, wxDOT);
629 dc.SetPen(dottedPen);
630 dc.SetBrush((* wxTRANSPARENT_BRUSH));
631
632 float x1 = (float)(dividedObject->GetX() - (dividedObject->GetWidth()/2.0));
633 float y1 = y;
634 float x2 = (float)(dividedObject->GetX() + (dividedObject->GetWidth()/2.0));
635 float y2 = y;
636 dc.DrawLine(x1, y1, x2, y2);
637 m_canvas->CaptureMouse();
638 }
639
640 void wxDividedShapeControlPoint::OnEndDragLeft(float x, float y, int keys, int attachment)
641 {
642 wxClientDC dc(GetCanvas());
643 GetCanvas()->PrepareDC(dc);
644
645 wxDividedShape *dividedObject = (wxDividedShape *)m_shape;
646 wxNode *node = dividedObject->GetRegions().Nth(regionId);
647 if (!node)
648 return;
649
650 wxShapeRegion *thisRegion = (wxShapeRegion *)node->Data();
651 wxShapeRegion *nextRegion = NULL; // Region below this one
652
653 dc.SetLogicalFunction(wxCOPY);
654
655 m_canvas->ReleaseMouse();
656
657 // Find the old top and bottom of this region,
658 // and calculate the new proportion for this region
659 // if legal.
660
661 float currentY = (float)(dividedObject->GetY() - (dividedObject->GetHeight() / 2.0));
662 float maxY = (float)(dividedObject->GetY() + (dividedObject->GetHeight() / 2.0));
663
664 // Save values
665 float thisRegionTop = 0.0;
666 float thisRegionBottom = 0.0;
667 float nextRegionBottom = 0.0;
668
669 node = dividedObject->GetRegions().First();
670 while (node)
671 {
672 wxShapeRegion *region = (wxShapeRegion *)node->Data();
673
674 float proportion = region->m_regionProportionY;
675 float yy = currentY + (dividedObject->GetHeight()*proportion);
676 float actualY = (float)(maxY < yy ? maxY : yy);
677
678 if (region == thisRegion)
679 {
680 thisRegionTop = currentY;
681 thisRegionBottom = actualY;
682 if (node->Next())
683 nextRegion = (wxShapeRegion *)node->Next()->Data();
684 }
685 if (region == nextRegion)
686 {
687 nextRegionBottom = actualY;
688 }
689
690 currentY = actualY;
691 node = node->Next();
692 }
693 if (!nextRegion)
694 return;
695
696 // Check that we haven't gone above this region or below
697 // next region.
698 if ((y <= thisRegionTop) || (y >= nextRegionBottom))
699 return;
700
701 dividedObject->EraseLinks(dc);
702
703 // Now calculate the new proportions of this region and the next region.
704 float thisProportion = (float)((y - thisRegionTop)/dividedObject->GetHeight());
705 float nextProportion = (float)((nextRegionBottom - y)/dividedObject->GetHeight());
706 thisRegion->SetProportions(0.0, thisProportion);
707 nextRegion->SetProportions(0.0, nextProportion);
708 m_yoffset = (float)(y - dividedObject->GetY());
709
710 // Now reformat text
711 int i = 0;
712 node = dividedObject->GetRegions().First();
713 while (node)
714 {
715 wxShapeRegion *region = (wxShapeRegion *)node->Data();
716 if (region->GetText())
717 {
718 char *s = copystring(region->GetText());
719 dividedObject->FormatText(dc, s, i);
720 delete[] s;
721 }
722 node = node->Next();
723 i++;
724 }
725 dividedObject->SetRegionSizes();
726 dividedObject->Draw(dc);
727 dividedObject->GetEventHandler()->OnMoveLinks(dc);
728 }
729