///////////////////////////////////////////////////////////////////////////// // Name: divided.cpp // Purpose: wxDividedShape class // Author: Julian Smart // Modified by: // Created: 12/07/98 // RCS-ID: $Id: divided.cpp 35812 2005-10-06 18:17:23Z ABX $ // Copyright: (c) Julian Smart // Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// // For compilers that support precompilation, includes "wx.h". #include "wx/wxprec.h" #ifdef __BORLANDC__ #pragma hdrstop #endif #ifndef WX_PRECOMP #include "wx/wx.h" #endif #if wxUSE_PROLOGIO #include "wx/deprecated/wxexpr.h" #endif #include "wx/ogl/ogl.h" class wxDividedShapeControlPoint: public wxControlPoint { DECLARE_DYNAMIC_CLASS(wxDividedShapeControlPoint) private: int regionId; public: wxDividedShapeControlPoint() { regionId = 0; } wxDividedShapeControlPoint(wxShapeCanvas *the_canvas, wxShape *object, int region, double size, double the_xoffset, double the_yoffset, int the_type); ~wxDividedShapeControlPoint(); void OnDragLeft(bool draw, double x, double y, int keys=0, int attachment = 0); void OnBeginDragLeft(double x, double y, int keys=0, int attachment = 0); void OnEndDragLeft(double x, double y, int keys=0, int attachment = 0); }; IMPLEMENT_DYNAMIC_CLASS(wxDividedShapeControlPoint, wxControlPoint) /* * Divided object * */ IMPLEMENT_DYNAMIC_CLASS(wxDividedShape, wxRectangleShape) wxDividedShape::wxDividedShape(double w, double h): wxRectangleShape(w, h) { ClearRegions(); } wxDividedShape::~wxDividedShape() { } void wxDividedShape::OnDraw(wxDC& dc) { wxRectangleShape::OnDraw(dc); } void wxDividedShape::OnDrawContents(wxDC& dc) { double defaultProportion = (double)(GetRegions().GetCount() > 0 ? (1.0/((double)(GetRegions().GetCount()))) : 0.0); double currentY = (double)(m_ypos - (m_height / 2.0)); double maxY = (double)(m_ypos + (m_height / 2.0)); double leftX = (double)(m_xpos - (m_width / 2.0)); double rightX = (double)(m_xpos + (m_width / 2.0)); if (m_pen) dc.SetPen(* m_pen); dc.SetTextForeground(m_textColour); #ifdef __WXMSW__ // For efficiency, don't do this under X - doesn't make // any visible difference for our purposes. if (m_brush) dc.SetTextBackground(m_brush->GetColour()); #endif /* if (!formatted) { FormatRegionText(); formatted = true; } */ if (GetDisableLabel()) return; double xMargin = 2; double yMargin = 2; dc.SetBackgroundMode(wxTRANSPARENT); wxObjectList::compatibility_iterator node = GetRegions().GetFirst(); while (node) { wxShapeRegion *region = (wxShapeRegion *)node->GetData(); dc.SetFont(* region->GetFont()); dc.SetTextForeground(region->GetActualColourObject()); double proportion = region->m_regionProportionY < 0.0 ? defaultProportion : region->m_regionProportionY; double y = currentY + m_height*proportion; double actualY = maxY < y ? maxY : y; double centreX = m_xpos; double centreY = (double)(currentY + (actualY - currentY)/2.0); oglDrawFormattedText(dc, ®ion->m_formattedText, (double)(centreX), (double)(centreY), (double)(m_width-2*xMargin), (double)(actualY - currentY - 2*yMargin), region->m_formatMode); if ((y <= maxY) && (node->GetNext())) { wxPen *regionPen = region->GetActualPen(); if (regionPen) { dc.SetPen(* regionPen); dc.DrawLine(WXROUND(leftX), WXROUND(y), WXROUND(rightX), WXROUND(y)); } } currentY = actualY; node = node->GetNext(); } } void wxDividedShape::SetSize(double w, double h, bool WXUNUSED(recursive)) { SetAttachmentSize(w, h); m_width = w; m_height = h; SetRegionSizes(); } void wxDividedShape::SetRegionSizes() { if (GetRegions().GetCount() == 0) return; double defaultProportion = (double)(GetRegions().GetCount() > 0 ? (1.0/((double)(GetRegions().GetCount()))) : 0.0); double currentY = (double)(m_ypos - (m_height / 2.0)); double maxY = (double)(m_ypos + (m_height / 2.0)); // double leftX = (double)(m_xpos - (m_width / 2.0)); // double rightX = (double)(m_xpos + (m_width / 2.0)); wxObjectList::compatibility_iterator node = GetRegions().GetFirst(); while (node) { wxShapeRegion *region = (wxShapeRegion *)node->GetData(); double proportion = region->m_regionProportionY <= 0.0 ? defaultProportion : region->m_regionProportionY; double sizeY = (double)proportion*m_height; double y = currentY + sizeY; double actualY = maxY < y ? maxY : y; double centreY = (double)(currentY + (actualY - currentY)/2.0); region->SetSize(m_width, sizeY); region->SetPosition(0.0, (double)(centreY - m_ypos)); currentY = actualY; node = node->GetNext(); } } // Attachment points correspond to regions in the divided box bool wxDividedShape::GetAttachmentPosition(int attachment, double *x, double *y, int nth, int no_arcs, wxLineShape *line) { int totalNumberAttachments = (GetRegions().GetCount() * 2) + 2; if ((GetAttachmentMode() == ATTACHMENT_MODE_NONE) || (attachment >= totalNumberAttachments)) { return wxShape::GetAttachmentPosition(attachment, x, y, nth, no_arcs); } int n = GetRegions().GetCount(); bool isEnd = (line && line->IsEnd(this)); double left = (double)(m_xpos - m_width/2.0); double right = (double)(m_xpos + m_width/2.0); double top = (double)(m_ypos - m_height/2.0); double bottom = (double)(m_ypos + m_height/2.0); // Zero is top, n+1 is bottom. if (attachment == 0) { *y = top; if (m_spaceAttachments) { if (line && (line->GetAlignmentType(isEnd) == LINE_ALIGNMENT_TO_NEXT_HANDLE)) { // Align line according to the next handle along wxRealPoint *point = line->GetNextControlPoint(this); if (point->x < left) *x = left; else if (point->x > right) *x = right; else *x = point->x; } else *x = left + (nth + 1)*m_width/(no_arcs + 1); } else *x = m_xpos; } else if (attachment == (n+1)) { *y = bottom; if (m_spaceAttachments) { if (line && (line->GetAlignmentType(isEnd) == LINE_ALIGNMENT_TO_NEXT_HANDLE)) { // Align line according to the next handle along wxRealPoint *point = line->GetNextControlPoint(this); if (point->x < left) *x = left; else if (point->x > right) *x = right; else *x = point->x; } else *x = left + (nth + 1)*m_width/(no_arcs + 1); } else *x = m_xpos; } // Left or right. else { bool isLeft = !(attachment < (n+1)); int i = (isLeft) ? (totalNumberAttachments - attachment - 1) : (attachment-1); wxObjectList::compatibility_iterator node = GetRegions().Item(i); if (node) { wxShapeRegion *region = (wxShapeRegion *)node->GetData(); if (isLeft) *x = left; else *x = right; // Calculate top and bottom of region top = (double)((m_ypos + region->m_y) - (region->m_height/2.0)); bottom = (double)((m_ypos + region->m_y) + (region->m_height/2.0)); // Assuming we can trust the absolute size and // position of these regions... if (m_spaceAttachments) { if (line && (line->GetAlignmentType(isEnd) == LINE_ALIGNMENT_TO_NEXT_HANDLE)) { // Align line according to the next handle along wxRealPoint *point = line->GetNextControlPoint(this); if (point->y < bottom) *y = bottom; else if (point->y > top) *y = top; else *y = point->y; } else // *y = (double)(((m_ypos + region->m_y) - (region->m_height/2.0)) + (nth + 1)*region->m_height/(no_arcs+1)); *y = (double)(top + (nth + 1)*region->m_height/(no_arcs+1)); } else *y = (double)(m_ypos + region->m_y); } else { *x = m_xpos; *y = m_ypos; return false; } } return true; } int wxDividedShape::GetNumberOfAttachments() const { // There are two attachments for each region (left and right), // plus one on the top and one on the bottom. int n = (GetRegions().GetCount() * 2) + 2; int maxN = n - 1; wxObjectList::compatibility_iterator node = m_attachmentPoints.GetFirst(); while (node) { wxAttachmentPoint *point = (wxAttachmentPoint *)node->GetData(); if (point->m_id > maxN) maxN = point->m_id; node = node->GetNext(); } return maxN + 1; } bool wxDividedShape::AttachmentIsValid(int attachment) const { int totalNumberAttachments = (GetRegions().GetCount() * 2) + 2; if (attachment >= totalNumberAttachments) { return wxShape::AttachmentIsValid(attachment); } else if (attachment >= 0) return true; else return false; } void wxDividedShape::Copy(wxShape& copy) { wxRectangleShape::Copy(copy); } // Region operations void wxDividedShape::MakeControlPoints() { wxRectangleShape::MakeControlPoints(); MakeMandatoryControlPoints(); } void wxDividedShape::MakeMandatoryControlPoints() { double currentY = (double)(GetY() - (m_height / 2.0)); double maxY = (double)(GetY() + (m_height / 2.0)); wxObjectList::compatibility_iterator node = GetRegions().GetFirst(); int i = 0; while (node) { wxShapeRegion *region = (wxShapeRegion *)node->GetData(); double proportion = region->m_regionProportionY; double y = currentY + m_height*proportion; double actualY = (double)(maxY < y ? maxY : y); if (node->GetNext()) { wxDividedShapeControlPoint *controlPoint = new wxDividedShapeControlPoint(m_canvas, this, i, CONTROL_POINT_SIZE, 0.0, (double)(actualY - GetY()), 0); m_canvas->AddShape(controlPoint); m_controlPoints.Append(controlPoint); } currentY = actualY; i ++; node = node->GetNext(); } } void wxDividedShape::ResetControlPoints() { // May only have the region handles, (n - 1) of them. if (m_controlPoints.GetCount() > (GetRegions().GetCount() - 1)) wxRectangleShape::ResetControlPoints(); ResetMandatoryControlPoints(); } void wxDividedShape::ResetMandatoryControlPoints() { double currentY = (double)(GetY() - (m_height / 2.0)); double maxY = (double)(GetY() + (m_height / 2.0)); wxObjectList::compatibility_iterator node = m_controlPoints.GetFirst(); int i = 0; while (node) { wxControlPoint *controlPoint = (wxControlPoint *)node->GetData(); if (controlPoint->IsKindOf(CLASSINFO(wxDividedShapeControlPoint))) { wxObjectList::compatibility_iterator node1 = GetRegions().Item(i); wxShapeRegion *region = (wxShapeRegion *)node1->GetData(); double proportion = region->m_regionProportionY; double y = currentY + m_height*proportion; double actualY = (double)(maxY < y ? maxY : y); controlPoint->m_xoffset = 0.0; controlPoint->m_yoffset = (double)(actualY - GetY()); currentY = actualY; i ++; } node = node->GetNext(); } } #if wxUSE_PROLOGIO void wxDividedShape::WriteAttributes(wxExpr *clause) { wxRectangleShape::WriteAttributes(clause); } void wxDividedShape::ReadAttributes(wxExpr *clause) { wxRectangleShape::ReadAttributes(clause); } #endif /* * Edit the division colour/style * */ void wxDividedShape::EditRegions() { wxMessageBox(wxT("EditRegions() is unimplemented."), wxT("OGL"), wxOK); // TODO #if 0 if (GetRegions().GetCount() < 2) return; wxBeginBusyCursor(); GraphicsForm *form = new GraphicsForm("Divided nodes"); // Need an array to store all the style strings, // since they need to be converted to integers char **styleStrings = new char *[GetRegions().GetCount()]; for (int j = 0; j < GetRegions().GetCount(); j++) styleStrings[j] = NULL; int i = 0; wxNode *node = GetRegions().GetFirst(); while (node && node->GetNext()) { wxShapeRegion *region = (wxShapeRegion *)node->GetData(); char buf[50]; sprintf(buf, "Region %d", (i+1)); form->Add(wxMakeFormMessage(buf)); form->Add(wxMakeFormNewLine()); form->Add(wxMakeFormString("Colour", ®ion->penColour, wxFORM_CHOICE, new wxList(wxMakeConstraintStrings( "Invisible" , "BLACK" , "BLUE" , "BROWN" , "CORAL" , "CYAN" , "DARK GREY" , "DARK GREEN" , "DIM GREY" , "GREY" , "GREEN" , "LIGHT BLUE" , "LIGHT GREY" , "MAGENTA" , "MAROON" , "NAVY" , "ORANGE" , "PURPLE" , "RED" , "TURQUOISE" , "VIOLET" , "WHITE" , "YELLOW" , NULL), NULL), NULL, wxVERTICAL, 150)); char *styleString = NULL; switch (region->penStyle) { case wxSHORT_DASH: styleString = "Short Dash"; break; case wxLONG_DASH: styleString = "Long Dash"; break; case wxDOT: styleString = "Dot"; break; case wxDOT_DASH: styleString = "Dot Dash"; break; case wxSOLID: default: styleString = "Solid"; break; } styleStrings[i] = copystring(styleString); form->Add(wxMakeFormString("Style", &(styleStrings[i]), wxFORM_CHOICE, new wxList(wxMakeConstraintStrings( "Solid" , "Short Dash" , "Long Dash" , "Dot" , "Dot Dash" , NULL), NULL), NULL, wxVERTICAL, 100)); node = node->GetNext(); i ++; if (node && node->GetNext()) form->Add(wxMakeFormNewLine()); } wxDialogBox *dialog = new wxDialogBox(m_canvas->GetParent(), "Divided object properties", 10, 10, 500, 500); if (GraphicsLabelFont) dialog->SetLabelFont(GraphicsLabelFont); if (GraphicsButtonFont) dialog->SetButtonFont(GraphicsButtonFont); form->AssociatePanel(dialog); form->dialog = dialog; dialog->Fit(); dialog->Centre(wxBOTH); wxEndBusyCursor(); dialog->Show(true); node = GetRegions().GetFirst(); i = 0; while (node) { wxShapeRegion *region = (wxShapeRegion *)node->GetData(); if (styleStrings[i]) { if (strcmp(styleStrings[i], "Solid") == 0) region->penStyle = wxSOLID; else if (strcmp(styleStrings[i], "Dot") == 0) region->penStyle = wxDOT; else if (strcmp(styleStrings[i], "Short Dash") == 0) region->penStyle = wxSHORT_DASH; else if (strcmp(styleStrings[i], "Long Dash") == 0) region->penStyle = wxLONG_DASH; else if (strcmp(styleStrings[i], "Dot Dash") == 0) region->penStyle = wxDOT_DASH; delete[] styleStrings[i]; } region->m_actualPenObject = NULL; node = node->GetNext(); i ++; } delete[] styleStrings; Draw(dc); #endif } void wxDividedShape::OnRightClick(double x, double y, int keys, int attachment) { if (keys & KEY_CTRL) { EditRegions(); } else { wxRectangleShape::OnRightClick(x, y, keys, attachment); } } wxDividedShapeControlPoint::wxDividedShapeControlPoint(wxShapeCanvas *the_canvas, wxShape *object, int region, double size, double the_m_xoffset, double the_m_yoffset, int the_type): wxControlPoint(the_canvas, object, size, the_m_xoffset, the_m_yoffset, the_type) { regionId = region; } wxDividedShapeControlPoint::~wxDividedShapeControlPoint() { } // Implement resizing of divided object division void wxDividedShapeControlPoint::OnDragLeft(bool WXUNUSED(draw), double WXUNUSED(x), double y, int WXUNUSED(keys), int WXUNUSED(attachment)) { wxClientDC dc(GetCanvas()); GetCanvas()->PrepareDC(dc); dc.SetLogicalFunction(OGLRBLF); wxPen dottedPen(*wxBLACK, 1, wxDOT); dc.SetPen(dottedPen); dc.SetBrush((* wxTRANSPARENT_BRUSH)); wxDividedShape *dividedObject = (wxDividedShape *)m_shape; double x1 = (double)(dividedObject->GetX() - (dividedObject->GetWidth()/2.0)); double y1 = y; double x2 = (double)(dividedObject->GetX() + (dividedObject->GetWidth()/2.0)); double y2 = y; dc.DrawLine(WXROUND(x1), WXROUND(y1), WXROUND(x2), WXROUND(y2)); } void wxDividedShapeControlPoint::OnBeginDragLeft(double WXUNUSED(x), double y, int WXUNUSED(keys), int WXUNUSED(attachment)) { wxClientDC dc(GetCanvas()); GetCanvas()->PrepareDC(dc); wxDividedShape *dividedObject = (wxDividedShape *)m_shape; dc.SetLogicalFunction(OGLRBLF); wxPen dottedPen(*wxBLACK, 1, wxDOT); dc.SetPen(dottedPen); dc.SetBrush((* wxTRANSPARENT_BRUSH)); double x1 = (double)(dividedObject->GetX() - (dividedObject->GetWidth()/2.0)); double y1 = y; double x2 = (double)(dividedObject->GetX() + (dividedObject->GetWidth()/2.0)); double y2 = y; dc.DrawLine(WXROUND(x1), WXROUND(y1), WXROUND(x2), WXROUND(y2)); m_canvas->CaptureMouse(); } void wxDividedShapeControlPoint::OnEndDragLeft(double WXUNUSED(x), double y, int WXUNUSED(keys), int WXUNUSED(attachment)) { wxClientDC dc(GetCanvas()); GetCanvas()->PrepareDC(dc); wxDividedShape *dividedObject = (wxDividedShape *)m_shape; wxObjectList::compatibility_iterator node = dividedObject->GetRegions().Item(regionId); if (!node) return; wxShapeRegion *thisRegion = (wxShapeRegion *)node->GetData(); wxShapeRegion *nextRegion = NULL; // Region below this one dc.SetLogicalFunction(wxCOPY); m_canvas->ReleaseMouse(); // Find the old top and bottom of this region, // and calculate the new proportion for this region // if legal. double currentY = (double)(dividedObject->GetY() - (dividedObject->GetHeight() / 2.0)); double maxY = (double)(dividedObject->GetY() + (dividedObject->GetHeight() / 2.0)); // Save values double thisRegionTop = 0.0; double nextRegionBottom = 0.0; node = dividedObject->GetRegions().GetFirst(); while (node) { wxShapeRegion *region = (wxShapeRegion *)node->GetData(); double proportion = region->m_regionProportionY; double yy = currentY + (dividedObject->GetHeight()*proportion); double actualY = (double)(maxY < yy ? maxY : yy); if (region == thisRegion) { thisRegionTop = currentY; if (node->GetNext()) nextRegion = (wxShapeRegion *)node->GetNext()->GetData(); } if (region == nextRegion) { nextRegionBottom = actualY; } currentY = actualY; node = node->GetNext(); } if (!nextRegion) return; // Check that we haven't gone above this region or below // next region. if ((y <= thisRegionTop) || (y >= nextRegionBottom)) return; dividedObject->EraseLinks(dc); // Now calculate the new proportions of this region and the next region. double thisProportion = (double)((y - thisRegionTop)/dividedObject->GetHeight()); double nextProportion = (double)((nextRegionBottom - y)/dividedObject->GetHeight()); thisRegion->SetProportions(0.0, thisProportion); nextRegion->SetProportions(0.0, nextProportion); m_yoffset = (double)(y - dividedObject->GetY()); // Now reformat text int i = 0; node = dividedObject->GetRegions().GetFirst(); while (node) { wxShapeRegion *region = (wxShapeRegion *)node->GetData(); if (region->GetText()) { wxString s(region->GetText()); dividedObject->FormatText(dc, s.c_str(), i); } node = node->GetNext(); i++; } dividedObject->SetRegionSizes(); dividedObject->Draw(dc); dividedObject->GetEventHandler()->OnMoveLinks(dc); }