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