home *** CD-ROM | disk | FTP | other *** search
/ Borland Programmer's Resource / Borland_Programmers_Resource_CD_1995.iso / code / wxwin140 / samples / objects / graphics.cc < prev    next >
Encoding:
C/C++ Source or Header  |  1995-05-19  |  81.1 KB  |  3,431 lines

  1. /*
  2.  * File:     graphics.cc
  3.  * Purpose:  Object graphics library for wxWindows.
  4.  *           Defines a canvas which repaints its own graphics objects.
  5.  *
  6.  *                       wxWindows 1.40
  7.  * Copyright (c) 1993 Artificial Intelligence Applications Institute,
  8.  *                   The University of Edinburgh
  9.  *
  10.  *                     Author: Julian Smart
  11.  *                        Date: 18-4-93
  12.  *
  13.  * Permission to use, copy, modify, and distribute this software and its
  14.  * documentation for any purpose is hereby granted without fee, provided
  15.  * that the above copyright notice, author statement and this permission
  16.  * notice appear in all copies of this software and related documentation.
  17.  *
  18.  * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, EXPRESS,
  19.  * IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF
  20.  * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
  21.  *
  22.  * IN NO EVENT SHALL THE ARTIFICIAL INTELLIGENCE APPLICATIONS INSTITUTE OR THE
  23.  * UNIVERSITY OF EDINBURGH BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR
  24.  * CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER RESULTING FROM
  25.  * LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF THE POSSIBILITY OF
  26.  * DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH
  27.  * THE USE OR PERFORMANCE OF THIS SOFTWARE.
  28.  *
  29.  */
  30.  
  31. #include <windows.h>
  32. #include <iostream.h>
  33. #include <ctype.h>
  34. #include <stdlib.h>
  35. #include <math.h>
  36. #include <wx.h>
  37. #include "graphics.h"
  38.  
  39. wxFont *normal_font;
  40. wxFont *swiss_font_4;
  41. wxFont *swiss_font_6;
  42. wxFont *swiss_font_8;
  43. wxFont *swiss_font_10;
  44. wxFont *swiss_font_12;
  45. wxFont *swiss_font_14;
  46. wxFont *swiss_font_18;
  47. wxFont *swiss_font_24;
  48.  
  49. wxFont *italic_font;
  50. wxPen *red_pen;
  51. wxPen *cyan_pen;
  52. wxPen *green_pen;
  53. wxPen *black_pen;
  54.  
  55. wxBrush *blue_brush;
  56. wxBrush *green_brush;
  57. wxBrush *white_brush;
  58. wxBrush *black_brush;
  59. wxBrush *cyan_brush;
  60. wxBrush *red_brush;
  61.  
  62. wxPen *white_background_pen;
  63. wxBrush *transparent_brush;
  64. wxBrush *white_background_brush;
  65. wxPen *black_foreground_pen;
  66. wxPen *black_dashed_pen;
  67.  
  68. wxCursor *GraphicsBullseyeCursor = NULL;
  69.  
  70. void GraphicsInitialize(void)
  71. {
  72.   GraphicsBullseyeCursor = new wxCursor(wxCURSOR_BULLSEYE);
  73.  
  74.   normal_font = new wxFont(12, wxMODERN, wxNORMAL, wxNORMAL);
  75.   italic_font = new wxFont(12, wxROMAN, wxITALIC, wxNORMAL);
  76.  
  77.   swiss_font_4 = new wxFont(4, wxSWISS, wxNORMAL, wxNORMAL);
  78.   swiss_font_6 = new wxFont(6, wxSWISS, wxNORMAL, wxNORMAL);
  79.   swiss_font_8 = new wxFont(8, wxSWISS, wxNORMAL, wxNORMAL);
  80.   swiss_font_10 = new wxFont(10, wxSWISS, wxNORMAL, wxNORMAL);
  81.   swiss_font_12 = new wxFont(12, wxSWISS, wxNORMAL, wxNORMAL);
  82.   swiss_font_14 = new wxFont(14, wxSWISS, wxNORMAL, wxNORMAL);
  83.   swiss_font_18 = new wxFont(18, wxSWISS, wxNORMAL, wxNORMAL);
  84.   swiss_font_24 = new wxFont(24, wxSWISS, wxNORMAL, wxNORMAL);
  85.  
  86.   red_pen = new wxPen("RED", 3, wxSOLID);
  87.   cyan_pen = new wxPen("CYAN", 3, wxSOLID);
  88.   green_pen = new wxPen("GREEN", 1, wxSOLID);
  89.   black_pen = new wxPen("BLACK", 1, wxSOLID);
  90.  
  91.   blue_brush = new wxBrush("BLUE", wxSOLID);
  92.   green_brush = new wxBrush("GREEN", wxSOLID);
  93.   white_brush = new wxBrush("WHITE", wxSOLID);
  94.   black_brush = new wxBrush("BLACK", wxSOLID);
  95.   cyan_brush = new wxBrush("CYAN", wxSOLID);
  96.   red_brush = new wxBrush("RED", wxSOLID);
  97.  
  98.   white_background_pen = new wxPen("WHITE", 4, wxSOLID);
  99.   transparent_brush = new wxBrush("BLACK", wxTRANSPARENT);
  100.   white_background_brush = new wxBrush("WHITE", wxSOLID);
  101.   black_foreground_pen = new wxPen("BLACK", 1, wxSOLID);
  102.   black_dashed_pen = new wxPen("BLACK", 1, wxSHORT_DASH);
  103. }
  104.  
  105. wxFont *MatchFont(int point_size)
  106. {
  107.   wxFont *font;
  108.   switch (point_size)
  109.   {
  110.     case 4:
  111.       font = swiss_font_4;
  112.       break;
  113.     case 6:
  114.       font = swiss_font_6;
  115.       break;
  116.     case 8:
  117.       font = swiss_font_8;
  118.       break;
  119.     case 12:
  120.       font = swiss_font_12;
  121.       break;
  122.     case 14:
  123.       font = swiss_font_14;
  124.       break;
  125.     case 18:
  126.       font = swiss_font_18;
  127.       break;
  128.     case 24:
  129.       font = swiss_font_24;
  130.       break;
  131.     default:
  132.     case 10:
  133.       font = swiss_font_10;
  134.       break;
  135.   }
  136.   return font;
  137. }
  138.  
  139. wxFont *FontSizeDialog(wxFrame *parent)
  140. {
  141.   char *strings[] = { "4", "6", "8", "10", "12", "14", "18", "24" };
  142.   char *ans = wxGetSingleChoice("Choose", "Choose a font size", 8, strings, parent);
  143.   if (ans)
  144.   {
  145.     int size = atoi(ans);
  146.     return MatchFont(size);
  147.   }
  148.   else return NULL;
  149. }
  150.  
  151. CanvasObjectTextLine::CanvasObjectTextLine(float the_x, float the_y, char *the_line)
  152. {
  153.   x = the_x; y = the_y; line = copystring(the_line);
  154. }
  155.  
  156. CanvasObjectTextLine::~CanvasObjectTextLine(void)
  157. {
  158.   if (line) delete line;
  159. }
  160.  
  161. CanvasObject::CanvasObject(void)
  162. {
  163.   formatted = FALSE;
  164.   canvas = NULL;
  165.   dc = NULL;
  166.   xpos = 0.0; ypos = 0.0;
  167.   draggable = TRUE;
  168.   pen = NULL; brush = NULL; font = NULL; text_colour = wxBLACK;
  169.   visible = FALSE;
  170.   ClientData = NULL;
  171.   selected = FALSE;
  172.   attachment_mode = FALSE;
  173.   disable_label = FALSE;
  174. }
  175.  
  176. CanvasObject::CanvasObject(ObjectCanvas *can)
  177. {
  178.   text.DeleteContents(TRUE); // List will delete contents when deleted
  179.   formatted = FALSE;
  180.   canvas = can;
  181.   xpos = 0.0; ypos = 0.0;
  182.   draggable = TRUE;
  183.   pen = NULL; brush = NULL; font = NULL; text_colour = NULL;
  184.   visible = FALSE;
  185.   ClientData = NULL;
  186.   selected = FALSE;
  187.   disable_label = FALSE;
  188. }
  189.  
  190. CanvasObject::~CanvasObject(void)
  191. {
  192.   ClearText();
  193.  
  194.   if (canvas)
  195.   {
  196.     canvas->RemoveObject(this);
  197.   }
  198.   
  199. }
  200.  
  201. void CanvasObject::SetClientData(wxObject *client_data)
  202. {
  203.   ClientData = client_data;
  204. }
  205.  
  206. wxObject *CanvasObject::GetClientData(void)
  207. {
  208.   return ClientData;
  209. }
  210.  
  211. void CanvasObject::SetDC(wxDC *the_dc)
  212. {
  213.   dc = the_dc;
  214. }
  215.  
  216. void CanvasObject::ClearText(void)
  217. {
  218.   text.Clear();
  219. }
  220.  
  221. Bool CanvasObject::HitTest(float x, float y, int *attachment, float *distance)
  222. {
  223.   float width = 0.0, height = 0.0;
  224.   GetBoundingBox(&width, &height);
  225.   if (fabs(width) < 4.0) width = 4.0;
  226.   if (fabs(height) < 4.0) height = 4.0;
  227.  
  228.   width += (float)4.0; height += (float)4.0; // Allowance for inaccurate mousing
  229.  
  230.   float left = (float)(xpos - (width/2.0));
  231.   float top = (float)(ypos - (height/2.0));
  232.   float right = (float)(xpos + (width/2.0));
  233.   float bottom = (float)(ypos + (height/2.0));
  234.  
  235.   int nearest_attachment = 0;
  236.  
  237.  
  238.   // If within the bounding box, check the attachment points
  239.   // within the object.
  240.  
  241.   if (x >= left && x <= right && y >= top && y <= bottom)
  242.   {
  243.     int n = GetNumberOfAttachments();
  244.     float nearest = 999999.0;
  245.  
  246.     for (int i = 0; i < n; i++)
  247.     {
  248.       float xpos, ypos;
  249.       GetAttachmentPosition(i, &xpos, &ypos);
  250.  
  251.       float l = (float)sqrt(((xpos - x) * (xpos - x)) +
  252.                  ((ypos - y) * (ypos - y)));
  253.  
  254.       if (l < nearest)
  255.       {
  256.         nearest = l;
  257.         nearest_attachment = i;
  258.       }
  259.     }
  260.     *attachment = nearest_attachment;
  261.     *distance = nearest;
  262.     return TRUE;
  263.   }
  264.   else return FALSE;
  265. }
  266.  
  267. // Format a text string according to the bounding box, add
  268. // strings to text list
  269. void CanvasObject::FormatText(char *s)
  270. {
  271.   if (!dc)
  272.     return;
  273.  
  274.   float w, h;
  275.   ClearText();
  276.   dc->SetFont(font);
  277.  
  278.   GetBoundingBox(&w, &h);
  279.  
  280.   wxList *string_list = ::FormatText(dc, s, (w-5), (h-5));
  281.   wxNode *node = string_list->First();
  282.   while (node)
  283.   {
  284.     char *s = (char *)node->Data();
  285.     CanvasObjectTextLine *line = new CanvasObjectTextLine(0.0, 0.0, s);
  286.     text.Append((wxObject *)line);
  287.     delete node;
  288.     node = string_list->First();
  289.   }
  290.   delete string_list;
  291.   CentreText(dc, &text, xpos, ypos, w, h);
  292.   formatted = TRUE;
  293. }
  294.  
  295. void CanvasObject::GetBoundingBox(float *width, float *height)
  296. {
  297. }
  298.  
  299. Bool CanvasObject::GetPerimeterPoint(float x1, float y1,
  300.                                      float x2, float y2,
  301.                                      float *x3, float *y3)
  302. {
  303.   return FALSE;
  304. }
  305.  
  306. float CanvasObject::GetX(void)
  307. {
  308.   return xpos;
  309. }
  310.  
  311. float CanvasObject::GetY(void)
  312. {
  313.   return ypos;
  314. }
  315.  
  316. void CanvasObject::SetPen(wxPen *the_pen)
  317. {
  318.   pen = the_pen;
  319. }
  320.  
  321. void CanvasObject::SetBrush(wxBrush *the_brush)
  322. {
  323.   brush = the_brush;
  324. }
  325.  
  326. void CanvasObject::SetFont(wxFont *the_font)
  327. {
  328.   font = the_font;
  329. }
  330.  
  331. void CanvasObject::OnDraw(void)
  332. {
  333. }
  334.  
  335. void CanvasObject::OnMoveLinks(void)
  336. {
  337.   // Want to set the ends of all attached links
  338.   // to point to/from this object
  339.  
  340.   wxNode *current = lines.First();
  341.   while (current)
  342.   {
  343.     LineObject *line = (LineObject *)current->Data();
  344.     line->OnMoveLink();
  345.     current = current->Next();
  346.   }
  347. }
  348.  
  349.  
  350. void CanvasObject::OnDrawContents(void)
  351. {
  352.   float bound_x, bound_y;
  353.   GetBoundingBox(&bound_x, &bound_y);
  354.   if (dc)
  355.   {
  356.     if (font) dc->SetFont(font);
  357.     if (pen) dc->SetPen(pen);
  358.  
  359.     if (text_colour) dc->SetTextForeground(text_colour);
  360. #ifdef wx_msw
  361.     // For efficiency, don't do this under X - doesn't make
  362.     // any visible difference for our purposes.
  363.     if (brush && brush->colour)
  364.       dc->SetTextBackground(brush->colour);
  365. #endif
  366.  
  367.     if (!formatted)
  368.     {
  369.       CentreText(dc, &text, xpos, ypos, bound_x, bound_y);
  370.       formatted = TRUE;
  371.     }
  372.     if (!disable_label)
  373.       DrawFormattedText(dc, &text, xpos, ypos, bound_x, bound_y);
  374.   }
  375. }
  376.  
  377. void CanvasObject::DrawContents(void)
  378. {
  379.   OnDrawContents();
  380. }
  381.  
  382. void CanvasObject::OnSize(float x, float y)
  383. {
  384. }
  385.  
  386. void CanvasObject::OnMove(float x, float y, float old_x, float old_y)
  387. {
  388. //  Draw();
  389. }
  390.  
  391. void CanvasObject::OnErase(void)
  392. {
  393.   if (!visible)
  394.     return;
  395.  
  396.   // Erase links
  397.   wxNode *current = lines.First();
  398.   while (current)
  399.   {
  400.     LineObject *line = (LineObject *)current->Data();
  401.     line->OnErase();
  402.     current = current->Next();
  403.   }
  404.  
  405.   float bound_x, bound_y;
  406.   GetBoundingBox(&bound_x, &bound_y);
  407.  
  408.   if (dc)
  409.   {
  410.     dc->SetPen(white_background_pen);
  411.     dc->SetBrush(white_background_brush);
  412.     dc->DrawRectangle(
  413.                  (float)(xpos - bound_x/2.0), (float)(ypos - bound_y/2.0),
  414.                  (float)bound_x, (float)bound_y);
  415.   }
  416. }
  417.  
  418. void CanvasObject::EraseLinks(int attachment)
  419. {
  420.   if (!visible)
  421.     return;
  422.  
  423.   wxNode *current = lines.First();
  424.   while (current)
  425.   {
  426.     LineObject *line = (LineObject *)current->Data();
  427.     if (attachment == -1 || ((line->to == this && line->attachment_to == attachment) ||
  428.                              (line->from == this && line->attachment_from == attachment)))
  429.       line->OnErase();
  430.     current = current->Next();
  431.   }
  432. }
  433.  
  434. void CanvasObject::DrawLinks(int attachment)
  435. {
  436.   if (!visible)
  437.     return;
  438.  
  439.   wxNode *current = lines.First();
  440.   while (current)
  441.   {
  442.     LineObject *line = (LineObject *)current->Data();
  443.     if (attachment == -1 ||
  444.         (line->to == this && line->attachment_to == attachment) ||
  445.         (line->from == this && line->attachment_from == attachment))
  446.       line->Draw();
  447.     current = current->Next();
  448.   }
  449. }
  450.  
  451. void CanvasObject::MoveLineToNewAttachment(LineObject *to_move,
  452.                                        float x, float y)
  453. {
  454.   int new_point;
  455.   float distance;
  456.  
  457.   // Is (x, y) on this object? If so, find the new attachment point
  458.   // the user has moved the point to
  459.   Bool hit = HitTest(x, y, &new_point, &distance);
  460.   if (!hit)
  461.     return;
  462.  
  463.   EraseLinks();
  464.  
  465.   int old_attachment;
  466.   if (to_move->to == this)
  467.     old_attachment = to_move->attachment_to;
  468.   else
  469.     old_attachment = to_move->attachment_from;
  470.  
  471.   // Delete the line object from the list of links; we're going to move
  472.   // it to another position in the list
  473.   lines.DeleteObject(to_move);
  474.  
  475.   float old_x = -9999.9;
  476.   float old_y = -9999.9;
  477.  
  478.   wxNode *node = lines.First();
  479.   Bool found = FALSE;
  480.  
  481.   while (node && !found)
  482.   {
  483.     LineObject *line = (LineObject *)node->Data();
  484.     if ((line->to == this && old_attachment == line->attachment_to) ||
  485.         (line->from == this && old_attachment == line->attachment_from))
  486.     {
  487.       float xp, yp;
  488.       if (line->to == this)
  489.       {
  490.         xp = line->xpos2 + line->xpos;
  491.         yp = line->ypos2 + line->ypos;
  492.       } else
  493.       {
  494.         xp = line->xpos1 + line->xpos;
  495.         yp = line->ypos1 + line->ypos;
  496.       }
  497.  
  498.       switch (old_attachment)
  499.       {
  500.         case 0:
  501.         case 2:
  502.         {
  503.           if (x > old_x && x <= xp)
  504.           {
  505.             found = TRUE;
  506.             lines.Insert(node, to_move);
  507.           }
  508.           break;
  509.         }
  510.         case 1:
  511.         case 3:
  512.         {
  513.           if (y > old_y && y <= yp)
  514.           {
  515.             found = TRUE;
  516.             lines.Insert(node, to_move);
  517.           }
  518.           break;
  519.         }
  520.       }
  521.       old_x = xp;
  522.       old_y = yp;
  523.     }
  524.     node = node->Next();
  525.   }
  526.   if (!found)
  527.     lines.Append(to_move);
  528.  
  529.  
  530. }
  531.  
  532. void CanvasObject::OnHighlight(void)
  533. {
  534. }
  535.  
  536. void CanvasObject::OnLeftClick(float x, float y, int keys, int attachment)
  537. {
  538. }
  539.  
  540. void CanvasObject::OnRightClick(float x, float y, int keys, int attachment)
  541. {
  542. }
  543.  
  544. void CanvasObject::OnDragLeft(Bool draw, float x, float y, int keys, int attachment)
  545. {
  546.   canvas->Snap(&x, &y);
  547.   xpos = x; ypos = y;
  548.   OnDrawOutline();
  549. }
  550.  
  551. void CanvasObject::OnBeginDragLeft(float x, float y, int keys, int attachment)
  552. {
  553.   Erase();
  554.   canvas->Snap(&x, &y);
  555.   xpos = x; ypos = y;
  556.   dc->SetLogicalFunction(wxXOR);
  557.   OnDrawOutline();
  558. }
  559.  
  560. void CanvasObject::OnEndDragLeft(float x, float y, int keys, int attachment)
  561. {
  562.   dc->SetLogicalFunction(wxCOPY);
  563.  
  564.   canvas->Snap(&xpos, &ypos);
  565.   Move(xpos, ypos);
  566.   if (canvas && !canvas->quick_edit_mode) canvas->Redraw();
  567. }
  568.  
  569. void CanvasObject::OnDragRight(Bool draw, float x, float y, int keys, int attachment)
  570. {
  571. }
  572.  
  573. void CanvasObject::OnBeginDragRight(float x, float y, int keys, int attachment)
  574. {
  575. }
  576.  
  577. void CanvasObject::OnEndDragRight(float x, float y, int keys, int attachment)
  578. {
  579. }
  580.  
  581. void CanvasObject::OnDrawOutline(void)
  582. {
  583.   float bound_x, bound_y;
  584.   GetBoundingBox(&bound_x, &bound_y);
  585.  
  586.   dc->SetPen(black_dashed_pen);
  587.   dc->SetBrush(transparent_brush);
  588.  
  589.   float top_left_x = (float)(xpos - bound_x/2.0);
  590.   float top_left_y = (float)(ypos - bound_y/2.0);
  591.   float top_right_x = (float)(xpos + bound_x/2.0);
  592.   float top_right_y = (float)top_left_y;
  593.   float bottom_left_x = (float)top_left_x;
  594.   float bottom_left_y = (float)(ypos + bound_y/2.0);
  595.   float bottom_right_x = (float)top_right_x;
  596.   float bottom_right_y = (float)bottom_left_y;
  597.  
  598.   dc->DrawLine(top_left_x, top_left_y, top_right_x, top_right_y);
  599.   dc->DrawLine(top_right_x, top_right_y, bottom_right_x, bottom_right_y);
  600.   dc->DrawLine(bottom_right_x, bottom_right_y, bottom_left_x, bottom_left_y);
  601.   dc->DrawLine(bottom_left_x, bottom_left_y, top_left_x, top_left_y);
  602. }
  603.  
  604. void CanvasObject::Attach(ObjectCanvas *can)
  605. {
  606.   canvas = can;
  607. }
  608.  
  609. void CanvasObject::Detach(void)
  610. {
  611.   canvas = NULL;
  612. }
  613.  
  614. void CanvasObject::Draggable(Bool truth)
  615. {
  616.   draggable = truth;
  617. }
  618.  
  619. void CanvasObject::Move(float x, float y)
  620. {
  621.   float old_x = xpos;
  622.   float old_y = ypos;
  623.  
  624.   xpos = x; ypos = y;
  625.  
  626.   OnMove(x, y, old_x, old_y);
  627.  
  628.  
  629.   ResetControlPoints();
  630.  
  631.   Draw();
  632.  
  633.   MoveLinks();
  634.   OnDrawControlPoints();
  635. }
  636.  
  637. void CanvasObject::MoveLinks(void)
  638. {
  639.   OnMoveLinks();
  640. }
  641.  
  642.  
  643. void CanvasObject::Draw(void)
  644. {
  645.   if (visible)
  646.   {
  647.     OnDraw();
  648.     OnDrawContents();
  649.   }
  650. }
  651.  
  652. void CanvasObject::Flash(void)
  653. {
  654.   if (dc)
  655.   {
  656.     dc->SetLogicalFunction(wxINVERT);
  657.     Draw();
  658.     dc->SetLogicalFunction(wxCOPY);
  659.     Draw();
  660.   }
  661. }
  662.  
  663. void CanvasObject::Show(Bool show)
  664. {
  665.   visible = show;
  666. }
  667.  
  668. void CanvasObject::Erase(void)
  669. {
  670.   OnErase();
  671.   OnEraseControlPoints();
  672. }
  673.  
  674. void CanvasObject::AddText(char *string)
  675. {
  676.   text.Append(new CanvasObjectTextLine(0.0, 0.0, string));
  677.   formatted = FALSE;
  678. }
  679.  
  680. void CanvasObject::SetSize(float x, float y)
  681. {
  682. }
  683.  
  684. // Add line FROM this object
  685. void CanvasObject::AddLine(LineObject *line, CanvasObject *other)
  686. {
  687.   lines.Append(line);
  688.   other->lines.Append(line);
  689.   line->from = this;
  690.   line->to = other;
  691. }
  692.  
  693. void CanvasObject::RemoveLine(LineObject *line)
  694. {
  695.   if (line->from == this)
  696.     line->to->lines.DeleteObject(line);
  697.   else
  698.    line->from->lines.DeleteObject(line);
  699.  
  700.   lines.DeleteObject(line);
  701. }
  702.  
  703. void CanvasObject::Copy(CanvasObject& copy)
  704. {
  705.   copy.xpos = xpos;
  706.   copy.ypos = ypos;
  707.   copy.pen = pen;
  708.   copy.brush = brush;
  709.   copy.text_colour = text_colour;
  710.  
  711.   wxNode *node = text.First();
  712.   while (node)
  713.   {
  714.     CanvasObjectTextLine *line = (CanvasObjectTextLine *)node->Data();
  715.     CanvasObjectTextLine *new_line =
  716.       new CanvasObjectTextLine(line->x, line->y, line->line);
  717.     copy.text.Append(new_line);
  718.     node = node->Next();
  719.   }
  720.   copy.draggable = draggable;
  721.   copy.visible = visible;
  722. }
  723.  
  724. // Default - make 6 control points
  725. void CanvasObject::MakeControlPoints(void)
  726. {
  727.   float bound_x;
  728.   float bound_y;
  729.  
  730.   GetBoundingBox(&bound_x, &bound_y);
  731.  
  732.   float new_width = (float)(bound_x*1.3);
  733.   float new_height = (float)(bound_y*1.3);
  734.  
  735.   // Offsets from main object
  736.   float top = (float)(- (new_height / 2.0));
  737.   float bottom = (float)(new_height / 2.0);
  738.   float left = (float)(- (new_width / 2.0));
  739.   float right = (float)(new_width / 2.0);
  740.  
  741.   ControlPoint *control = new ControlPoint(canvas, this, CONTROL_POINT_SIZE, left, top, 
  742.                                            CONTROL_POINT_DIAGONAL);
  743.   canvas->AddObject(control);
  744.   control_points.Append(control);
  745.  
  746.   control = new ControlPoint(canvas, this, CONTROL_POINT_SIZE, 0, top, 
  747.                                            CONTROL_POINT_VERTICAL);
  748.   canvas->AddObject(control);
  749.   control_points.Append(control);
  750.  
  751.   control = new ControlPoint(canvas, this, CONTROL_POINT_SIZE, right, top, 
  752.                                            CONTROL_POINT_DIAGONAL);
  753.   canvas->AddObject(control);
  754.   control_points.Append(control);
  755.  
  756.   control = new ControlPoint(canvas, this, CONTROL_POINT_SIZE, right, 0, 
  757.                                            CONTROL_POINT_HORIZONTAL);
  758.   canvas->AddObject(control);
  759.   control_points.Append(control);
  760.  
  761.   control = new ControlPoint(canvas, this, CONTROL_POINT_SIZE, right, bottom, 
  762.                                            CONTROL_POINT_DIAGONAL);
  763.   canvas->AddObject(control);
  764.   control_points.Append(control);
  765.  
  766.   control = new ControlPoint(canvas, this, CONTROL_POINT_SIZE, 0, bottom, 
  767.                                            CONTROL_POINT_VERTICAL);
  768.   canvas->AddObject(control);
  769.   control_points.Append(control);
  770.  
  771.   control = new ControlPoint(canvas, this, CONTROL_POINT_SIZE, left, bottom, 
  772.                                            CONTROL_POINT_DIAGONAL);
  773.   canvas->AddObject(control);
  774.   control_points.Append(control);
  775.  
  776.   control = new ControlPoint(canvas, this, CONTROL_POINT_SIZE, left, 0, 
  777.                                            CONTROL_POINT_HORIZONTAL);
  778.   canvas->AddObject(control);
  779.   control_points.Append(control);
  780.  
  781. }
  782.  
  783. void CanvasObject::ResetControlPoints(void)
  784. {
  785.   if (control_points.Number() < 1)
  786.     return;
  787.  
  788.   float bound_x;
  789.   float bound_y;
  790.  
  791.   GetBoundingBox(&bound_x, &bound_y);
  792.  
  793.   float new_width = (float)(bound_x*1.3);
  794.   float new_height = (float)(bound_y*1.3);
  795.  
  796.   // Offsets from main object
  797.   float top = (float)(- (new_height / 2.0));
  798.   float bottom = (float)(new_height / 2.0);
  799.   float left = (float)(- (new_width / 2.0));
  800.   float right = (float)(new_width / 2.0);
  801.  
  802.   wxNode *node = control_points.First();
  803.   ControlPoint *control = (ControlPoint *)node->Data();
  804.   control->xoffset = left; control->yoffset = top;
  805.  
  806.   node = node->Next(); control = (ControlPoint *)node->Data();
  807.   control->xoffset = 0; control->yoffset = top;
  808.  
  809.   node = node->Next(); control = (ControlPoint *)node->Data();
  810.   control->xoffset = right; control->yoffset = top;
  811.  
  812.   node = node->Next(); control = (ControlPoint *)node->Data();
  813.   control->xoffset = right; control->yoffset = 0;
  814.  
  815.   node = node->Next(); control = (ControlPoint *)node->Data();
  816.   control->xoffset = right; control->yoffset = bottom;
  817.  
  818.   node = node->Next(); control = (ControlPoint *)node->Data();
  819.   control->xoffset = 0; control->yoffset = bottom;
  820.  
  821.   node = node->Next(); control = (ControlPoint *)node->Data();
  822.   control->xoffset = left; control->yoffset = bottom;
  823.  
  824.   node = node->Next(); control = (ControlPoint *)node->Data();
  825.   control->xoffset = left; control->yoffset = 0;
  826. }
  827.  
  828. void CanvasObject::DeleteControlPoints(void)
  829. {
  830.   wxNode *node = control_points.First();
  831.   while (node)
  832.   {
  833.     ControlPoint *control = (ControlPoint *)node->Data();
  834.     control->OnErase();
  835.     canvas->RemoveObject(control);
  836.     delete control;
  837.     delete node;
  838.     node = control_points.First();
  839.   }
  840. }
  841.  
  842. void CanvasObject::OnDrawControlPoints(void)
  843. {
  844.   wxNode *node = control_points.First();
  845.   while (node)
  846.   {
  847.     ControlPoint *control = (ControlPoint *)node->Data();
  848.     control->Draw();
  849.     node = node->Next();
  850.   }
  851. }
  852.  
  853. void CanvasObject::OnEraseControlPoints(void)
  854. {
  855.   wxNode *node = control_points.First();
  856.   while (node)
  857.   {
  858.     ControlPoint *control = (ControlPoint *)node->Data();
  859.     control->Erase();
  860.     node = node->Next();
  861.   }
  862. }
  863.  
  864. void CanvasObject::Select(Bool select)
  865. {
  866.   selected = select;
  867.   if (select && (control_points.Number() == 0))
  868.   {
  869.     MakeControlPoints();
  870.     OnDrawControlPoints();
  871.   }
  872.   if (!select && (control_points.Number() > 0))
  873.   {
  874.     DeleteControlPoints();
  875.   }
  876. }
  877.  
  878. Bool CanvasObject::Selected(void)
  879. {
  880.   return selected;
  881. }
  882.  
  883. int CanvasObject::GetNumberOfAttachments(void)
  884. {
  885.   return 1;
  886. }
  887.  
  888. void CanvasObject::GetAttachmentPosition(int attachment, float *x, float *y, 
  889.                                          int nth, int no_arcs)
  890. {
  891.   *x = xpos; *y = ypos;
  892. }
  893.  
  894. void CanvasObject::SetAttachmentMode(Bool flag)
  895. {
  896.   attachment_mode = flag;
  897. }
  898.  
  899. // Two stage construction: need to call Create
  900. PolygonObject::PolygonObject(void)
  901. {
  902.   points = NULL;
  903.   original_points = NULL;
  904. }
  905.  
  906. void PolygonObject::Create(wxList *the_points)
  907. {
  908.   original_points = the_points;
  909.  
  910.   // Duplicate the list of points
  911.   points = new wxList;
  912.  
  913.   wxNode *node = the_points->First();
  914.   while (node)
  915.   {
  916.     wxPoint *point = (wxPoint *)node->Data();
  917.     wxPoint *new_point = new wxPoint(point->x, point->y);
  918.     points->Append(new_point);
  919.     node = node->Next();
  920.   }
  921.   CalculateBoundingBox();
  922.   original_width = bound_width;
  923.   original_height = bound_height;
  924. }
  925.  
  926. PolygonObject::~PolygonObject(void)
  927. {
  928.   if (points)
  929.   {
  930.     wxNode *node = points->First();
  931.     while (node)
  932.     {
  933.       wxPoint *point = (wxPoint *)node->Data();
  934.       delete point;
  935.       delete node;
  936.       node = points->First();
  937.     }
  938.     delete points;
  939.   }
  940.   if (original_points)
  941.   {
  942.     wxNode *node = original_points->First();
  943.     while (node)
  944.     {
  945.       wxPoint *point = (wxPoint *)node->Data();
  946.       delete point;
  947.       delete node;
  948.       node = original_points->First();
  949.     }
  950.     delete original_points;
  951.   }
  952. }
  953.  
  954.  
  955. // Width and height. Centre of object is centre of box.
  956. void PolygonObject::GetBoundingBox(float *width, float *height)
  957. {
  958.   *width = bound_width;
  959.   *height = bound_height;
  960. }
  961.  
  962. void PolygonObject::CalculateBoundingBox(void)
  963. {
  964.   // Calculate bounding box at construction (and presumably resize) time
  965.   float left = 10000;
  966.   float right = -10000;
  967.   float top = 10000;
  968.   float bottom = -10000;
  969.  
  970.   wxNode *node = points->First();
  971.   while (node)
  972.   {
  973.     wxPoint *point = (wxPoint *)node->Data();
  974.     if (point->x < left) left = point->x;
  975.     if (point->x > right) right = point->x;
  976.  
  977.     if (point->y < top) top = point->y;
  978.     if (point->y > bottom) bottom = point->y;
  979.  
  980.     node = node->Next();
  981.   }
  982.   bound_width = right - left;
  983.   bound_height = bottom - top;
  984. }
  985.  
  986. // Really need to be able to reset the shape! Otherwise, if the
  987. // points ever go to zero, we've lost it, and can't resize.
  988. void PolygonObject::SetSize(float new_width, float new_height)
  989. {
  990.   // Multiply all points by proportion of new size to old size
  991.   float x_proportion = (float)(fabs(new_width/original_width));
  992.   float y_proportion = (float)(fabs(new_height/original_height));
  993.  
  994.   wxNode *node = points->First();
  995.   wxNode *original_node = original_points->First();
  996.   while (node && original_node)
  997.   {
  998.     wxPoint *point = (wxPoint *)node->Data();
  999.     wxPoint *original_point = (wxPoint *)original_node->Data();
  1000.  
  1001.     point->x = (original_point->x * x_proportion);
  1002.     point->y = (original_point->y * y_proportion);
  1003.  
  1004.     node = node->Next();
  1005.     original_node = original_node->Next();
  1006.   }
  1007.  
  1008. //  CalculateBoundingBox();
  1009.   bound_width = (float)fabs(new_width);
  1010.   bound_height = (float)fabs(new_height);
  1011. }
  1012.  
  1013. // Assume (x1, y1) is centre of box (most generally, line end at box)
  1014. Bool PolygonObject::GetPerimeterPoint(float x1, float y1,
  1015.                                      float x2, float y2,
  1016.                                      float *x3, float *y3)
  1017. {
  1018.   int n = points->Number();
  1019.  
  1020.   float *xpoints = new float[n];
  1021.   float *ypoints = new float[n];
  1022.  
  1023.   wxNode *node = points->First();
  1024.   int i = 0;
  1025.   while (node)
  1026.   {
  1027.     wxPoint *point = (wxPoint *)node->Data();
  1028.     xpoints[i] = point->x + xpos;
  1029.     ypoints[i] = point->y + ypos;
  1030.     node = node->Next();
  1031.     i ++;
  1032.   }
  1033.  
  1034.   find_end_for_polyline(n, xpoints, ypoints, 
  1035.                         x1, y1, x2, y2, x3, y3);
  1036.  
  1037.   delete xpoints;
  1038.   delete ypoints;
  1039.  
  1040.   return TRUE;
  1041. }
  1042.  
  1043. void PolygonObject::OnDraw(void)
  1044. {
  1045.   if (dc)
  1046.   {
  1047.     if (pen)
  1048.       dc->SetPen(pen);
  1049.     if (brush)
  1050.       dc->SetBrush(brush);
  1051.     dc->DrawPolygon(points, xpos, ypos);
  1052.   }
  1053. }
  1054.  
  1055. void PolygonObject::Copy(PolygonObject& copy)
  1056. {
  1057.   CanvasObject::Copy(copy);
  1058.  
  1059.   if (!copy.points)
  1060.     copy.points = new wxList;
  1061.  
  1062.   if (!copy.original_points)
  1063.     copy.original_points = new wxList;
  1064.  
  1065.   wxNode *node = points->First();
  1066.   while (node)
  1067.   {
  1068.     wxPoint *point = (wxPoint *)node->Data();
  1069.     wxPoint *new_point = new wxPoint(point->x, point->y);
  1070.     copy.points->Append(new_point);
  1071.     node = node->Next();
  1072.   }
  1073.   node = original_points->First();
  1074.   while (node)
  1075.   {
  1076.     wxPoint *point = (wxPoint *)node->Data();
  1077.     wxPoint *new_point = new wxPoint(point->x, point->y);
  1078.     copy.original_points->Append(new_point);
  1079.     node = node->Next();
  1080.   }
  1081.   copy.bound_width = bound_width;
  1082.   copy.bound_height = bound_height;
  1083.   copy.original_width = original_width;
  1084.   copy.original_height = original_height;
  1085. }
  1086.  
  1087. int PolygonObject::GetNumberOfAttachments(void)
  1088. {
  1089.   if (points)
  1090.     return points->Number();
  1091.   else return 1;
  1092. }
  1093.  
  1094. void PolygonObject::GetAttachmentPosition(int attachment, float *x, float *y,
  1095.                                          int nth, int no_arcs)
  1096. {
  1097.   if (attachment_mode && points && attachment < points->Number())
  1098.   {
  1099.     wxPoint *point = (wxPoint *)points->Nth(attachment)->Data();
  1100.     *x = point->x + xpos;
  1101.     *y = point->y + ypos;
  1102.   }
  1103.   else
  1104.   { *x = xpos; *y = ypos; }
  1105. }
  1106.  
  1107. // Rectangle object
  1108.  
  1109. RectangleObject::RectangleObject(float w, float h)
  1110. {
  1111.   width = w; height = h; corner_radius = 0.0;
  1112. }
  1113.  
  1114. void RectangleObject::OnDraw(void)
  1115. {
  1116.   if (dc)
  1117.   {
  1118.     if (pen)
  1119.       dc->SetPen(pen);
  1120.     if (brush)
  1121.       dc->SetBrush(brush);
  1122.  
  1123.     float x1 = (float)(xpos - width/2.0);
  1124.     float y1 = (float)(ypos - height/2.0);
  1125.     if (corner_radius > 0.0)
  1126.       dc->DrawRoundedRectangle(x1, y1, width, height, corner_radius);
  1127.     else
  1128.       dc->DrawRectangle(x1, y1, width, height);
  1129.   }
  1130. }
  1131.  
  1132. void RectangleObject::GetBoundingBox(float *the_width, float *the_height)
  1133. {
  1134.   *the_width = width;
  1135.   *the_height = height;
  1136. }
  1137.  
  1138. void RectangleObject::SetSize(float x, float y)
  1139. {
  1140.   width = x;
  1141.   height = y;
  1142. }
  1143.  
  1144. void RectangleObject::SetCornerRadius(float rad)
  1145. {
  1146.   corner_radius = rad;
  1147. }
  1148.  
  1149. // Assume (x1, y1) is centre of box (most generally, line end at box)
  1150. Bool RectangleObject::GetPerimeterPoint(float x1, float y1,
  1151.                                      float x2, float y2,
  1152.                                      float *x3, float *y3)
  1153. {
  1154.   float bound_x, bound_y;
  1155.   GetBoundingBox(&bound_x, &bound_y);
  1156.   find_end_for_box(bound_x, bound_y, xpos, ypos, x2, y2, x3, y3);
  1157.  
  1158.   return TRUE;
  1159. }
  1160.  
  1161. void RectangleObject::Copy(RectangleObject& copy)
  1162. {
  1163.   CanvasObject::Copy(copy);
  1164.   copy.width = width;
  1165.   copy.height = height;
  1166.   copy.corner_radius = corner_radius;
  1167. }
  1168.  
  1169. int RectangleObject::GetNumberOfAttachments(void)
  1170. {
  1171.   return 4;
  1172. }
  1173.  
  1174. // There are 4 attachment points on a rectangle - 0 = top, 1 = right, 2 = bottom,
  1175. // 3 = left.
  1176. void RectangleObject::GetAttachmentPosition(int attachment, float *x, float *y,
  1177.                                          int nth, int no_arcs)
  1178. {
  1179.   if (attachment_mode)
  1180.   {
  1181.     float top = (float)(ypos + height/2.0);
  1182.     float bottom = (float)(ypos - height/2.0);
  1183.     float left = (float)(xpos - width/2.0);
  1184.     float right = (float)(xpos + width/2.0);
  1185.     switch (attachment)
  1186.     {
  1187.       case 0:
  1188.       {
  1189.         *x = left + (nth + 1)*width/(no_arcs + 1);
  1190.         *y = bottom;
  1191.         break;
  1192.       }
  1193.       case 1:
  1194.       {
  1195.         *x = right;
  1196.         *y = bottom + (nth + 1)*height/(no_arcs + 1);
  1197.         break;
  1198.       }
  1199.       case 2:
  1200.       {
  1201.         *x = left + (nth + 1)*width/(no_arcs + 1);
  1202.         *y = top;
  1203.         break;
  1204.       }
  1205.       case 3:
  1206.       {
  1207.         *x = left;
  1208.         *y = bottom + (nth + 1)*height/(no_arcs + 1);
  1209.         break;
  1210.       }
  1211.       default:
  1212.       {
  1213.         *x = xpos; *y = ypos;
  1214.         break;
  1215.       }
  1216.     }
  1217.   }
  1218.   else
  1219.   { *x = xpos; *y = ypos; }
  1220. }
  1221.  
  1222. // Text object (no box)
  1223.  
  1224. TextObject::TextObject(float width, float height):
  1225.   RectangleObject(width, height)
  1226. {
  1227. }
  1228.  
  1229. void TextObject::OnDraw(void)
  1230. {
  1231. }
  1232.  
  1233. // Ellipse object
  1234.  
  1235. EllipseObject::EllipseObject(float w, float h)
  1236. {
  1237.   width = w; height = h;
  1238. }
  1239.  
  1240. void EllipseObject::GetBoundingBox(float *w, float *h)
  1241. {
  1242.   *w = width; *h = height;
  1243. }
  1244.  
  1245. Bool EllipseObject::GetPerimeterPoint(float x1, float y1,
  1246.                                       float x2, float y2,
  1247.                                       float *x3, float *y3)
  1248. {
  1249.   float bound_x, bound_y;
  1250.   GetBoundingBox(&bound_x, &bound_y);
  1251.   find_end_for_box(bound_x, bound_y, xpos, ypos, x2, y2, x3, y3);
  1252.  
  1253. /*
  1254.   float X2 = x2 - xpos;
  1255.   float Y2 = -(y2 - ypos);
  1256.  
  1257.   double a = 1/((width/2)*(width/2));
  1258.   double b = (Y2/X2)/((height/2)*(height/2));
  1259.   double c = -1;
  1260.  
  1261.   double X3a = (-b+sqrt(b*b - 4*a*c))/(2*a);
  1262.   double X3b = (-b-sqrt(b*b - 4*a*c))/(2*a);
  1263.  
  1264.   double Y3a = X3a*Y2/X2;
  1265.   double Y3b = X3b*Y2/X2;
  1266.  
  1267.   double x3a = X3a + xpos;
  1268.   double y3a = -Y3a + ypos;
  1269.  
  1270.   double x3b = X3b + xpos;
  1271.   double y3b = -Y3b + ypos;
  1272.  
  1273.   *x3 = (float)x3a;
  1274.   *y3 = (float)y3a;
  1275.  
  1276.   cout << "Ellipse: xpos = " << xpos << ", ypos = " << ypos << "; X2 = " << X2 << ", Y2 = "
  1277.        << Y2 << "\na = " << a << ", b = " << b << ", c = " << c << "\n";
  1278.   cout << "width = " << width << ", height = " << height << "\n";
  1279.   cout << "X3a = " << X3a << ", Y3a = " << Y3a << "\n";
  1280.   cout << "X3b = " << X3b << ", Y3b = " << Y3b << "\n";
  1281.   cout << "x3 = " << *x3 << ", y3 = " <<
  1282.        *y3 << "\n";
  1283.  
  1284.   ObjectCanvas *canvas = GetCanvas();
  1285.   dc->DrawLine(x3a - 10, y3a, x3a + 10, y3a);
  1286.   dc->DrawLine(x3b - 10, y3b, x3b + 10, y3b);
  1287.   */
  1288.   return TRUE;
  1289. }
  1290.  
  1291. void EllipseObject::OnDraw(void)
  1292. {
  1293.   if (dc)
  1294.   {
  1295.     if (pen)
  1296.       dc->SetPen(pen);
  1297.     if (brush)
  1298.       dc->SetBrush(brush);
  1299.     dc->DrawEllipse((xpos - width/2), (ypos - height/2), width, height);
  1300.   }
  1301. }
  1302.  
  1303. void EllipseObject::SetSize(float x, float y)
  1304. {
  1305.   width = x;
  1306.   height = y;
  1307. }
  1308.  
  1309. void EllipseObject::Copy(EllipseObject& copy)
  1310. {
  1311.   CanvasObject::Copy(copy);
  1312.  
  1313.   copy.width = width;
  1314.   copy.height = height;
  1315. }
  1316.  
  1317. int EllipseObject::GetNumberOfAttachments(void)
  1318. {
  1319.   return 4;
  1320. }
  1321.  
  1322. // There are 4 attachment points on an ellipse - 0 = top, 1 = right, 2 = bottom,
  1323. // 3 = left.
  1324. void EllipseObject::GetAttachmentPosition(int attachment, float *x, float *y,
  1325.                                          int nth, int no_arcs)
  1326. {
  1327.   if (attachment_mode)
  1328.   {
  1329.     float top = (float)(ypos + height/2.0);
  1330.     float bottom = (float)(ypos - height/2.0);
  1331.     float left = (float)(xpos - width/2.0);
  1332.     float right = (float)(xpos + width/2.0);
  1333.     switch (attachment)
  1334.     {
  1335.       case 0:
  1336.       {
  1337.         *x = left + (nth + 1)*width/(no_arcs + 1);
  1338.         *y = top;
  1339.         break;
  1340.       }
  1341.       case 1:
  1342.       {
  1343.         *x = right;
  1344.         *y = bottom + (nth + 1)*height/(no_arcs + 1);
  1345.         break;
  1346.       }
  1347.       case 2:
  1348.       {
  1349.         *x = left + (nth + 1)*width/(no_arcs + 1);
  1350.         *y = bottom;
  1351.         break;
  1352.       }
  1353.       case 3:
  1354.       {
  1355.         *x = left;
  1356.         *y = bottom + (nth + 1)*height/(no_arcs + 1);
  1357.         break;
  1358.       }
  1359.       default:
  1360.       {
  1361.         *x = xpos; *y = ypos;
  1362.         break;
  1363.       }
  1364.     }
  1365.   }
  1366.   else
  1367.   { *x = xpos; *y = ypos; }
  1368. }
  1369.  
  1370.  
  1371. // Circle object
  1372. CircleObject::CircleObject(float diameter):EllipseObject(diameter, diameter)
  1373. {
  1374. }
  1375.  
  1376. Bool CircleObject::GetPerimeterPoint(float x1, float y1,
  1377.                                       float x2, float y2,
  1378.                                       float *x3, float *y3)
  1379. {
  1380.   find_end_for_circle(width/2, 
  1381.                       xpos, ypos,  // Centre of circle
  1382.                       x2, y2,  // Other end of line
  1383.                       x3, y3);
  1384.  
  1385.   return TRUE;
  1386. }
  1387.  
  1388. // Line object
  1389.  
  1390. LineObject::LineObject(void)
  1391. {
  1392.   xpos1 = 0.0; ypos1 = 0.0; xpos2 = 0.0; ypos2 = 0.0;
  1393.   SetArrowSize(10, 5);
  1394.   SetStartArrow(ARROW_NONE);
  1395.   SetEndArrow(ARROW_NONE);
  1396.   SetMiddleArrow(ARROW_NONE);
  1397.   linked_up = FALSE;
  1398.   attachment_to = 0;
  1399.   attachment_from = 0;
  1400.   actual_text_width = 0.0;
  1401.   actual_text_height = 0.0;
  1402.  
  1403.   line_control_points = NULL;
  1404. }
  1405.  
  1406. LineObject::LineObject(wxList *list)
  1407. {
  1408.   line_control_points = list;
  1409. }
  1410.  
  1411. LineObject::~LineObject(void)
  1412. {
  1413.   if (line_control_points)
  1414.   {
  1415.     wxNode *node = line_control_points->First();
  1416.     while (node)
  1417.     {
  1418.       wxPoint *cp = (wxPoint *)node->Data();
  1419.       delete cp;
  1420.       node = node->Next();
  1421.     }
  1422.     delete line_control_points;
  1423.   }
  1424. }
  1425.  
  1426. void LineObject::MakeLineControlPoints(int n)
  1427. {
  1428.   if (line_control_points)
  1429.   {
  1430.     wxNode *node = line_control_points->First();
  1431.     while (node)
  1432.     {
  1433.       wxPoint *cp = (wxPoint *)node->Data();
  1434.       delete cp;
  1435.       node = node->Next();
  1436.     }
  1437.     delete line_control_points;
  1438.   }
  1439.   line_control_points = new wxList;
  1440.  
  1441.   int i = 0;
  1442.   for (i = 0; i < n; i++)
  1443.   {
  1444.     wxPoint *point = new wxPoint(-999, -999);
  1445.     line_control_points->Append(point);
  1446.   }
  1447. }
  1448.  
  1449. void LineObject::InsertLineControlPoint(void)
  1450. {
  1451.   Erase();
  1452.  
  1453.   wxNode *last = line_control_points->Last();
  1454.   wxNode *second_last = last->Previous();
  1455.   wxPoint *last_point = (wxPoint *)last->Data();
  1456.   wxPoint *second_last_point = (wxPoint *)second_last->Data();
  1457.  
  1458.   // Choose a point half way between the last and penultimate points
  1459.   float line_x = ((last_point->x + second_last_point->x)/2);
  1460.   float line_y = ((last_point->y + second_last_point->y)/2);
  1461.  
  1462.   wxPoint *point = new wxPoint(line_x, line_y);
  1463.   line_control_points->Insert(last, point);
  1464. }
  1465.  
  1466. Bool LineObject::DeleteLineControlPoint(void)
  1467. {
  1468.   if (line_control_points->Number() < 3)
  1469.     return FALSE;
  1470.  
  1471.   wxNode *last = line_control_points->Last();
  1472.   wxNode *second_last = last->Previous();
  1473.  
  1474.   wxPoint *second_last_point = (wxPoint *)second_last->Data();
  1475.   delete second_last_point;
  1476.   delete second_last;
  1477.  
  1478.   return TRUE;
  1479. }
  1480.  
  1481. void LineObject::Initialise(void)
  1482. {
  1483.   if (line_control_points)
  1484.   {
  1485.     // Just move the first and last control points
  1486.     wxNode *first = line_control_points->First();
  1487.     wxPoint *first_point = (wxPoint *)first->Data();
  1488.  
  1489.     wxNode *last = line_control_points->Last();
  1490.     wxPoint *last_point = (wxPoint *)last->Data();
  1491.     first_point->x = xpos1;
  1492.     first_point->y = ypos1;
  1493.     last_point->x = xpos2;
  1494.     last_point->y = ypos2;
  1495.  
  1496.     // If any of the line points are at -999, we must
  1497.     // initialize them by placing them half way between the first
  1498.     // and the last.
  1499.     wxNode *node = first->Next();
  1500.     while (node)
  1501.     {
  1502.       wxPoint *point = (wxPoint *)node->Data();
  1503.       if (point->x == -999)
  1504.       {
  1505.         float x1, y1, x2, y2;
  1506.         if (from->xpos < to->xpos)
  1507.           { x1 = from->xpos; x2 = to->xpos; }
  1508.         else
  1509.           { x2 = from->xpos; x1 = to->xpos; }
  1510.  
  1511.         if (from->ypos < to->ypos)
  1512.           { y1 = from->ypos; y2 = to->ypos; }
  1513.         else
  1514.           { y2 = from->ypos; y1 = to->ypos; }
  1515.  
  1516.         point->x = ((x2 - x1)/2 + x1);
  1517.         point->y = ((y2 - y1)/2 + y1);
  1518.       }
  1519.       node = node->Next();
  1520.     }
  1521.   }
  1522. }
  1523.  
  1524.  
  1525. void LineObject::FormatText(char *s)
  1526. {
  1527.   if (!dc)
  1528.     return;
  1529.  
  1530.   ClearText();
  1531.  
  1532.   float w = 100.0;
  1533.   float h = 50.0;
  1534.  
  1535.   // What should the label bounding box be really?? User definable??
  1536.   wxList *string_list = ::FormatText(dc, s, w, h);
  1537.   wxNode *node = string_list->First();
  1538.   while (node)
  1539.   {
  1540.     char *s = (char *)node->Data();
  1541.     CanvasObjectTextLine *line = new CanvasObjectTextLine(0.0, 0.0, s);
  1542.     text.Append((wxObject *)line);
  1543.     delete node;
  1544.     node = string_list->First();
  1545.   }
  1546.   delete string_list;
  1547.   CentreText(dc, &text, xpos, ypos, w, h);
  1548.  
  1549.   if (font) dc->SetFont(font);
  1550.  
  1551.   GetCentredTextExtent(dc, &text, xpos, ypos, w, h,
  1552.                                      &actual_text_width, &actual_text_height);
  1553.   formatted = TRUE;
  1554. }
  1555.  
  1556. /*
  1557.  * Find whether line is supposed to be vertical or horizontal and
  1558.  * make it so.
  1559.  *
  1560.  */
  1561. void GraphicsStraightenLine(wxPoint *point1, wxPoint *point2)
  1562. {
  1563.   float dx = point2->x - point1->x;
  1564.   float dy = point2->y - point1->y;
  1565.  
  1566.   if (dx == 0.0)
  1567.     return;
  1568.   else if (fabs(dy/dx) > 1.0)
  1569.   {
  1570.     point2->x = point1->x;
  1571.   }
  1572.   else point2->y = point1->y;
  1573. }
  1574.  
  1575. void LineObject::Straighten(void)
  1576. {
  1577.   if (!line_control_points || line_control_points->Number() < 3)
  1578.     return;
  1579.  
  1580.   Erase();
  1581.  
  1582.   wxNode *first_point_node = line_control_points->First();
  1583.   wxNode *last_point_node = line_control_points->Last();
  1584.   wxNode *second_last_point_node = last_point_node->Previous();
  1585.  
  1586.   wxPoint *last_point = (wxPoint *)last_point_node->Data();
  1587.   wxPoint *second_last_point = (wxPoint *)second_last_point_node->Data();
  1588.  
  1589.   GraphicsStraightenLine(last_point, second_last_point);
  1590.  
  1591.   wxNode *node = first_point_node;
  1592.   while (node && (node != second_last_point_node))
  1593.   {
  1594.     wxPoint *point = (wxPoint *)node->Data();
  1595.     wxPoint *next_point = (wxPoint *)(node->Next()->Data());
  1596.  
  1597.     GraphicsStraightenLine(point, next_point);
  1598.     node = node->Next();
  1599.   }
  1600.  
  1601.   Draw();
  1602. }
  1603.  
  1604.  
  1605. void LineObject::Unlink(void)
  1606. {
  1607.   if (to)
  1608.     to->lines.DeleteObject(this);
  1609.   if (from)
  1610.     from->lines.DeleteObject(this);
  1611.   to = NULL;
  1612.   from = NULL;
  1613. }
  1614.  
  1615. void LineObject::SetEnds(float x1, float y1, float x2, float y2)
  1616. {
  1617.   // Find centre point
  1618.   float min_x;
  1619.   float min_y;
  1620.   if (x1 < x2)
  1621.     min_x = x1;
  1622.   else min_x = x2;
  1623.  
  1624.   if (y1 < y2)
  1625.     min_y = y1;
  1626.   else min_y = y2;
  1627.  
  1628.   xpos = (float)(fabs((x2 - x1)/2.0) + min_x);
  1629.   ypos = (float)(fabs((y2 - y1)/2.0) + min_y);
  1630.  
  1631.   // Now find offsets from centre
  1632.   xpos1 = x1 - xpos; ypos1 = y1 - ypos;
  1633.   xpos2 = x2 - xpos; ypos2 = y2 - ypos;
  1634. }
  1635.  
  1636. // Get absolute positions of ends
  1637. void LineObject::GetEnds(float *x1, float *y1, float *x2, float *y2)
  1638. {
  1639.   *x1 = xpos1 + xpos; *y1 = ypos1 + ypos;
  1640.   *x2 = xpos2 + xpos; *y2 = ypos2 + ypos;
  1641. }
  1642.  
  1643. void LineObject::SetAttachments(int from_attach, int to_attach)
  1644. {
  1645.   attachment_from = from_attach;
  1646.   attachment_to = to_attach;
  1647. }
  1648.  
  1649. Bool LineObject::HitTest(float x, float y, int *attachment, float *distance)
  1650. {
  1651.   if (!line_control_points)
  1652.     return FALSE;
  1653.  
  1654.   wxNode *node = line_control_points->First();
  1655.  
  1656.   while (node && node->Next())
  1657.   {
  1658.     wxPoint *point1 = (wxPoint *)node->Data();
  1659.     wxPoint *point2 = (wxPoint *)node->Next()->Data();
  1660.  
  1661.     // Allow for inaccurate mousing or vert/horiz lines
  1662.     int extra = 4;
  1663.     float left = min(point1->x, point2->x) - extra;
  1664.     float right = max(point1->x, point2->x) + extra;
  1665.  
  1666.     float bottom = min(point1->y, point2->y) - extra;
  1667.     float top = max(point1->y, point2->y) + extra;
  1668.  
  1669.     if (x > left && x < right && y > bottom && y < top)
  1670.     {
  1671.       // Work out distance from centre of line
  1672.       float centre_x = left + (right - left)/2.0;
  1673.       float centre_y = bottom + (top - bottom)/2.0;
  1674.  
  1675.       *attachment = 0;
  1676.       *distance = sqrt((centre_x - x)*(centre_x - x) + (centre_y - y)*(centre_y - y));
  1677.       return TRUE;
  1678.     }
  1679.  
  1680.     node = node->Next();
  1681.   }
  1682.   return FALSE;
  1683. }
  1684.  
  1685.  
  1686. void LineObject::DrawArrows(void)
  1687. {
  1688.   if (dc)
  1689.   {
  1690.  
  1691.     switch (end_style)
  1692.     {
  1693.       case ARROW_ONE:
  1694.       {
  1695.         float x1, y1, x2, y2;
  1696.         wxNode *last_line_node = line_control_points->Last();
  1697.         wxPoint *last_line_point = (wxPoint *)last_line_node->Data();
  1698.         wxNode *second_last_line_node = last_line_node->Previous();
  1699.         wxPoint *second_last_line_point = (wxPoint *)second_last_line_node->Data();
  1700.  
  1701.         x2 = (float)last_line_point->x;
  1702.         y2 = (float)last_line_point->y;
  1703.         x1 = (float)second_last_line_point->x;
  1704.         y1 = (float)second_last_line_point->y;
  1705.  
  1706.         float tip_x, tip_y, side1_x, side1_y, side2_x, side2_y;
  1707.         get_arrow_points(x1, y1, x2, y2, arrow_length, arrow_width, &tip_x, &tip_y,
  1708.                          &side1_x, &side1_y, &side2_x, &side2_y);
  1709.  
  1710.         wxPoint points[4];
  1711.         points[0].x = tip_x; points[0].y = tip_y;
  1712.         points[1].x = side1_x; points[1].y = side1_y;
  1713.         points[2].x = side2_x; points[2].y = side2_y;
  1714.         points[3].x = tip_x; points[3].y = tip_y;
  1715.  
  1716.         dc->DrawPolygon(4, points);
  1717.         break;
  1718.       }
  1719.       case ARROW_BOTH:
  1720.       {
  1721.         float x1, y1, x2, y2;
  1722.         wxNode *last_line_node = line_control_points->Last();
  1723.         wxPoint *last_line_point = (wxPoint *)last_line_node->Data();
  1724.         wxNode *second_last_line_node = last_line_node->Previous();
  1725.         wxPoint *second_last_line_point = (wxPoint *)second_last_line_node->Data();
  1726.  
  1727.         x2 = (float)last_line_point->x;
  1728.         y2 = (float)last_line_point->y;
  1729.         x1 = (float)second_last_line_point->x;
  1730.         y1 = (float)second_last_line_point->y;
  1731.  
  1732.         float tip_x, tip_y, side1_x, side1_y, side2_x, side2_y;
  1733.         get_arrow_points(x1, y1, x2, y2, arrow_length, arrow_width, &tip_x, &tip_y,
  1734.                          &side1_x, &side1_y, &side2_x, &side2_y);
  1735.  
  1736.         wxPoint points[4];
  1737.         points[0].x = tip_x; points[0].y = tip_y;
  1738.         points[1].x = side1_x; points[1].y = side1_y;
  1739.         points[2].x = side2_x; points[2].y = side2_y;
  1740.         points[3].x = tip_x; points[3].y = tip_y;
  1741.  
  1742.         dc->DrawPolygon(4, points);
  1743.  
  1744.         // Other end
  1745.         last_line_node = line_control_points->First();
  1746.         last_line_point = (wxPoint *)last_line_node->Data();
  1747.         second_last_line_node = last_line_node->Next();
  1748.         second_last_line_point = (wxPoint *)second_last_line_node->Data();
  1749.  
  1750.         x2 = (float)last_line_point->x;
  1751.         y2 = (float)last_line_point->y;
  1752.         x1 = (float)second_last_line_point->x;
  1753.         y1 = (float)second_last_line_point->y;
  1754.  
  1755.         get_arrow_points(x1, y1, x2, y2, arrow_length, arrow_width, &tip_x, &tip_y,
  1756.                          &side1_x, &side1_y, &side2_x, &side2_y);
  1757.  
  1758.         points[0].x = tip_x; points[0].y = tip_y;
  1759.         points[1].x = side1_x; points[1].y = side1_y;
  1760.         points[2].x = side2_x; points[2].y = side2_y;
  1761.         points[3].x = tip_x; points[3].y = tip_y;
  1762.  
  1763.         dc->DrawPolygon(4, points);
  1764.         break;
  1765.       }
  1766.       case ARROW_NONE:
  1767.       default: break;
  1768.     }
  1769.   }
  1770. }
  1771.  
  1772.  
  1773. void LineObject::OnErase(void)
  1774. {
  1775.   if (dc)
  1776.   {
  1777.     wxPen *old_pen = pen;
  1778.     wxBrush *old_brush = brush;
  1779.     SetPen(white_background_pen);
  1780.     SetBrush(white_background_brush);
  1781.  
  1782.     float bound_x, bound_y;
  1783.     GetBoundingBox(&bound_x, &bound_y);
  1784.     if (font) dc->SetFont(font);
  1785.  
  1786.     // Undraw text
  1787.     dc->SetPen(white_background_pen);
  1788.     dc->SetBrush(white_background_brush);
  1789.  
  1790.     // Want to take the middle section for the label
  1791.     int n = line_control_points->Number();
  1792.     int half_way = (int)(n/2);
  1793.  
  1794.     // Find middle of this line
  1795.     wxNode *node = line_control_points->Nth(half_way - 1);
  1796.     wxPoint *point = (wxPoint *)node->Data();
  1797.     wxNode *next_node = node->Next();
  1798.     wxPoint *next_point = (wxPoint *)next_node->Data();
  1799.  
  1800.     float dx = (next_point->x - point->x);
  1801.     float dy = (next_point->y - point->y);
  1802.     float x = point->x + dx/2.0;
  1803.     float y = point->y + dy/2.0;
  1804.  
  1805.     dc->DrawRectangle(
  1806.                      (float)(x - actual_text_width/2.0), (float)(y - actual_text_height/2.0),
  1807.                      (float)actual_text_width, (float)actual_text_height);
  1808.  
  1809.     // Undraw line
  1810.     SetPen(white_background_pen);
  1811.     SetBrush(white_background_brush);
  1812.  
  1813.     OnDraw();
  1814.     OnEraseControlPoints();
  1815.  
  1816.     if (old_pen) SetPen(old_pen);
  1817.     if (old_brush) SetBrush(old_brush);
  1818.   }
  1819. }
  1820.  
  1821. void LineObject::GetBoundingBox(float *w, float *h)
  1822. {
  1823.   float x1 = 10000;
  1824.   float y1 = 10000;
  1825.   float x2 = -10000;
  1826.   float y2 = -10000;
  1827.  
  1828.   wxNode *node = line_control_points->First();
  1829.   while (node)
  1830.   {
  1831.     wxPoint *point = (wxPoint *)node->Data();
  1832.     
  1833.     if (point->x < x1) x1 = point->x;
  1834.     if (point->y < y1) y1 = point->y;
  1835.     if (point->x > x2) x2 = point->x;
  1836.     if (point->y > y2) y2 = point->y;
  1837.  
  1838.     node = node->Next();
  1839.   }
  1840.   *w = (float)(x2 - x1);
  1841.   *h = (float)(y2 - y1);
  1842. }
  1843.  
  1844. /*
  1845.  * For a node image of interest, finds the position of this arc
  1846.  * amongst all the arcs which are attached to THIS SIDE of the node image,
  1847.  * and the number of same.
  1848.  */
  1849. void LineObject::FindNth(CanvasObject *image, int *nth, int *no_arcs)
  1850. {
  1851.   int n = -1;
  1852.   int num = 0;
  1853.   wxNode *node = image->lines.First();
  1854.   int this_attachment;
  1855.   if (image == to)
  1856.     this_attachment = attachment_to;
  1857.   else
  1858.     this_attachment = attachment_from;
  1859.  
  1860.   // Find number of lines going into/out of this particular attachment point
  1861.   while (node)
  1862.   {
  1863.     LineObject *line = (LineObject *)node->Data();
  1864.  
  1865.     // This is the nth line attached to 'image'
  1866.     if (line == this)
  1867.       n = num;
  1868.  
  1869.     if (line->to == image)
  1870.     {
  1871.       // Increment num count if this is the same side (attachment number)
  1872.       if (line->attachment_to == this_attachment)
  1873.         num ++;
  1874.     }
  1875.     if (line->from == image)
  1876.     {
  1877.       // Increment num count if this is the same side (attachment number)
  1878.       if (line->attachment_from == this_attachment)
  1879.         num ++;
  1880.     }
  1881.     node = node->Next();
  1882.   }
  1883.   *nth = n;
  1884.   *no_arcs = num;
  1885. }
  1886.  
  1887. void LineObject::OnDrawOutline(void)
  1888. {
  1889.   wxPen *old_pen = pen;
  1890.   wxBrush *old_brush = brush;
  1891.  
  1892.   SetPen(black_dashed_pen);
  1893.   SetBrush(transparent_brush);
  1894.  
  1895.   OnDraw();
  1896.  
  1897.   if (old_pen) SetPen(old_pen);
  1898.   if (old_brush) SetBrush(old_brush);
  1899. }
  1900.  
  1901. void LineObject::OnMove(float x, float y, float old_x, float old_y)
  1902. {
  1903.   float x_offset = x - old_x;
  1904.   float y_offset = y - old_y;
  1905.  
  1906.   if (line_control_points && !(x_offset == 0.0 && y_offset == 0.0))
  1907.   {
  1908.     wxNode *node = line_control_points->First();
  1909.     while (node)
  1910.     {
  1911.       wxPoint *point = (wxPoint *)node->Data();
  1912.       point->x += x_offset;
  1913.       point->y += y_offset;
  1914.       node = node->Next();
  1915.     }
  1916.  
  1917.   }
  1918.   find_polyline_centroid(line_control_points, &xpos, &ypos);
  1919. }
  1920.  
  1921. void LineObject::OnMoveLink(void)
  1922. {
  1923.   if (dc)
  1924.   {
  1925.     if (line_control_points->Number() > 2)
  1926.       Initialise();
  1927.  
  1928.     // Do each end - nothing in the middle. User has to move other points
  1929.     // manually if necessary.
  1930.     float end_x, end_y;
  1931.     float other_end_x, other_end_y;
  1932.  
  1933.     wxNode *first = line_control_points->First();
  1934.     wxPoint *first_point = (wxPoint *)first->Data();
  1935.     wxNode *last = line_control_points->Last();
  1936.     wxPoint *last_point = (wxPoint *)last->Data();
  1937.  
  1938.     wxNode *second = first->Next();
  1939.     wxPoint *second_point = (wxPoint *)second->Data();
  1940.  
  1941.     wxNode *second_last = last->Previous();
  1942.     wxPoint *second_last_point = (wxPoint *)second_last->Data();
  1943.  
  1944.     // Should use to->xpos rather than other current end!
  1945.     if (line_control_points->Number() > 2)
  1946.     {
  1947.       if (from->attachment_mode)
  1948.       {
  1949.         int nth, no_arcs;
  1950.         FindNth(from, &nth, &no_arcs);
  1951.         from->GetAttachmentPosition(attachment_from, &end_x, &end_y, nth, no_arcs);
  1952.       }
  1953.       else
  1954.         (void)from->GetPerimeterPoint(from->xpos, from->ypos,
  1955.                                    (float)second_point->x, (float)second_point->y,
  1956.                                     &end_x, &end_y);
  1957.  
  1958.       if (to->attachment_mode)
  1959.       {
  1960.         int nth, no_arcs;
  1961.         FindNth(to, &nth, &no_arcs);
  1962.         to->GetAttachmentPosition(attachment_to, &other_end_x, &other_end_y, nth, no_arcs);
  1963.       }
  1964.       else
  1965.         (void)to->GetPerimeterPoint(to->xpos, to->ypos,
  1966.                                   (float)second_last_point->x, (float)second_last_point->y,
  1967.                                   &other_end_x, &other_end_y);
  1968.     }
  1969.     else
  1970.     {
  1971.       if (from->attachment_mode)
  1972.       {
  1973.         int nth, no_arcs;
  1974.         FindNth(from, &nth, &no_arcs);
  1975.         from->GetAttachmentPosition(attachment_from, &end_x, &end_y, nth, no_arcs);
  1976.       }
  1977.       else
  1978.         (void)from->GetPerimeterPoint(from->xpos, from->ypos,
  1979.                                     to->xpos, to->ypos,
  1980.                                     &end_x, &end_y);
  1981.  
  1982.       if (to->attachment_mode)
  1983.       {
  1984.         int nth, no_arcs;
  1985.         FindNth(to, &nth, &no_arcs);
  1986.         to->GetAttachmentPosition(attachment_to, &other_end_x, &other_end_y, nth, no_arcs);
  1987.       }
  1988.       else
  1989.         (void)to->GetPerimeterPoint(to->xpos, to->ypos,
  1990.                                   from->xpos, from->ypos,
  1991.                                   &other_end_x, &other_end_y);
  1992.     }
  1993.  
  1994.     first_point->x = end_x; first_point->y = end_y;
  1995.     last_point->x = other_end_x; last_point->y = other_end_y;
  1996.  
  1997.     SetEnds(end_x, end_y, other_end_x, other_end_y);
  1998.     Move(xpos, ypos);
  1999.  
  2000.   }
  2001. }
  2002.  
  2003. void LineObject::OnDraw(void)
  2004. {
  2005.   if (dc && line_control_points)
  2006.   {
  2007.     if (pen)
  2008.       dc->SetPen(pen);
  2009.     if (brush)
  2010.       dc->SetBrush(brush);
  2011.  
  2012.     dc->DrawLines(line_control_points);
  2013.     DrawArrows();
  2014.   }
  2015. }
  2016.  
  2017. void LineObject::OnDragLeft(Bool draw, float x, float y, int keys, int attachment)
  2018. {
  2019. }
  2020.  
  2021. void LineObject::OnBeginDragLeft(float x, float y, int keys, int attachment)
  2022. {
  2023. }
  2024.  
  2025. void LineObject::OnEndDragLeft(float x, float y, int keys, int attachment)
  2026. {
  2027. }
  2028.  
  2029. void LineObject::SetArrowSize(float length, float width)
  2030. {
  2031.   arrow_length = length;
  2032.   arrow_width = width;
  2033. }
  2034.  
  2035. void LineObject::SetStartArrow(int style)
  2036. {
  2037.   start_style = style;
  2038. }
  2039.  
  2040. void LineObject::SetMiddleArrow(int style)
  2041. {
  2042.   middle_style = style;
  2043. }
  2044.  
  2045. void LineObject::SetEndArrow(int style)
  2046. {
  2047.   end_style = style;
  2048. }
  2049.  
  2050. void LineObject::OnDrawContents(void)
  2051. {
  2052.   if (disable_label)
  2053.     return;
  2054.  
  2055.   // Want to take the middle section for the label
  2056.   int n = line_control_points->Number();
  2057.   int half_way = (int)(n/2);
  2058.  
  2059.   // Find middle of this line
  2060.   wxNode *node = line_control_points->Nth(half_way - 1);
  2061.   wxPoint *point = (wxPoint *)node->Data();
  2062.   wxNode *next_node = node->Next();
  2063.   wxPoint *next_point = (wxPoint *)next_node->Data();
  2064.  
  2065.   float dx = (next_point->x - point->x);
  2066.   float dy = (next_point->y - point->y);
  2067.   float x = point->x + dx/2.0;
  2068.   float y = point->y + dy/2.0;
  2069.  
  2070.   float bound_x, bound_y;
  2071.   GetBoundingBox(&bound_x, &bound_y);
  2072.   if (dc)
  2073.   {
  2074.     // First, clear a rectangle for the text IF there is any
  2075.     if (text.Number() > 0)
  2076.     {
  2077.       dc->SetPen(white_background_pen);
  2078.       dc->SetBrush(white_background_brush);
  2079.  
  2080.       // Now draw the text
  2081.       if (font) dc->SetFont(font);
  2082.  
  2083.       if (!formatted || (actual_text_width == 0.0 && actual_text_height == 0.0))
  2084.       {
  2085.         GetCentredTextExtent(dc, &text, x, y, bound_x, bound_y,
  2086.                                      &actual_text_width, &actual_text_height);
  2087.       }
  2088.  
  2089.       dc->DrawRectangle((float)(x - actual_text_width/2.0), (float)(y - actual_text_height/2.0),
  2090.                         (float)actual_text_width, (float)actual_text_height);
  2091.  
  2092.       if (pen) dc->SetPen(pen);
  2093.       if (text_colour) dc->SetTextForeground(text_colour);
  2094.  
  2095. #ifdef wx_msw
  2096.       dc->SetTextBackground(white_background_brush->colour);
  2097. #endif
  2098.  
  2099.       if (!formatted)
  2100.       {
  2101.         CentreTextNoClipping(dc, &text, x, y, actual_text_width, actual_text_height);
  2102.         formatted = TRUE;
  2103.       }
  2104.       DrawFormattedText(dc, &text, x, y, actual_text_width, actual_text_height);
  2105.     }
  2106.   }
  2107. }
  2108.  
  2109. CanvasObject *LineObject::GetTo(void)
  2110. {
  2111.   return to;
  2112. }
  2113.  
  2114. CanvasObject *LineObject::GetFrom(void)
  2115. {
  2116.   return from;
  2117. }
  2118.  
  2119. void LineObject::SetTo(CanvasObject *object)
  2120. {
  2121.   to = object;
  2122. }
  2123.  
  2124. void LineObject::SetFrom(CanvasObject *object)
  2125. {
  2126.   from = object;
  2127. }
  2128.  
  2129. void LineObject::MakeControlPoints(void)
  2130. {
  2131.   if (canvas && dc && line_control_points)
  2132.   {
  2133.     wxNode *first = line_control_points->First();
  2134.     wxNode *last = line_control_points->Last();
  2135.     wxPoint *first_point = (wxPoint *)first->Data();
  2136.     wxPoint *last_point = (wxPoint *)last->Data();
  2137.  
  2138.     LineControlPoint *control = new LineControlPoint(canvas, this, CONTROL_POINT_SIZE, 
  2139.                                                first_point->x, first_point->y,
  2140.                                                CONTROL_POINT_ENDPOINT_FROM);
  2141.     control->point = first_point;
  2142.     canvas->AddObject(control);
  2143.     control_points.Append(control);
  2144.  
  2145.  
  2146.     wxNode *node = first->Next();
  2147.     while (node != last)
  2148.     {
  2149.       wxPoint *point = (wxPoint *)node->Data();
  2150.  
  2151.       control = new LineControlPoint(canvas, this, CONTROL_POINT_SIZE, 
  2152.                                                point->x, point->y,
  2153.                                                CONTROL_POINT_LINE);
  2154.       control->point = point;
  2155.  
  2156.       canvas->AddObject(control);
  2157.       control_points.Append(control);
  2158.  
  2159.       node = node->Next();
  2160.     }
  2161.     control = new LineControlPoint(canvas, this, CONTROL_POINT_SIZE, 
  2162.                                                last_point->x, last_point->y,
  2163.                                                CONTROL_POINT_ENDPOINT_TO);
  2164.     control->point = last_point;
  2165.     canvas->AddObject(control);
  2166.     control_points.Append(control);
  2167.  
  2168.   }
  2169.  
  2170. }
  2171.  
  2172. void LineObject::ResetControlPoints(void)
  2173. {
  2174.   if (canvas && line_control_points && control_points.Number() > 0)
  2175.   {
  2176.     wxNode *node = control_points.First();
  2177.     wxNode *control_node = line_control_points->First();
  2178.     while (node)
  2179.     {
  2180.       wxPoint *point = (wxPoint *)control_node->Data();
  2181.       LineControlPoint *control = (LineControlPoint *)node->Data();
  2182.       control->xpos = point->x;
  2183.       control->ypos = point->y;
  2184.  
  2185.       node = node->Next();
  2186.       control_node = control_node->Next();
  2187.     }
  2188.   }
  2189. }
  2190.  
  2191. void LineObject::Copy(LineObject& copy)
  2192. {
  2193.   CanvasObject::Copy(copy);
  2194.  
  2195.   copy.xpos1 = xpos1;
  2196.   copy.xpos2 = xpos2;
  2197.   copy.ypos1 = ypos1;
  2198.   copy.ypos2 = ypos2;
  2199.  
  2200.   if (!copy.line_control_points)
  2201.     copy.line_control_points = new wxList;
  2202.  
  2203.   wxNode *node = line_control_points->First();
  2204.   while (node)
  2205.   {
  2206.     wxPoint *point = (wxPoint *)node->Data();
  2207.     wxPoint *new_point = new wxPoint(point->x, point->y);
  2208.     copy.line_control_points->Append(new_point);
  2209.     node = node->Next();
  2210.   }
  2211.  
  2212.   copy.start_style = start_style;
  2213.   copy.end_style = end_style;
  2214.   copy.middle_style = middle_style;
  2215.  
  2216.   copy.arrow_length = arrow_length;
  2217.   copy.arrow_width = arrow_width;
  2218. }
  2219.  
  2220. // Spline object
  2221.  
  2222. SplineObject::SplineObject(void)
  2223. {
  2224.   line_control_points = NULL;
  2225. }
  2226.  
  2227. SplineObject::SplineObject(wxList *list)
  2228. {
  2229.   line_control_points = list;
  2230. }
  2231.  
  2232. SplineObject::~SplineObject(void)
  2233. {
  2234. }
  2235.  
  2236. void SplineObject::OnDraw(void)
  2237. {
  2238.   if (dc && line_control_points)
  2239.   {
  2240.     if (pen)
  2241.       dc->SetPen(pen);
  2242.     if (brush)
  2243.       dc->SetBrush(brush);
  2244.  
  2245.     dc->DrawSpline(line_control_points);
  2246.     DrawArrows();
  2247.   }
  2248. }
  2249.  
  2250. Bool SplineObject::HitTest(float x, float y, int *attachment, float *distance)
  2251. {
  2252.   return LineObject::HitTest(x, y, attachment, distance);
  2253. }
  2254.  
  2255. void SplineObject::Copy(SplineObject& copy)
  2256. {
  2257.   LineObject::Copy(copy);
  2258. }
  2259.  
  2260. // Control points
  2261. ControlPoint::ControlPoint(ObjectCanvas *the_canvas, CanvasObject *object, float size, float the_xoffset, float the_yoffset, int the_type):RectangleObject(size, size)
  2262. {
  2263.   canvas = the_canvas;
  2264.   canvas_object = object;
  2265.   xoffset = the_xoffset;
  2266.   yoffset = the_yoffset;
  2267.   type = the_type;
  2268.   SetPen(black_foreground_pen);
  2269.   SetBrush(black_brush);
  2270.   old_cursor = NULL;
  2271.   visible = TRUE;
  2272. }
  2273.  
  2274. ControlPoint::~ControlPoint(void)
  2275. {
  2276. }
  2277.  
  2278. // Don't even attempt to draw any text - waste of time!
  2279. void ControlPoint::OnDrawContents(void)
  2280. {
  2281. }
  2282.  
  2283. void ControlPoint::OnDraw(void)
  2284. {
  2285.   xpos = canvas_object->GetX() + xoffset;
  2286.   ypos = canvas_object->GetY() + yoffset;
  2287.   RectangleObject::OnDraw();
  2288. }
  2289.  
  2290. void ControlPoint::OnErase(void)
  2291. {
  2292.   RectangleObject::OnErase();
  2293. }
  2294.  
  2295. // Implement resizing of canvas object
  2296. void ControlPoint::OnDragLeft(Bool draw, float x, float y, int keys, int attachment)
  2297. {
  2298.   float bound_x;
  2299.   float bound_y;
  2300.   canvas_object->GetBoundingBox(&bound_x, &bound_y);
  2301.   float new_width = (float)(2.0*fabs(x - canvas_object->xpos));
  2302.   float new_height = (float)(2.0*fabs(y - canvas_object->ypos));
  2303.  
  2304.   // Constrain sizing according to what control point you're dragging
  2305.   if (type == CONTROL_POINT_HORIZONTAL)
  2306.     new_height = bound_y;
  2307.   if (type == CONTROL_POINT_VERTICAL)
  2308.     new_width = bound_x;
  2309.   if (type == CONTROL_POINT_DIAGONAL && (keys & KEY_SHIFT))
  2310.   {
  2311.     new_height = bound_y*(new_width/bound_x);
  2312.   }
  2313.  
  2314.   canvas_object->SetSize(new_width, new_height);
  2315.   canvas_object->OnDrawOutline();
  2316. }
  2317.  
  2318. void ControlPoint::OnBeginDragLeft(float x, float y, int keys, int attachment)
  2319. {
  2320.   canvas_object->Erase();
  2321.  
  2322.   dc->SetLogicalFunction(wxXOR);
  2323.  
  2324.   float bound_x;
  2325.   float bound_y;
  2326.   canvas_object->GetBoundingBox(&bound_x, &bound_y);
  2327.   float new_width = (float)(2.0*fabs(x - canvas_object->xpos));
  2328.   float new_height = (float)(2.0*fabs(y - canvas_object->ypos));
  2329.  
  2330.   // Constrain sizing according to what control point you're dragging
  2331.   if (type == CONTROL_POINT_HORIZONTAL)
  2332.     new_height = bound_y;
  2333.   if (type == CONTROL_POINT_VERTICAL)
  2334.     new_width = bound_x;
  2335.   if (type == CONTROL_POINT_DIAGONAL && (keys & KEY_SHIFT))
  2336.   {
  2337.     new_height = bound_y*(new_width/bound_x);
  2338.   }
  2339.  
  2340.   canvas_object->SetSize(new_width, new_height);
  2341.   canvas_object->OnDrawOutline();
  2342. }
  2343.  
  2344. void ControlPoint::OnEndDragLeft(float x, float y, int keys, int attachment)
  2345. {
  2346.   dc->SetLogicalFunction(wxCOPY);
  2347.   canvas_object->ResetControlPoints();
  2348.   canvas_object->Move(canvas_object->GetX(), canvas_object->GetY());
  2349.   if (!canvas->quick_edit_mode) canvas->Redraw();
  2350. }
  2351.  
  2352. int ControlPoint::GetNumberOfAttachments(void)
  2353. {
  2354.   return 1;
  2355. }
  2356.  
  2357. void ControlPoint::GetAttachmentPosition(int attachment, float *x, float *y,
  2358.                                          int nth, int no_arcs)
  2359. {
  2360.   *x = xpos; *y = ypos;
  2361. }
  2362.  
  2363.  
  2364. /*
  2365.  * Line control point
  2366.  *
  2367.  */
  2368.  
  2369. LineControlPoint::LineControlPoint(ObjectCanvas *the_canvas, CanvasObject *object, float size, float x, float y, int the_type):
  2370.   ControlPoint(the_canvas, object, size, x, y, the_type)
  2371. {
  2372.   xpos = x;
  2373.   ypos = y;
  2374.   type = the_type;
  2375. }
  2376.  
  2377. LineControlPoint::~LineControlPoint(void)
  2378. {
  2379. }
  2380.  
  2381. void LineControlPoint::OnDraw(void)
  2382. {
  2383.   RectangleObject::OnDraw();
  2384. }
  2385.  
  2386. // Implement movement of Line point
  2387. void LineControlPoint::OnDragLeft(Bool draw, float x, float y, int keys, int attachment)
  2388. {
  2389.   if (type == CONTROL_POINT_LINE)
  2390.   {
  2391.     canvas->Snap(&x, &y);
  2392.  
  2393.     xpos = x; ypos = y;
  2394.     point->x = x; point->y = y;
  2395.  
  2396.     LineObject *line_object = (LineObject *)canvas_object;
  2397.     wxPen *old_pen = line_object->pen;
  2398.     wxBrush *old_brush = line_object->brush;
  2399.  
  2400.     line_object->SetPen(black_dashed_pen);
  2401. //    line_object->SetBrush(transparent_brush);
  2402.  
  2403.     line_object->OnMoveLink();
  2404.  
  2405.     if (old_pen) line_object->SetPen(old_pen);
  2406.     if (old_brush) line_object->SetBrush(old_brush);
  2407.   }
  2408.  
  2409.   if (type == CONTROL_POINT_ENDPOINT_FROM || type == CONTROL_POINT_ENDPOINT_TO)
  2410.   {
  2411.     xpos = x; ypos = y;
  2412.   }
  2413.  
  2414. }
  2415.  
  2416. void LineControlPoint::OnBeginDragLeft(float x, float y, int keys, int attachment)
  2417. {
  2418.   if (type == CONTROL_POINT_LINE)
  2419.   {
  2420.     canvas->Snap(&x, &y);
  2421.  
  2422.     canvas_object->Erase();
  2423.     canvas_object->disable_label = TRUE;
  2424.     dc->SetLogicalFunction(wxXOR);
  2425.  
  2426.     xpos = x; ypos = y;
  2427.     point->x = x; point->y = y;
  2428.  
  2429.     LineObject *line_object = (LineObject *)canvas_object;
  2430.     wxPen *old_pen = line_object->pen;
  2431.     wxBrush *old_brush = line_object->brush;
  2432.  
  2433.     line_object->SetPen(black_dashed_pen);
  2434. //    line_object->SetBrush(transparent_brush);
  2435.  
  2436.     line_object->OnMoveLink();
  2437.  
  2438.     if (old_pen) line_object->SetPen(old_pen);
  2439.     if (old_brush) line_object->SetBrush(old_brush);
  2440.   }
  2441.  
  2442.   if (type == CONTROL_POINT_ENDPOINT_FROM || type == CONTROL_POINT_ENDPOINT_TO)
  2443.   {
  2444.     old_cursor = canvas->SetCursor(GraphicsBullseyeCursor);
  2445.   }
  2446. }
  2447.   
  2448. void LineControlPoint::OnEndDragLeft(float x, float y, int keys, int attachment)
  2449. {
  2450.   canvas_object->disable_label = FALSE;
  2451.   if (type == CONTROL_POINT_LINE)
  2452.   {
  2453.     canvas->Snap(&x, &y);
  2454.  
  2455.     dc->SetLogicalFunction(wxCOPY);
  2456.     xpos = x; ypos = y;
  2457.     point->x = x; point->y = y;
  2458.  
  2459.     LineObject *line_object = (LineObject *)canvas_object;
  2460.     line_object->OnMoveLink();
  2461.  
  2462.     if (!canvas->quick_edit_mode) canvas->Redraw();
  2463.   }
  2464.   if (type == CONTROL_POINT_ENDPOINT_FROM)
  2465.   {
  2466.     if (old_cursor)
  2467.       canvas->SetCursor(old_cursor);
  2468.  
  2469.     xpos = x; ypos = y;
  2470.  
  2471.     LineObject *line_object = (LineObject *)canvas_object;
  2472.     line_object->from->MoveLineToNewAttachment(line_object, x, y);
  2473.  
  2474.     line_object->from->MoveLinks();
  2475.  
  2476.     if (!canvas->quick_edit_mode) canvas->Redraw();
  2477.   }
  2478.   if (type == CONTROL_POINT_ENDPOINT_TO)
  2479.   {
  2480.     if (old_cursor)
  2481.       canvas->SetCursor(old_cursor);
  2482.  
  2483.     xpos = x; ypos = y;
  2484.  
  2485.     LineObject *line_object = (LineObject *)canvas_object;
  2486.     line_object->to->MoveLineToNewAttachment(line_object, x, y);
  2487.  
  2488.     line_object->to->MoveLinks();
  2489.  
  2490.     if (!canvas->quick_edit_mode) canvas->Redraw();
  2491.   }
  2492. }
  2493.  
  2494. // Implement movement of endpoint to a new attachment
  2495. void LineControlPoint::OnDragRight(Bool draw, float x, float y, int keys, int attachment)
  2496. {
  2497.   if (type == CONTROL_POINT_ENDPOINT_FROM || type == CONTROL_POINT_ENDPOINT_TO)
  2498.   {
  2499.     xpos = x; ypos = y;
  2500.   }
  2501.  
  2502. }
  2503.  
  2504. void LineControlPoint::OnBeginDragRight(float x, float y, int keys, int attachment)
  2505. {
  2506.   if (type == CONTROL_POINT_ENDPOINT_FROM || type == CONTROL_POINT_ENDPOINT_TO)
  2507.   {
  2508.     old_cursor = canvas->SetCursor(GraphicsBullseyeCursor);
  2509.   }
  2510. }
  2511.   
  2512. void LineControlPoint::OnEndDragRight(float x, float y, int keys, int attachment)
  2513. {
  2514.   if (type == CONTROL_POINT_ENDPOINT_FROM)
  2515.   {
  2516.     if (old_cursor)
  2517.       canvas->SetCursor(old_cursor);
  2518.  
  2519.     xpos = x; ypos = y;
  2520.  
  2521.     LineObject *line_object = (LineObject *)canvas_object;
  2522.     line_object->from->EraseLinks();
  2523.  
  2524.     int new_attachment;
  2525.     float distance;
  2526.     if (line_object->from->HitTest(x, y, &new_attachment, &distance))
  2527.       line_object->attachment_from = new_attachment;
  2528.  
  2529.     line_object->from->MoveLinks();
  2530.  
  2531.     if (!canvas->quick_edit_mode) canvas->Redraw();
  2532.   }
  2533.   if (type == CONTROL_POINT_ENDPOINT_TO)
  2534.   {
  2535.     if (old_cursor)
  2536.       canvas->SetCursor(old_cursor);
  2537.  
  2538.     xpos = x; ypos = y;
  2539.  
  2540.     LineObject *line_object = (LineObject *)canvas_object;
  2541.     line_object->to->EraseLinks();
  2542.  
  2543.     int new_attachment;
  2544.     float distance;
  2545.     if (line_object->to->HitTest(x, y, &new_attachment, &distance))
  2546.       line_object->attachment_to = new_attachment;
  2547.  
  2548.     line_object->to->MoveLinks();
  2549.  
  2550.     if (!canvas->quick_edit_mode) canvas->Redraw();
  2551.   }
  2552. }
  2553.  
  2554.  
  2555.  
  2556.  
  2557. // Object canvas
  2558. ObjectCanvas::ObjectCanvas(wxFrame *frame, int x, int y, int w, int h, int style):
  2559.   wxCanvas(frame, x, y, w, h, style)
  2560. {
  2561.   quick_edit_mode = FALSE;
  2562.   snap_to_grid = TRUE;
  2563.   grid_spacing = 5.0;
  2564.   object_list = new wxList;
  2565.   DragState = NoDragging;
  2566.   DraggedObject = NULL;
  2567.   old_drag_x = 0;
  2568.   old_drag_y = 0;
  2569. }
  2570.  
  2571. ObjectCanvas::~ObjectCanvas(void)
  2572. {
  2573.   if (object_list)
  2574.     delete object_list;
  2575. }
  2576.  
  2577. void ObjectCanvas::SetSnapToGrid(Bool snap)
  2578. {
  2579.   snap_to_grid = snap;
  2580. }
  2581.  
  2582. void ObjectCanvas::SetGridSpacing(float spacing)
  2583. {
  2584.   grid_spacing = spacing;
  2585. }
  2586.  
  2587. void ObjectCanvas::Snap(float *x, float *y)
  2588. {
  2589.   if (snap_to_grid)
  2590.   {
  2591.     *x = grid_spacing * ((int)(*x/grid_spacing + 0.5));
  2592.     *y = grid_spacing * ((int)(*y/grid_spacing + 0.5));
  2593.   }
  2594. }
  2595.  
  2596.  
  2597. void ObjectCanvas::OnPaint(void)
  2598. {
  2599.   GetDC()->Clear();
  2600.  
  2601.   Redraw();
  2602. }
  2603.  
  2604. void ObjectCanvas::Redraw(void)
  2605. {
  2606.   if (object_list)
  2607.   {
  2608.     wxCursor *old_cursor = SetCursor(wxHOURGLASS_CURSOR);
  2609.     wxNode *current = object_list->First();
  2610.  
  2611.     while (current)
  2612.     {
  2613.       CanvasObject *object = (CanvasObject *)current->Data();
  2614.       object->Draw();
  2615.  
  2616.       current = current->Next();
  2617.     }
  2618.     
  2619.     SetCursor(old_cursor);
  2620.   }
  2621. }
  2622.  
  2623. void ObjectCanvas::Clear(void)
  2624. {
  2625.    wxCanvas::Clear();
  2626. }
  2627.  
  2628. ObjectCanvas *CanvasObject::GetCanvas(void)
  2629. {
  2630.   return canvas;
  2631. }
  2632.  
  2633. void CanvasObject::SetCanvas(ObjectCanvas *the_canvas)
  2634. {
  2635.   canvas = the_canvas;
  2636. }
  2637.  
  2638. void ObjectCanvas::AddObject(CanvasObject *object)
  2639. {
  2640.   object_list->Append(object);
  2641.   object->SetCanvas(this);
  2642.   object->SetDC(context);
  2643. }
  2644.  
  2645. void ObjectCanvas::InsertObject(CanvasObject *object)
  2646. {
  2647.   object_list->Insert(object);
  2648.   object->SetCanvas(this);
  2649. }
  2650.  
  2651. void ObjectCanvas::RemoveObject(CanvasObject *object)
  2652. {
  2653.   object_list->DeleteObject(object);
  2654. }
  2655.  
  2656. // Should this delete the actual objects too? I think not.
  2657. void ObjectCanvas::RemoveAllObjects(void)
  2658. {
  2659.   object_list->Clear();
  2660. }
  2661.  
  2662. void ObjectCanvas::ShowAll(Bool show)
  2663. {
  2664.   wxNode *current = object_list->First();
  2665.  
  2666.   while (current)
  2667.   {
  2668.     CanvasObject *object = (CanvasObject *)current->Data();
  2669.     object->Show(show);
  2670.  
  2671.     current = current->Next();
  2672.   }
  2673. }
  2674.  
  2675.  
  2676. void ObjectCanvas::OnEvent(wxEvent& event)
  2677. {
  2678.   float x, y;
  2679.   event.Position(&x, &y);
  2680.   int keys = 0;
  2681.   if (event.ShiftDown())
  2682.     keys = keys | KEY_SHIFT;
  2683.   if (event.ControlDown())
  2684.     keys = keys | KEY_CTRL;
  2685.  
  2686.   // Dragging - note that the effect of dragging is left entirely up
  2687.   // to the object, so no movement is done unless explicitly done by
  2688.   // object.
  2689.   if (event.Dragging() && DraggedObject && DragState == StartDraggingLeft)
  2690.   {
  2691.     DragState = ContinueDraggingLeft;
  2692.     DraggedObject->OnBeginDragLeft((float)x, (float)y, keys, DraggedAttachment);
  2693.     old_drag_x = x; old_drag_y = y;
  2694.   }
  2695.   else if (event.Dragging() && DraggedObject && DragState == ContinueDraggingLeft)
  2696.   { 
  2697.     // Continue dragging
  2698.     DraggedObject->OnDragLeft(FALSE, old_drag_x, old_drag_y, keys, DraggedAttachment);
  2699.     DraggedObject->OnDragLeft(TRUE, (float)x, (float)y, keys, DraggedAttachment);
  2700.     old_drag_x = x; old_drag_y = y;
  2701.   }
  2702.   else if (event.LeftUp() && DraggedObject && DragState == ContinueDraggingLeft)
  2703.   {
  2704.     DragState = NoDragging;
  2705.  
  2706.     DraggedObject->OnDragLeft(FALSE, old_drag_x, old_drag_y, keys, DraggedAttachment);
  2707.  
  2708.     DraggedObject->OnEndDragLeft((float)x, (float)y, keys, DraggedAttachment);
  2709.     DraggedObject = NULL;
  2710.   }
  2711.   else if (event.Dragging() && DraggedObject && DragState == StartDraggingRight)
  2712.   {
  2713.     DragState = ContinueDraggingRight;
  2714.     DraggedObject->OnBeginDragRight((float)x, (float)y, keys, DraggedAttachment);
  2715.     old_drag_x = x; old_drag_y = y;
  2716.   }
  2717.   else if (event.Dragging() && DraggedObject && DragState == ContinueDraggingRight)
  2718.   { 
  2719.     // Continue dragging
  2720.     DraggedObject->OnDragRight(FALSE, old_drag_x, old_drag_y, keys, DraggedAttachment);
  2721.     DraggedObject->OnDragRight(TRUE, (float)x, (float)y, keys, DraggedAttachment);
  2722.     old_drag_x = x; old_drag_y = y;
  2723.   }
  2724.   else if (event.RightUp() && DraggedObject && DragState == ContinueDraggingRight)
  2725.   {
  2726.     DragState = NoDragging;
  2727.  
  2728.     DraggedObject->OnDragRight(FALSE, old_drag_x, old_drag_y, keys, DraggedAttachment);
  2729.  
  2730.     DraggedObject->OnEndDragRight((float)x, (float)y, keys, DraggedAttachment);
  2731.     DraggedObject = NULL;
  2732.   }
  2733.  
  2734.   // All following events sent to canvas, not object
  2735.   else if (event.Dragging() && !DraggedObject && DragState == StartDraggingLeft)
  2736.   {
  2737.     DragState = ContinueDraggingLeft;
  2738.     OnBeginDragLeft((float)x, (float)y, keys);
  2739.     old_drag_x = x; old_drag_y = y;
  2740.   }
  2741.   else if (event.Dragging() && !DraggedObject && DragState == ContinueDraggingLeft)
  2742.   { 
  2743.     // Continue dragging
  2744.     OnDragLeft(FALSE, old_drag_x, old_drag_y, keys);
  2745.     OnDragLeft(TRUE, (float)x, (float)y, keys);
  2746.     old_drag_x = x; old_drag_y = y;
  2747.   }
  2748.   else if (event.LeftUp() && !DraggedObject && DragState == ContinueDraggingLeft)
  2749.   {
  2750.     DragState = NoDragging;
  2751.     OnDragLeft(FALSE, old_drag_x, old_drag_y, keys);
  2752.     OnEndDragLeft((float)x, (float)y, keys);
  2753.     DraggedObject = NULL;
  2754.   }
  2755.   else if (event.Dragging() && !DraggedObject && DragState == StartDraggingRight)
  2756.   {
  2757.     DragState = ContinueDraggingRight;
  2758.     OnBeginDragRight((float)x, (float)y, keys);
  2759.     old_drag_x = x; old_drag_y = y;
  2760.   }
  2761.   else if (event.Dragging() && !DraggedObject && DragState == ContinueDraggingRight)
  2762.   { 
  2763.     // Continue dragging
  2764.     OnDragRight(FALSE, old_drag_x, old_drag_y, keys);
  2765.     OnDragRight(TRUE, (float)x, (float)y, keys);
  2766.     old_drag_x = x; old_drag_y = y;
  2767.   }
  2768.   else if (event.RightUp() && !DraggedObject && DragState == ContinueDraggingRight)
  2769.   {
  2770.     DragState = NoDragging;
  2771.  
  2772.     OnDragRight(FALSE, old_drag_x, old_drag_y, keys);
  2773.     OnEndDragRight((float)x, (float)y, keys);
  2774.     DraggedObject = NULL;
  2775.   }
  2776.  
  2777.   // Non-dragging events
  2778.   else if (event.IsButton())
  2779.   {
  2780.     // Find the nearest object
  2781.     int attachment = 0;
  2782.     CanvasObject *nearest_object = FindObject(x, y, &attachment);
  2783.  
  2784.     if (nearest_object) // Object event
  2785.     {
  2786.       if (event.LeftDown())
  2787.       {
  2788.         if (nearest_object->draggable)
  2789.     {
  2790.           DraggedObject = nearest_object;
  2791.           DraggedAttachment = attachment;
  2792.           DragState = StartDraggingLeft;
  2793.         }
  2794.       }
  2795.       else if (event.LeftUp())
  2796.       {
  2797.         // N.B. Only register a click if the same object was
  2798.         // identified for down *and* up.
  2799.         if (nearest_object == DraggedObject)
  2800.           nearest_object->OnLeftClick((float)x, (float)y, keys, attachment);
  2801.  
  2802.         DraggedObject = NULL;
  2803.         DragState = NoDragging;
  2804.       }
  2805.       else if (event.RightDown())
  2806.       {
  2807.         if (nearest_object->draggable)
  2808.     {
  2809.           DraggedObject = nearest_object;
  2810.           DraggedAttachment = attachment;
  2811.           DragState = StartDraggingRight;
  2812.         }
  2813.       }
  2814.       else if (event.RightUp())
  2815.       {
  2816.         if (nearest_object == DraggedObject)
  2817.           nearest_object->OnRightClick((float)x, (float)y, keys, attachment);
  2818.  
  2819.         DraggedObject = NULL;
  2820.         DragState = NoDragging;
  2821.       }
  2822.     }
  2823.     else // Canvas event (no nearest object)
  2824.     {
  2825.       if (event.LeftDown())
  2826.       {
  2827.         DraggedObject = NULL;
  2828.         DragState = StartDraggingLeft;
  2829.       }
  2830.       else if (event.LeftUp())
  2831.       {
  2832.         OnLeftClick((float)x, (float)y, keys);
  2833.  
  2834.         DraggedObject = NULL;
  2835.         DragState = NoDragging;
  2836.       }
  2837.       else if (event.RightDown())
  2838.       {
  2839.         DraggedObject = NULL;
  2840.         DragState = StartDraggingRight;
  2841.       }
  2842.       else if (event.RightUp())
  2843.       {
  2844.         OnRightClick((float)x, (float)y, keys);
  2845.  
  2846.         DraggedObject = NULL;
  2847.         DragState = NoDragging;
  2848.       }
  2849.     }
  2850.   }
  2851. }
  2852.  
  2853. CanvasObject *ObjectCanvas::FindObject(float x, float y, int *attachment)
  2854. {
  2855.   float nearest = 100000.0;
  2856.   int nearest_attachment = 0;
  2857.   CanvasObject *nearest_object = NULL;
  2858.  
  2859.   // Go backward through the object list, since we want:
  2860.   // (a) to have the control points drawn LAST to overlay
  2861.   //     the other objects
  2862.   // (b) to find the control points FIRST if they exist
  2863.  
  2864.   wxNode *current = object_list->Last();
  2865.   while (current)
  2866.   {
  2867.     CanvasObject *object = (CanvasObject *)current->Data();
  2868.  
  2869.     float dist;
  2870.     int temp_attachment;
  2871.  
  2872.     if (object->HitTest(x, y, &temp_attachment, &dist))
  2873.     {
  2874.       if (dist < nearest)
  2875.       {
  2876.         nearest = dist;
  2877.         nearest_object = object;
  2878.         nearest_attachment = temp_attachment;
  2879.       }
  2880.     }
  2881.  
  2882.     current = current->Previous();
  2883.   }
  2884.  
  2885.   *attachment = nearest_attachment;
  2886.   return nearest_object;
  2887. }
  2888.  
  2889. void ObjectCanvas::DrawOutline(float x1, float y1, float x2, float y2)
  2890. {
  2891.   wxDC *dc = GetDC();
  2892.  
  2893.   dc->SetPen(black_dashed_pen);
  2894.   dc->SetBrush(transparent_brush);
  2895.  
  2896.   dc->DrawLine(x1, y1, x2, y1);
  2897.   dc->DrawLine(x2, y1, x2, y2);
  2898.   dc->DrawLine(x2, y2, x1, y2);
  2899.   dc->DrawLine(x1, y2, x1, y1);
  2900. }
  2901.  
  2902. /*
  2903.  * Higher-level events called by OnEvent
  2904.  *
  2905.  */
  2906.  
  2907. void ObjectCanvas::OnLeftClick(float x, float y, int keys)
  2908. {
  2909. }
  2910.  
  2911. void ObjectCanvas::OnRightClick(float x, float y, int keys)
  2912. {
  2913. }
  2914.  
  2915. void ObjectCanvas::OnDragLeft(Bool draw, float x, float y, int keys)
  2916. {
  2917. }
  2918.  
  2919. void ObjectCanvas::OnBeginDragLeft(float x, float y, int keys)
  2920. {
  2921. }
  2922.  
  2923. void ObjectCanvas::OnEndDragLeft(float x, float y, int keys)
  2924. {
  2925. }
  2926.  
  2927. void ObjectCanvas::OnDragRight(Bool draw, float x, float y, int keys)
  2928. {
  2929. }
  2930.  
  2931. void ObjectCanvas::OnBeginDragRight(float x, float y, int keys)
  2932. {
  2933. }
  2934.  
  2935. void ObjectCanvas::OnEndDragRight(float x, float y, int keys)
  2936. {
  2937. }
  2938.  
  2939. // Centre a list of strings in the given box
  2940. void CentreText(wxDC *context, wxList *text_list,
  2941.                               float xpos, float ypos, float width, float height)
  2942. {
  2943.   int n = text_list->Number();
  2944.  
  2945.   if (!text_list || (n == 0))
  2946.     return;
  2947.  
  2948.   // First, get maximum dimensions of box enclosing text
  2949.  
  2950.   float char_height = 0;
  2951.   float max_width = 0;
  2952.   float current_width = 0;
  2953.  
  2954.   // Store text extents for speed
  2955.   float *widths = new float[n];
  2956.  
  2957.   wxNode *current = text_list->First();
  2958.   int i = 0;
  2959.   while (current)
  2960.   {
  2961. //    char *string = (char *)current->Data();
  2962.     CanvasObjectTextLine *line = (CanvasObjectTextLine *)current->Data();
  2963.     context->GetTextExtent(line->line, ¤t_width, &char_height);
  2964.     widths[i] = current_width;
  2965.  
  2966.     if (current_width > max_width)
  2967.       max_width = current_width;
  2968.     current = current->Next();
  2969.     i ++;
  2970.   }
  2971.  
  2972.   float max_height = n*char_height;
  2973.  
  2974.   float yoffset;
  2975.  
  2976.   if (max_height < height)
  2977.     yoffset = (float)(ypos - (height/2.0) + (height - max_height)/2.0);
  2978.   else
  2979.     yoffset = (float)(ypos - (height/2.0));
  2980.  
  2981.   float xoffset = (float)(xpos - width/2.0);
  2982.  
  2983.   current = text_list->First();
  2984.   i = 0;
  2985.  
  2986.   while (current)
  2987.   {
  2988.     CanvasObjectTextLine *line = (CanvasObjectTextLine *)current->Data();
  2989.  
  2990.     float x;
  2991.     if (widths[i] < width)
  2992.       x = (float)((width - widths[i])/2.0 + xoffset);
  2993.     else
  2994.       x = xoffset;
  2995.     float y = (float)(i*char_height + yoffset);
  2996.  
  2997.     line->x = x - xpos; line->y = y - ypos;
  2998.     current = current->Next();
  2999.     i ++;
  3000.   }
  3001.  
  3002.   delete widths;
  3003. }
  3004.  
  3005. // Centre a list of strings in the given box
  3006. void CentreTextNoClipping(wxDC *context, wxList *text_list,
  3007.                               float xpos, float ypos, float width, float height)
  3008. {
  3009.   int n = text_list->Number();
  3010.  
  3011.   if (!text_list || (n == 0))
  3012.     return;
  3013.  
  3014.   // First, get maximum dimensions of box enclosing text
  3015.  
  3016.   float char_height = 0;
  3017.   float max_width = 0;
  3018.   float current_width = 0;
  3019.  
  3020.   // Store text extents for speed
  3021.   float *widths = new float[n];
  3022.  
  3023.   wxNode *current = text_list->First();
  3024.   int i = 0;
  3025.   while (current)
  3026.   {
  3027.     CanvasObjectTextLine *line = (CanvasObjectTextLine *)current->Data();
  3028.     context->GetTextExtent(line->line, ¤t_width, &char_height);
  3029.     widths[i] = current_width;
  3030.  
  3031.     if (current_width > max_width)
  3032.       max_width = current_width;
  3033.     current = current->Next();
  3034.     i ++;
  3035.   }
  3036.  
  3037.   float max_height = n*char_height;
  3038.  
  3039.   float yoffset = (float)(ypos - (height/2.0) + (height - max_height)/2.0);
  3040.  
  3041.   float xoffset = (float)(xpos - width/2.0);
  3042.  
  3043.   current = text_list->First();
  3044.   i = 0;
  3045.  
  3046.   while (current)
  3047.   {
  3048.     CanvasObjectTextLine *line = (CanvasObjectTextLine *)current->Data();
  3049.  
  3050.     float x = (float)((width - widths[i])/2.0 + xoffset);
  3051.     float y = (float)(i*char_height + yoffset);
  3052.  
  3053.     line->x = x - xpos; line->y = y - ypos;
  3054.     current = current->Next();
  3055.     i ++;
  3056.   }
  3057.   delete widths;
  3058. }
  3059.  
  3060. void GetCentredTextExtent(wxDC *context, wxList *text_list,
  3061.                               float xpos, float ypos, float width, float height,
  3062.                               float *actual_width, float *actual_height)
  3063. {
  3064.   int n = text_list->Number();
  3065.  
  3066.   if (!text_list || (n == 0))
  3067.   {
  3068.     *actual_width = 0;
  3069.     *actual_height = 0;
  3070.     return;
  3071.   }
  3072.  
  3073.   // First, get maximum dimensions of box enclosing text
  3074.  
  3075.   float char_height = 0;
  3076.   float max_width = 0;
  3077.   float current_width = 0;
  3078.  
  3079.   wxNode *current = text_list->First();
  3080.   int i = 0;
  3081.   while (current)
  3082.   {
  3083.     CanvasObjectTextLine *line = (CanvasObjectTextLine *)current->Data();
  3084.     context->GetTextExtent(line->line, ¤t_width, &char_height);
  3085.  
  3086.     if (current_width > max_width)
  3087.       max_width = current_width;
  3088.     current = current->Next();
  3089.     i ++;
  3090.   }
  3091.  
  3092.   *actual_height = n*char_height;
  3093.   *actual_width = max_width;
  3094. }
  3095.  
  3096. // Format a string to a list of strings that fit in the given box.
  3097. // Interpret %n as a new line.
  3098. wxList *FormatText(wxDC *context, char *text, float width, float height)
  3099. {
  3100.   // First, parse the string into a list of words
  3101.   wxList word_list;
  3102.  
  3103.   // Make new lines into NULL strings at this point
  3104.   int i = 0; int j = 0; int len = strlen(text);
  3105.   char word[100]; word[0] = 0;
  3106.   Bool end_word = FALSE; Bool new_line = FALSE;
  3107.   while (i < len)
  3108.   {
  3109.     switch (text[i])
  3110.     {
  3111.       case '%':
  3112.       {
  3113.         i ++;
  3114.         if (i == len)
  3115.         { word[j] = '%'; j ++; }
  3116.         else
  3117.         {
  3118.           if (text[i] == 'n')
  3119.           { new_line = TRUE; end_word = TRUE; i++; }
  3120.           else
  3121.           { word[j] = '%'; j ++; word[j] = text[i]; j ++; i ++; }
  3122.         }
  3123.         break;
  3124.       }
  3125.       case ' ':
  3126.       {
  3127.         end_word = TRUE;
  3128.         i ++;
  3129.         break;
  3130.       }
  3131.       default:
  3132.       {
  3133.         word[j] = text[i];
  3134.         j ++; i ++;
  3135.         break;
  3136.       }
  3137.     }
  3138.     if (i == len) end_word = TRUE;
  3139.     if (end_word)
  3140.     {
  3141.       word[j] = 0;
  3142.       j = 0;
  3143.       word_list.Append((wxObject *)copystring(word));
  3144.       end_word = FALSE;
  3145.     }
  3146.     if (new_line)
  3147.     {
  3148.       word_list.Append((wxObject *)NULL);
  3149.       new_line = FALSE;
  3150.     }
  3151.   }
  3152.   // Now, make a list of strings which can fit in the box
  3153.   wxList *string_list = new wxList;
  3154.  
  3155.   char buffer[200];
  3156.   buffer[0] = 0;
  3157.   wxNode *node = word_list.First();
  3158.   float x, y;
  3159.  
  3160.   while (node)
  3161.   {
  3162.     char *keep_string = copystring(buffer);
  3163.  
  3164.     char *s = (char *)node->Data();
  3165.     if (!s)
  3166.     {
  3167.       // FORCE NEW LINE
  3168.       if (strlen(keep_string) > 0)
  3169.         string_list->Append((wxObject *)keep_string);
  3170.       else
  3171.         delete keep_string;
  3172.  
  3173.       buffer[0] = 0;
  3174.     }
  3175.     else
  3176.     {
  3177.       if (buffer[0] != 0)
  3178.         strcat(buffer, " ");
  3179.  
  3180.       strcat(buffer, s);
  3181.       context->GetTextExtent(buffer, &x, &y);
  3182.  
  3183.       if (x > width)
  3184.       {
  3185.         // Deal with first word being wider than box
  3186.         if (strlen(keep_string) > 0)
  3187.           string_list->Append((wxObject *)keep_string);
  3188.         else
  3189.           delete keep_string;
  3190.  
  3191.         buffer[0] = 0;
  3192.         strcat(buffer, s);
  3193.         delete s;
  3194.       }
  3195.       else
  3196.         delete keep_string;
  3197.     }
  3198.  
  3199.     node = node->Next();
  3200.   }
  3201.   if (buffer[0] != 0)
  3202.     string_list->Append((wxObject *)copystring(buffer));
  3203.  
  3204.   return string_list;
  3205. }
  3206.  
  3207. void DrawFormattedText(wxDC *context, wxList *text_list,
  3208.                               float xpos, float ypos, float width, float height)
  3209. {
  3210.   context->SetClippingRegion(
  3211.                     (float)(xpos - width/2.0), (float)(ypos - height/2.0),
  3212.                     (float)width, (float)height);
  3213.  
  3214.   wxNode *current = text_list->First();
  3215.   while (current)
  3216.   {
  3217.     CanvasObjectTextLine *line = (CanvasObjectTextLine *)current->Data();
  3218.  
  3219.     context->DrawText(line->line, xpos + line->x, ypos + line->y);
  3220.     current = current->Next();
  3221.   }
  3222.  
  3223.   context->DestroyClippingRegion();
  3224. }
  3225.  
  3226. /*
  3227.  * Find centroid given list of points comprising polyline
  3228.  *
  3229.  */
  3230.  
  3231. void find_polyline_centroid(wxList *points, float *x, float *y)
  3232. {
  3233.   float xcount = 0;
  3234.   float ycount = 0;
  3235.  
  3236.   wxNode *node = points->First();
  3237.   while (node)
  3238.   {
  3239.     wxPoint *point = (wxPoint *)node->Data();
  3240.     xcount += point->x;
  3241.     ycount += point->y;
  3242.     node = node->Next();
  3243.   }
  3244.  
  3245.   *x = (xcount/points->Number());
  3246.   *y = (ycount/points->Number());
  3247. }
  3248.  
  3249. /*
  3250.  * Check that (x1, y1) -> (x2, y2) hits (x3, y3) -> (x4, y4).
  3251.  * If so, ratio1 gives the proportion along the first line
  3252.  * that the intersection occurs (or something like that).
  3253.  * Used by functions below.
  3254.  *
  3255.  */
  3256. void check_line_intersection(float x1, float y1, float x2, float y2, 
  3257.                              float x3, float y3, float x4, float y4,
  3258.                              float *ratio1, float *ratio2)
  3259. {
  3260.   float denominator_term = (y4 - y3)*(x2 - x1) - (y2 - y1)*(x4 - x3);
  3261.   float numerator_term = (x3 - x1)*(y4 - y3) + (x4 - x3)*(y1 - y3);
  3262.  
  3263.   float line_constant;
  3264.   float length_ratio = 1.0;
  3265.   float k_line = 1.0;
  3266.  
  3267.   // Check for parallel lines
  3268.   if ((denominator_term < 0.005) && (denominator_term > -0.005))
  3269.     line_constant = -1.0;
  3270.   else
  3271.     line_constant = numerator_term/denominator_term;
  3272.  
  3273.   // Check for intersection
  3274.   if ((line_constant < 1.0) && (line_constant > 0.0))
  3275.   {
  3276.     // Now must check that other line hits 
  3277.     if (((y4 - y3) < 0.005) && ((y4 - y3) > -0.005))
  3278.       k_line = ((x1 - x3) + line_constant*(x2 - x1))/(x4 - x3);
  3279.     else
  3280.       k_line = ((y1 - y3) + line_constant*(y2 - y1))/(y4 - y3);
  3281.  
  3282.     if ((k_line > 0.0) && (k_line < 1.0))
  3283.       length_ratio = line_constant;
  3284.     else
  3285.       k_line = 1.0;
  3286.   }
  3287.   *ratio1 = length_ratio;
  3288.   *ratio2 = k_line;
  3289. }
  3290.  
  3291. /*
  3292.  * Find where (x1, y1) -> (x2, y2) hits one of the lines in xvec, yvec.
  3293.  * (*x3, *y3) is the point where it hits.
  3294.  *
  3295.  */
  3296. void find_end_for_polyline(float n, float xvec[], float yvec[], 
  3297.                            float x1, float y1, float x2, float y2, float *x3, float *y3)
  3298. {
  3299.   int i;
  3300.   float lastx = xvec[0];
  3301.   float lasty = yvec[0];
  3302.  
  3303.   float min_ratio = 1.0;
  3304.   float line_ratio;
  3305.   float other_ratio;
  3306.  
  3307.   for (i = 1; i < n; i++)
  3308.   {
  3309.     check_line_intersection(x1, y1, x2, y2, lastx, lasty, xvec[i], yvec[i],
  3310.                             &line_ratio, &other_ratio);
  3311.     lastx = xvec[i];
  3312.     lasty = yvec[i];
  3313.  
  3314.     if (line_ratio < min_ratio)
  3315.       min_ratio = line_ratio;
  3316.   }
  3317.  
  3318.   // Do last (implicit) line if last and first pofloats are not identical
  3319.   if (!(xvec[0] == lastx) && (yvec[0] == lasty))
  3320.   {
  3321.     check_line_intersection(x1, y1, x2, y2, lastx, lasty, xvec[0], yvec[0],
  3322.                             &line_ratio, &other_ratio);
  3323.  
  3324.     if (line_ratio < min_ratio)
  3325.       min_ratio = line_ratio;
  3326.   }
  3327.  
  3328.   *x3 = (x1 + (x2 - x1)*min_ratio);
  3329.   *y3 = (y1 + (y2 - y1)*min_ratio);
  3330.  
  3331. }
  3332.  
  3333. /*
  3334.  * Find where the line hits the box.
  3335.  *
  3336.  */
  3337.  
  3338. void find_end_for_box(float width, float height, 
  3339.                       float x1, float y1,         // Centre of box (possibly)
  3340.                       float x2, float y2,         // other end of line
  3341.                       float *x3, float *y3)       // End on box edge
  3342. {
  3343.   float xvec[5];
  3344.   float yvec[5];
  3345.  
  3346.   xvec[0] = (float)(x1 - width/2.0);
  3347.   yvec[0] = (float)(y1 - height/2.0);
  3348.   xvec[1] = (float)(x1 - width/2.0);
  3349.   yvec[1] = (float)(y1 + height/2.0);
  3350.   xvec[2] = (float)(x1 + width/2.0);
  3351.   yvec[2] = (float)(y1 + height/2.0);
  3352.   xvec[3] = (float)(x1 + width/2.0);
  3353.   yvec[3] = (float)(y1 - height/2.0);
  3354.   xvec[4] = (float)(x1 - width/2.0);
  3355.   yvec[4] = (float)(y1 - height/2.0);
  3356.  
  3357.   find_end_for_polyline(5, xvec, yvec, x2, y2, x1, y1, x3, y3);
  3358. }
  3359.  
  3360. /*
  3361.  * Find where the line hits the circle.
  3362.  *
  3363.  */
  3364.  
  3365. void find_end_for_circle(float radius, 
  3366.                          float x1, float y1,  // Centre of circle
  3367.                          float x2, float y2,  // Other end of line
  3368.                          float *x3, float *y3)
  3369. {
  3370.   float H = (float)sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1));
  3371.  
  3372.   if (H == 0.0)
  3373.   {
  3374.     *x3 = x1;
  3375.     *y3 = y1;
  3376.   }
  3377.   else
  3378.   {
  3379.    *y3 = radius * (y2 - y1)/H + y1;
  3380.    *x3 = radius * (x2 - x1)/H + x1;
  3381.   }
  3382. }
  3383.  
  3384. /*
  3385.  * Given the line (x1, y1) -> (x2, y2), and an arrow size of given length and width,
  3386.  * return the position of the tip of the arrow and the left and right vertices of the arrow.
  3387.  *
  3388.  */
  3389.  
  3390. void get_arrow_points(float x1, float y1, float x2, float y2,
  3391.                       float length, float width,
  3392.                       float *tip_x, float *tip_y,
  3393.                       float *side1_x, float *side1_y,
  3394.                       float *side2_x, float *side2_y)
  3395. {
  3396.   float l = (float)sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1));
  3397.  
  3398.   if (l < 0.01)
  3399.     l = 0.01;
  3400.  
  3401.   float i_bar = (x2 - x1)/l;
  3402.   float j_bar = (y2 - y1)/l;
  3403.  
  3404.   float x3 = (- length*i_bar) + x2;
  3405.   float y3 = (- length*j_bar) + y2;
  3406.  
  3407.   *side1_x = width*(-j_bar) + x3;
  3408.   *side1_y = width*i_bar + y3;
  3409.  
  3410.   *side2_x = -width*(-j_bar) + x3;
  3411.   *side2_y = -width*i_bar + y3;
  3412.  
  3413.   *tip_x = x2; *tip_y = y2;
  3414. }
  3415.  
  3416. // Update a list item from a list of strings
  3417. void UpdateListBox(wxListBox *item, wxList *list)
  3418. {
  3419.   item->Clear();
  3420.   if (!list)
  3421.     return;
  3422.  
  3423.   wxNode *node = list->First();
  3424.   while (node)
  3425.   {
  3426.     char *s = (char *)node->Data();
  3427.     item->Append(s);
  3428.     node = node->Next();
  3429.   }
  3430. }
  3431.