netscape-revival
lib/layout/layutil.c
/* -*- Mode: C; tab-width: 4; tabs-indent-mode: t -*- */
#include "xp.h"
#include "pa_parse.h"
#include "layout.h"
#ifdef MOCHA
#include "libmocha.h"
#endif /* MOCHA */
#ifdef EDITOR
#include "edt.h"
#endif
#ifdef PROFILE
#pragma profile on
#endif
#ifdef XP_WIN16
#define SIZE_LIMIT 32000
#endif /* XP_WIN16 */
#define FONT_FACE_INC 10
#define FONT_FACE_MAX 1000
void
LO_InvalidateFontData(MWContext *context)
{
int32 i, doc_id;
lo_TopState *top_state;
lo_DocState *state;
LO_TextAttr **text_attr_hash;
LO_TextAttr *attr_ptr;
/*
* Get the unique document ID, and retreive this
* documents layout state.
*/
doc_id = XP_DOCID(context);
top_state = lo_FetchTopState(doc_id);
if ((top_state == NULL)||(top_state->doc_state == NULL))
{
return;
}
state = top_state->doc_state;
if (state->top_state->text_attr_hash == NULL)
{
return;
}
XP_LOCK_BLOCK(text_attr_hash, LO_TextAttr **,
state->top_state->text_attr_hash);
for (i=0; i < FONT_HASH_SIZE; i++)
{
attr_ptr = text_attr_hash[i];
while (attr_ptr != NULL)
{
attr_ptr->FE_Data = NULL;
attr_ptr = attr_ptr->next;
}
}
}
static char *
lo_find_face_in_array(char **face_list, intn len, char *face)
{
intn i;
char *the_face;
if (face == NULL)
{
return(NULL);
}
the_face = NULL;
for (i=0; i<len; i++)
{
if ((face_list[i] != NULL)&&
(XP_STRCMP(face, face_list[i]) == 0))
{
the_face = face_list[i];
break;
}
}
return(the_face);
}
char *
lo_FetchFontFace(MWContext *context, lo_DocState *state, char *face)
{
char **face_list;
char *new_face;
/*
* If this is our first addition, allocate the array.
*/
if (state->top_state->font_face_array == NULL)
{
PA_Block buff;
buff = PA_ALLOC(FONT_FACE_INC * sizeof(char *));
if (buff == NULL)
{
state->top_state->out_of_memory = TRUE;
return(NULL);
}
state->top_state->font_face_array = buff;
state->top_state->font_face_array_size = FONT_FACE_INC;
state->top_state->font_face_array_len = 0;
}
/*
* Else if the list is full, grow the array.
*/
else if (state->top_state->font_face_array_len >=
state->top_state->font_face_array_size)
{
PA_Block buff;
intn new_size;
if (state->top_state->font_face_array_size >= FONT_FACE_MAX)
{
return(NULL);
}
new_size = state->top_state->font_face_array_size +
FONT_FACE_INC;
if (new_size > FONT_FACE_MAX)
{
new_size = FONT_FACE_MAX;
}
buff = XP_REALLOC_BLOCK(state->top_state->font_face_array,
(new_size * sizeof(char *)));
if (buff == NULL)
{
state->top_state->out_of_memory = TRUE;
return(NULL);
}
state->top_state->font_face_array = buff;
state->top_state->font_face_array_size = new_size;
}
PA_LOCK(face_list, char **, state->top_state->font_face_array);
new_face = lo_find_face_in_array(face_list,
state->top_state->font_face_array_len, face);
if (new_face == NULL)
{
char *str;
str = XP_STRDUP(face);
if (str == NULL)
{
PA_UNLOCK(state->top_state->font_face_array);
state->top_state->out_of_memory = TRUE;
return(NULL);
}
face_list[state->top_state->font_face_array_len] = str;
state->top_state->font_face_array_len++;
new_face = str;
}
PA_UNLOCK(state->top_state->font_face_array);
return(new_face);
}
Bool
lo_IsValidTarget(char *target)
{
if (target == NULL)
{
return(FALSE);
}
if ((XP_IS_ALPHA(target[0]) != FALSE)||
(XP_IS_DIGIT(target[0]) != FALSE)||
(target[0] == '_'))
{
return(TRUE);
}
return(FALSE);
}
int32
lo_StripTextNewlines(char *text, int32 text_len)
{
char *from_ptr;
char *to_ptr;
int32 len;
if ((text == NULL)||(text_len < 1))
{
return(0);
}
/*
* remove all non-space whitespace from the middle of the string.
*/
from_ptr = text;
len = text_len;
to_ptr = text;
while (*from_ptr != '\0')
{
if (XP_IS_SPACE(*from_ptr) && (*from_ptr != ' '))
{
from_ptr++;
len--;
}
else
{
*to_ptr++ = *from_ptr++;
}
}
*to_ptr = '\0';
return(len);
}
PA_Block
lo_FEToNetLinebreaks(PA_Block text_block)
{
PA_Block new_block;
char *new_text;
char *text;
char *from_ptr;
char *to_ptr;
char break_char;
int32 len, new_len;
int32 linebreak;
if (text_block == NULL)
{
return(NULL);
}
PA_LOCK(text, char *, text_block);
XP_BCOPY(LINEBREAK, (char *)(&break_char), 1);
len = 0;
linebreak = 0;
from_ptr = text;
while (*from_ptr != '\0')
{
if (*from_ptr == break_char)
{
if (strncmp(from_ptr, LINEBREAK, LINEBREAK_LEN) == 0)
{
linebreak++;
len += LINEBREAK_LEN;
from_ptr = (char *)(from_ptr + LINEBREAK_LEN);
}
else
{
len++;
from_ptr++;
}
}
else
{
len++;
from_ptr++;
}
}
new_len = len - (LINEBREAK_LEN * linebreak) + (2 * linebreak);
new_block = PA_ALLOC(new_len + 1);
if (new_block == NULL)
{
PA_UNLOCK(text_block);
return(NULL);
}
PA_LOCK(new_text, char *, new_block);
from_ptr = text;
to_ptr = new_text;
while (*from_ptr != '\0')
{
if (*from_ptr == break_char)
{
if (strncmp(from_ptr, LINEBREAK, LINEBREAK_LEN) == 0)
{
from_ptr = (char *)(from_ptr + LINEBREAK_LEN);
*to_ptr++ = CR;
*to_ptr++ = LF;
}
else
{
*to_ptr++ = *from_ptr++;
}
}
else
{
*to_ptr++ = *from_ptr++;
}
}
*to_ptr = '\0';
new_text[new_len] = '\0';
PA_UNLOCK(new_block);
PA_UNLOCK(text_block);
return(new_block);
}
PA_Block
lo_ConvertToFELinebreaks(char *text, int32 text_len, int32 *new_len_ptr)
{
PA_Block new_block;
char *new_text;
char *from_ptr;
char *to_ptr;
char break_char;
int32 len, new_len;
int32 skip, linebreak;
if (text == NULL)
{
return(NULL);
}
break_char = '\0';
len = 0;
skip = 0;
linebreak = 0;
from_ptr = text;
to_ptr = text;
while (len < text_len)
{
if ((*from_ptr == CR)||(*from_ptr == LF))
{
if ((break_char != '\0')&&(break_char != *from_ptr))
{
skip++;
break_char = '\0';
}
else
{
skip++;
linebreak++;
break_char = *from_ptr;
}
}
from_ptr++;
len++;
}
new_len = len - skip + (LINEBREAK_LEN * linebreak);
new_block = PA_ALLOC(new_len + 1);
if (new_block == NULL)
{
return(NULL);
}
PA_LOCK(new_text, char *, new_block);
break_char = '\0';
len = 0;
from_ptr = text;
to_ptr = new_text;
while (len < text_len)
{
if ((*from_ptr == CR)||(*from_ptr == LF))
{
if ((break_char != '\0')&&(break_char != *from_ptr))
{
break_char = '\0';
}
else
{
XP_BCOPY(LINEBREAK, to_ptr, LINEBREAK_LEN);
to_ptr = (char *)(to_ptr + LINEBREAK_LEN);
break_char = *from_ptr;
}
from_ptr++;
len++;
}
else
{
*to_ptr++ = *from_ptr++;
len++;
}
}
new_text[new_len] = '\0';
*new_len_ptr = new_len;
PA_UNLOCK(new_block);
return(new_block);
}
intn
lo_EvalVAlignParam(char *str)
{
intn alignment;
alignment = LO_ALIGN_TOP;
if (pa_TagEqual("top", str))
{
alignment = LO_ALIGN_TOP;
}
else if (pa_TagEqual("bottom", str))
{
alignment = LO_ALIGN_BOTTOM;
}
else if ((pa_TagEqual("middle", str))||
(pa_TagEqual("center", str)))
{
alignment = LO_ALIGN_CENTER;
}
else if (pa_TagEqual("baseline", str))
{
alignment = LO_ALIGN_BASELINE;
}
return(alignment);
}
intn
lo_EvalDivisionAlignParam(char *str)
{
intn alignment;
alignment = LO_ALIGN_LEFT;
if (pa_TagEqual("left", str))
{
alignment = LO_ALIGN_LEFT;
}
else if (pa_TagEqual("right", str))
{
alignment = LO_ALIGN_RIGHT;
}
else if (pa_TagEqual("center", str))
{
alignment = LO_ALIGN_CENTER;
}
return(alignment);
}
intn
lo_EvalCellAlignParam(char *str)
{
intn alignment;
alignment = LO_ALIGN_LEFT;
if (pa_TagEqual("left", str))
{
alignment = LO_ALIGN_LEFT;
}
else if (pa_TagEqual("right", str))
{
alignment = LO_ALIGN_RIGHT;
}
else if ((pa_TagEqual("middle", str))||
(pa_TagEqual("center", str)))
{
alignment = LO_ALIGN_CENTER;
}
return(alignment);
}
#ifdef EDITOR
/* this is a replacement routine for the non-editor version and should eventually
* replace the non-edit version. It uses an array of values. instead of an
* unrolled loop.
*/
/*
* Indexed by LO_ALIGN_ETC
*/
char* lo_alignStrings[] = {
"abscenter", /* LO_ALIGN_CENTER */
"left", /* LO_ALIGN_LEFT */
"right", /* LO_ALIGN_RIGHT */
"texttop", /* LO_ALIGN_TOP */
"absbottom", /* LO_ALIGN_BOTTOM */
"baseline", /* LO_ALIGN_BASELINE */
"center", /* LO_ALIGN_NCSA_CENTER */
"bottom", /* LO_ALIGN_NCSA_BOTTOM */
"top", /* LO_ALIGN_NCSA */
0
};
intn
lo_EvalAlignParam(char *str, Bool *floating)
{
intn alignment;
*floating = FALSE;
alignment = LO_ALIGN_BASELINE;
if (pa_TagEqual("middle", str))
{
alignment = LO_ALIGN_NCSA_CENTER;
}
else if (pa_TagEqual("absmiddle", str))
{
alignment = LO_ALIGN_CENTER;
}
else
{
intn i = 0;
Bool bFound = FALSE;
while( lo_alignStrings[i] != NULL && bFound == FALSE )
{
if( pa_TagEqual( lo_alignStrings[i], str) )
{
bFound = TRUE;
alignment = i;
}
i++;
}
}
if( alignment == LO_ALIGN_LEFT || alignment == LO_ALIGN_RIGHT)
{
*floating = TRUE;
}
return alignment;
}
#else
intn
lo_EvalAlignParam(char *str, Bool *floating)
{
intn alignment;
*floating = FALSE;
alignment = LO_ALIGN_BASELINE;
if (pa_TagEqual("top", str))
{
alignment = LO_ALIGN_NCSA_TOP;
}
else if (pa_TagEqual("texttop", str))
{
alignment = LO_ALIGN_TOP;
}
else if (pa_TagEqual("bottom", str))
{
alignment = LO_ALIGN_NCSA_BOTTOM;
}
else if (pa_TagEqual("absbottom", str))
{
alignment = LO_ALIGN_BOTTOM;
}
else if (pa_TagEqual("baseline", str))
{
alignment = LO_ALIGN_BASELINE;
}
else if ((pa_TagEqual("middle", str))||
(pa_TagEqual("center", str)))
{
alignment = LO_ALIGN_NCSA_CENTER;
}
else if ((pa_TagEqual("absmiddle", str))||
(pa_TagEqual("abscenter", str)))
{
alignment = LO_ALIGN_CENTER;
}
else if (pa_TagEqual("left", str))
{
alignment = LO_ALIGN_LEFT;
*floating = TRUE;
}
else if (pa_TagEqual("right", str))
{
alignment = LO_ALIGN_RIGHT;
*floating = TRUE;
}
return(alignment);
}
#endif
void
lo_CalcAlignOffsets(lo_DocState *state, LO_TextInfo *text_info, intn alignment,
int32 width, int32 height, int16 *x_offset, int32 *y_offset,
int32 *line_inc, int32 *baseline_inc)
{
*line_inc = 0;
*baseline_inc = 0;
switch (alignment)
{
case LO_ALIGN_CENTER:
*x_offset = 0;
/*
* Center after current text.
*/
/*
* If the image is shorter than the height
* of this line, just increase y_offset
* to get it centered.
*/
if (height < state->line_height)
{
*y_offset += ((state->line_height - height)
/ 2);
}
/*
* Else for images taller than the line
* we will need to move all their
* baselines down, plus increase the rest
* of their lineheights to reflect the
* big centered image.
*/
else
{
*y_offset = 0;
*baseline_inc = (height - state->line_height)
/ 2;
*line_inc = height - state->line_height -
*baseline_inc;
}
break;
case LO_ALIGN_NCSA_CENTER:
*x_offset = 0;
/*
* Center on baseline of current text.
*/
/*
* If the image centered on the baseline will not
* extend below or above the current line,
* just increase y_offset to get it centered.
*/
if (((height / 2) <= state->baseline)&&
((height / 2) <= (state->line_height -
state->baseline)))
{
*y_offset = state->baseline -
(height / 2);
}
/*
* Else the image extends over the bottom, but
* not off the top.
*/
else if ((height / 2) <= state->baseline)
{
*y_offset = state->baseline -
(height / 2);
*line_inc = *y_offset + height -
state->line_height;
}
/*
* Else for images taller than the line
* we will need to move all their
* baselines down, plus and maybe increase the rest
* of their lineheights to reflect the
* big centered image.
*/
else
{
*y_offset = 0;
*baseline_inc = (height / 2) - state->baseline;
if ((state->baseline + (height / 2)) >
state->line_height)
{
*line_inc = state->baseline +
(height / 2) -
state->line_height;
}
}
break;
case LO_ALIGN_TOP:
*x_offset = 0;
/*
* Move the top of the image to the top of the
* last text placed. On negative offsets
* (such as at the start of a line) just
* start the image at the top.
*/
*y_offset = state->baseline - text_info->ascent;
if (*y_offset < 0)
{
*y_offset = 0;
}
/*
* Top aligned images can easily hang down and make
* all the rest of the line taller. Set line_inc
* to properly increase the height of all previous
* elements.
*/
if ((*y_offset + height) > state->line_height)
{
*line_inc = *y_offset + height -
state->line_height;
}
break;
case LO_ALIGN_NCSA_TOP:
*x_offset = 0;
/*
* Move the top of the image to the top of the
* line.
*/
*y_offset = 0;
/*
* Top aligned images can easily hang down and make
* all the rest of the line taller. Set line_inc
* to properly increase the height of all previous
* elements.
*/
if ((*y_offset + height) > state->line_height)
{
*line_inc = *y_offset + height -
state->line_height;
}
break;
case LO_ALIGN_BOTTOM:
*x_offset = 0;
/*
* Like centered images, images shorter than
* the line just get move with y_offset.
*/
if (height < state->line_height)
{
*y_offset = state->line_height - height;
}
/*
* Else since they are bottom aligned.
* they move everyone's baselines and can't
* change the height below the baseline.
*/
else
{
*y_offset = 0;
*baseline_inc = height - state->line_height;
}
break;
case LO_ALIGN_NCSA_BOTTOM:
case LO_ALIGN_BASELINE:
default:
*x_offset = 0;
/*
* Like centered images, images shorter than
* the baseline just get move with y_offset.
*/
if (height < state->baseline)
{
*y_offset = state->baseline - height;
}
/*
* Else since they are baseline aligned.
* they move everyone's baselines and can't
* change the height below the baseline.
*/
else
{
*y_offset = 0;
*baseline_inc = height - state->baseline;
}
break;
}
}
int32
lo_ValueOrPercent(char *str, Bool *is_percent)
{
int32 val;
char *tptr;
*is_percent = FALSE;
if (str == NULL)
{
return(0);
}
tptr = str;
while (*tptr != '\0')
{
if (*tptr == '%')
{
*is_percent = TRUE;
*tptr = '\0';
break;
}
tptr++;
}
val = XP_ATOI(str);
return(val);
}
void
lo_CopyTextAttr(LO_TextAttr *old_attr, LO_TextAttr *new_attr)
{
if ((old_attr == NULL)||(new_attr == NULL))
{
return;
}
new_attr->size = old_attr->size;
new_attr->fontmask = old_attr->fontmask;
new_attr->fg.red = old_attr->fg.red;
new_attr->fg.green = old_attr->fg.green;
new_attr->fg.blue = old_attr->fg.blue;
new_attr->bg.red = old_attr->bg.red;
new_attr->bg.green = old_attr->bg.green;
new_attr->bg.blue = old_attr->bg.blue;
new_attr->no_background = old_attr->no_background;
new_attr->attrmask = old_attr->attrmask;
new_attr->font_face = old_attr->font_face;
/*
* FE_Data is not copied, it belongs to the FE,
* And will need to be generated by the FE the first time this
* new TextAttr is used.
*/
new_attr->FE_Data = NULL;
new_attr->charset = old_attr->charset;
}
LO_TextAttr *
lo_FetchTextAttr(lo_DocState *state, LO_TextAttr *old_attr)
{
LO_TextAttr **text_attr_hash;
LO_TextAttr *attr_ptr;
int32 hash_index;
if (old_attr == NULL)
{
return(NULL);
}
hash_index = (old_attr->fontmask & old_attr->attrmask) % FONT_HASH_SIZE;
XP_LOCK_BLOCK(text_attr_hash, LO_TextAttr **,
state->top_state->text_attr_hash);
attr_ptr = text_attr_hash[hash_index];
while (attr_ptr != NULL)
{
if ((attr_ptr->size == old_attr->size)&&
(attr_ptr->fontmask == old_attr->fontmask)&&
(attr_ptr->attrmask == old_attr->attrmask)&&
(attr_ptr->fg.red == old_attr->fg.red)&&
(attr_ptr->fg.green == old_attr->fg.green)&&
(attr_ptr->fg.blue == old_attr->fg.blue)&&
(attr_ptr->bg.red == old_attr->bg.red)&&
(attr_ptr->bg.green == old_attr->bg.green)&&
(attr_ptr->bg.blue == old_attr->bg.blue)&&
(attr_ptr->font_face == old_attr->font_face)&&
(attr_ptr->charset == old_attr->charset))
{
break;
}
attr_ptr = attr_ptr->next;
}
if (attr_ptr == NULL)
{
LO_TextAttr *new_attr;
new_attr = XP_NEW(LO_TextAttr);
if (new_attr != NULL)
{
lo_CopyTextAttr(old_attr, new_attr);
new_attr->next = text_attr_hash[hash_index];
text_attr_hash[hash_index] = new_attr;
}
else
{
state->top_state->out_of_memory = TRUE;
}
attr_ptr = new_attr;
}
XP_UNLOCK_BLOCK(state->top_state->text_attr_hash);
return(attr_ptr);
}
void
lo_AddMarginStack(lo_DocState *state, int32 x, int32 y,
int32 width, int32 height,
int32 border_width,
int32 border_vert_space, int32 border_horiz_space,
intn alignment)
{
lo_MarginStack *mptr;
mptr = XP_NEW(lo_MarginStack);
if (mptr == NULL)
{
state->top_state->out_of_memory = TRUE;
return;
}
mptr->y_min = y;
if (y >= 0)
{
mptr->y_max = y + height + (2 * border_width) +
(2 * border_vert_space);
}
else
{
mptr->y_max = height + (2 * border_width) +
(2 * border_vert_space);
}
if (alignment == LO_ALIGN_RIGHT)
{
if (state->right_margin_stack == NULL)
{
mptr->margin = state->right_margin - width -
(2 * border_width) - (2 * border_horiz_space);
}
else
{
mptr->margin = state->right_margin_stack->margin -
width - (2 * border_width) -
(2 * border_horiz_space);
}
mptr->next = state->right_margin_stack;
state->right_margin_stack = mptr;
}
else
{
mptr->margin = state->left_margin + width +
(2 * border_width) + (2 * border_horiz_space);
mptr->next = state->left_margin_stack;
state->left_margin_stack = mptr;
}
}
/*
* When clipping lines we have essentially scrolled the document up.
* Move the margin blocks up too.
*/
void
lo_ShiftMarginsUp(MWContext *context, lo_DocState *state, int32 dy)
{
lo_MarginStack *mptr;
int32 y;
mptr = state->left_margin_stack;
while (mptr != NULL)
{
y = mptr->y_min - dy;
if (y < 0)
{
y = 0;
}
mptr->y_min = y;
y = mptr->y_max - dy;
if (y < 0)
{
y = 0;
}
mptr->y_max = y;
mptr = mptr->next;
}
mptr = state->right_margin_stack;
while (mptr != NULL)
{
y = mptr->y_min - dy;
if (y < 0)
{
y = 0;
}
mptr->y_min = y;
y = mptr->y_max - dy;
if (y < 0)
{
y = 0;
}
mptr->y_max = y;
mptr = mptr->next;
}
}
void
lo_ClearToLeftMargin(MWContext *context, lo_DocState *state)
{
lo_MarginStack *mptr;
int32 y;
mptr = state->left_margin_stack;
if (mptr == NULL)
{
return;
}
y = state->y;
if (mptr->y_max > y)
{
y = mptr->y_max;
}
while (mptr->next != NULL)
{
lo_MarginStack *margin;
if (mptr->y_max > y)
{
y = mptr->y_max;
}
margin = mptr;
mptr = mptr->next;
XP_DELETE(margin);
}
XP_DELETE(mptr);
state->y = y;
state->left_margin_stack = NULL;
state->left_margin = state->win_left;
state->x = state->left_margin;
/*
* Find where we hit the right margin, popping old
* obsolete elements as we go.
*/
mptr = state->right_margin_stack;
while ((mptr != NULL)&&(state->y > mptr->y_max))
{
lo_MarginStack *margin;
margin = mptr;
mptr = mptr->next;
XP_DELETE(margin);
}
if (mptr != NULL)
{
state->right_margin_stack = mptr;
state->right_margin = mptr->margin;
}
else
{
state->right_margin_stack = NULL;
state->right_margin = (state->list_stack != NULL)
? state->list_stack->old_right_margin
: 0;
}
}
void
lo_ClearToRightMargin(MWContext *context, lo_DocState *state)
{
lo_MarginStack *mptr;
int32 y;
mptr = state->right_margin_stack;
if (mptr == NULL)
{
return;
}
y = state->y;
if (mptr->y_max > y)
{
y = mptr->y_max;
}
while (mptr->next != NULL)
{
lo_MarginStack *margin;
if (mptr->y_max > y)
{
y = mptr->y_max;
}
margin = mptr;
mptr = mptr->next;
XP_DELETE(margin);
}
XP_DELETE(mptr);
state->y = y;
state->right_margin_stack = NULL;
state->right_margin = state->win_width - state->win_right;
/*
* Find where we hit the left margin, popping old
* obsolete elements as we go.
*/
mptr = state->left_margin_stack;
while ((mptr != NULL)&&(state->y > mptr->y_max))
{
lo_MarginStack *margin;
margin = mptr;
mptr = mptr->next;
XP_DELETE(margin);
}
if (mptr != NULL)
{
state->left_margin_stack = mptr;
state->left_margin = mptr->margin;
}
else
{
state->left_margin_stack = NULL;
state->left_margin = (state->list_stack != NULL)
? state->list_stack->old_left_margin
: 0;
}
}
void
lo_ClearToBothMargins(MWContext *context, lo_DocState *state)
{
lo_MarginStack *mptr;
int32 y;
y = state->y;
mptr = state->right_margin_stack;
if (mptr != NULL)
{
if (mptr->y_max > y)
{
y = mptr->y_max;
}
while (mptr->next != NULL)
{
lo_MarginStack *margin;
if (mptr->y_max > y)
{
y = mptr->y_max;
}
margin = mptr;
mptr = mptr->next;
XP_DELETE(margin);
}
XP_DELETE(mptr);
state->y = y;
state->right_margin_stack = NULL;
state->right_margin = state->win_width - state->win_right;
}
mptr = state->left_margin_stack;
if (mptr != NULL)
{
if (mptr->y_max > y)
{
y = mptr->y_max;
}
while (mptr->next != NULL)
{
lo_MarginStack *margin;
if (mptr->y_max > y)
{
y = mptr->y_max;
}
margin = mptr;
mptr = mptr->next;
XP_DELETE(margin);
}
XP_DELETE(mptr);
if (y > state->y)
{
state->y = y;
}
state->left_margin_stack = NULL;
state->left_margin = state->win_left;
state->x = state->left_margin;
}
}
void
lo_FindLineMargins(MWContext *context, lo_DocState *state)
{
lo_MarginStack *mptr;
LO_Element *eptr;
eptr = state->float_list;
while ((eptr != NULL)&&(eptr->lo_any.y < 0))
{
int32 expose_y;
int32 expose_height;
/*
* Float the y to our current position.
*/
eptr->lo_any.y = state->y;
/*
* These get coppied into special variables because they
* may get changed by a table in the floating list.
*/
expose_y = eptr->lo_any.y;
expose_height = eptr->lo_any.height;
/*
* Special case: If we have just placed a TABLE element,
* we need to properly offset all the CELLs inside it.
*/
if (eptr->type == LO_TABLE)
{
int32 y2;
int32 change_y;
LO_Element *tptr;
/*
* We need to know the bottom and top extents of
* the table to know big an area to refresh.
*/
y2 = expose_y + eptr->lo_any.y_offset + expose_height;
/*
* This is how far to move the cells based on
* where the table thought it was placed.
*/
change_y = state->y - eptr->lo_table.expected_y;
/*
* set tptr to the start of the cell list.
*/
tptr = eptr->lo_any.next;
while ((tptr != NULL)&&(tptr->type == LO_CELL))
{
/*
* Only do this work if the table has moved.
*/
if (change_y != 0)
{
tptr->lo_any.y += change_y;
lo_ShiftCell((LO_CellStruct *)tptr,
0, change_y);
}
/*
* Find upper and lower bounds of the
* cells.
*/
if (tptr->lo_any.y < expose_y)
{
expose_y = tptr->lo_any.y;
}
if ((tptr->lo_any.y + tptr->lo_any.y_offset +
tptr->lo_any.height) > y2)
{
y2 = tptr->lo_any.y +
tptr->lo_any.y_offset +
tptr->lo_any.height;
}
tptr = tptr->lo_any.next;
}
/*
* Adjust exposed area height for lower bound.
* Upper bound already possibly moved.
*/
if ((y2 - expose_y - eptr->lo_any.y_offset) >
expose_height)
{
expose_height = y2 - expose_y -
eptr->lo_any.y_offset;
}
}
/*
* We may have been blocking display on this floating image.
* If so deal with it here.
*/
if ((state->display_blocked != FALSE)&&
(state->is_a_subdoc == SUBDOC_NOT)&&
(state->display_blocking_element_y == 0)&&
(state->display_blocking_element_id != -1)&&
(eptr->lo_any.ele_id >=
state->display_blocking_element_id))
{
state->display_blocking_element_y =
state->y;
/*
* Special case, if the blocking element
* is on the first line, no blocking
* at all needs to happen.
*/
if (state->y == state->win_top)
{
state->display_blocked = FALSE;
FE_SetDocPosition(context,
FE_VIEW, 0, state->base_y);
lo_RefreshDocumentArea(context, state, 0, 0,
state->win_width, state->win_height);
}
}
lo_RefreshDocumentArea(context, state,
eptr->lo_any.x + eptr->lo_any.x_offset,
expose_y + eptr->lo_any.y_offset,
eptr->lo_any.width, expose_height);
/*
* In case these floating elements are the only thing in the
* doc (or subdoc) we need to set the state min and max widths
* when we place them.
*/
if (eptr->type == LO_TABLE)
{
int32 width;
width = eptr->lo_table.width +
(2 * eptr->lo_table.border_width) +
(2 * eptr->lo_table.border_horiz_space);
if (width > state->min_width)
{
state->min_width = width;
}
if (width > state->max_width)
{
state->max_width = width;
}
}
else if (eptr->type == LO_IMAGE)
{
int32 width;
width = eptr->lo_image.width +
(2 * eptr->lo_image.border_width) +
(2 * eptr->lo_image.border_horiz_space);
if (width > state->min_width)
{
state->min_width = width;
}
if (width > state->max_width)
{
state->max_width = width;
}
}
eptr = eptr->lo_any.next;
}
/*
* Find floating elements just added to the left margin stack
* from the last line, and set their absolute
* coordinates.
*/
mptr = state->left_margin_stack;
while ((mptr != NULL)&&(mptr->y_min < 0))
{
mptr->y_min = state->y;
mptr->y_max = mptr->y_min + mptr->y_max;
mptr = mptr->next;
}
/*
* Find where we hit the left margin, popping old
* obsolete elements as we go.
*/
mptr = state->left_margin_stack;
while ((mptr != NULL)&&(state->y > mptr->y_max))
{
lo_MarginStack *margin;
margin = mptr;
mptr = mptr->next;
XP_DELETE(margin);
}
if (mptr != NULL)
{
state->left_margin_stack = mptr;
state->left_margin = mptr->margin;
/*
* For a list wrapped around a floating image we may
* need to be indented from the image edge.
*/
if (state->list_stack->old_left_margin > mptr->margin)
{
state->left_margin = state->list_stack->old_left_margin;
}
}
else
{
state->left_margin_stack = NULL;
state->left_margin = (state->list_stack != NULL)
? state->list_stack->old_left_margin
: 0;
}
/*
* Find floating elements just added to the right margin stack
* from the last line, and set their absolute
* coordinates.
*/
mptr = state->right_margin_stack;
while ((mptr != NULL)&&(mptr->y_min < 0))
{
mptr->y_min = state->y;
mptr->y_max = mptr->y_min + mptr->y_max;
mptr = mptr->next;
}
/*
* Find where we hit the right margin, popping old
* obsolete elements as we go.
*/
mptr = state->right_margin_stack;
while ((mptr != NULL)&&(state->y > mptr->y_max))
{
lo_MarginStack *margin;
margin = mptr;
mptr = mptr->next;
XP_DELETE(margin);
}
if (mptr != NULL)
{
state->right_margin_stack = mptr;
state->right_margin = mptr->margin;
}
else
{
state->right_margin_stack = NULL;
state->right_margin = (state->list_stack != NULL)
? state->list_stack->old_right_margin
: 0;
}
}
void
lo_RecycleElements(MWContext *context, lo_DocState *state, LO_Element *elements)
{
LO_Element *eptr;
if (elements == NULL)
{
return;
}
eptr = elements;
while(eptr->lo_any.next != NULL)
{
lo_ScrapeElement(context, eptr);
eptr = eptr->lo_any.next;
}
lo_ScrapeElement(context, eptr);
#ifdef MEMORY_ARENAS
if ( EDT_IS_EDITOR(context) ) {
eptr->lo_any.next = state->top_state->recycle_list;
state->top_state->recycle_list = elements;
}
#else
eptr->lo_any.next = state->top_state->recycle_list;
state->top_state->recycle_list = elements;
#endif /* MEMORY_ARENAS */
}
LO_Element *
lo_NewElement(MWContext *context, lo_DocState *state, intn type,
ED_Element *edit_element, intn edit_offset)
{
LO_Element *eptr;
int32 size;
#ifdef MEMORY_ARENAS
switch(type)
{
case LO_TEXT:
size = sizeof(LO_TextStruct);
break;
case LO_LINEFEED:
size = sizeof(LO_LinefeedStruct);
break;
case LO_HRULE:
size = sizeof(LO_HorizRuleStruct);
break;
case LO_IMAGE:
size = sizeof(LO_ImageStruct);
break;
case LO_BULLET:
size = sizeof(LO_BulletStruct);
break;
case LO_FORM_ELE:
size = sizeof(LO_FormElementStruct);
break;
case LO_SUBDOC:
size = sizeof(LO_SubDocStruct);
break;
case LO_TABLE:
size = sizeof(LO_TableStruct);
break;
case LO_CELL:
size = sizeof(LO_CellStruct);
break;
case LO_EMBED:
size = sizeof(LO_EmbedStruct);
break;
case LO_JAVA:
size = sizeof(LO_JavaAppStruct);
break;
case LO_EDGE:
size = sizeof(LO_EdgeStruct);
break;
default:
size = sizeof(LO_Any);
break;
}
if ( ! EDT_IS_EDITOR(context) ) {
eptr = (LO_Element *)lo_MemoryArenaAllocate(state->top_state, size);
}
else {
if (state->top_state->recycle_list != NULL)
{
eptr = state->top_state->recycle_list;
state->top_state->recycle_list = eptr->lo_any.next;
}
else
{
eptr = XP_NEW(LO_Element);
}
}
#else
/* sorry about all the asserts, in my debug version somehow state
* becomes nil after a malloc failure. */
#ifdef DEBUG
assert (state);
#endif
if (state->top_state->recycle_list != NULL)
{
eptr = state->top_state->recycle_list;
state->top_state->recycle_list = eptr->lo_any.next;
}
else
{
eptr = XP_NEW(LO_Element);
#ifdef DEBUG
assert (state);
#endif
}
#endif /* MEMORY_ARENAS */
if (eptr == NULL)
{
#ifdef DEBUG
assert (state);
#endif
state->top_state->out_of_memory = TRUE;
}
else
{
eptr->lo_any.next = NULL;
eptr->lo_any.prev = NULL;
#ifdef EDITOR
eptr->lo_any.edit_element = NULL;
eptr->lo_any.edit_offset = NULL;
if( EDT_IS_EDITOR(context)
&& lo_EditableElement( type ))
{
if( edit_element == 0 ){
edit_element = state->edit_current_element;
edit_offset = state->edit_current_offset;
}
EDT_SetLayoutElement( edit_element,
edit_offset,
type,
eptr );
}
state->edit_force_offset = FALSE;
#endif
}
return(eptr);
}
Bool
lo_SetNamedAnchor(lo_DocState *state, PA_Block name, LO_Element *element)
{
LO_Element *eptr;
lo_NameList *name_rec;
/*
* No named anchors are allowed within a
* hacked scrolling content layout document.
*/
if (state->top_state->scrolling_doc != FALSE)
{
if (name != NULL)
{
PA_FREE(name);
}
return FALSE;
}
if (element == NULL)
{
if (state->line_list == NULL)
{
eptr = state->end_last_line;
}
else
{
eptr = state->line_list;
while (eptr->lo_any.next != NULL)
{
eptr = eptr->lo_any.next;
}
}
}
else
{
eptr = element;
}
name_rec = XP_NEW(lo_NameList);
if (name_rec == NULL)
{
state->top_state->out_of_memory = TRUE;
PA_FREE(name);
return FALSE;
}
if (eptr == NULL)
{
name_rec->x = 0;
name_rec->y = 0;
}
else
{
name_rec->x = eptr->lo_any.x;
name_rec->y = eptr->lo_any.y;
}
name_rec->element = eptr;
name_rec->name = name;
name_rec->next = state->name_list;
state->name_list = name_rec;
return TRUE;
}
void
lo_AddNameList(lo_DocState *state, lo_DocState *old_state)
{
lo_NameList *name_list;
lo_NameList *nptr;
name_list = old_state->name_list;
while (name_list != NULL)
{
/*
* Extract the current record from the list.
*/
nptr = name_list;
name_list = name_list->next;
/*
* The element almost certainly has been moved
* from subdoc to main cell, correct its position.
*/
if (nptr->element != NULL)
{
nptr->x = nptr->element->lo_any.x;
nptr->y = nptr->element->lo_any.y;
}
/*
* Check is this name is one we are waiting on.
*/
if ((state->display_blocking_element_id == -1)&&
(nptr->name != NULL))
{
char *name;
PA_LOCK(name, char *, nptr->name);
if (XP_STRCMP(name,
(char *)(state->top_state->name_target + 1)) == 0)
{
XP_FREE(state->top_state->name_target);
state->top_state->name_target = NULL;
if (nptr->element != NULL)
{
state->display_blocking_element_id =
nptr->element->lo_any.ele_id;
}
else
{
state->display_blocking_element_id =
state->top_state->element_id;
}
/*
* If the display is blocked for an element
* we havn't reached yet, check to see if
* it is in this line, and if so, save its
* y position.
*/
if ((state->display_blocked != FALSE)&&
(state->is_a_subdoc == SUBDOC_NOT)&&
(state->display_blocking_element_y == 0)&&
(state->display_blocking_element_id != -1))
{
state->display_blocking_element_y = nptr->y;
}
}
PA_UNLOCK(nptr->name);
}
/*
* Stick it into the main name list.
*/
nptr->next = state->name_list;
state->name_list = nptr;
}
old_state->name_list = name_list;
}
void
lo_AppendToLineList(MWContext *context, lo_DocState *state,
LO_Element *element, int32 baseline_inc)
{
LO_Element *eptr;
if (state->current_named_anchor != NULL)
{
lo_SetNamedAnchor(state, state->current_named_anchor, element);
}
state->current_named_anchor = NULL;
element->lo_any.next = NULL;
element->lo_any.prev = NULL;
eptr = state->line_list;
if (eptr == NULL)
{
state->line_list = element;
}
else
{
while (eptr->lo_any.next != NULL)
{
eptr->lo_any.y_offset += baseline_inc;
eptr = eptr->lo_any.next;
}
eptr->lo_any.y_offset += baseline_inc;
eptr->lo_any.next = element;
element->lo_any.prev = eptr;
}
}
void
lo_SetLineArrayEntry(lo_DocState *state, LO_Element *eptr, int32 line)
{
LO_Element **line_array;
#ifdef XP_WIN16
intn a_size;
intn a_indx;
intn a_line;
XP_Block *larray_array;
#endif /* XP_WIN16 */
if (line >= (state->line_num - 1))
{
return;
}
#ifdef XP_WIN16
a_size = SIZE_LIMIT / sizeof(LO_Element *);
a_indx = (intn)(line / a_size);
a_line = (intn)(line - (a_indx * a_size));
XP_LOCK_BLOCK(larray_array, XP_Block *, state->larray_array);
state->line_array = larray_array[a_indx];
XP_LOCK_BLOCK(line_array, LO_Element **, state->line_array);
line_array[a_line] = eptr;
XP_UNLOCK_BLOCK(state->line_array);
XP_UNLOCK_BLOCK(state->larray_array);
#else
XP_LOCK_BLOCK(line_array, LO_Element **, state->line_array);
line_array[line] = eptr;
XP_UNLOCK_BLOCK(state->line_array);
#endif /* XP_WIN16 */
}
/*
* Return the width of the window for this context in chars in the default
* fixed width font. Return -1 on error.
*/
int16
LO_WindowWidthInFixedChars(MWContext *context)
{
int32 doc_id;
lo_TopState *top_state;
lo_DocState *state;
LO_TextInfo tmp_info;
LO_TextAttr tmp_attr;
LO_TextStruct tmp_text;
PA_Block buff;
char *str;
int32 width;
int16 char_cnt;
/*
* Get the unique document ID, and retreive this
* documents layout state.
*/
doc_id = XP_DOCID(context);
top_state = lo_FetchTopState(doc_id);
if ((top_state == NULL)||(top_state->doc_state == NULL))
{
return((int16)-1);
}
state = top_state->doc_state;
/*
* Create a fake text buffer to measure an 'M'.
*/
memset (&tmp_text, 0, sizeof (tmp_text));
buff = PA_ALLOC(1);
if (buff == NULL)
{
return((int16)-1);
}
PA_LOCK(str, char *, buff);
str[0] = 'M';
PA_UNLOCK(buff);
tmp_text.text = buff;
tmp_text.text_len = 1;
/*
* Fill in default fixed font information.
*/
lo_SetDefaultFontAttr(state, &tmp_attr, context);
tmp_attr.fontmask |= LO_FONT_FIXED;
tmp_text.text_attr = &tmp_attr;
/*
* Get the width of that 'M'
*/
FE_GetTextInfo(context, &tmp_text, &tmp_info);
PA_FREE(buff);
/*
* Error if bad character width
*/
if (tmp_info.max_width <= 0)
{
return((int16)-1);
}
width = state->win_width - state->win_left - state->win_right;
char_cnt = (int16)(width / tmp_info.max_width);
return(char_cnt);
}
LO_Element *
lo_FirstElementOfLine(MWContext *context, lo_DocState *state, int32 line)
{
LO_Element *eptr;
LO_Element **line_array;
if ((line < 0)||(line > (state->line_num - 2)))
{
return(NULL);
}
#ifdef XP_WIN16
{
intn a_size;
intn a_indx;
intn a_line;
XP_Block *larray_array;
a_size = SIZE_LIMIT / sizeof(LO_Element *);
a_indx = (intn)(line / a_size);
a_line = (intn)(line - (a_indx * a_size));
XP_LOCK_BLOCK(larray_array, XP_Block *,
state->larray_array);
state->line_array = larray_array[a_indx];
XP_LOCK_BLOCK(line_array, LO_Element **,
state->line_array);
eptr = line_array[a_line];
XP_UNLOCK_BLOCK(state->line_array);
XP_UNLOCK_BLOCK(state->larray_array);
}
#else
{
XP_LOCK_BLOCK(line_array, LO_Element **,
state->line_array);
eptr = line_array[line];
XP_UNLOCK_BLOCK(state->line_array);
}
#endif /* XP_WIN16 */
return(eptr);
}
#ifdef MOCHA
static Bool
lo_find_mocha_expr(char *str, char **expr_start, char **expr_end)
{
char *tptr;
char *start;
char *end;
if (str == NULL)
{
return(FALSE);
}
start = NULL;
end = NULL;
tptr = str;
while (*tptr != '\0')
{
if ((*tptr == '&')&&(*(char *)(tptr + 1) == '{'))
{
start = tptr;
break;
}
tptr++;
}
if (start == NULL)
{
return(FALSE);
}
tptr = (char *)(start + 2);
while (*tptr != '\0')
{
if ((*tptr == '}')&&(*(char *)(tptr + 1) == ';'))
{
end = tptr;
break;
}
tptr++;
}
if (end == NULL)
{
return(FALSE);
}
end = (char *)(end + 1);
*expr_start = start;
*expr_end = end;
return(TRUE);
}
static char *
lo_add_string(char *base, char *add)
{
char *new;
int32 base_len, add_len;
if (add == NULL)
{
return(base);
}
if (base == NULL)
{
new = XP_STRDUP(add);
return(new);
}
base_len = XP_STRLEN(base);
add_len = XP_STRLEN(add);
new = (char *)XP_ALLOC(base_len + add_len + 1);
if (new == NULL)
{
return(base);
}
XP_STRCPY(new, base);
XP_STRCAT(new, add);
XP_FREE(base);
return(new);
}
static char *
lo_eval_javascript_entities(MWContext *context, char *orig_value,
uint newline_count)
{
char *tptr;
char *str;
char *new_str;
int32 len;
char tchar;
Bool has_mocha;
char *start, *end;
if (orig_value == NULL)
{
return(NULL);
}
str = orig_value;
len = XP_STRLEN(str);
new_str = NULL;
tptr = str;
has_mocha = lo_find_mocha_expr(tptr, &start, &end);
while (has_mocha != FALSE)
{
char *expr_start, *expr_end;
if (start > tptr)
{
tchar = *start;
*start = '\0';
new_str = lo_add_string(new_str, tptr);
*start = tchar;
}
expr_start = (char *)(start + 2);
expr_end = (char *)(end - 1);
if (expr_end > expr_start)
{
char *eval_str;
tchar = *expr_end;
*expr_end = '\0';
eval_str = LM_EvaluateAttribute(context, expr_start,
newline_count + 1);
*expr_end = tchar;
new_str = lo_add_string(new_str, eval_str);
if (eval_str != NULL)
{
XP_FREE(eval_str);
}
}
tptr = (char *)(end + 1);
has_mocha = lo_find_mocha_expr(tptr, &start, &end);
}
if (tptr != str)
{
new_str = lo_add_string(new_str, tptr);
}
if (new_str == NULL)
{
new_str = str;
}
return(new_str);
}
#endif /* MOCHA */
void
lo_ConvertAllValues(MWContext *context, char **value_list, int32 list_len,
uint newline_count)
{
#ifdef MOCHA
int32 i;
if (LM_GetMochaEnabled() == FALSE)
{
return;
}
for (i=0; i < list_len; i++)
{
char *str;
char *new_str;
str = value_list[i];
if (str != NULL)
{
new_str = lo_eval_javascript_entities(context, str, newline_count);
if (new_str != str)
{
XP_FREE(str);
value_list[i] = new_str;
}
}
}
#endif /* MOCHA */
}
PA_Block
lo_FetchParamValue(MWContext *context, PA_Tag *tag, char *param_name)
{
PA_Block buff;
char *str;
buff = PA_FetchParamValue(tag, param_name);
#ifdef MOCHA /* added by jwz */
if (LM_GetMochaEnabled() == FALSE)
{
return(buff);
}
if (buff != NULL)
{
char *new_str;
PA_LOCK(str, char *, buff);
new_str = lo_eval_javascript_entities(context, str, tag->newline_count);
if (new_str != NULL)
{
int32 new_len;
PA_Block new_buff;
new_len = XP_STRLEN(new_str);
new_buff = PA_ALLOC(new_len + 1);
if (new_buff != NULL)
{
char *tmp_str;
PA_LOCK(tmp_str, char *, new_buff);
XP_STRCPY(tmp_str, new_str);
PA_UNLOCK(new_buff);
/*
* If the original buffer was
* copied, free the copy.
*/
if (new_str != str)
{
XP_FREE(new_str);
}
PA_UNLOCK(buff);
PA_FREE(buff);
buff = new_buff;
PA_LOCK(str, char *, buff);
}
}
PA_UNLOCK(buff);
}
#endif /* MOCHA */
return(buff);
}
#ifdef PROFILE
#pragma profile off
#endif