Sindbad~EG File Manager
/* 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,<);
err = lbreak(<);
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