Sindbad~EG File Manager

Current Path : /usr/home/beeson/MathXpert/docdata/
Upload File :
Current File : /usr/home/beeson/MathXpert/docdata/papyrus.c

/* M. Beeson, for MathXpert.
File created 6.26.13
Interface-free parts of the former solution.c
2.10.24 modified init_papyrus for finalremark and hint.
4.19.24 changed pPapyrus->leftmargin to 8
8.19.24 removed unused variable in newstep
9.27.24  replaced report_err by printf
2.16.25  removed unused variable was_a_selected_term
*/
/*____________________________________________________________________*/
#include <assert.h>
#include <stdio.h>   /* FILE needed by wfile.h */
#include <stddef.h>
#include <string.h>
#include <stdlib.h> 
#include "globals.h"
#include "graphstr.h"
#include "mpdoc.h"
#include "mathpert.h"
#include "cflags.h"   /* get_controldata   */
#include "display.h"  /* wdisplay          */
#include "display1.h" /* block             */
#include "userfunc.h" /* MAXUSERFUNCTIONS  */
#include "heaps.h"    /* nuserfunctions etc */
#include "checkarg.h" /* operator typedef  */
#include "execute.h"  /* cogitate          */
#include "bigrect.h"
#include "lterm.h"    /* select_term, wdisplay_lterm */
#include "automode.h" /* get_activeline    */
#include "selectop.h" /* selectops         */
#include "select.h"   /* disjoint, select_term */
#include "docdata.h"  /* get_comment       */
#include "islinear.h" /* is_linear_system  */
#include "debug.h"    /* FRAMES            */
#include "errbuf.h"   /* get_comment_buffer */
#include "mstring.h"  /* mstrlen           */
#include "pcontrol.h" /* get_parser_flags  */
#include "activedoc.h" /* GetActiveDoc */
#include "papyrus.h"

static coord measure_reason(const char *reason);
static int break_reason(char *reason, int lineheights[3],  char lines[3][MAXONELINE]);

#ifndef PRODUCTION
#include "autotest.h"
#endif


int get_scrollbar_width(void);
int get_scrollbar_width(void)
{ return 0;
}
/*__________________________________________________________________*/
static void firstrow(lterm t, BIGRECT *r, int *z)
/* get the rectangle and pixel y-coordinate of the waterlevel
of the first line of broken term t.  If the term t is parenthesized,
this rectangle does NOT include the open parenthesis (since you
don't have to include the parentheses in term selection.) */

{ if(t.linelist)
     { *r = t.linelist->rect;
       *z = t.linelist->wl;
       return;
     }
  *r = t.r;
  *z = (int) (papyrus_to_ypixel(t.block.wl) + r->top);
}
/*__________________________________________________________________*/
void lastrow(lterm t, BIGRECT *r, int *z)
/* Fill in *r with the rectangle of the last line of t */
/* Fill in *z with the .wl field of the last line of t,
which is the pixel coordinate of the water level */

{ rectlist *marker;
  if(t.linelist == NULL)
     { firstrow(t,r,z);
       return;
     }
  for(marker = t.linelist; marker->next; marker=marker->next)
     ;  /* point marker to last node */
  *r = marker->rect;
  *z = marker->wl;
}

extern int get_scrollbar_width(void);  // in solution.c, but solution.h is Windows-dependent 
/*_______________________________________________________________*/
/* Following function is for debugging only */
#ifdef FRAMES

void frame(HDC hdc, lterm lt)
/* Draw boxes around all the subterms */
{ unsigned short i;
  unsigned n = ARITY(lt);
  RECT r;
  if(!ATOMIC(lt))
     { for(i=0;i<n;i++)
         frame(hdc,LARG(i,lt));
     }
  r.left = (int) lt.r.left;
  r.right = (int) lt.r.right;
  r.top = (int) lt.r.top;
  r.bottom = (int) lt.r.bottom;
  FrameRect(hdc,&r,GetStockObject(GRAY_BRUSH));
  if(lt.linelist)
     { HBRUSH RedBrush = CreateSolidBrush(RGB(255,0,0));
       rectlist *marker;
       for(marker = lt.linelist; marker; marker= marker->next)
           { r.left = (int) marker->rect.left;
             r.right = (int) marker->rect.right;
             r.top = (int) marker->rect.top;
             r.bottom = (int) marker->rect.bottom;
             FrameRect(hdc,&r,RedBrush);
           }
       DeleteObject(RedBrush);
     }
 }
#endif


static int linespacing = 16;  /* in papyrus coordinates */

 

/*_____________________________________________________________________*/
int new_step(PDOCDATA pDocData, int *newhscrollbars,int *newscrollbars)
/* Handle user-defined message WM_NEWSTEP in the solution window */
/* a new last line has been generated but not displayed yet.
   Measure the new line's height, set linemarks, scroll the
   window up to receive the new line, and
   invalidate the correct rectangle, namely between
   lines[currentline].formula.x and the bottom of the client rectangle;
   or, if lines[currentline].formula.x is below the rectangle,
   invalidate back to lines[currentline-1].formula.x, so a new
   WM_PAINT will be generated and we won't paint only part
   of a line (which can cause roundoff-error problems).
      Return nonzero if we need to scroll the solution window after this step.
*/
{ Papyrus *pPapyrus = pDocData->papyrus;
  int currentline = get_currentline();
  coord lineheight,next,linewidth, lastlineheight, reasonheight;
  int extra;
  term *history = pDocData->DocProverData.history;
  term t = history[currentline];
  const char *reason = get_reason(pDocData,currentline);
  unsigned char *comment;

  if(reason == NULL && currentline == 0)
     /* This is redundant code because the reason for line 0 is
         now set in symproc.c under WM_GETPROBLEM.  But it's
         harmless. */
     { store_reason(pDocData,0,english(1188));  /* the problem */
       /* the justification string for the first line */
       pDocData->DocProverData.permhistory[0] = pDocData->DocProverData.nextworkspace;
     }
  if(FUNCTOR(t) == OR || FUNCTOR(t) == AND)
     { int selected_equation = get_selected_equation();
       if(selected_equation < 0)
          t = get_selected_arg(t);
     }
  reset_heap(NULL);  /* back to square zero, all done
               calculating this line and storing the results in
               permspace.  We need to free up memory here so it is
               available for line-breaking calculations needed to
               update the screen, instead of waiting until the
               end of processing this message. */

  lineheight = measure_height(pPapyrus,pDocData->DocControlData.linebreaks,t,&extra,&lastlineheight,&linewidth);
  next = (coord)( pPapyrus->lines[currentline].formula.x + lineheight + linespacing);
  pPapyrus->lines[currentline].formula.y = (coord)( pPapyrus->lines[currentline].formula.x + lineheight);
  pPapyrus->lines[currentline].comment.x = next;  /* the top y-coordinate of the comment, in spite of the .x label */
    /* whether or not there actually is a comment; because the next
       operator may fail on purpose and leave a comment such as
       "Solutions all check in the original equation.", and under
       WM_PAINT we need the coordinate where to paint this comment.
    */
  comment = get_comment(pDocData,currentline);
  /* This only gets 'permanent' comments that have been stored in the document
     data structure. */
  if(comment)
     { /* How many lines will it require? */
       int k = (int) strlen((char *)comment);
       int nlines = k /((pPapyrus->width - pPapyrus->leftmargin) >> 4) +1;
       pPapyrus->lines[currentline].comment.y = (coord)( next + (nlines << 4));
       /* bottom y-coordinate of the comment.  The top y-coordinate was
          set a few lines above. */
       lineheight = (coord)(lineheight + linespacing + (nlines << 4));
       /* this linespacing for the space between the line and comment */
       /* Now lineheight is the height of the formula, the comment,
          and the line between */
     }
  /* We may need extra space for the reason string if it extends
     below the formula rectangle. The variable 'extra' has been set
     by measure_height; it is negative if the reason has to START below
     the formula.  Otherwise it is the papyrus-coordinate height-level
     of (the last row of, if broken) the new line.  Next we need to
     know how much space the reason will occupy.
  */
  reasonheight = measure_reason(reason);
  pPapyrus->lines[currentline].reasonrect =
     reasonrect(reasonheight,pPapyrus->lines[currentline].formula.y,
                lastlineheight, extra
               );
  if(pPapyrus->lines[currentline].reasonrect.y >
     pPapyrus->lines[currentline].formula.y
    )
     { lineheight = (coord)(lineheight + pPapyrus->lines[currentline].reasonrect.y -
                   pPapyrus->lines[currentline].formula.y);
       pPapyrus->lines[currentline].formula.y =
           pPapyrus->lines[currentline].reasonrect.y;
     }

  /* Now lineheight has been properly adjusted to allow space
     for the comment if any (and a blank line if there's a comment),
     the formula, and the reason.  */

  if(pPapyrus->height + pPapyrus->bottommargin <= pPapyrus->bottom-pPapyrus->top &&
     pPapyrus->height + linespacing + lineheight + pPapyrus->bottommargin > pPapyrus->bottom-pPapyrus->top
    )
     { /* We didn't have vertical scroll bars before,
          but we will need them after this line */
       *newscrollbars = 1;
     }
  else
     *newscrollbars = 0;
  if(pPapyrus->width <= pPapyrus->right - pPapyrus->left &&
     linewidth > pPapyrus->width
    )
     { *newhscrollbars = 1;  /* we'll need new horizontal scroll bars */
       pPapyrus->width = linewidth;
     }
  else
     *newhscrollbars = 0;
  pPapyrus->height = (coord)( pPapyrus->lines[currentline].formula.x + lineheight);
  /* this includes space for the reason and comment, if they stick out below
  the formula.  Thus we don't want just ...formula.y here. */
  /* Now set the entry for the top of the NEXT line already */
  if(currentline + 1 == pPapyrus->nlines)
     { pPapyrus->lines = (line *) realloc(pPapyrus->lines, (pPapyrus->nlines + 100) * sizeof(line));
       if(!pPapyrus->lines)
          nospace();
       pPapyrus->nlines = (short)(pPapyrus->nlines + 100);
     }
  pPapyrus->lines[currentline+1].formula.x = (coord)( pPapyrus->height + linespacing);
  extra = pPapyrus->lines[currentline].formula.x + lineheight - pPapyrus->bottom + pPapyrus->bottommargin;
  if(extra > 0)
     { /* scroll */
       if(*newscrollbars)
          { int scrollbarwidth = (int) xpixel_to_papyrus(get_scrollbar_width());
            pPapyrus->right = (coord)(pPapyrus->right - scrollbarwidth);
            pPapyrus->reasonstart = (coord)(pPapyrus->reasonstart - scrollbarwidth);
            setup_linebreaks(pPapyrus);
            compute_linemarks(pDocData);
            /*  Windows will send a WM_SIZE when the new scroll bars
                are created, and compute_linemarks is called under
                WM_SIZE, which will duplicate this call, but
                we need it anyway to get pPapyrus->height and
                pPapyrus->bottom correct so we can set yscrollrange. */
          }
     }
  else
     { if(pPapyrus->selected)
          {
            pPapyrus->selected = NULL;
            /* This is case (2) under 'Memory management' in
               specifications document lterm.tex.  Note that
               reset_heap is called no matter whether there
               were or were not selected terms.  We don't
               call destroy_selections to erase visible screen
               rectangles, because WM_PAINT processing makes
               sure to repaint the active line if there are
               selected terms.
            */
          }
     }
  reset_heap(NULL);  /* back to square zero, ready for next line */

  /* Since this is done above, and since measure_height cleans
     up after itself, there shouldn't really be anything more to
     clean up, but let's just be sure; reset_heap is quite fast
     so there's no penalty to pay. */
  if(extra > 0)
     return 1;
  return 0;
}

 
/*_________________________________________________________________*/
 coord measure_height(Papyrus *pPapyrus, int linebreaks, term t, int *reasonspace, coord *lastlineheight, coord *linewidth)
/* return the height in papyrus coordinates required to display the term t,
breaking lines as required by pPapyrus's dimensions if linebreaks is nonzero.
Instantiate *reasonspace to the (height-level) of the (last line of the) term
t, if the last line does not extend into the reason area of the papyrus;
or if it does so extend, return -1 in *reasonspace.  Instantiate *lastlineheight
to the papyrus-coordinate height of the last row of t (if t is broken)
or the height of t (if t is not broken). Lastlineheight and reasonspace
will be used for determing where to place the reason.  If reasonspace is
returned negative, then lastlineheight will be garbage (and irrelevant).
   Uses reset_heap to make sure it does not use up memory; otherwise
this can run us out of memory when there are a lot of broken terms to
measure, because locate_term cannot be cleaned up after except by
reset_heap.
*/

{ coord lineheight;
  lterm lt;
  BIGRECT r;
  term bt, vlist,system;
  int err;
  void  *savenode = heapmax();
  if(FUNCTOR(t) == AND &&
     LINEUP(t) &&   /* this specifies to line up the variables */
     !is_linear_system(t,get_currentline(),&vlist)
     /* is_linear_system returns 0 for success */
    )
     /* send a term with functor LINEARSYSTEM to be displayed */
     { system = make_term(LINEARSYSTEM,2);
       ARGREP(system,0,t);
       ARGREP(system,1,vlist);
       t = system;
     }
  bblock(t,&bt);
  if(FUNCTOR(t)==LINEARSYSTEM)
     RELEASE(t);
  if(!linebreaks || (WIDTH(bt) <= (unsigned long)(pPapyrus->right - pPapyrus->leftmargin)))
     { /* Term will not be broken, but displayed on one line */
       lineheight = HEIGHT(bt);
       *linewidth = (int) WIDTH(bt);
       if(WIDTH(bt) <= (unsigned long)(pPapyrus->reasonstart - pPapyrus->leftmargin))
          { *reasonspace = HEIGHT(bt) - LEVEL(bt);
            *lastlineheight = HEIGHT(bt);
          }
       else
          *reasonspace = -1;  /* reason will start below the line */
       reset_heap(savenode);
       return lineheight;
     }
  else  /* Term will be broken, unless impossible to break.  */
     { r.left = papyrus_to_xpixel(pPapyrus->leftmargin);
       r.top = 0;  /* the y-coord doesn't matter*/
       locate_term(bt,&r,&lt);
       err = lbreak(&lt);
       if(err == -1)
          { /* unbreakable term */
            *linewidth = (int) WIDTH(bt);
            lineheight = HEIGHT(bt);
            *reasonspace = -1;  /* reason will start below the line */
            reset_heap(savenode);
            return lineheight;
          }
       lineheight = (int) ypixel_to_papyrus(lt.r.bottom - lt.r.top);
       *linewidth =  (int) xpixel_to_papyrus(lt.r.right-lt.r.left);
       /* Now, theoretically this term didn't fit, so it should
          have a non-null lt.linelist.  Nevertheless, I'm worried about the
          possibility that although the whole term doesn't fit, when lbreak
          gets it, it fits within one pixel and round-off error enters in.
          Therefore assert(lt.linelist) might cause a crash here.
          Also, if it's a really large term, there may have been
          overflow somewhere.
       */
       if(lt.linelist)
          { BIGRECT r;
            int z;
            lastrow(lt,&r,&z);
            if(xpixel_to_papyrus(r.right) > pPapyrus->reasonstart)
               *reasonspace = -1;
            else
               { *reasonspace = (int) ypixel_to_papyrus(r.bottom-z);
                 *lastlineheight = (int) ypixel_to_papyrus(r.bottom-r.top);
               }
          }
       else
          *reasonspace = -1;  /* If the term was supposed to not fit but
                          then after all did, it must have just BARELY fit
                          so there won't be enough room for a reason */
     }
  reset_heap(savenode);
  return lineheight;
}
/*__________________________________________________________________*/
term get_selected_arg(term t)
/* t is an OR term;  all its args should be equations or
inequalities or further ORS (a selected equation may split into an OR).
If one is marked SELECTED, return that arg.  Otherwise return t itself.
Public because called in printdlg.c.
*/
{ unsigned short n = ARITY(t);
  unsigned short f = FUNCTOR(t);
  unsigned short i;
  term u;
  if(f != OR && f != AND)
    assert(0);
  for(i=0;i<n;i++)
     { u = ARG(i,t);
       if(SELECTED(u))
          return u;
     }
  return t; /* no arg was selected */
}

 
 
/*__________________________________________________________________*/
 int overlapping(ltermlist *p)
/* return 1 if the rectangles of any two terms in the list overlap */
{ ltermlist *marker;
  BIGRECT s;
  if(p->next ==NULL)
     return 0;   /* one rectangle can't possibly overlap */
  if(p->next->next == NULL)
     return disjoint(&p->data.r, &p->next->data.r) ? 0 : 1;
  s = p->data.r;
  for(marker=p->next;marker; marker=marker->next)
     { if(!disjoint(&marker->data.r,&s))
           return 1;
     }
  /* OK, first item does not overlap any of the rest */
  return overlapping(p->next);
}
/*________________________________________________________________________*/
int compute_linemarks(PDOCDATA pDocData)
/* recompute all lines in the papyrus structure, without drawing anything.
   Called after resizing window, or when opening a saved document.
   hwnd is the solution window.  When changing font sizes,
   this is called after cxChar and cyChar have been changed,
   but the papyrus structure hasn't changed yet.
     cyClient is the height of the solution window in pixels.
     Returns the pixel coordinate y  below which everything should be redrawn.
*/
{ coord lineheight,linewidth;
  line *lines;
  unsigned char *comment;
  static coord linespacing = 16;
  int i,sizeflag,linebreaks,extra;
  short y;
  term *history;
  term t;
  Papyrus *pPapyrus = pDocData->papyrus;
  PDOCDATA old_active_doc;
  int currentline,cyClient,pcyClient,bottomflag;
  void  *savenode;
  const char *reason;
  coord lastlineheight,reasonheight;
  cyClient = pPapyrus->clientBottom;
  currentline = pDocData->DocVarData.currentline;
  if(currentline < 0)
     return cyClient;  /* nothing to do, no line has been computed yet */
  old_active_doc = GetActiveDoc();
  if(old_active_doc != pDocData)
     activate(pDocData);
  savenode = heapmax();
  lines = pPapyrus->lines;
  bottomflag = pPapyrus->bottom < pPapyrus->height ? 1 : 0;
   /* bottomflag is nonzero if part of the solution is
      off-window at the bottom before the WM_SIZE message. */
  setup_linebreaks(pPapyrus);
  sizeflag = 0;  /* set if any linemarks will change */
  linebreaks = pDocData->DocControlData.linebreaks;
  history = pDocData->DocProverData.history;
  y = lines[0].formula.x = pPapyrus->topmargin;
  for(i=0;i<=currentline;i++)
     { t = history[i];
       if(FUNCTOR(t) == AND || FUNCTOR(t) == OR)
          t = get_selected_arg(t);
       lineheight = measure_height(pPapyrus,linebreaks,t,&extra,&lastlineheight,&linewidth);
       lines[i].formula.y = (coord)( lines[i].formula.x + lineheight);
       if(i > 0)
          y = (coord)(y + linespacing); /* the line before the formula */
       y += lineheight;   /* the formula */
       reason = (char *) get_reason(pDocData,i);
       if(reason == NULL)
          { reasonheight = 16;  /* when i==0 and the Next button has been pushed */
            assert(i==0);
            reason =  english(1188);  /* the problem */
            store_reason(pDocData,0,reason);
          }
       else
          reasonheight = measure_reason(reason);
       pPapyrus->lines[i].reasonrect = reasonrect(reasonheight, pPapyrus->lines[i].formula.y,lastlineheight, extra);
       if(pPapyrus->lines[i].reasonrect.y >
          pPapyrus->lines[i].formula.y
         )
         y = (coord)(y + pPapyrus->lines[i].reasonrect.y - pPapyrus->lines[i].formula.y);
       comment = get_comment(pDocData,i);
       if(comment)
          { int nlines = (int) strlen((char *) comment)/ ((pPapyrus->width - pPapyrus->leftmargin) >> 4) + 1;
            y = (coord)(y + linespacing);  /* space before the comment */
            lines[i].comment.x = y;
            y = (coord)(y + nlines * linespacing);
            lines[i].comment.y = y;
          }
       if(pPapyrus->lines[i+1].formula.x != y + linespacing)
          { pPapyrus->lines[i+1].formula.x = (coord)(y + linespacing);
            if(sizeflag == 0)
               sizeflag = i+1;  /* so sizeflag-1 is the first line
                                   whose linemark has changed */
          }
       if(i % 10 == 9)
          reset_heap(savenode);  /* watch out for using too much memory */
              /* Even though reset_heap is very fast, there's no need
                 to do it every line. */
     }
  if(!sizeflag)
     { reset_heap(savenode);
       if(old_active_doc && (old_active_doc != GetActiveDoc()))
          activate(old_active_doc);
        return  (int)( papyrus_to_ypixel(lines[sizeflag-1].formula.x - 1));
     }
 
  pPapyrus->height = MAXIMUM(lines[currentline].formula.y,
                             lines[currentline].reasonrect.y
                            );
  /* Will the whole solution fit in the window? */
  pcyClient = (int) ypixel_to_papyrus(cyClient);
  if(pPapyrus->height <= pcyClient)
     { /* yes, it will fit */
       pPapyrus->top = 0;
       pPapyrus->bottom = (short) pcyClient;
     }
  else if(bottomflag)
     { /* Whole solution won't fit, and before the
          WM_SIZE part of it was off-window below. Then
          leave pPapyrus->bottom the same and adjust
          pPapyrus->top.
       */
       pPapyrus->top = (short)(pPapyrus->bottom - pcyClient);
       if(pPapyrus->top < 0)
          pPapyrus->top = 0;   /* not assert(0) as previously;
                                  somehow this assertion did fail. */
     }
  else
     { /* Whole solution won't fit, and none of it was
          formerly off-screen below.  Then show the last
          part. */
       pPapyrus->bottom = (short)(pPapyrus->height + pPapyrus->bottommargin);
       pPapyrus->top = (short)(pPapyrus->bottom - pcyClient + pPapyrus->bottommargin);
     }
  if(old_active_doc && (old_active_doc != GetActiveDoc()))
     activate(old_active_doc);
  reset_heap(savenode);
  return  (int)( papyrus_to_ypixel(lines[sizeflag-1].formula.x - 1));
  /* the -1 is to account for roundoff error;  we want
     to be sure the redrawing includes all of line sizeflag-1,
     which is the first line whose linemark has changed. */
 
 
 

}
/*______________________________________________________________*/
  void setup_linebreaks(Papyrus *p)
/* call set_paper with an appropriate rectangle */
{ BIGRECT r;
  r.left = papyrus_to_xpixel(p->leftmargin);
  r.right= papyrus_to_xpixel(p->right - p->rightmargin);
  r.top = papyrus_to_ypixel(p->topmargin);
  r.bottom = papyrus_to_ypixel(p->height);  /* doesn't matter anyway */
  set_paper(&r);
}
/*______________________________________________________________*/
YPAIR reasonrect(coord reasonheight, coord formulabottom, coord lastlineheight, int extra)
/* reasonheight is the papyrus-coordinate vertical space required to
display the reason for the line specified by line number currentline.
Formulabottom is the papyrus coordinate of the bottom of the formula
on that line.
Lastlineheight gives the height of the (last row of) the formula.
Extra gives the height-level of that last row, both in
papyrus coordinates.  If extra is negative, lastlineheight is garbage;
this means the reason must start on a new line.  This function must
return a point whose .x and .y respectively are the
top and bottom y-coordinates (in papyrus coordinates) of the
reason for this line.
*/
{ YPAIR ans;
  if(extra < 0)
     ans.x = formulabottom;
  else
     { if(reasonheight <= extra + 8)
          { /* it fits fine */
            ans.x = (coord)(formulabottom - extra - 8);
          }
       else  /* if(reasonheight > extra + 8) */
           { /* extra is the amount of space between the water level
                and bottom of the (last row of the) current line.
                There may, however, be some available space ABOVE
                the water level on that line. */
             if(reasonheight <= lastlineheight)
                /* the reason doesn't have to stick out below the line */
                ans.x = (coord)(formulabottom-reasonheight);
             else  /* it will stick out */
                ans.x = (coord)(formulabottom - lastlineheight);
           }
     }
  ans.y = (coord)(ans.x + reasonheight);
  return ans;
}
/*____________________________________________________________________*/
static coord measure_reason(const char *reason)
/* return the height in papyrus coordinates required to display the
given reason string.   Reason strings are of three kinds:
(i) beginning and ending in $$, in which case the text must be
parseable and wdisplay will be used;
(ii) No displayed formulas:  then SymbolTextOut will be used.
(iii) A displayed formula (enclosed in $$...$$) followed by a comment,
such as "$$lim(x->a,f(x)) = f(a) $$ (f polynomial)".
*/
{ char *end, *marker;
  int err,n,i;
  int nlines;
  char lines[3][MAXONELINE];
  int lineheights[3];
  char buffer[256];
  coord height,ans;
  term t,bt;
  if((reason[0] == '$') && (reason[1] == '$'))
     { end = strstr((char *) reason+2,"$$");
       if(end == NULL)
          assert(0);
       n = (unsigned) (end- (char *) reason - 2);
       assert(n > 0);
       assert(n <= 255);
       strncpy((char *) buffer, (char *) reason+2,n);
       buffer[n] = '\0';
       err = bparse(get_parser_flags(),&t,(char *) buffer,&marker);
       if(err)
          assert(0);
       bblock(t,&bt);
       height = HEIGHT(bt);
       destroy_bblocked_term(bt);
       destroy_term(t);
       marker = end+2;
       while(*marker == 32)
          ++marker;
       if(*marker == 0)
          return height;
       else
          return height + INTERLINE_SPACE + measure_reason( marker);
          /* this 6 must match the code in SymbolsOut in symsout2.c */
     }
  marker = strstr((char *)reason,"$$");
  if(marker)
     { strcpy((char *) buffer,(char *)reason);
       marker = strstr((char *)buffer, "$$");
       *marker = 0;
       height = measure_reason(buffer);
       *marker = '$';
       return height + 6 + measure_reason(marker);
     }
  /* Now it's just an ordinary string reason (no displayed formulas) */
  n = mstrlen((char *)reason);
     /* mstrlen doesn't count '^', and makes other adjustments */
  if(n < MAXREASONSTRING + 4)
      return 16;
  nlines = break_reason((char *)reason, lineheights, lines);
  ans = 0;
  for(i=0;i<nlines;i++)
     { ans += lineheights[i];
       if(i < nlines-1)
          ans += INTERLINE_SPACE;
     }
  return ans;
}
/*_________________________________________________________________________*/
 static int break_reason(char *reason, int lineheights[3],  char lines[3][MAXONELINE])
/* break the reason into lines for display, placing the lines in
the 'lines' array, and returning the number of lines used (1,2, or 3).
The heights of the lines go into the lineheights array, in papyrus coordinates.
Each displayed formula gets its own line.  n is the value computed by mstrlen(reason).
*/
{  char *marker;
  int currentline = 0;
  char buffer[256];
  int nchars;
  start:
  if(currentline > 2)
     {
       printf("%s\n", "Translation Error in Reason");
       printf("%s\n",reason);
       return 3;
     }
  if(reason[0] == '$' && reason[1] == '$')
     { marker = strstr((char *)(reason+2), "$$");
       if(marker == NULL)
          { printf("%s\n","missing $$ in reason;  programmer error" );
            printf("%s\n",reason);
            strcpy((char *)lines[currentline],"oops");
            return currentline + 1;
          }
       if(marker-reason > MAXONELINE-3)
          { printf("%s\n","displayed formula too long in reason;  programmer error");
            printf("%s\n",reason);
            strcpy((char *)lines[currentline],"oops");
            return currentline + 1;
          }
       nchars = (int)(marker-reason+2);
       strncpy((char *)lines[currentline],(char *)reason,nchars);
       lines[currentline][(int)(marker-reason+2)] = 0;
       marker += 2;
       lineheights[currentline] = measure_reason(lines[currentline]);
       while(*marker == 32)
          ++marker;
       if(*marker == 0)
          return 1;
       reason = marker;
       ++currentline;
       goto start;
     }
  marker = strstr((char *)reason,"$$");
  if(marker)
     { strncpy((char *)buffer,(char *)reason,(int)(marker-reason));
       buffer[(int)(marker-reason)] = 0;
       currentline += break_reason(buffer,lineheights + currentline, &lines[currentline]);
       reason =  marker;
       goto start;
     }
  lineheights[currentline] = splitline(reason,lines[currentline],&marker);
  ++currentline;
  if(*marker == 0)
     return currentline;
  reason =  marker;
  goto start;
}
/*_______________________________________________________________________________*/
  int splitline(char *reason,  char line[MAXONELINE], char **rest)
/* split off one line of reason into the 'line' array, and set rest to point to
the first character of the rest reason. It is presumed that
reason does not contain any $$ (signifying displayed formulae).
Return the height required for the line, which will be 16 or 20,
according as subscripts are involved.
*/
{ int n, count = 0;
  char *source, *target;
  char *goodbreak = reason;
  char *wordbreak = reason;
  char *forcedbreak;
  char *intentionalbreak=NULL;
  int spacecount=0;
  int dollarflag = 0;
  n = mstrlen((char *)reason);
  if(n <= MAXREASONSTRING + 4)
     { strcpy((char *)line,(char *)reason);
       *rest = reason + strlen((char *)reason);
       goto out;
     }
  /* Now the line must be broken.  We don't want to break it in the middle of
     a word or formula if possible */
  source = reason;
  target = line;
  /* copy characters from source to target, using 'count' to count the
  spaces to be occupied on the screen, i.e., correcting for backslash codes,
  exponents, etc., as in mstrlen */
  while(count < MAXREASONSTRING + 4)
     { if(*source == 0)
          { strcpy((char *)line,(char *)reason);
            *rest = reason + strlen((char *)reason);
            goto out;
          }
       if(*source == '\\')
          { *target = *source;
            ++count;
            ++target;
            ++source;
            while(isalpha(*source))
               { *target = *source;
                 ++target;
                 ++source;
               }
            continue;
          }
       if(*source == '^')
          { *target = *source;
            ++target;
            ++source;
            /* but not ++count */
            if(*source == '(')
               { *target = *source;
                 --count;  /* to compensate for the closing paren */
               }
            continue;
          }
       *target = *source;
       if(*source == '$')
          { dollarflag = dollarflag ? 0 : 1;
            goodbreak = dollarflag ? source : source + 1;
          }
       ++count;
       if(count > MAXREASONSTRING)
           forcedbreak = source;
       if(*source == 32)
          { wordbreak = source;
            ++spacecount;
            if(spacecount >= 5)
               intentionalbreak = source;
          }
       else
          spacecount = 0;
       ++target;
       ++source;
     }
  /* Now, the reason is too long to fit on one line.  Where shall we break it? */
  if(intentionalbreak)
     { /* 5 or more spaces left on purpose */
       *rest = intentionalbreak + 1;
       line[(int)(intentionalbreak-reason)] = 0;
       goto out;
     }
  if(dollarflag && goodbreak > reason)
     {  /* we're in the middle of a formula that was preceded by text; break at the formula */
       *rest = goodbreak;
       line[(int)(goodbreak-reason)] = 0;
       goto out;
     }
  if(wordbreak > reason)
     /* break at the last space before MAXREASONSTRING + 4 */
     { *rest = wordbreak + 1;
       line[(int)(wordbreak-reason)] = 0;
       goto out;
     }
  /* reason is all in dollar signs and there are no spaces! Nothing to do but break arbitrarily.
     But NOT in a backslash code */

  printf("%s\n", "Translation Error in Reason" );
  printf("%s\n", reason);

  *rest = forcedbreak;
  line[(int)(forcedbreak-reason)] = 0;
  out:
  return strchr((char *)line,'_') ? 20 : strchr((char *)line,'g') ? 18 : 16;
}
/*______________________________________________________________*/
 void NormalizeRect(BIGRECT *r)
/* make sure that r->top <= r->bottom */
{ long temp;
  if(r->bottom < r->top)
     { temp = r->top;
       r->top = r->bottom;
       r->bottom = temp;
     }
}
 

/*_______________________________________________________________*/
void init_papyrus(PDOCDATA pDocData, int  width, int height, int reasonstart)
/* width and height are the solution window width and height in pixels.
   Initialize
   the papyrus dimensions.  Make sure scroll bars won't
   appear in the solution window (at least on a new document).
   Call setup_linebreaks so line breaks can be done properly.
   This function is called when a new solution window is opened,
   and also when a saved document is opened, so it can't assume
   there are no solution lines.
*/
{
  // printf("In init_papyrus, reasonstart is %d\n",reasonstart);
  Papyrus *pPapyrus = pDocData->papyrus;
  pPapyrus->cxChar = (int) papyrus_to_xpixel(16);  /* see wdisplay.c */
  pPapyrus->cyChar = (int)papyrus_to_ypixel(16);
  pPapyrus->leftmargin = 8;  /* It has to be 8 in Web MathXpert,
           because that's the margin in each <svg> element of class "line"  */
  pPapyrus->rightmargin = 16; /* one character is enough */
  pPapyrus->topmargin = 8;    /* one-half character */
  pPapyrus->height = pPapyrus->topmargin;
     /* nothing has been written in it yet, but the height
        gets incremented from this starting point.
     */
  pPapyrus->bottommargin = 16;  /* one line bottom margin */
  pPapyrus->top = pPapyrus->left = 0;

  pPapyrus->right = (int) xpixel_to_papyrus(width);
  pPapyrus->clientRight = width;
  pPapyrus->clientBottom = height;
  pPapyrus->width = pPapyrus->right;
  pPapyrus->reasonstart = reasonstart;
  pPapyrus->bottom = (int) ypixel_to_papyrus(height);
  pPapyrus->top = 0;
  pPapyrus->height = 0;  // a blank papyrus with nothing on it yet
  pPapyrus->nlines = (short) pDocData->DocProverData.maxhistory;
  pPapyrus->lines = (line *) calloc(1,pPapyrus->nlines * sizeof(line));
  pPapyrus->lines[0].formula.x = pPapyrus->topmargin;
  pPapyrus->hint[0] = '\0';   // no hint
  pPapyrus->finalremark[0] = '\0';  // no final remark
  setup_linebreaks(pPapyrus);
}

Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists