home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-385-Vol-1of3.iso / x / xhyper10.zip / XHyper_v1.0 / src / TextView.c < prev    next >
C/C++ Source or Header  |  1992-12-08  |  18KB  |  765 lines

  1.  
  2. /*
  3.  * Copyright (c) 1992 U.S. Geological Survey (USGS)
  4.  *
  5.  * Permission to use, copy, modify, distribute, and sell this software and its
  6.  * documentation for any purpose is hereby granted without fee, provided that
  7.  * the above copyright notice appear in all copies and that both that
  8.  * copyright notice and this permission notice appear in supporting
  9.  * documentation, and that the name of USGS not be used in advertising or
  10.  * publicity pertaining to distribution of the software without specific,
  11.  * written prior permission.  USGS makes no representations about the
  12.  * suitability of this software for any purpose.  It is provided "as is"
  13.  * without express or implied warranty.
  14.  *
  15.  * USGS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
  16.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL USGS
  17.  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  18.  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
  19.  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  20.  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  21.  *
  22.  */
  23.  
  24. /*
  25.  * TextView
  26.  */
  27. #include <InterViews/background.h>
  28. #include <InterViews/box.h>
  29. #include <InterViews/character.h>
  30. #include <InterViews/color.h>
  31. #include <InterViews/composition.h>
  32. #include <InterViews/deck.h>
  33. #include <InterViews/discretion.h>
  34. #include <InterViews/font.h>
  35. #include <InterViews/glue.h>
  36. #include <InterViews/hit.h>
  37. #include <InterViews/listener.h>
  38. #include <InterViews/patch.h>
  39. #include <InterViews/printer.h>
  40. #include <InterViews/session.h>
  41. #include <InterViews/strut.h>
  42. #include <InterViews/style.h>
  43. #include <InterViews/simplecomp.h>
  44. #include <InterViews/texcomp.h>
  45. #include <InterViews/world.h>
  46. #include <fstream.h>
  47. #include <string.h>
  48. #include <stdlib.h>
  49.  
  50. #include "HyperView.h"
  51. #include "list.h"
  52. #include "PSFigItem.h"
  53. #include "TextView.h"
  54. #include "Tag.h"
  55.  
  56. /*-------------------------------------------------------------------------
  57.    Class TextView
  58.  
  59.    This class is the heart of the On-Line Help display.  It translates
  60.    the MML files, displays text and figures, and processes hypertext
  61.    requests.
  62.  
  63.    Members:
  64.     appendchar    - adds character to view
  65.     Count        - returns number of pages in view
  66.     draw        - draw the page
  67.     event        - catch hypertext request
  68.     fontstyle    - changes style of current font (e.g. bold)
  69.     jump_to        - jump to hypertext offset
  70.     last_page    - jump to last page  
  71.     make_defaults    - create default font and paragraph
  72.     MMLFile        - returns name of file
  73.     parse_command    - process an MML variable
  74.     printout    - generate Postscript output
  75.     read        - translate MML file
  76.     view_page    - view specific page 
  77.     wordhit        - processes a hypertext request
  78.  
  79.  *-------------------------------------------------------------------------*/
  80.  
  81. const Coord TEXTWIDTH  = 450.0;
  82. const Coord TEXTHEIGHT = 240.0;
  83.  
  84. //
  85. // *** create Text information list structure (see list.h)
  86. //
  87. declareList(TextInfo_List,TextInfo)
  88. implementList(TextInfo_List,TextInfo)
  89.  
  90. extern int getline(FILE*, char*);
  91.  
  92. TextView::TextView (HyperViewer* p, char* filename, int tindex) 
  93.                     : MonoGlyph(nil), Handler()
  94.    {
  95.    char     fullname[256];
  96.    FILE  *fd;
  97.  
  98.    _home = (char*)getenv("HYPERDOC");
  99.    _parent = p;
  100.    _numfont = 0;
  101.    _numpar  = 0;
  102.    _listener = new Listener(nil, this);
  103.    _text = new TextInfo_List(1000);
  104.    _ishyper = false;
  105.    _tocindex = tindex;
  106.  
  107.    make_defaults();
  108.   
  109.    _pages = new Deck();
  110.  
  111.    //
  112.    //  *** pages are broken into lines
  113.    //
  114.    _lines = new TBComposition(
  115.                _pages,
  116.                new SimpleCompositor(), 
  117.                new Discretionary(
  118.                   0, 
  119.                   new VStrut(0, TEXTHEIGHT, 20.0, 0, 0),
  120.                   new VStrut(0, TEXTHEIGHT, 0.0, 0, 0), nil, nil
  121.                   ),
  122.                TEXTHEIGHT, 10 
  123.                );
  124.     _lines->ref();
  125.  
  126.    //
  127.    //  *** lines are broken into characters
  128.    //
  129.    _characters = new LRComposition(
  130.                      _lines, 
  131.                      new TeXCompositor(10),
  132.                      nil,
  133.                      TEXTWIDTH, 
  134.                      1000
  135.                      );
  136.     _characters->ref();
  137.  
  138.    //
  139.    //   *** open and translate file
  140.    //
  141.    if (_home != NULL)
  142.       {
  143.       strcpy(fullname, _home);
  144.       strcat(fullname, "/");
  145.       strcat(fullname, filename);
  146.       }
  147.    else
  148.       strcpy(fullname, filename);
  149.  
  150.    if ((fd = fopen(fullname, "r")) == (FILE*)NULL)
  151.       {
  152.       printf("Cannot open file: %s\n", filename);
  153.       exit(-1);
  154.       }
  155.  
  156.    read(fd);
  157.    fclose(fd);
  158.  
  159.    appendchar('\n');
  160.    _file = filename;
  161.    _characters->repair();
  162.    _lines->repair();
  163.    _patch = new Patch(_characters);
  164.    _listener->body(_patch);
  165.    _listener->button(true, Event::left);
  166.    _listener->key(true);
  167.    body(_listener);
  168.  
  169.    _curpage = 1;
  170.    view_page(1);
  171.    }
  172.  
  173. TextView::~TextView()
  174.    {
  175.    _lines->unref(); 
  176.    _characters->unref(); 
  177.  
  178.    delete _file;
  179.    delete _text; 
  180.    delete _pages; 
  181.    delete _patch; 
  182.    delete _listener; 
  183.    }
  184.  
  185. int TextView::Count()
  186.    {
  187.    return (_pages->count()/2);
  188.    }
  189.  
  190. const char* TextView::MMLFile()
  191.    {
  192.    return _file;
  193.    }
  194.  
  195. void TextView::view_page(int page)
  196.    {
  197.    page = 2*(page-1);
  198.    _pages->flip_to(page);
  199.  
  200.    //
  201.    //   *** find first and last characters of page
  202.    //
  203.    long  first_line = _lines->beginning_of(page);
  204.    long  last_line  = _lines->end_of(page);
  205.    long  first_char = _characters->beginning_of(first_line);
  206.    long  last_char  = _characters->end_of(last_line);
  207.  
  208.    _characters->view(first_char, last_char);
  209.    _patch->reallocate();
  210.    _patch->redraw();
  211.    }
  212.  
  213. void TextView::event (Event& e)
  214.    {
  215.    char keystring[10];
  216.  
  217.    Hit hit(e.pointer_x(), e.pointer_y());
  218.  
  219.    //
  220.    //   *** at present, only up events are processed
  221.    //
  222.    switch (e.type())
  223.       {
  224.       case Event::down:
  225.          break;
  226.  
  227.       case Event::up:
  228.          _patch->repick(0, hit);
  229.  
  230.          if (hit.any())
  231.             wordhit(hit.index(0));
  232.          break;
  233.  
  234.       case Event::key:
  235.          break;
  236.       }
  237.    }
  238.  
  239. void TextView::wordhit(GlyphIndex i)
  240.    {
  241.    //
  242.    //  *** tell parent of hypertext request
  243.    //
  244.    if (_text->item(i)._tocindex != 255)
  245.       _parent->hypertext(_text->item(i)._tocindex);
  246.    }
  247.  
  248. int TextView::last_page()
  249.    {
  250.    view_page((int)_pages->count()/2);
  251.    return(_pages->count()/2);
  252.    }
  253.  
  254. void TextView::jump_to(int offset)
  255.    {
  256.    int   p;
  257.    long  first_line;
  258.    long  first_char;
  259.    long  last_line;
  260.    long  last_char;
  261.  
  262.    //
  263.    //  *** find page containing offset and inform parent
  264.    //
  265.    for (p = 0; p < _pages->count(); p++)
  266.       {
  267.       first_line = _lines->beginning_of(p);
  268.       last_line  = _lines->end_of(p);
  269.       first_char = _characters->beginning_of(first_line);
  270.       last_char  = _characters->end_of(last_line);
  271.  
  272.       if (((first_char-2) <= offset) && (last_char > offset))
  273.          {
  274.          _parent->page_to(p/2+1);
  275.          break;
  276.          }
  277.       }
  278.  
  279.    if (p == _pages->count())
  280.       _parent->page_to((int)(_pages->count())/2);
  281.    }
  282.  
  283. void TextView::read(FILE* fd)
  284.    {
  285.    char    line[100];
  286.    int     textout;
  287.    int     eol;
  288.    int     i;
  289.    boolean newline;
  290.  
  291.    eol = false;
  292.    textout = false;
  293.  
  294.    _fg = Session::instance()->style()->foreground();
  295.    _bg = Session::instance()->style()->background();
  296.  
  297.    while (getline(fd, line) != EOF)
  298.       {
  299.       i = 0;
  300.  
  301.       switch (line[0])
  302.          {
  303.          case ' ':
  304.             break;
  305.  
  306.          case '\n':
  307.             if (textout)
  308.                {
  309.                appendchar('\n');
  310.                textout = false;
  311.                }
  312.             break;
  313.  
  314.          //
  315.          //  *** MML definition
  316.          //
  317.          case '<':
  318.             newline = parse_command(line, fd);
  319.             if (newline && textout)
  320.                {
  321.                appendchar('\n');
  322.                textout = false;
  323.                break;
  324.                }
  325.             else
  326.                {
  327.                while ((line[i] != '>') && (line[i] != '\0'))
  328.                     i++;
  329.  
  330.                if (line[i] == '>')
  331.                   {
  332.                   i++;
  333.                   while (((line[i] == ' ') || (line[i] == '\n')) && 
  334.                          (line[i] != '\0'))
  335.                       i++;
  336.                   if (line[i] == '\0')
  337.                      break;
  338.                   }
  339.                else if (line[i] == '\0')
  340.                   break;
  341.                }
  342.  
  343.  
  344.          //
  345.          //  *** block of text
  346.          //
  347.          default: 
  348.             textout = true;
  349.             while (i < strlen(line))
  350.                {
  351.                switch (line[i])
  352.                   {
  353.                   case ' ':
  354.                      appendchar(' ');
  355.                      break;
  356.  
  357.                   case '\\':
  358.                      if (line[++i] == 'n')
  359.                         {
  360.                         appendchar('\n');
  361.                         // while(++i < strlen(line));
  362.                         eol = true;
  363.                         }
  364.                      else
  365.                         appendchar(line[i]);
  366.                      break;
  367.  
  368.                   //
  369.                   //  *** interline definition (e.g. bold)
  370.                   //
  371.                   case '<':
  372.                      parse_command(line+i, fd);
  373.                      while(line[i] != '>')
  374.                         i++;
  375.                      break;
  376.  
  377.                   default:
  378.                      appendchar(line[i]);
  379.                      break;
  380.                   }
  381.  
  382.                i++;
  383.                } /* while */
  384.  
  385.             if (textout && !eol)
  386.                appendchar(' ');
  387.             else if (eol)
  388.                eol = false;
  389.             break;
  390.          };
  391.       }
  392.  
  393.    }
  394.  
  395. void TextView::appendchar(const char c)
  396.    {
  397.    TextInfo t;
  398.    
  399.    //
  400.    //  *** if defining hypertext, define TOC index
  401.    //
  402.    if (_ishyper)
  403.       t._tocindex = _tocindex;
  404.    else
  405.       t._tocindex = 255;
  406.  
  407.    if ((_text->count() == 0) && (c != '\n'))
  408.       {
  409.       _characters->append( 
  410.           new Discretionary(
  411.               PenaltyGood, 
  412.               _partag->_fil_strut,
  413.               new VStrut(0), 
  414.               nil,        
  415.               _partag->_begin_par_strut
  416.               ),
  417.           );
  418.       }
  419.       
  420.    t._code  = c;
  421.    _text->append(t);
  422.  
  423.    switch (c)
  424.       {
  425.       //
  426.       //  ***  define space based on paragraph definition
  427.       //
  428.       case '\t':
  429.          _characters->append(
  430.                new Discretionary(0,
  431.                         new HStrut(20), // _partag->_word_space,
  432.                         _partag->_end_line_strut,
  433.                         new Discretionary(0, _partag->_interline_glue,
  434.                             _partag->_vfil_glue, nil, nil
  435.                             ),
  436.                         _partag->_begin_line_strut
  437.                )
  438.          );
  439.          break;
  440.  
  441.       case '\n':
  442.          _characters->append( 
  443.              new Discretionary(
  444.                  PenaltyGood, 
  445.                  _partag->_fil_strut,
  446.                  _partag->_end_par_strut,    // stretch after last line
  447.                  new Discretionary(
  448.                      0, 
  449.                      _partag->_interpar_glue,   // space before a paragraph??
  450.                      _partag->_vfil_glue,     // space at bottom of a page
  451.                      nil,                       // space after page break
  452.                      nil                // stetchability of page
  453.                      ),
  454.                  _partag->_begin_par_strut    // space before a paragraph
  455.                  ),
  456.              );
  457.  
  458.          break;
  459.  
  460.       //
  461.       //  ***  if hypertext, draw reversed
  462.       //
  463.       case ' ':
  464.          if (_ishyper)
  465.             _characters->append(
  466.                  new Discretionary(0, 
  467.                       new Background(
  468.                           new Character(' ', _fonttag->font(), _bg),_fg),
  469.                       new HGlue(fil), nil, nil)
  470.             );
  471.          else
  472.             _characters->append( 
  473.                   new Discretionary(0, 
  474.                            _partag->_word_space,
  475.                            _partag->_end_line_strut, 
  476.                            new Discretionary(
  477.                                0, 
  478.                                _partag->_interline_glue,
  479.                                _partag->_vfil_glue, 
  480.                                nil, 
  481.                                nil
  482.                                ),
  483.                            _partag->_begin_line_strut
  484.                   )
  485.             );
  486.          break;
  487.  
  488.       default:
  489.          //
  490.          //  ***  if hypertext, draw reversed
  491.          //
  492.          if (_ishyper)
  493.             _characters->append(
  494.                  new Background( new Character(c, _fonttag->font(), _bg), _fg)
  495.             ); 
  496.          else
  497.             _characters->append(new Character(c, _fonttag->font(), _fg)); 
  498.          break;
  499.       }
  500.    }
  501.  
  502. void TextView::draw(Canvas* c, const Allocation& a)const
  503.    {
  504.    MonoGlyph::draw(c, a);
  505.    }
  506.  
  507. boolean TextView::parse_command(char* line, FILE* fd)
  508.    {
  509.    char   c;
  510.    char   command[100];
  511.    char   *gt;
  512.    int    cindex;
  513.    boolean retval = false;
  514.  
  515.    cindex = 0;
  516.  
  517.   /*
  518.    *  *** extract command
  519.    */
  520.    sscanf(line+1, "%s", command);
  521.    gt = strchr(command, '>');
  522.    if (gt != NULL)
  523.       gt[0] = '\0';
  524.  
  525.   /*
  526.    *  *** if comment or MML version, ignore remainder
  527.    */
  528.    if ((strcasecmp(command, "Comment") == 0) || 
  529.        (strcasecmp(command, "MML") == 0))
  530.       {
  531.       if (strchr(line, '>') == NULL)
  532.          {
  533.          while ((c = getc(fd)) != '>');
  534.          while ((c = getc(fd)) != '\n');
  535.          }
  536.       line[0] = '\0';
  537.       }
  538.    //
  539.    //  *** insert Idraw figure
  540.    //
  541.    else if (strcasecmp(command, "Figure") == 0)
  542.       {
  543.       char    fullname[256];
  544.       PSFigItem *psfig;
  545.       char     *file;
  546.       char     *endfile;
  547.       char      creator[100];
  548.  
  549.       file = strchr(line, '"')+1;
  550.       endfile = strrchr(line, '"');
  551.       endfile[0] = '\0';
  552.  
  553.       if (_home != NULL)
  554.          {
  555.          strcpy(fullname, _home);
  556.          strcat(fullname, "/");
  557.          strcat(fullname, file);
  558.          }
  559.       else
  560.          strcpy(fullname, file);
  561.  
  562.       psfig = new PSFigItem(fullname);
  563.       appendchar('\n');
  564.       _characters->append(
  565.               new LRBox(new HGlue(),
  566.                         psfig->graphic(),
  567.                         new HGlue()
  568.                         )
  569.       );
  570.       retval = true;
  571.       }
  572.    else if (strcasecmp(command, "Include") == 0)
  573.       {
  574.       char fullname[256];
  575.       FILE *finclude;
  576.       char *file;
  577.       char *endfile;
  578.  
  579.       file = strchr(line, '"');
  580.       endfile = strrchr(line, '"');
  581.       endfile[0] = '\0';
  582.  
  583.       if (_home != NULL)
  584.          {
  585.          strcpy(fullname, _home);
  586.          strcat(fullname, "/");
  587.          strcat(fullname, file+1);
  588.          }
  589.       else
  590.          strcpy(fullname, file+1);
  591.  
  592.       if ((finclude = fopen(fullname, "r")) != NULL)
  593.          read(finclude);
  594.       }
  595.   /*
  596.    *  ***  if definition, create definition and return
  597.    */
  598.    else if (command[0] == '!')
  599.       define(line, fd);
  600.    else if (strcasecmp(command, "plain") == 0)
  601.       fontstyle("roman");
  602.    else if (strcasecmp(command, "bold") == 0)
  603.       fontstyle("bold");
  604.    else if (strncasecmp(command, "hyper", 5) == 0)
  605.       {
  606.       char   *gt;
  607.       char   tag[80];
  608.  
  609.       _ishyper = true;
  610.       _tocindex = 255;
  611.       sscanf(line+strlen("hyper")+2, "%s", tag);
  612.       gt = strchr(tag, '>');
  613.       if (gt != nil)
  614.          gt[0] = '\0';
  615.  
  616.       _tocindex = _parent->TOC(tag);
  617.  
  618.       if (_tocindex == 255)
  619.          _ishyper = false;
  620.       }
  621.    else if (strncasecmp(command, "endhyp", 6) == 0)
  622.       _ishyper = false;
  623.  
  624.   /*
  625.    *  ***  must be a previously defined user or system definition
  626.    */
  627.    else
  628.       {
  629.       int  i;
  630.  
  631.       for (i = 0; i < _numpar; i++)
  632.          {
  633.          if (strcasecmp(command, parlist[i]->Id()) == 0)
  634.             {
  635.             _partag = parlist[i];
  636.             if (_partag->fonttag() != nil)
  637.                _fonttag = _partag->fonttag();
  638.  
  639.             if (_partag->_hypertext)
  640.                _parent->updateTOC(fd, (int)_text->count());
  641.  
  642.              return(true);
  643.              }
  644.          }
  645.  
  646.       for (i = 0; i < _numfont; i++)
  647.          if (strcasecmp(command, fontlist[i]->Id()) == 0)
  648.             { 
  649.             _fonttag = (FontTag*)fontlist[i];
  650.             return(retval);
  651.             }
  652.       }
  653.  
  654.    return(retval);
  655.    }
  656.  
  657. void TextView::define(const char *line, FILE* fd)
  658.    {
  659.    char   idbuf[40];
  660.    char   tag[40];
  661.    int    i;
  662.  
  663.    //
  664.    //  *** get tag name
  665.    //
  666.    sscanf(line, "%s", &tag);
  667.    i = strlen(tag);
  668.    while((line[i] == ' ') || (line[i] == '\t'))
  669.       i++;
  670.  
  671.    sscanf(line+i, "%s", &idbuf);
  672.  
  673.    //
  674.    //  *** if already defined, ignore it
  675.    //
  676.    for (i = 0; i < _numpar; i++)
  677.       {
  678.       if (strcasecmp(parlist[i]->Id(), idbuf) == 0)
  679.          return;
  680.       }
  681.  
  682.    for (i = 0; i < _numfont; i++)
  683.       {
  684.       if (strcasecmp(fontlist[i]->Id(), idbuf) == 0)
  685.          return;
  686.       }
  687.  
  688.    if (strncmp(line+2, "DefineFont", 10) == 0)
  689.       fontlist[_numfont++] = new FontTag(line, fd);
  690.    else if (strncmp(line+2, "DefinePar", 9) == 0)
  691.       parlist[_numpar++] = new ParaTag(line, fd, this);
  692.    }
  693.  
  694. void TextView::fontstyle(const char* style)
  695.    {
  696.    char     fontname[80];
  697.    char    *stptr;
  698.    int      i;
  699.  
  700.    strcpy(fontname, _fonttag->Name());
  701.   
  702.    //
  703.    //  *** lookup font among defined font tags
  704.    //
  705.    if (((stptr = strchr(fontname, '-')) != NULL) &&
  706.        (strcmp(stptr+1, style) != NULL))
  707.       {
  708.       strcpy(stptr+1, style);
  709.       for (i = 0; i < _numfont; i++)
  710.          {
  711.          if ((strcmp((fontlist[i])->Name(), fontname) == 0) &&
  712.              (fontlist[i])->Points() == _fonttag->Points())
  713.             {
  714.             _fonttag = fontlist[i];
  715.             break;
  716.             }
  717.          }
  718.       }
  719.    }
  720.  
  721. void TextView::make_defaults()
  722.    {
  723.    _fonttag = new FontTag("deffont", 0);
  724.    _partag = new ParaTag("defpar");
  725.  
  726.    fontlist[_numfont++] =  _fonttag;
  727.    parlist[_numpar++]   =  _partag;
  728.    }
  729.  
  730. //
  731. //  *** generats postscript
  732. //
  733. void TextView::printout()
  734.    {
  735.    char  psfile[256];
  736.    char  *ext;
  737.  
  738.    strcpy(psfile, _file);
  739.    ext = strrchr(psfile, '.');
  740.    if (ext != NULL)
  741.       ext[0] = '\0';
  742.    strcat(psfile, ".ps");
  743.    ofstream out(psfile);
  744.    Printer* ps = new Printer(&out);
  745.   
  746.    //
  747.    //  *** set size of page
  748.    //
  749.    const Allocation& a = _patch->allocation();
  750.    ps->prolog(psfile);
  751.    ps->resize(a.left(), a.bottom(), a.right(), a.top());
  752.  
  753.    long current_page = 0;
  754.    long count = _pages->count();
  755.  
  756.    for (long i = 1; i <= count/2; i++) {
  757.       ps->page(nil);
  758.       view_page((int)i);
  759.       _patch->print(ps, a);
  760.       }
  761.  
  762.    _parent->page_to(1);
  763.    }
  764.  
  765.