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, ®ion->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", ®ion->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