home *** CD-ROM | disk | FTP | other *** search
/ Borland Programmer's Resource / Borland_Programmers_Resource_CD_1995.iso / code / wxwin140 / utils / hytext / src / hytext.cc next >
Encoding:
C/C++ Source or Header  |  1995-05-19  |  47.1 KB  |  1,854 lines

  1. /*
  2.  * File:     hytext.cc
  3.  * Purpose:  Hypertext library for wxWindows
  4.  *
  5.  *                       wxWindows 1.40
  6.  * Copyright (c) 1993 Artificial Intelligence Applications Institute,
  7.  *                   The University of Edinburgh
  8.  *
  9.  *                     Author: Julian Smart
  10.  *                        Date: 18-4-93
  11.  *
  12.  * Permission to use, copy, modify, and distribute this software and its
  13.  * documentation for any purpose is hereby granted without fee, provided
  14.  * that the above copyright notice, author statement and this permission
  15.  * notice appear in all copies of this software and related documentation.
  16.  *
  17.  * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, EXPRESS,
  18.  * IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF
  19.  * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
  20.  *
  21.  * IN NO EVENT SHALL THE ARTIFICIAL INTELLIGENCE APPLICATIONS INSTITUTE OR THE
  22.  * UNIVERSITY OF EDINBURGH BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR
  23.  * CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER RESULTING FROM
  24.  * LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF THE POSSIBILITY OF
  25.  * DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH
  26.  * THE USE OR PERFORMANCE OF THIS SOFTWARE.
  27.  */
  28.  
  29. /*
  30.  * hytext.cc
  31.  * Hypertext Window implementation.
  32.  * This is a text window with highlighted text which can respond
  33.  * to mouse clicks. Blocks are stored in ordinary text files as:
  34.  * \hy-A{B}{C}  where A is an integer representing the type of
  35.  * block, B is an integer identifier for this block, and C is the
  36.  * text enclosed by the block. Blocks may be nested.
  37.  * Mouse input member functions are provided for overriding by an application,
  38.  * e.g. for displaying further information.
  39.  */
  40.  
  41. #include <windows.h>
  42. #include <math.h>
  43. #include <stdlib.h>
  44. #include <stdio.h>
  45. #include <ctype.h>
  46. #include <wx.h>
  47. #include "hytext.h"
  48.  
  49. // Drag states
  50. #define NoDragging             0
  51. #define StartDraggingLeft      1
  52. #define ContinueDraggingLeft   2
  53. #define StartDraggingRight     3
  54. #define ContinueDraggingRight  4
  55.  
  56. wxBrush *wx_transparent_brush = NULL;
  57. wxPen *wx_black_dashed_pen = NULL;
  58.  
  59. #define HYTEXT_DEFAULT_LEFT_MARGIN 10
  60. #define HYTEXT_DEFAULT_TOP_MARGIN  5
  61.  
  62.  
  63. /*
  64.  * HyperText Mapping Structure
  65.  *
  66.  */
  67.  
  68. wxHTMappingStructure::wxHTMappingStructure(void)
  69. {
  70.   block_type = -1;
  71.   text_size = -1;
  72.   text_family = -1;
  73.   text_style = -1;
  74.   text_weight = -1;
  75.   text_colour = NULL;
  76.   name = NULL;
  77.   special_attribute = -1;
  78.   visibility = -1;
  79.   logical_op = wxCOPY;
  80.   background_colour = NULL;
  81. }
  82.  
  83. wxHTMappingStructure::wxHTMappingStructure(int the_block_type,
  84.                   int the_text_size, int the_text_family,
  85.                   int the_text_style, int the_text_weight,
  86.                   char *the_text_colour, char *the_name,
  87.                   int the_attribute, Bool the_visibility)
  88. {
  89.   block_type = the_block_type;
  90.   text_size = the_text_size;
  91.   text_family = the_text_family;
  92.   text_style = the_text_style;
  93.   text_weight = the_text_weight;
  94.   if (the_text_colour)
  95.     text_colour = copystring(the_text_colour);
  96.   else text_colour = NULL;
  97.  
  98.   if (the_name)
  99.     name = copystring(the_name);
  100.   else name = NULL;
  101.  
  102.   logical_op = wxCOPY;
  103.   background_colour = NULL;
  104.   special_attribute = the_attribute;
  105.   visibility = the_visibility;
  106. }
  107.  
  108. wxHTMappingStructure::~wxHTMappingStructure(void)
  109. {
  110.   if (name) delete name;
  111.   if (text_colour) delete text_colour;
  112. }
  113.  
  114. wxFont *wxHTMappingStructure::GetFont(void)
  115. {
  116.   wxFont *the_font = wxFindOrCreateFont(text_size, text_family, text_style, text_weight);
  117.   return the_font;
  118. }
  119.  
  120. wxHTMappingStructure *wxHTMappingStructure::Copy(void)
  121. {
  122.   wxHTMappingStructure *the_copy = new wxHTMappingStructure(block_type,
  123.                   text_size, text_family,
  124.                   text_style, text_weight,
  125.                   text_colour, name, special_attribute, visibility);
  126.   the_copy->logical_op = logical_op;
  127.   the_copy->background_colour = background_colour;
  128.   return the_copy;  
  129. }
  130.  
  131. /*
  132.  * HyperText Mapping List
  133.  *
  134.  */
  135.  
  136. wxHyperTextMapping::wxHyperTextMapping(void):wxList(wxKEY_INTEGER)
  137. {
  138.   current_mapping = NULL;
  139.  
  140.   AddMapping(BLOCK_TYPE_SELECTION, -1, -1, -1, -1, NULL, "RESERVED");
  141. }
  142.  
  143. wxHyperTextMapping::~wxHyperTextMapping(void)
  144. {
  145. }
  146.  
  147. void wxHyperTextMapping::ClearMapping(void)
  148. {
  149.   wxNode *node = First();
  150.   while (node)
  151.   {
  152.     wxHTMappingStructure *struc = (wxHTMappingStructure *)node->Data();
  153.     wxNode *next_node = node->Next();
  154.     if (strcmp(struc->name, "RESERVED") != 0)
  155.     {
  156.       delete struc;
  157.       delete node;
  158.     }
  159.     node = next_node;
  160.   }
  161. }
  162.  
  163. void wxHyperTextMapping::AddMapping(int block_type, int text_size, int text_family,
  164.                   int text_style, int text_weight, char *text_colour, 
  165.                   char *name, int attribute, Bool visibility)
  166. {
  167.   wxHTMappingStructure *ms =
  168.     new wxHTMappingStructure(block_type, text_size, text_family, text_style,
  169.                              text_weight, text_colour, name, attribute, visibility);
  170.   Append((long)block_type, ms);
  171. }
  172.  
  173. Bool wxHyperTextMapping::GetMapping(int block_type, int *text_size, int *text_family,
  174.                   int *text_style, int *text_weight, char **text_colour,
  175.                   char **name, int *attribute, Bool *visibility)
  176. {
  177.   wxNode *node = Find((long)block_type);
  178.   if (node)
  179.   {
  180.     wxHTMappingStructure *ms = (wxHTMappingStructure *)node->Data();
  181.     *text_size = ms->text_size;
  182.     *text_family = ms->text_family;
  183.     *text_style = ms->text_style;
  184.     *text_colour = ms->text_colour;
  185.     *text_weight = ms->text_weight;
  186.     *name = ms->name;
  187.     *attribute = ms->special_attribute;
  188.     *visibility = ms->visibility;
  189.     return TRUE;
  190.   }
  191.   else return FALSE;
  192. }
  193.  
  194. // Find a mapping structure given a name
  195. wxHTMappingStructure *wxHyperTextMapping::FindByName(char *name)
  196. {
  197.   wxNode *node = First();
  198.   wxHTMappingStructure *found = NULL;
  199.   while (node && !found)
  200.   {
  201.     wxHTMappingStructure *thing = (wxHTMappingStructure *)node->Data();
  202.     if (strcmp(name, thing->name) == 0)
  203.       found = thing;
  204.     else node = node->Next();
  205.   }
  206.   return found;
  207. }
  208.  
  209.  
  210. // Pop up dialog box for editing mappings
  211. // TO BE IMPLEMENTED
  212. void wxHyperTextMapping::Edit(wxWindow *parent)
  213. {
  214. }
  215.  
  216. // -1 if no more types
  217. int wxHyperTextMapping::GetFirstType(void)
  218. {
  219.   current_mapping = First();
  220.   if (current_mapping)
  221.   {
  222.     wxHTMappingStructure *ms = (wxHTMappingStructure *)current_mapping->Data();
  223.     return ms->block_type;
  224.   }
  225.   else return -1;
  226. }
  227.  
  228. int wxHyperTextMapping::GetNextType(void)
  229. {
  230.   if (current_mapping)
  231.     current_mapping = current_mapping->Next();
  232.   if (current_mapping)
  233.   {
  234.     wxHTMappingStructure *ms = (wxHTMappingStructure *)current_mapping->Data();
  235.     return ms->block_type;
  236.   }
  237.   else return -1;
  238. }
  239.  
  240. /*
  241.  * wxHyperTextWindow - based on a wxCanvas: make sure you make it
  242.  * NOT retained!
  243.  *
  244.  */
  245.  
  246. /* PUBLIC MEMBER FUNCTIONS */
  247.  
  248. wxHyperTextWindow::wxHyperTextWindow(wxFrame *parent, 
  249.                                      int x, int y, int w, int h, int style):
  250.   wxCanvas(parent, x, y, w, h, style)
  251. {
  252.   Title = NULL;
  253.   indexWriting = FALSE;
  254.   mapping = NULL;
  255.   current_block = NULL;
  256.   selection_current = NULL;
  257.   no_lines = 0;
  258.   text_chunks.DeleteContents(TRUE);
  259.   no_displayed_lines = 0;
  260.   default_mapping_structure = NULL;
  261.   DragState = NoDragging;
  262.   DraggedBlock = -1;
  263.   old_drag_x = 0;
  264.   old_drag_y = 0;
  265.   OutlineStartX = 0.0;
  266.   OutlineStartY = 0.0;
  267. //  current_block_type = -1;
  268.   modified = FALSE;
  269.   edit_mode = TRUE;
  270.   current_section = NULL;
  271.   section_start_line = 0;
  272.   section_end_line = 0;
  273.   LinkTable = new wxHashTable(wxKEY_INTEGER);
  274.  
  275.   hyleft_margin = HYTEXT_DEFAULT_LEFT_MARGIN;
  276.   hytop_margin = HYTEXT_DEFAULT_TOP_MARGIN;
  277.  
  278.   EnableScrolling(TRUE, FALSE);
  279. }
  280.  
  281. wxHyperTextWindow::~wxHyperTextWindow(void)
  282. {
  283.   delete LinkTable;
  284. }
  285.  
  286. void wxHyperTextWindow::SetMapping(wxHyperTextMapping *the_mapping)
  287. {
  288.   mapping = the_mapping;
  289.  
  290.   default_mapping_structure = mapping->FindByName("Default");
  291. }
  292.  
  293. Bool wxReadALine(FILE *fd, char *buf)
  294. {
  295.   int ch = -2;
  296.   int i = 0;
  297.   buf[0] = 0;
  298.   while (ch != EOF && ch != '\n')
  299.   {
  300.     ch = getc(fd);
  301. //    if (ch == 10) { }
  302.    if (ch != EOF && ch != '\n')
  303.     {
  304.       buf[i] = ch;
  305.       i ++;
  306.     }
  307.   }
  308.   buf[i] = 0;
  309.   return (ch == EOF);
  310. }
  311.  
  312. Bool wxHyperTextWindow::ReadIndex(char *filename, FILE *fd)
  313. {
  314.   static char line_buffer[300];
  315.   Bool eof = FALSE;
  316.   // Get title
  317.   eof = wxReadALine(fd, line_buffer);
  318.   for (int i = 1; i < 300; i ++)
  319.     if (line_buffer[i] == '"')
  320.       { line_buffer[i] = 0; break; }
  321.  
  322.   if (strlen(line_buffer+1) > 0)
  323.     Title = copystring(line_buffer+1);
  324.   while (!eof)
  325.   {
  326.     eof = wxReadALine(fd, line_buffer);
  327.     if (line_buffer[0] == '}')
  328.       return eof;
  329.     else
  330.     {
  331.       long block_from, block_to;
  332.       int next = wxReadInteger(line_buffer, 0, &block_from);
  333.       next = wxReadInteger(line_buffer, next+1, &block_to);
  334.       char *file = filename;
  335.       if (strlen(line_buffer + next) >  0)
  336.         file = line_buffer + next + 1;
  337.       LinkTable->Put(block_from, (wxObject *)new HypertextItem(file, block_to));
  338.     }
  339.   }
  340.   return eof;
  341. }
  342.  
  343. Bool wxHyperTextWindow::LoadFile(char *filename)
  344. {
  345.   if (!filename)
  346.     return FALSE;
  347.  
  348.   wxCursor *old_cursor = SetCursor(wxHOURGLASS_CURSOR);
  349.   ::wxSetCursor(wxHOURGLASS_CURSOR);
  350.  
  351.   // Update to delete all HypertextItems!!!
  352.   LinkTable->Clear();
  353.   static char line_buffer[300];
  354.   FILE *fd = fopen(filename, "r");
  355.   if (fd)
  356.   {
  357.     ClearFile();
  358.     Bool eof = FALSE;
  359.     while (!eof)
  360.     {
  361.       eof = wxReadALine(fd, line_buffer);
  362.       if ((strlen(line_buffer) > 8) && strncmp(line_buffer,"\\hyindex{", 9) == 0)
  363.         eof = ReadIndex(filename, fd);
  364.       else
  365.       {
  366.         ParseLine(line_buffer);
  367.         no_lines ++;
  368.       }
  369.     }
  370.     fclose(fd);
  371.     Compile();
  372.     current_section = NULL;
  373.     section_start_line = 0;
  374. //    section_end_line = no_lines-1;
  375.     section_end_line = 0;
  376.  
  377.     SetCursor(old_cursor);
  378.     ::wxSetCursor(wxSTANDARD_CURSOR);
  379.  
  380.     return TRUE;
  381.   }
  382.   else
  383.   {
  384.     SetCursor(old_cursor);
  385.     ::wxSetCursor(wxSTANDARD_CURSOR);
  386.  
  387.     return FALSE;
  388.   }
  389. }
  390.  
  391. void wxHyperTextWindow::ParseLine(char *line)
  392. {
  393.   static char temp_buffer[300];
  394.   int len = strlen(line);
  395.   int i = 0;
  396.   int ptr = 0;  // How much of temp_buffer we've used up
  397.   char ch;
  398.   wxTextChunk *current_chunk =
  399.     new wxTextChunk(CHUNK_START_LINE, no_lines, NULL, NULL, NULL, -1, -1, -1, -1);
  400.   while (i < len)
  401.   {
  402.     ch = line[i];
  403.     switch (ch)
  404.     {
  405.       case '\\':
  406.       {
  407.         // Trying to parse something like \hy-123{456}{
  408.         Bool success = FALSE;
  409.  
  410.         if ((len > i + 5) &&
  411.             line[i+1] == 'h' && line[i+2] == 'y' && line[i+3] == '-')
  412.         {
  413.           int next = i + 4;
  414.           long hy_type = 0;
  415.           long hy_id = 0;
  416.           next = wxReadInteger(line, next, &hy_type);
  417.           if (next > -1 && line[next] == '{')
  418.           {
  419.             next = wxReadInteger(line, next+1, &hy_id);
  420.             if (next > -1 && line[next + 1] == '{')
  421.             {
  422.               RegisterId(hy_id); // Ensure GetId() returns a unique Id
  423.               i = next + 2;
  424.               temp_buffer[ptr] = 0;
  425.               ptr = 0;
  426.               current_chunk->text = copystring(temp_buffer);
  427.               text_chunks.Append(current_chunk);
  428.  
  429.               current_chunk = new wxTextChunk(CHUNK_START_BLOCK, -1, NULL, NULL, NULL,
  430.                                               hy_type, hy_id, -1, -1);
  431.               success = TRUE;
  432.             }
  433.           }
  434.         }
  435.         if (!success)
  436.         {
  437.           temp_buffer[ptr] = '\\';
  438.           ptr ++;
  439.           i ++;
  440.         }
  441.         break;
  442.       }
  443.       case '{':
  444.       {
  445.         temp_buffer[ptr] = 0;
  446.         ptr = 0;
  447.         current_chunk->text = copystring(temp_buffer);
  448.         text_chunks.Append(current_chunk);
  449.  
  450.         current_chunk = new wxTextChunk(CHUNK_START_UNRECOGNIZED_BLOCK, -1,
  451.                                         NULL, NULL, NULL,
  452.                                         -1, -1, -1, -1);
  453.  
  454.         temp_buffer[ptr] = '{';
  455.         ptr ++;
  456.         i ++;
  457.  
  458.         break;
  459.       }
  460.       case '}':
  461.       {
  462.         temp_buffer[ptr] = 0;
  463.         ptr = 0;
  464.         current_chunk->text = copystring(temp_buffer);
  465.         text_chunks.Append(current_chunk);
  466.  
  467.         current_chunk = new wxTextChunk(CHUNK_END_BLOCK, -1,
  468.                                         NULL, NULL, NULL,
  469.                                         -1, -1, -1, -1);
  470. //        temp_buffer[ptr] = '}';
  471. //        ptr ++;
  472.         i ++;
  473.  
  474.         break;
  475.       }
  476.       default:
  477.       {
  478.         temp_buffer[ptr] = ch;
  479.         ptr ++;
  480.         i ++;
  481.         break;
  482.       }
  483.     }
  484.   }
  485.  
  486.   temp_buffer[ptr] = 0;
  487.   current_chunk->text = copystring(temp_buffer);
  488.   text_chunks.Append(current_chunk);
  489. }
  490.  
  491. void wxHyperTextWindow::DisplayFileAtTop(void)
  492. {
  493.   DisplayFileAt(-1);
  494. }
  495.  
  496. void wxHyperTextWindow::DisplayFileAt(long block_id, Bool refresh)
  497. {
  498.   wxTextChunk *first_chunk;
  499.   wxNode *node;
  500.  
  501.   if (block_id == -1)
  502.   {
  503.     node = text_chunks.First();
  504.     if (!node)
  505.       return;
  506.     first_chunk = (wxTextChunk *)node->Data();
  507.   }
  508.   else
  509.   {
  510.     node = FindChunkAtBlock(block_id);
  511.     if (!node)
  512.       return;
  513.     first_chunk = (wxTextChunk *)node->Data();
  514.   }
  515.  
  516.   int start_line = first_chunk->line_no;
  517.   section_start_line = 0;   // Default
  518.   section_end_line = no_lines - 1;   // Default
  519.   current_section = NULL;            // Default
  520.  
  521.   // Try to find section info
  522.   if (sections.Number() > 0)
  523.   {
  524.     wxNode *temp_node = node;
  525.     wxTextChunk *found = NULL;
  526.     // FIND SECTION START
  527.  
  528.     // May have a start of line just before the section block
  529.     if (first_chunk->chunk_type == CHUNK_START_LINE)
  530.       temp_node = node->Next();
  531.  
  532.     while (temp_node && !found)
  533.     {
  534.       wxTextChunk *chunk = (wxTextChunk *)temp_node->Data();
  535.       if (sections.Member(chunk))
  536.         found = chunk;
  537.       else temp_node = temp_node->Previous();
  538.     }
  539.     if (!found)
  540.     {
  541.       temp_node = text_chunks.First();
  542.       found = (wxTextChunk *)temp_node->Data();
  543.     }
  544.     if (found)
  545.     {
  546.       section_start_line = found->line_no;
  547.       current_section = sections.Member(found);
  548.  
  549.       // FIND SECTION END
  550.       if (current_section)
  551.       {
  552.         wxNode *end_node = current_section->Next();
  553.         if (end_node)
  554.         {
  555.           wxTextChunk *chunk = (wxTextChunk *)end_node->Data();
  556.           section_end_line = chunk->line_no - 1;
  557.         }
  558.         else section_end_line = no_lines - 1;
  559.       }
  560.       else
  561.       {
  562.         wxNode *next_node = sections.First();
  563.         wxTextChunk *chunk = (wxTextChunk *)next_node->Data();
  564.         section_end_line = chunk->line_no - 1;
  565.       }
  566.     }
  567.   }
  568.   int no_section_lines = section_end_line - section_start_line + 1;
  569.  
  570.   // Set scrollbars for this section
  571.   SetScrollbars(10, 10, 200, no_section_lines+50, 40, 20);
  572.  
  573.   // Now scroll because we may not be viewing at this position
  574.   if (refresh)
  575.   {
  576.     Scroll(0, (int)(start_line - section_start_line));
  577.     DisplayFile();
  578.   }
  579. }
  580.  
  581. void wxHyperTextWindow::DisplayFile(void)
  582. {
  583.   if (section_end_line == 0)
  584.     return;
  585.  
  586.   wxDC *dc = GetDC();
  587.   dc->Clear();
  588.   no_displayed_lines = 0;
  589.  
  590.   float max_line_height = 0.0;
  591.  
  592.   int start_x = 0;
  593.   int start_y = 0;
  594.   int width, height;
  595.  
  596.   ViewStart(&start_x, &start_y);
  597.   GetClientSize(&width, &height);
  598.  
  599.   float current_x = 0.0;
  600.   float current_y = (float)(start_y*10.0);
  601.  
  602.   int start_line_no = (int)(start_y + section_start_line);
  603.   float max_y = (float)(current_y + height);
  604.  
  605.   if (start_line_no > section_end_line) start_line_no = section_end_line;
  606.  
  607.   wxNode *node = FindChunkAtLine(start_line_no);
  608.  
  609.   current_y += hytop_margin;
  610.  
  611.   int i = start_line_no;
  612. //  while (node && (current_y <= max_y) && (i <= section_end_line))
  613.   while (node && (i <= section_end_line))
  614.   {
  615.     wxTextChunk *chunk = (wxTextChunk *)node->Data();
  616.     dc->SetTextForeground(chunk->colour);
  617.     float extent_x = 0.0;
  618.     float extent_y = 0.0;
  619.  
  620.     if (chunk->chunk_type == CHUNK_START_LINE)
  621.     {
  622.       i = chunk->line_no;
  623.  
  624.       // Find max. line height
  625.       max_line_height = 0.0;
  626.       wxNode *node1 = node;
  627.       while (node1)
  628.       {
  629.         wxTextChunk *chunk1 = (wxTextChunk *)node1->Data();
  630.         if (chunk1 != chunk && chunk1->chunk_type == CHUNK_START_LINE)
  631.           node1 = NULL;
  632.         else
  633.     {
  634.           if (chunk1->visibility || edit_mode)
  635.       {
  636.             dc->SetFont(chunk1->font);
  637.             dc->GetTextExtent(chunk1->text, &extent_x, &extent_y);
  638.             if (extent_y > max_line_height) max_line_height = extent_y;
  639.       }
  640.           node1 = node1->Next();
  641.     }
  642.       }
  643.       if (max_line_height < 0.1)
  644.       {
  645.         dc->GetTextExtent("X", &extent_x, &max_line_height);
  646.       }
  647.  
  648.       current_y += max_line_height;
  649.       current_x = hyleft_margin;
  650.       displayed_lines[no_displayed_lines] = current_y;
  651.       no_displayed_lines ++;
  652.     }
  653.  
  654.     if (chunk->visibility || edit_mode)
  655.     {
  656.       dc->SetFont(chunk->font);
  657.       if (!dc->Colour)
  658.         dc->SetLogicalFunction(chunk->logical_op);
  659.       if (chunk->background_colour)
  660.         dc->SetTextBackground(chunk->background_colour);
  661.       else
  662. #ifdef wx_msw
  663.         dc->SetTextBackground(wxWHITE);
  664. #endif
  665. #ifdef wx_x  // For efficiency only!
  666.         dc->SetTextBackground(NULL);
  667. #endif
  668.  
  669.       dc->GetTextExtent(chunk->text, &extent_x, &extent_y);
  670.       dc->DrawText(chunk->text, current_x, current_y-extent_y);
  671.       current_x += extent_x;
  672.     }
  673.  
  674.     node = node->Next();
  675.   }
  676. }
  677.  
  678. Bool wxHyperTextWindow::SaveFile(char *filename)
  679. {
  680.   wxCursor *old_cursor = SetCursor(wxHOURGLASS_CURSOR);
  681.   ::wxSetCursor(wxHOURGLASS_CURSOR);
  682.  
  683.   ofstream output(filename);
  684.   if (!output.bad())
  685.   {
  686.     wxNode *node = text_chunks.First();
  687.     while (node)
  688.     {
  689.       wxTextChunk *chunk = (wxTextChunk *)node->Data();
  690.       switch (chunk->chunk_type)
  691.       {
  692.         case CHUNK_START_LINE:
  693.         {
  694.           if (node != text_chunks.First())
  695.             output << '\n';
  696.           output << chunk->text;
  697.           break;
  698.         }
  699.         case CHUNK_START_BLOCK:
  700.         {
  701.           output << "\\hy-" << chunk->block_type << "{"
  702.                  << chunk->block_id << "}{" << chunk->text;
  703.           break;
  704.         }
  705.         case CHUNK_END_BLOCK:
  706.         {
  707.           if (chunk->end_id > -1)
  708.             output << "}";
  709.           output << chunk->text;
  710.           break;
  711.         }
  712.         case CHUNK_START_UNRECOGNIZED_BLOCK:
  713.         default:
  714.         {
  715.           output << chunk->text;
  716.           break;
  717.         }
  718.       }
  719.       node = node->Next();
  720.     }
  721.  
  722.     if (indexWriting)
  723.     {
  724.       // Now output link table, if more than 0
  725.       LinkTable->BeginFind();
  726.       if (LinkTable->Next() || Title)
  727.       {
  728.         output << "\n\\hyindex{\n";
  729.         output << '"';
  730.         if (Title) output << Title;
  731.         output << "\"\n";
  732.         LinkTable->BeginFind();
  733.         wxNode *node;
  734.         while (node = LinkTable->Next())
  735.         {
  736.           HypertextItem *item = (HypertextItem *)node->Data();
  737.           output << node->key.integer << " " << item->block_id;
  738.           if (item->filename && (strcmp(item->filename, filename) != 0))
  739.             output << " " << item->filename;
  740.           output << "\n";
  741.         }
  742.         output << "}\n";
  743.       }
  744.     }
  745.  
  746.     modified = FALSE;
  747.     SetCursor(old_cursor);
  748.     ::wxSetCursor(wxSTANDARD_CURSOR);
  749.  
  750.     return TRUE;
  751.   }
  752.   else
  753.   {
  754.     SetCursor(old_cursor);
  755.     ::wxSetCursor(wxSTANDARD_CURSOR);
  756.  
  757.     return FALSE;
  758.   }
  759. }
  760.  
  761. Bool wxHyperTextWindow::ClearFile(void)
  762. {
  763.   current_selections.Clear();
  764.   text_chunks.Clear();
  765.   selection_current = NULL;
  766.   no_lines = 0;
  767.   Clear();
  768.   // Should reset scrollbar
  769.   return TRUE;
  770. }
  771.  
  772. Bool wxHyperTextWindow::GetEditMode(void)
  773. {
  774.   return edit_mode;
  775. }
  776.  
  777. void wxHyperTextWindow::SetEditMode(Bool the_edit_mode)
  778. {
  779.   edit_mode = the_edit_mode;
  780. }
  781.  
  782. Bool wxHyperTextWindow::Modified(void)
  783. {
  784.   return modified;
  785. }
  786.  
  787. void wxHyperTextWindow::DiscardEdits(void)
  788. {
  789.   modified = FALSE;
  790. }
  791.  
  792. long wxHyperTextWindow::GenerateId(void)
  793. {
  794.   return NewId();
  795. }
  796.  
  797. void wxHyperTextWindow::OnPaint(void)
  798. {
  799.   DisplayFile();
  800. }
  801.  
  802. /*
  803.  * Finds x, y character position from mouse click position,
  804.  * returning block id if any.
  805.  * Returns FALSE if fails.
  806.  */
  807. Bool wxHyperTextWindow::FindPosition(float mouse_x, float mouse_y, 
  808.                                      int *char_pos, int *line_pos,
  809.                                      long *block_id)
  810. {
  811.   int current_x, current_y;
  812.   ViewStart(¤t_x, ¤t_y);
  813. //  if (current_y > no_displayed_lines - 1)
  814. //    return FALSE;
  815.  
  816.   float current_y_pixels = current_y*10;
  817.   int line_found = -1;
  818.  
  819.   // Find line position
  820.   for (int i = 0; i < no_displayed_lines; i++)
  821.   {
  822.     if (mouse_y <= displayed_lines[i] && mouse_y > current_y_pixels)
  823.     {
  824.       line_found = current_y + i + section_start_line;
  825.       break;
  826.     } else current_y_pixels = (int)displayed_lines[i];
  827.   }
  828.  
  829.   if (line_found == -1)
  830.     return FALSE;
  831.  
  832.   // Now find character position and block id (if any)
  833.   wxNode *chunk_node = FindChunkAtLine(line_found);
  834.   int char_found = -1;
  835.   long block_found = -1;
  836.   current_x = hyleft_margin;
  837.   wxNode *node = chunk_node;
  838.   wxDC *dc = GetDC();
  839.   int char_pos1 = 0;
  840.   while (node && char_found == -1)
  841.   {
  842.     wxTextChunk *chunk = (wxTextChunk *)node->Data();
  843.     // Next line so give up
  844.     if (chunk->chunk_type == CHUNK_START_LINE && node != chunk_node)
  845.       node = NULL;
  846.     else
  847.     {
  848.       dc->SetFont(chunk->font);
  849.       int len = strlen(chunk->text);
  850.       float x_extent, y_extent;
  851.       for (int i = 0; i < len; i++)
  852.       {
  853.         char buf[2];
  854.         buf[0] = chunk->text[i];
  855.         buf[1] = 0;
  856.         dc->GetTextExtent(buf, &x_extent, &y_extent);
  857.         if (mouse_x >= current_x && mouse_x < (current_x + x_extent))
  858.     {
  859.           char_found = char_pos1;
  860.           block_found = chunk->block_id;
  861.           break;
  862.     }
  863.         else
  864.     {
  865.           current_x += (int)x_extent;
  866.           char_pos1 ++;
  867.     }
  868.       }
  869.       node = node->Next();
  870.     }
  871.   }
  872.   if (char_found == -1)
  873.     return FALSE;
  874.  
  875.   *char_pos = char_found;
  876.   *line_pos = line_found;
  877.   *block_id = block_found;
  878.   return TRUE;
  879. }
  880.  
  881. void wxHyperTextWindow::OnLeftClick(float x, float y, int char_pos, int line_pos, long block_id, int keys)
  882. {
  883. //  cout << "Left click at " << char_pos << ", " << line_pos << ": block id = " << block_id << "\n";
  884.   if (!edit_mode)
  885.     return;
  886.  
  887.   if ((keys & KEY_SHIFT) && block_id > -1)
  888.   {
  889.     wxTextChunk *chunk = FindBlock(block_id);
  890.     if (chunk)
  891.     {
  892.       SaveSection();
  893.       SelectBlock(chunk, !chunk->selected);
  894.       Compile();
  895.       RestoreSection();
  896.     }
  897.   }
  898. }
  899.  
  900. void wxHyperTextWindow::OnRightClick(float x, float y, int char_pos, int line_pos, long block_id, int keys)
  901. {
  902. //  cout << "Right click at " << char_pos << ", " << line_pos << ": block id = " << block_id << "\n";
  903. }
  904.  
  905. void wxHyperTextWindow::OnBeginDragLeft(float x, float y, long block_id, int keys)
  906. {
  907.   if (!edit_mode)
  908.     return;
  909.  
  910.   OutlineStartX = x;
  911.   OutlineStartY = y;
  912.  
  913.   GetDC()->SetLogicalFunction(wxXOR);
  914.   DrawOutline(OutlineStartX, OutlineStartY, x, y);
  915. }
  916.  
  917. void wxHyperTextWindow::OnDragLeft(Bool draw, float x, float y, long block_id, int keys)
  918. {
  919.   if (!edit_mode)
  920.     return;
  921.  
  922.   DrawOutline(OutlineStartX, OutlineStartY, x, y);
  923. }
  924.  
  925. void wxHyperTextWindow::OnEndDragLeft(float x, float y, long block_id, int keys)
  926. {
  927.   if (!edit_mode)
  928.     return;
  929.  
  930.   GetDC()->SetLogicalFunction(wxCOPY);
  931.  
  932.   int char_pos1, line_pos1;
  933.   int char_pos2, line_pos2;
  934.   long block_id1;
  935.  
  936.   float min_x, max_x, min_y, max_y;
  937.   min_x = min(x, OutlineStartX);
  938.   max_x = max(x, OutlineStartX);
  939.   min_y = min(y, OutlineStartY);
  940.   max_y = max(y, OutlineStartY);
  941.  
  942.   if (!FindPosition(min_x, min_y, &char_pos1, &line_pos1, &block_id1))
  943.     return;
  944.   if (!FindPosition(max_x, max_y, &char_pos2, &line_pos2, &block_id1))
  945.     return;
  946.  
  947.   long id = GenerateId();
  948.   if (!AddBlock(char_pos1, line_pos1, char_pos2, line_pos2,
  949.                 BLOCK_TYPE_SELECTION, id))
  950.     return;
  951.  
  952.   SelectBlock(id, TRUE);
  953.  
  954. //  cout << "Added block " << id << "\n";
  955.   SaveSection();
  956.   Compile();
  957.   RestoreSection();
  958. //  DisplayFile();
  959. }
  960.  
  961. void wxHyperTextWindow::OnBeginDragRight(float x, float y, long block_id, int keys)
  962. {
  963. //  cout << "Begin right drag: block id = " << block_id << "\n";
  964. }
  965.  
  966. void wxHyperTextWindow::OnDragRight(Bool draw, float x, float y, long block_id, int keys)
  967. {
  968. //  cout << "On right drag: block id = " << block_id << "\n";
  969. }
  970.  
  971. void wxHyperTextWindow::OnEndDragRight(float x, float y, long block_id, int keys)
  972. {
  973. //  cout << "End right drag: block id = " << block_id << "\n";
  974. }
  975.  
  976. void wxHyperTextWindow::OnEvent(wxEvent& event)
  977. {
  978.   float x, y;
  979.   event.Position(&x, &y);
  980.   int keys = 0;
  981.   if (event.ShiftDown())
  982.     keys = keys | KEY_SHIFT;
  983.   if (event.ControlDown())
  984.     keys = keys | KEY_CTRL;
  985.  
  986.   int char_pos, line_pos;
  987.   long block_id;
  988.  
  989.   // All following events sent to canvas
  990.   if (event.Dragging() && DragState == StartDraggingLeft)
  991.   {
  992.     DragState = ContinueDraggingLeft;
  993.     OnBeginDragLeft((float)x, (float)y, DraggedBlock, keys);
  994.     old_drag_x = x; old_drag_y = y;
  995.   }
  996.   else if (event.Dragging() && DragState == ContinueDraggingLeft)
  997.   { 
  998.     // Continue dragging
  999.     OnDragLeft(FALSE, old_drag_x, old_drag_y, DraggedBlock, keys);
  1000.     OnDragLeft(TRUE, (float)x, (float)y, DraggedBlock, keys);
  1001.     old_drag_x = x; old_drag_y = y;
  1002.   }
  1003.   else if (event.LeftUp() && DragState == ContinueDraggingLeft)
  1004.   {
  1005.     DragState = NoDragging;
  1006.     OnDragLeft(FALSE, old_drag_x, old_drag_y, DraggedBlock, keys);
  1007.     OnEndDragLeft((float)x, (float)y, DraggedBlock, keys);
  1008. //    DraggedBlock = -1;
  1009.   }
  1010.   else if (event.Dragging() && DragState == StartDraggingRight)
  1011.   {
  1012.     DragState = ContinueDraggingRight;
  1013.     OnBeginDragRight((float)x, (float)y, DraggedBlock, keys);
  1014.     old_drag_x = x; old_drag_y = y;
  1015.   }
  1016.   else if (event.Dragging() && DragState == ContinueDraggingRight)
  1017.   { 
  1018.     // Continue dragging
  1019.     OnDragRight(FALSE, old_drag_x, old_drag_y, DraggedBlock, keys);
  1020.     OnDragRight(TRUE, (float)x, (float)y, DraggedBlock, keys);
  1021.     old_drag_x = x; old_drag_y = y;
  1022.   }
  1023.   else if (event.RightUp() &&  DragState == ContinueDraggingRight)
  1024.   {
  1025.     DragState = NoDragging;
  1026.  
  1027.     OnDragRight(FALSE, old_drag_x, old_drag_y, DraggedBlock, keys);
  1028.     OnEndDragRight((float)x, (float)y, DraggedBlock, keys);
  1029.   }
  1030.  
  1031.   // Non-dragging events
  1032.   else if (event.IsButton())
  1033.   {
  1034.     // Find block (if any)
  1035.     if (!FindPosition(x, y, &char_pos, &line_pos, &block_id))
  1036.       return;
  1037.  
  1038.     if (event.LeftDown())
  1039.     {
  1040.       DraggedBlock = block_id;
  1041.       DragState = StartDraggingLeft;
  1042.     }
  1043.     else if (event.LeftUp())
  1044.     {
  1045.       // N.B. Only register a click if the same object was
  1046.       // identified for down *and* up.
  1047.       if (block_id == DraggedBlock)
  1048.         OnLeftClick((float)x, (float)y, char_pos, line_pos, block_id, keys);
  1049.  
  1050.       DraggedBlock = -1;
  1051.       DragState = NoDragging;
  1052.     }
  1053.     else if (event.RightDown())
  1054.     {
  1055.       DraggedBlock = block_id;
  1056.       DragState = StartDraggingRight;
  1057.     }
  1058.     else if (event.RightUp())
  1059.     {
  1060.       if (block_id == DraggedBlock)
  1061.         OnRightClick((float)x, (float)y, char_pos, line_pos, block_id, keys);
  1062.  
  1063.       DraggedBlock = -1;
  1064.       DragState = NoDragging;
  1065.     }
  1066.   }
  1067. }
  1068.  
  1069.  
  1070. /* Add a block, giving:
  1071.  * - start and end coordinates in character/line positions
  1072.  * - block type (arbitrary ID)
  1073.  * - integer block ID
  1074.  * Insert START_BLOCK before first char, insert END_BLOCK after
  1075.  * last char.
  1076.  */
  1077. Bool wxHyperTextWindow::AddBlock(int xstart, int ystart, int xend, int yend,
  1078.               int block_type, long id)
  1079. {
  1080.   wxNode *start_node = FindChunkAtLine(ystart);
  1081.   wxNode *end_node = FindChunkAtLine(yend);
  1082.   if (!(start_node && end_node))
  1083.     return FALSE;
  1084.  
  1085.   // Find which existing chunk we have to carve up
  1086.   wxNode *node = start_node;
  1087.   int char_pos = 0;
  1088.   static char buffer[300];
  1089.   Bool success = FALSE;
  1090.   while (node)
  1091.   {
  1092.     wxTextChunk *chunk = (wxTextChunk *)node->Data();
  1093.     char *text = chunk->text;
  1094.     int len = strlen(text);
  1095.     if (xstart < (char_pos + len))
  1096.     {
  1097.       // Break up this chunk
  1098.       int split_point = xstart - char_pos;
  1099.       strncpy(buffer, text, split_point);
  1100.       buffer[split_point] = 0;
  1101.       char *new_text1 = copystring(buffer);
  1102.       chunk->text = new_text1;
  1103.  
  1104.       strncpy(buffer, text + split_point, len - split_point);
  1105.       buffer[len-split_point] = 0;
  1106.       delete text;
  1107.  
  1108.       wxTextChunk *new_chunk =
  1109.          new wxTextChunk(CHUNK_START_BLOCK, -1, buffer, NULL, NULL,
  1110.                          block_type, id, -1, -1);
  1111.       wxNode *next_node = node->Next();
  1112.       if (!next_node)
  1113.         text_chunks.Append(new_chunk);
  1114.       else text_chunks.Insert(next_node, new_chunk);
  1115.  
  1116.       success = TRUE;
  1117.       node = NULL;
  1118.     }
  1119.     else
  1120.     {
  1121.       char_pos += len;
  1122.       node = node->Next();
  1123.     }
  1124.   }
  1125.   if (!success)
  1126.     return FALSE;
  1127.  
  1128.   // Insert end block chunk
  1129.   node = end_node;
  1130.   char_pos = 0;
  1131.   success = FALSE;
  1132.   while (node)
  1133.   {
  1134.     wxTextChunk *chunk = (wxTextChunk *)node->Data();
  1135.     char *text = chunk->text;
  1136.     int len = strlen(text);
  1137.     if (xend < (char_pos + len))
  1138.     {
  1139.       // Break up this chunk
  1140.       int split_point = xend - char_pos + 1;  // Add 1 since we inserting
  1141.                                                 // AFTER this character
  1142.       strncpy(buffer, text, split_point);
  1143.       buffer[split_point] = 0;
  1144.       char *new_text1 = copystring(buffer);
  1145.       chunk->text = new_text1;
  1146.  
  1147.       strncpy(buffer, text + split_point, len - split_point);
  1148.       buffer[len-split_point] = 0;
  1149.       delete text;
  1150.  
  1151.       wxTextChunk *new_chunk =
  1152.          new wxTextChunk(CHUNK_END_BLOCK, -1, buffer, NULL, NULL,
  1153.                          block_type, id, -1, -1);
  1154.       wxNode *next_node = node->Next();
  1155.       if (!next_node)
  1156.         text_chunks.Append(new_chunk);
  1157.       else text_chunks.Insert(next_node, new_chunk);
  1158.  
  1159.       success = TRUE;
  1160.       node = NULL;
  1161.     }
  1162.     else
  1163.     {
  1164.       char_pos += len;
  1165.       node = node->Next();
  1166.     }
  1167.   }
  1168.   return success;
  1169. }
  1170.  
  1171. Bool wxHyperTextWindow::ClearBlock(long id)
  1172. {
  1173.   static char buffer[400];
  1174.  
  1175.   wxNode *first_node = FindChunkAtBlock(id);
  1176.   if (!first_node)
  1177.     return FALSE;
  1178.  
  1179. //  SelectBlock(id, FALSE); // Would make SelectBlock recurse if selection block type!
  1180.  
  1181.   // First find start of block and delete it.
  1182.   wxNode *block_node = NULL;
  1183.   while (!block_node && first_node)
  1184.   {
  1185.     wxTextChunk *chunk = (wxTextChunk *)first_node->Data();
  1186.     if (chunk->chunk_type == CHUNK_START_BLOCK && chunk->block_id == id)
  1187.     {
  1188.       block_node = first_node;
  1189.     }
  1190.     else first_node = first_node->Next();
  1191.   }
  1192.   wxTextChunk *first_chunk = (wxTextChunk *)block_node->Data();
  1193.   wxNode *previous_node = block_node->Previous();
  1194.   if (!previous_node)
  1195.     return FALSE;
  1196.  
  1197.   wxTextChunk *previous_chunk = (wxTextChunk *)previous_node->Data();
  1198.  
  1199.   strcpy(buffer, previous_chunk->text);
  1200.   strcat(buffer, first_chunk->text);
  1201.   delete previous_chunk->text;
  1202.   previous_chunk->text = copystring(buffer);
  1203.  
  1204.   // Before we delete the chunk, make sure we're not displaying
  1205.   // this section!
  1206.   if (current_section && (first_chunk == (wxTextChunk *)current_section->Data()))
  1207.   {
  1208.     current_section = current_section->Previous();
  1209.   }
  1210.  
  1211.   text_chunks.DeleteObject(first_chunk);
  1212.  
  1213.   // Now find end of block and delete that too.
  1214.   block_node = NULL;
  1215.   first_node = previous_node;
  1216.   while (!block_node && first_node)
  1217.   {
  1218.     wxTextChunk *chunk = (wxTextChunk *)first_node->Data();
  1219.     if (chunk->chunk_type == CHUNK_END_BLOCK && chunk->end_id == id)
  1220.     {
  1221.       block_node = first_node;
  1222.     }
  1223.     else first_node = first_node->Next();
  1224.   }
  1225.  
  1226.   wxTextChunk *last_chunk = (wxTextChunk *)block_node->Data();
  1227.   previous_node = block_node->Previous();
  1228.   if (!previous_node)
  1229.     return FALSE;
  1230.  
  1231.   previous_chunk = (wxTextChunk *)previous_node->Data();
  1232.  
  1233.   strcpy(buffer, previous_chunk->text);
  1234.   strcat(buffer, last_chunk->text);
  1235.   delete previous_chunk->text;
  1236.   previous_chunk->text = copystring(buffer);
  1237.  
  1238.   text_chunks.DeleteObject(last_chunk);
  1239.   modified = TRUE;
  1240.   return TRUE;
  1241. }
  1242.  
  1243. // Get blocks in this file (returns -1 for no more)
  1244. long wxHyperTextWindow::GetFirstBlock(void)
  1245. {
  1246.   current_block = text_chunks.First();
  1247.   wxTextChunk *found = NULL;
  1248.  
  1249.   while (current_block && !found)
  1250.   {
  1251.     wxTextChunk *chunk = (wxTextChunk *)current_block->Data();
  1252.     if (chunk->chunk_type == CHUNK_START_BLOCK)
  1253.       found = chunk;
  1254.     else current_block = current_block->Next();
  1255.   }
  1256.   if (found)
  1257.     return found->block_id;
  1258.   else return -1;
  1259. }
  1260.  
  1261. long wxHyperTextWindow::GetNextBlock(void)
  1262. {
  1263.   if (!current_block)
  1264.     return -1;
  1265.  
  1266.   current_block = current_block->Next();
  1267.   wxTextChunk *found = NULL;
  1268.  
  1269.   while (current_block && !found)
  1270.   {
  1271.     wxTextChunk *chunk = (wxTextChunk *)current_block->Data();
  1272.     if (chunk->chunk_type == CHUNK_START_BLOCK)
  1273.       found = chunk;
  1274.     else current_block = current_block->Next();
  1275.   }
  1276.   if (found)
  1277.     return found->block_id;
  1278.   else return -1;
  1279. }
  1280.  
  1281.  
  1282. void wxHyperTextWindow::DrawOutline(float x1, float y1, float x2, float y2)
  1283. {
  1284.   wxDC *dc = GetDC();
  1285.  
  1286.   if (!wx_black_dashed_pen)
  1287.     wx_black_dashed_pen = new wxPen("BLACK", 1, wxSHORT_DASH);
  1288.   dc->SetPen(wx_black_dashed_pen);
  1289.  
  1290.   if (!wx_transparent_brush)
  1291.     wx_transparent_brush = new wxBrush("BLACK", wxTRANSPARENT);
  1292.   dc->SetBrush(wx_transparent_brush);
  1293.  
  1294.   dc->DrawLine(x1, y1, x2, y1);
  1295.   dc->DrawLine(x2, y1, x2, y2);
  1296.   dc->DrawLine(x2, y2, x1, y2);
  1297.   dc->DrawLine(x1, y2, x1, y1);
  1298. }
  1299.  
  1300. /*
  1301. void wxHyperTextWindow::SetCurrentBlockType(int type)
  1302. {
  1303.   current_block_type = type;
  1304. }
  1305. */
  1306.  
  1307. void wxHyperTextWindow::SetBlockType(long block_id, int block_type)
  1308. {
  1309.   wxTextChunk *chunk = FindBlock(block_id);
  1310.   if (chunk) chunk->block_type = block_type;
  1311.   modified = TRUE;
  1312. }
  1313.  
  1314. int wxHyperTextWindow::GetBlockType(long block_id)    // Gets block type
  1315. {
  1316.   wxTextChunk *chunk = FindBlock(block_id);
  1317.   if (chunk) return chunk->block_type;
  1318.   return -1;
  1319. }
  1320.  
  1321. void wxHyperTextWindow::GetBlockText(char *buffer, int max_size, long block_id) // Gets plain text
  1322. {
  1323.   wxNode *node = FindChunkAtBlock(block_id);
  1324.   if (!node)
  1325.     return;
  1326.   GetBlockText(buffer, max_size, node, block_id);
  1327. }
  1328.  
  1329. void wxHyperTextWindow::GetBlockText(char *buffer, int max_size, wxNode *node, long block_id) // Gets plain text
  1330. {
  1331.   int i = 0;
  1332.   buffer[0] = 0;
  1333.   if (!node)
  1334.     return;
  1335.  
  1336.   Bool started = FALSE;
  1337.   while (node && i <= max_size)
  1338.   {
  1339.     wxTextChunk *chunk = (wxTextChunk *)node->Data();
  1340.     if (!started && (chunk->chunk_type == CHUNK_START_BLOCK && chunk->block_id == block_id))
  1341.     {
  1342.       started = TRUE;
  1343.     }
  1344.     if (started && (chunk->chunk_type == CHUNK_END_BLOCK && chunk->end_id == block_id))
  1345.       node = NULL;
  1346.     else if (started)
  1347.     {
  1348.       int len = strlen(chunk->text);
  1349.       if ((i + len) <= max_size)
  1350.       {
  1351.         strcat(buffer, chunk->text);
  1352.         i += len;
  1353.         node = node->Next();
  1354.       }
  1355.       else node = NULL;
  1356.     } else node = node->Next();
  1357.   }
  1358.   buffer[i] = 0;
  1359. }
  1360.  
  1361. void wxHyperTextWindow::SetMargins(int left, int top)
  1362. {
  1363.   hyleft_margin = left;
  1364.   hytop_margin = top;
  1365. }
  1366.  
  1367. // Traverses chunks assigning fonts/colours based on mapping.
  1368. // Uses a stack to determine the scope of the attributes,
  1369. // so when a block scope ends (with a }) the previous scope's attributes
  1370. // come into play.
  1371. // This scoping scheme is 'compiled' into the chunks so all the
  1372. // repaint proc has to do is scan through the chunks setting the
  1373. // font and colour according to the info in each chunk.
  1374.  
  1375. /*
  1376.  * NEED TO have a structure holding block id and wxHTMappingStructure,
  1377.  * so can assign id to end of block.
  1378.  */
  1379.  
  1380. class wxHTStackThing: public wxObject
  1381. {
  1382.  public:
  1383.   long block_id;
  1384.   wxHTMappingStructure *mapping;
  1385. };
  1386.  
  1387. Bool wxHyperTextWindow::Compile(void)
  1388. {
  1389.   current_section = NULL;
  1390.   sections.Clear();
  1391.  
  1392.   wxList stack;
  1393.  
  1394.   // Put first item on stack, for default font.
  1395.   wxHTMappingStructure *default_mapping = NULL;
  1396.  
  1397.   wxHTMappingStructure *first_mapping;
  1398.  
  1399.   if (default_mapping_structure)
  1400.     first_mapping = default_mapping_structure->Copy();
  1401.   else
  1402.   {
  1403.     default_mapping = new wxHTMappingStructure(-1,
  1404.                   12, wxSWISS, wxNORMAL, wxNORMAL, "BLACK", "Default");
  1405.     first_mapping = default_mapping->Copy();
  1406.   }
  1407.  
  1408.   wxHTStackThing *first_thing = new wxHTStackThing;
  1409.   first_thing->block_id = -1;
  1410.   first_thing->mapping = first_mapping;
  1411.  
  1412.   stack.Append(first_thing);
  1413.  
  1414.   wxHTMappingStructure *current_mapping = first_mapping;
  1415.  
  1416.   int line_no = 0;
  1417.   wxNode *node = text_chunks.First();
  1418.   while (node)
  1419.   {
  1420.     wxTextChunk *chunk = (wxTextChunk *)node->Data();
  1421.     switch (chunk->chunk_type)
  1422.     {
  1423.       case CHUNK_START_BLOCK:
  1424.       {
  1425.         wxNode *node1 = mapping->Find(chunk->block_type);
  1426.         if (node1)
  1427.     {
  1428.           wxHTMappingStructure *mapping1 =
  1429.             (wxHTMappingStructure *)node1->Data();
  1430.           if (mapping1)
  1431.       {
  1432.             // Must make a copy of the mapping so we don't overwrite
  1433.             // defaults. This COPY has to be put on the stack so
  1434.             // as we unwind, we can set the attributes back to previous values.
  1435.             int old_text_size = current_mapping->text_size;
  1436.             int old_text_style = current_mapping->text_style;
  1437.             int old_text_weight = current_mapping->text_weight;
  1438.             int old_text_family = current_mapping->text_family;
  1439.             int old_logical_op = current_mapping->logical_op;
  1440.             Bool old_visibility = current_mapping->visibility;
  1441. //            int old_attribute = current_mapping->special_attribute;
  1442.  
  1443.             char *old_text_colour = current_mapping->text_colour;
  1444.             wxColour *old_background_colour = current_mapping->background_colour;
  1445.  
  1446.             current_mapping = mapping1->Copy();
  1447.  
  1448.             // Fill in any missing values
  1449.             if (current_mapping->text_size < 0)
  1450.               current_mapping->text_size = old_text_size;
  1451.             if (current_mapping->text_style < 0)
  1452.               current_mapping->text_style = old_text_style;
  1453.             if (current_mapping->text_weight < 0)
  1454.               current_mapping->text_weight = old_text_weight;
  1455.             if (current_mapping->text_family < 0)
  1456.               current_mapping->text_family = old_text_family;
  1457.             if (current_mapping->visibility < 0)
  1458.               current_mapping->visibility = old_visibility;
  1459. //            if (current_mapping->special_attribute < 0)
  1460. //              current_mapping->special_attribute = old_attribute;
  1461.             if (!current_mapping->text_colour && old_text_colour)
  1462.               current_mapping->text_colour = copystring(old_text_colour);
  1463.  
  1464.             // Only this chunk should get a special attribute
  1465.             chunk->special_attribute = mapping1->special_attribute;
  1466.  
  1467.             // Starting a selected block will cause everything after it
  1468.             // to have a background, since no other block has one
  1469.             if (chunk->selected)
  1470.             {
  1471.               if (!GetDC()->Colour)
  1472.                 current_mapping->background_colour = wxWHITE;
  1473.               else current_mapping->background_colour = wxCYAN;
  1474.  
  1475.               current_mapping->logical_op = wxINVERT;
  1476.             }
  1477.             else
  1478.             {
  1479.               current_mapping->background_colour = old_background_colour;
  1480.               current_mapping->logical_op = old_logical_op;
  1481.             }
  1482.       }
  1483.     }
  1484.  
  1485.         wxHTStackThing *thing = new wxHTStackThing;
  1486.         thing->block_id = chunk->block_id;
  1487.         thing->mapping = current_mapping;
  1488.  
  1489.         stack.Append(thing);
  1490.         break;
  1491.       }
  1492.  
  1493.       case CHUNK_START_UNRECOGNIZED_BLOCK:
  1494.       {
  1495.         wxHTStackThing *thing = new wxHTStackThing;
  1496.         thing->block_id = -1;
  1497.         thing->mapping = current_mapping->Copy();
  1498.  
  1499.         stack.Append(thing);
  1500.         break;
  1501.       }
  1502.  
  1503.       case CHUNK_END_BLOCK:
  1504.       {
  1505.         if (stack.Number() > 1)
  1506.         {
  1507.           wxNode *node1 = stack.Last();
  1508.           // Assign the block id from the starting chunk to
  1509.           // this end chunk, so we can recognize both start and end
  1510.           // chunks. Also, end braces from unrecognized constructs
  1511.           // can be displayed, and recognized block braces NOT.
  1512.           wxHTStackThing *thing = (wxHTStackThing *)node1->Data();
  1513.           chunk->end_id = thing->block_id;
  1514.  
  1515.           // If unrecognised block, stick right brace back into text
  1516.           if (chunk->end_id == -1)
  1517.           {
  1518.             static char buf[300];
  1519.             buf[0] = '}'; buf[1] = 0;
  1520.             strcat(buf, chunk->text);
  1521.             delete chunk->text;
  1522.             chunk->text = copystring(buf);
  1523.           }
  1524.  
  1525.           delete node1;
  1526.           if (thing->mapping != first_mapping)
  1527.             delete thing->mapping;
  1528.           delete thing;
  1529.  
  1530.           node1 = stack.Last();
  1531.           thing = (wxHTStackThing *)node1->Data();
  1532.           chunk->block_id = thing->block_id;
  1533.           current_mapping = thing->mapping;
  1534.         }
  1535.         break;
  1536.       }
  1537.  
  1538.       case CHUNK_START_LINE:
  1539.       default:
  1540.       {
  1541.         line_no = chunk->line_no;
  1542.  
  1543.         wxNode *node1 = stack.Last();
  1544.         wxHTStackThing *thing = (wxHTStackThing *)node1->Data();
  1545.         chunk->block_id = thing->block_id;
  1546.         break;
  1547.       }
  1548.     }
  1549.     if (current_mapping->text_colour)
  1550.       chunk->colour = wxTheColourDatabase->FindColour(current_mapping->text_colour);
  1551.     chunk->background_colour = current_mapping->background_colour;
  1552.     chunk->font = current_mapping->GetFont();
  1553.     chunk->logical_op = current_mapping->logical_op;
  1554. //    chunk->special_attribute = current_mapping->special_attribute;
  1555.     chunk->visibility = current_mapping->visibility;
  1556.     chunk->line_no = line_no;
  1557.     if (chunk->special_attribute == wxHYPER_SECTION)
  1558.       sections.Append(chunk);
  1559.  
  1560.     node = node->Next();
  1561.   }
  1562.   if (default_mapping) delete default_mapping;
  1563.  
  1564.   delete first_mapping;
  1565.   return TRUE;
  1566. }
  1567.  
  1568. // Find first chunk at given line (returns a node so can traverse
  1569. // forward from here)
  1570. wxNode *wxHyperTextWindow::FindChunkAtLine(int line_no)
  1571. {
  1572.   wxNode *node = text_chunks.First();
  1573.   wxNode *found = NULL;
  1574.   while (node && !found)
  1575.   {
  1576.     wxTextChunk *chunk = (wxTextChunk *)node->Data();
  1577.     if (chunk->line_no == line_no)
  1578.       found = node;
  1579.     else node = node->Next();
  1580.   }
  1581.   return found;
  1582. }
  1583.  
  1584. // Finds first line-start chunk containing the given block
  1585. // (NOT the actual chunk containing the block)
  1586. wxNode *wxHyperTextWindow::FindChunkAtBlock(long block_id)
  1587. {
  1588.   wxNode *node = text_chunks.First();
  1589.   wxNode *found = NULL;
  1590.   wxNode *last_starting_chunk = NULL;
  1591.   while (node && !found)
  1592.   {
  1593.     wxTextChunk *chunk = (wxTextChunk *)node->Data();
  1594.     if (chunk->chunk_type == CHUNK_START_LINE)
  1595.       last_starting_chunk = node;
  1596.  
  1597.     if ((chunk->chunk_type == CHUNK_START_BLOCK) && (chunk->block_id == block_id))
  1598.       found = last_starting_chunk;
  1599.     else node = node->Next();
  1600.   }
  1601.   return found;
  1602. }
  1603.  
  1604. // Finds chunk at given block
  1605. wxTextChunk *wxHyperTextWindow::FindBlock(long block_id)
  1606. {
  1607.   wxNode *node = text_chunks.First();
  1608.   wxTextChunk *found = NULL;
  1609.   while (node && !found)
  1610.   {
  1611.     wxTextChunk *chunk = (wxTextChunk *)node->Data();
  1612.     if ((chunk->chunk_type == CHUNK_START_BLOCK) && (chunk->block_id == block_id))
  1613.       found = chunk;
  1614.     else node = node->Next();
  1615.   }
  1616.   return found;
  1617. }
  1618.  
  1619. long wxHyperTextWindow::FindBlockForSection(wxNode *node)
  1620. {
  1621.   if (!node)
  1622.     return -1;
  1623.   wxTextChunk *chunk = (wxTextChunk *)node->Data();
  1624.   return chunk->block_id;
  1625. }
  1626.  
  1627. // Select/deselect block.
  1628. // Need to call DisplayFile explicitly before screen is updated.
  1629. void wxHyperTextWindow::SelectBlock(wxTextChunk *chunk, Bool select)
  1630. {
  1631.   if (chunk)
  1632.   {
  1633.     chunk->selected = select;
  1634.     if (select)
  1635.     {
  1636.       current_selections.Append((wxObject *)chunk->block_id);
  1637.       OnSelectBlock(chunk->block_id, select);
  1638.     }
  1639.     else
  1640.     {
  1641.       if (chunk->block_type == BLOCK_TYPE_SELECTION)
  1642.         ClearBlock(chunk->block_id);
  1643.       current_selections.DeleteObject((wxObject *)chunk->block_id);
  1644.       OnSelectBlock(chunk->block_id, select);
  1645.     }
  1646.   }
  1647. }
  1648.  
  1649. void wxHyperTextWindow::SelectBlock(long block_id, Bool select)
  1650. {
  1651.   wxTextChunk *chunk = FindBlock(block_id);
  1652.   if (chunk) SelectBlock(chunk, select);
  1653. }
  1654.  
  1655. void wxHyperTextWindow::OnSelectBlock(long block_id, Bool select)
  1656. {
  1657. }
  1658.  
  1659. long wxHyperTextWindow::GetFirstSelection(void)
  1660. {
  1661.   selection_current = current_selections.First();
  1662.   if (selection_current)
  1663.   {
  1664.     long id = (long)selection_current->Data();
  1665.     selection_current = selection_current->Next();
  1666.     return id;
  1667.   } else return -1;
  1668. }
  1669.  
  1670. long wxHyperTextWindow::GetNextSelection(void)
  1671. {
  1672.   if (selection_current)
  1673.   {
  1674.     long id = (long)selection_current->Data();
  1675.     selection_current = selection_current->Next();
  1676.     return id;
  1677.   }  else return -1;
  1678. }
  1679.  
  1680. void wxHyperTextWindow::DisplayNextSection(void)
  1681. {
  1682.   if (current_section)
  1683.   {
  1684.     wxNode *next = current_section->Next();
  1685.     if (!next)
  1686.       return;
  1687.     current_section = next;
  1688.     wxTextChunk *chunk = (wxTextChunk *)current_section->Data();
  1689.     DisplayFileAt(chunk->block_id);
  1690.   } else if (sections.Number() > 0)
  1691.   {
  1692.     current_section = sections.First();
  1693.     wxTextChunk *chunk = (wxTextChunk *)current_section->Data();
  1694.     DisplayFileAt(chunk->block_id);
  1695.   }
  1696. }
  1697.  
  1698. void wxHyperTextWindow::DisplayPreviousSection(void)
  1699. {
  1700.   if (current_section)
  1701.   {
  1702.     wxNode *prev = current_section->Previous();
  1703.     if (prev)
  1704.     {
  1705.       current_section = prev;
  1706.       wxTextChunk *chunk = (wxTextChunk *)current_section->Data();
  1707.       DisplayFileAt(chunk->block_id);
  1708.     }
  1709.     else DisplayFileAt(-1);
  1710.   }
  1711. }
  1712.  
  1713. int wxHyperTextWindow::GetCurrentSectionNumber(void)
  1714. {
  1715.   if (current_section)
  1716.   {
  1717.     int i = 1;
  1718.     wxNode *node = sections.First();
  1719.     while (node)
  1720.     {
  1721.       if (node == current_section)
  1722.         return i;
  1723.       else
  1724.         node = node->Next();
  1725.       i ++;
  1726.     }
  1727.     return 0;
  1728.   }
  1729.   return 0;
  1730. }
  1731.  
  1732. // Save the block id of the current section, since current_section
  1733. // is always set to NULL during a compile, and the display must be
  1734. // restored.
  1735. void wxHyperTextWindow::SaveSection(void)
  1736. {
  1737.   if (current_section)
  1738.   {
  1739.     wxTextChunk *chunk = (wxTextChunk *)current_section->Data();
  1740.     saved_block_id = chunk->block_id;
  1741.     ViewStart(&saved_x, &saved_y);
  1742.   } else saved_block_id = -1;
  1743. }
  1744.  
  1745. void wxHyperTextWindow::RestoreSection(void)
  1746. {
  1747.   // See if the section is still there
  1748.   wxNode *node = sections.First();
  1749.   while (node)
  1750.   {
  1751.     wxTextChunk *chunk = (wxTextChunk *)node->Data();
  1752.     if (chunk->block_id == saved_block_id)
  1753.     {
  1754.       DisplayFileAt(chunk->block_id, FALSE);
  1755.       Scroll(saved_x, saved_y);
  1756.       DisplayFile();
  1757.       return;
  1758.     }
  1759.     node = node->Next();
  1760.   }
  1761.   DisplayFileAtTop();
  1762. }
  1763.  
  1764. // A chunk is not a block, since it may signify the start of a
  1765. // line as well as a block boundary.
  1766. wxTextChunk::wxTextChunk(int the_chunk_type, int the_line_no, char *the_text,
  1767.                          wxFont *the_font, wxColour *the_colour,
  1768.                          int the_block_type, long the_block_id,
  1769.                          int the_attribute, Bool the_visibility)
  1770. {
  1771.   chunk_type = the_chunk_type;
  1772.   line_no = the_line_no;
  1773.   if (the_text) text = copystring(the_text);
  1774.     else text = NULL;
  1775.   font = the_font;
  1776.   colour = the_colour;
  1777.   block_type = the_block_type;
  1778.   block_id = the_block_id;
  1779.  
  1780.   logical_op = wxCOPY;
  1781.   background_colour = NULL;
  1782.   selected = FALSE;
  1783.   special_attribute = the_attribute;
  1784.   visibility = the_visibility;
  1785. }
  1786.  
  1787. wxTextChunk::~wxTextChunk(void)
  1788. {
  1789.   if (text) delete text;
  1790. }
  1791.  
  1792. HypertextItem::HypertextItem(char *new_filename, long new_block)
  1793. {
  1794.   if (new_filename)
  1795.     filename = copystring(new_filename);
  1796.   else filename = NULL;
  1797.   block_id = new_block;
  1798. }
  1799.  
  1800. HypertextItem::~HypertextItem(void)
  1801. {
  1802.   if (filename)
  1803.     delete filename;
  1804. }
  1805.  
  1806. // Read an integer from the buffer, returning next index if
  1807. // successful, -1 if not.
  1808. int wxReadInteger(char *line, int next, long *value)
  1809. {
  1810.   long the_value = 0;
  1811.   char digits[10];
  1812.   int no_digits = 0;
  1813.   int next1 = next;
  1814.   while ((next1 < next + 10) && isdigit(line[next1]))
  1815.   {
  1816.  
  1817.     digits[no_digits] = line[next1];
  1818.     no_digits ++;
  1819.     next1 ++;
  1820.   }
  1821.   if (no_digits == 0)
  1822.     return -1;
  1823.   else
  1824.   {
  1825.     for (int i = 0; i < no_digits; i++)
  1826.       the_value += (long)(((int)digits[i] - 48) * pow(10, no_digits - i - 1));
  1827.     *value = the_value;
  1828.     return next1;
  1829.   }
  1830. }
  1831.  
  1832. /*
  1833.  * Font stuff
  1834.  *
  1835.  */
  1836.  
  1837. wxFont *wxFindOrCreateFont(int PointSize, int Family, int Style, int Weight)
  1838. {
  1839.   wxNode *node = wxTheFontList->First();
  1840.   wxFont *font = NULL;
  1841.   while (node && !font)
  1842.   {
  1843.     wxFont *the_font = (wxFont *)node->Data();
  1844.     if (the_font->family == Family && the_font->point_size == PointSize
  1845.         && the_font->style == Style && the_font->weight == Weight)
  1846.       font = the_font;
  1847.     else node = node->Next();
  1848.   }
  1849.   if (font)
  1850.     return font;
  1851.   else
  1852.     return new wxFont(PointSize, Family, Style, Weight);
  1853. }
  1854.