/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public License * Version 1.0 (the "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ #include "xp.h" #include "pa_parse.h" #include "layout.h" #include "laylayer.h" #include "laystyle.h" #include "libmocha.h" #include "stystruc.h" #include "stystack.h" #include "layers.h" #ifdef XP_MAC #include "prpriv.h" #else #include "private/prpriv.h" /* for PR_NewNamedMonitor */ #endif #include "intl_csi.h" #ifdef DOM #include "lm_dom.h" #endif #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_GetElementBbox(LO_Element *element, XP_Rect *rect) { rect->left = element->lo_any.x + element->lo_any.x_offset; rect->top = element->lo_any.y + element->lo_any.y_offset; rect->right = rect->left + element->lo_any.width; rect->bottom = rect->top + element->lo_any.height; switch(element->type) { case LO_IMAGE: { LO_ImageStruct *image = &element->lo_image; rect->right += 2 * (image->border_width + image->border_horiz_space); rect->bottom += 2 * (image->border_width + image->border_vert_space); } break; case LO_SUBDOC: { LO_SubDocStruct *subdoc = &element->lo_subdoc; rect->right += 2 * (subdoc->border_width + subdoc->border_horiz_space); rect->bottom += 2 * (subdoc->border_width + subdoc->border_vert_space); } break; case LO_TABLE: { LO_TableStruct *table = &element->lo_table; rect->right += 2 * (table->border_width + table->border_horiz_space); rect->bottom += 2 * (table->border_width + table->border_vert_space); } break; case LO_CELL: { LO_CellStruct *cell = &element->lo_cell; rect->right += 2 * (cell->border_width + cell->border_horiz_space); rect->bottom += 2 * (cell->border_width + cell->border_vert_space); } break; case LO_EMBED: { LO_EmbedStruct *embed = &element->lo_embed; rect->right += 2 * (embed->objTag.border_width + embed->objTag.border_horiz_space); rect->bottom += 2 * (embed->objTag.border_width + embed->objTag.border_vert_space); } break; case LO_BUILTIN: { LO_BuiltinStruct *builtin = &element->lo_builtin; rect->right += 2 * (builtin->border_width + builtin->border_horiz_space); rect->bottom += 2 * (builtin->border_width + builtin->border_vert_space); } break; #ifdef JAVA case LO_JAVA: { LO_JavaAppStruct *java = &element->lo_java; rect->right += 2 * (java->objTag.border_width + java->objTag.border_horiz_space); rect->bottom += 2 * (java->objTag.border_width + java->objTag.border_vert_space); } break; #endif default: break; } } void lo_RefreshElement(LO_Element *element, CL_Layer *layer, Bool update_now) { XP_Rect rect; int32 x_offset, y_offset; CL_Compositor *compositor = CL_GetLayerCompositor(layer); lo_GetElementBbox(element, &rect); lo_GetLayerXYShift(layer, &x_offset, &y_offset); XP_OffsetRect(&rect, -x_offset, -y_offset); CL_UpdateLayerRect(compositor, layer, &rect, (PRBool)update_now); } /* Given a layout element, retrieve its anchor information. */ LO_AnchorData * lo_GetElementAnchor(LO_Element *element) { switch(element->type) { case LO_TEXT: return element->lo_text.anchor_href; case LO_IMAGE: return element->lo_image.anchor_href; case LO_TABLE: return element->lo_table.anchor_href; case LO_SUBDOC: return element->lo_subdoc.anchor_href; case LO_LINEFEED: return element->lo_linefeed.anchor_href; default: return NULL; } } /* Return an element's foreground color */ void lo_GetElementFGColor(LO_Element *element, LO_Color *color) { switch (element->type) { case LO_TEXT: color->red = element->lo_text.text_attr->fg.red; color->green = element->lo_text.text_attr->fg.green; color->blue = element->lo_text.text_attr->fg.blue; break; case LO_IMAGE: color->red = element->lo_image.text_attr->fg.red; color->green = element->lo_image.text_attr->fg.green; color->blue = element->lo_image.text_attr->fg.blue; break; case LO_SUBDOC: color->red = element->lo_subdoc.text_attr->fg.red; color->green = element->lo_subdoc.text_attr->fg.green; color->blue = element->lo_subdoc.text_attr->fg.blue; break; default: XP_ASSERT(0); /* Not implemented */ break; } } /* Set an element's foreground color */ void lo_SetElementFGColor(LO_Element *element, LO_Color *color) { switch (element->type) { case LO_TEXT: element->lo_text.text_attr->fg.red = color->red; element->lo_text.text_attr->fg.green = color->green; element->lo_text.text_attr->fg.blue = color->blue; break; case LO_IMAGE: element->lo_image.text_attr->fg.red = color->red; element->lo_image.text_attr->fg.green = color->green; element->lo_image.text_attr->fg.blue = color->blue; break; case LO_SUBDOC: element->lo_subdoc.text_attr->fg.red = color->red; element->lo_subdoc.text_attr->fg.green = color->green; element->lo_subdoc.text_attr->fg.blue = color->blue; break; default: XP_ASSERT(0); /* Not implemented */ break; } } NET_ReloadMethod LO_GetReloadMethod(MWContext *context) { int32 doc_id; lo_TopState *top_state; lo_DocState *state; /* * 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 NET_NORMAL_RELOAD; } state = top_state->doc_state; return (FORCE_RELOAD_FLAG(state->top_state)); } 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) { if (attr_ptr->FE_Data != NULL) { FE_ReleaseTextAttrFeData(context, attr_ptr); } 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; itop_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; } else if (pa_TagEqual("justify", str)) { alignment = LO_ALIGN_JUSTIFY; } 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); } #if defined( EDITOR ) || !defined( MEMORY_ARENAS ) /* 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 */ "middle", /* 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 /* set *alignment and *floating iff there * is a valid stylesheet alignment property. */ MODULE_PRIVATE void lo_EvalStyleSheetAlignment(StyleStruct *style_struct, intn *alignment, Bool *floating) { char *valign, *halign; if(!style_struct) return; valign = STYLESTRUCT_GetString(style_struct,VERTICAL_ALIGN_STYLE); halign = STYLESTRUCT_GetString(style_struct,HORIZONTAL_ALIGN_STYLE); if(valign) { if (pa_TagEqual("top", valign)) { *alignment = LO_ALIGN_NCSA_TOP; *floating = FALSE; } else if (pa_TagEqual("texttop", valign) || pa_TagEqual("text-top", valign)) { *alignment = LO_ALIGN_TOP; *floating = FALSE; } else if (pa_TagEqual("bottom", valign)) { *alignment = LO_ALIGN_NCSA_BOTTOM; *floating = FALSE; } else if (pa_TagEqual("absbottom", valign) || pa_TagEqual("text-bottom", valign)) { *alignment = LO_ALIGN_BOTTOM; *floating = FALSE; } else if (pa_TagEqual("baseline", valign)) { *alignment = LO_ALIGN_BASELINE; *floating = FALSE; } else if ((pa_TagEqual("middle", valign))|| (pa_TagEqual("center", valign))) { *alignment = LO_ALIGN_NCSA_CENTER; *floating = FALSE; } else if ((pa_TagEqual("absmiddle", valign))|| (pa_TagEqual("abscenter", valign))) { *alignment = LO_ALIGN_CENTER; *floating = FALSE; } } if(halign) { if (pa_TagEqual("left", halign)) { *alignment = LO_ALIGN_LEFT; *floating = TRUE; } else if (pa_TagEqual("right", halign)) { *alignment = LO_ALIGN_RIGHT; *floating = TRUE; } } XP_FREEIF(valign); XP_FREEIF(halign); return; } 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); if (*is_percent) *tptr = '%'; /* Restore original string */ return(val); } void lo_SetElementTextAttr(LO_Element *element, LO_TextAttr *attr) { switch(element->type) { case LO_TEXT: element->lo_text.text_attr = attr; break; case LO_IMAGE: element->lo_image.text_attr = attr; break; case LO_SUBDOC: element->lo_subdoc.text_attr = attr; break; case LO_LINEFEED: element->lo_linefeed.text_attr = attr; break; case LO_FORM_ELE: element->lo_form.text_attr = attr; break; case LO_BULLET: element->lo_bullet.text_attr = attr; break; default: XP_ASSERT(0); } } LO_TextAttr * lo_GetElementTextAttr(LO_Element *element) { switch(element->type) { case LO_TEXT: return element->lo_text.text_attr; case LO_IMAGE: return element->lo_image.text_attr; case LO_SUBDOC: return element->lo_subdoc.text_attr; case LO_LINEFEED: return element->lo_linefeed.text_attr; case LO_FORM_ELE: return element->lo_form.text_attr; case LO_BULLET: return element->lo_bullet.text_attr; default: return NULL; } } 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; new_attr->point_size = old_attr->point_size; new_attr->font_weight = old_attr->font_weight; } LO_TextAttr * lo_NewCopyTextAttr(lo_DocState *state, LO_TextAttr *old_attr) { LO_TextAttr *new_attr; LO_TextAttr **text_attr_hash; int32 hash_index; 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); new_attr = XP_NEW_ZAP(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; new_attr->refcnt = 1; } else { state->top_state->out_of_memory = TRUE; } XP_UNLOCK_BLOCK(state->top_state->text_attr_hash); return new_attr; } 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)&& (attr_ptr->point_size == old_attr->point_size)&& (attr_ptr->font_weight == old_attr->font_weight) ) { attr_ptr->refcnt++; break; } attr_ptr = attr_ptr->next; } if (attr_ptr == NULL) { LO_TextAttr *new_attr; new_attr = lo_NewCopyTextAttr(state, old_attr); 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, Bool updateFE) { lo_MarginStack *mptr; LO_Element *eptr; eptr = state->float_list; /* while ((eptr != NULL)&&(eptr->lo_any.y < 0)) */ while (eptr != NULL) { LO_Element *next_eptr = NULL; if (eptr->lo_any.y < 0) { int32 expose_y; int32 expose_height; /* LO_Element *next_eptr; */ next_eptr = NULL; /* * 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; } /* * Whatever is after all the table cells will * really be the next eptr. */ if (tptr != NULL) { next_eptr = tptr; } /* * 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; } } if (updateFE) { /* * 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); if (context->compositor) { XP_Rect rect; rect.left = 0; rect.top = 0; rect.right = state->win_width; rect.bottom = state->win_height; CL_UpdateDocumentRect(context->compositor, &rect, (PRBool)FALSE); } } } } /* end if (updateFE) */ if (context->compositor) { XP_Rect rect; rect.left = eptr->lo_any.x + eptr->lo_any.x_offset; rect.top = expose_y + eptr->lo_any.y_offset; rect.right = rect.left + eptr->lo_any.width; rect.bottom = rect.top + expose_height; CL_UpdateDocumentRect(context->compositor, &rect, (PRBool)FALSE); } /* * 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. */ { int32 width; XP_Rect rect; rect.left = 0; rect.right = 0; lo_GetElementBbox(eptr, &rect); width = rect.right - rect.left; if (width > 0) { if (width > state->min_width) { state->min_width = width; } /* * Only do this if not in a table */ if (state->is_a_subdoc == SUBDOC_NOT) { width += rect.left; } if (width > state->max_width) { state->max_width = width; } } } /* eptr = eptr->lo_any.next; */ /* * If we are skipping a table's cells, set eptr * properly now. */ /* if (next_eptr != NULL) { eptr = next_eptr; } */ } /* End if (eptr->lo_any.y < 0) */ eptr = eptr->lo_any.next; /* * If we are skipping a table's cells, set eptr * properly now. */ if (next_eptr != NULL) { eptr = next_eptr; } } /* End while */ /* * 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, TRUE); eptr = eptr->lo_any.next; } lo_ScrapeElement(context, eptr, TRUE); #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; #ifdef MEMORY_ARENAS int32 size; 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_BUILTIN: size = sizeof(LO_BuiltinStruct); break; case LO_CELL: size = sizeof(LO_CellStruct); break; case LO_EMBED: size = sizeof(LO_EmbedStruct); break; #ifdef JAVA case LO_JAVA: size = sizeof(LO_JavaAppStruct); break; #endif case LO_EDGE: size = sizeof(LO_EdgeStruct); break; case LO_OBJECT: size = sizeof(LO_ObjectStruct); break; case LO_PARAGRAPH: size = sizeof(LO_ParagraphStruct); break; case LO_CENTER: size = sizeof(LO_CenterStruct); break; case LO_MULTICOLUMN: size = sizeof(LO_MulticolumnStruct); break; case LO_TEXTBLOCK: size = sizeof(LO_TextBlock); break; case LO_LIST: size = sizeof(LO_ListStruct); break; case LO_DESCTITLE: size = sizeof(LO_DescTitleStruct); break; case LO_DESCTEXT: size = sizeof(LO_DescTextStruct); break; case LO_BLOCKQUOTE: size = sizeof(LO_BlockQuoteStruct); break; case LO_LAYER: size = sizeof(LO_LayerStruct); break; case LO_HEADING: size = sizeof(LO_HeadingStruct); break; case LO_SPAN: size = sizeof(LO_SpanStruct); break; case LO_DIV: size = sizeof(LO_DivStruct); break; case LO_SPACER: size = sizeof(LO_SpacerStruct); break; case LO_FLOAT: size = sizeof(LO_FloatStruct); break; case LO_SUPER: size = sizeof(LO_SuperStruct); break; case LO_SUB: size = sizeof(LO_SubStruct); 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_ZAP(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 { #ifdef DOM eptr->lo_any.node = NULL; #endif eptr->lo_any.width = 0; eptr->lo_any.height = 0; eptr->lo_any.x_offset = 0; eptr->lo_any.y_offset = 0; eptr->lo_any.x = 0; eptr->lo_any.y = 0; eptr->lo_any.line_height = 0; eptr->lo_any.next = NULL; eptr->lo_any.prev = NULL; #ifdef EDITOR eptr->lo_any.edit_element = NULL; eptr->lo_any.edit_offset = 0; if( EDT_IS_EDITOR(context) ) { /* if( lo_EditableElement( type ) ) Change from toshok: */ if( lo_EditableElement( type ) && type != LO_LINEFEED ) { 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); } /* Add a named anchor to the global list of named anchors. This does not associate the anchor with a layout element. lo_SetNamedAnchor should be followed by a call to lo_BindNamedAnchorToElement in order to associate the named anchor with a layout element. */ Bool lo_SetNamedAnchor(lo_DocState *state, PA_Block name) { lo_NameList *name_rec = NULL; lo_DocLists *doc_lists; /* * 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; } doc_lists = lo_GetCurrentDocLists(state); if (state->in_relayout) { /* Look for the old lo_NameList. Its mocha_object is still valid. */ lo_NameList *name_list = doc_lists->name_list; lo_NameList *prev; Bool no_match; char *s1, *s2; XP_ASSERT(name_list); /* If in relayout, this cannot be empty. */ prev = name_list; name_rec = name_list; while (name_rec != NULL) { PA_LOCK(s1, char*, name); PA_LOCK(s2, char*, name_rec->name); no_match = XP_STRCMP(s1, s2); PA_UNLOCK(name_rec->name); PA_UNLOCK(name); if (!no_match) { if (name_rec == name_list) doc_lists->name_list = name_list->next; else prev->next = name_rec->next; PA_FREE(name_rec->name); /* No need for this, use new one. */ break; } prev = name_rec; name_rec = name_rec->next; } } if (!name_rec || !state->in_relayout) { name_rec = XP_NEW(lo_NameList); if (name_rec == NULL) { state->top_state->out_of_memory = TRUE; PA_FREE(name); return FALSE; } name_rec->mocha_object = NULL; } name_rec->x = 0; name_rec->y = 0; name_rec->name = name; name_rec->element = NULL; /* Set by lo_BindNamedAnchorToElement. */ name_rec->next = doc_lists->name_list; doc_lists->name_list = name_rec; return TRUE; } /* Associate a named anchor with a layout element. The named anchor should be the first one in the list of named anchors.*/ Bool lo_BindNamedAnchorToElement(lo_DocState *state, PA_Block name, LO_Element *element) { Bool no_match; LO_Element *eptr; lo_NameList *name_rec; char *s1, *s2; lo_DocLists *doc_lists; doc_lists = lo_GetCurrentDocLists(state); 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; } /* Find the matching name_rec in the name_list */ name_rec = doc_lists->name_list; PA_LOCK(s1, char*, name); while (name_rec != NULL) { PA_LOCK(s2, char*, name_rec->name); no_match = XP_STRCMP(s1, s2); PA_UNLOCK(name_rec->name); if (!no_match) break; name_rec = name_rec->next; } PA_UNLOCK(name); if (name_rec == NULL) 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; return TRUE; } void lo_AddNameList(lo_DocState *state, lo_DocState *old_state) { /* * Now named anchors are added directly to the * top_state (or layer's) name_list, so we don't have to push * them up. We do, however, need to update their positions, * though it seems like this is done at the end of table * processing. Hence this code is no longer necessary. */ #if 0 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; } /* * Stick it into the main name list. */ nptr->next = state->name_list; state->name_list = nptr; } old_state->name_list = name_list; #endif /* 0 */ } void lo_CheckNameList(MWContext *context, lo_DocState *state, int32 min_id) { lo_NameList *nptr; lo_DocLists *doc_lists; /* * Positions of named elements are only accurate if * we are not in a nested subdoc. */ if (state->is_a_subdoc != SUBDOC_NOT) { return; } doc_lists = lo_GetCurrentDocLists(state); nptr = doc_lists->name_list; while (nptr != NULL) { /* * If the element's id is greater * than the passed minimum, then * the element almost certainly has been moved * during table processing, * correct its position. */ if ((nptr->element != NULL)&& (nptr->element->lo_any.ele_id > min_id)) { nptr->x = nptr->element->lo_any.x; nptr->y = nptr->element->lo_any.y; } /* * If we are blocked on a named element. * Check if 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); } else { PA_UNLOCK(nptr->name); } } nptr = nptr->next; } } #if DOM extern char *element_names[]; /* Wire the element to the DOM Node data for DOM-driven feedback. */ void lo_SetNodeElement(lo_DocState *state, LO_Element *element) { LO_Element *eptr; DOM_Node *node = ACTIVE_NODE(state); #ifdef DEBUG_shaver if (!node) fprintf(stderr, "NULL node\n"); else if (node->type == NODE_TYPE_DOCUMENT) fprintf(stderr, "NODE_TYPE_DOCUMENT node\n"); #endif if (!node || node->type == NODE_TYPE_DOCUMENT) return; XP_ASSERT(!ELEMENT_PRIV(node)->ele_end); #ifdef DEBUG_shaver if (ELEMENT_PRIV(node)->ele_end) { fprintf(stderr, "lo_SetNodeElement: node %s has end already!\n", node->type == NODE_TYPE_TEXT ? "text" : ((DOM_Element *)node)->tagName); } #endif eptr = ELEMENT_PRIV(node)->ele_start; if (!eptr) { /* this the first element for this node, so mark it */ ELEMENT_PRIV(node)->ele_start = element; } #ifdef DEBUG_shaver_verbose if (element->lo_any.node) fprintf(stderr, "element %p (type %d) already has node %x\n", element, element->lo_any.type, element->lo_any.node); #endif element->lo_any.node = node; } #endif void lo_AppendToLineList(MWContext *context, lo_DocState *state, LO_Element *element, int32 baseline_inc) { LO_Element *eptr; #ifdef DOM lo_SetNodeElement(state, element); #endif if (state->current_named_anchor != NULL) { if (!lo_BindNamedAnchorToElement(state, state->current_named_anchor, element)) { XP_ASSERT(FALSE); } state->current_named_anchor = NULL; } else if (state->current_anchor) { state->current_anchor->element = element; } 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 < 0) || (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 */ } #define POSITIVE_SCALE_FACTOR 1.10 /* 10% */ #define NEGATIVE_SCALE_FACTOR .90 /* 10% */ /* * Return the scaling percentage given a font scaler * */ double LO_GetScalingFactor(int32 scaler) { double scale=1.0; double mult; int32 count; if(scaler < 0) { count = -scaler; mult = NEGATIVE_SCALE_FACTOR; } else { count = scaler; mult = POSITIVE_SCALE_FACTOR; } /* use the percentage scaling factor to the power of the pref */ while(count--) scale *= mult; return scale; } /* * 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); } Bool lo_FindMochaExpr(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_FindMochaExpr(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_FindMochaExpr(tptr, &start, &end); } if (tptr != str) { new_str = lo_add_string(new_str, tptr); } if (new_str == NULL) { new_str = str; } return(new_str); } void lo_ConvertAllValues(MWContext *context, char **value_list, int32 list_len, uint newline_count) { int32 i; if (LM_CanDoJS(context) == 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; } } } } PA_Block lo_FetchParamValue(MWContext *context, PA_Tag *tag, char *param_name) { PA_Block buff; INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(context); buff = PA_FetchParamValue(tag, param_name, INTL_GetCSIWinCSID(c)); return(buff); } #include "prthread.h" #include "prmon.h" static PRMonitor * layout_lock_monitor = NULL; static PRThread * layout_lock_owner = NULL; static int layout_lock_count = 0; /* * Grab the lock around layout so that we can delete things / manipulate * layout owned data structures in a separate thread */ void LO_LockLayout() { if(!layout_lock_monitor) layout_lock_monitor = PR_NewNamedMonitor("layout-lock"); PR_EnterMonitor(layout_lock_monitor); while(TRUE) { /* no current owner or owned by this thread */ if(layout_lock_owner == NULL || layout_lock_owner == PR_CurrentThread()) { layout_lock_owner = PR_CurrentThread(); layout_lock_count++; PR_ExitMonitor(layout_lock_monitor); return; } /* owned by someone else -- wait till we can get it */ PR_Wait(layout_lock_monitor, PR_INTERVAL_NO_TIMEOUT); } } /* * Release the lock and wake up anyone who was waiting to get it if * we are the last release of the lock from this thread */ void LO_UnlockLayout() { PR_EnterMonitor(layout_lock_monitor); #ifdef DEBUG /* make sure someone doesn't try to free a lock they don't own */ XP_ASSERT(layout_lock_owner == PR_CurrentThread()); #endif layout_lock_count--; if(layout_lock_count == 0) { layout_lock_owner = NULL; PR_Notify(layout_lock_monitor); } PR_ExitMonitor(layout_lock_monitor); } /* * A (debugging) way to make sure the current thread doesn't have the * layout lock */ Bool LO_VerifyUnlockedLayout() { #ifdef DEBUG Bool bHolding = TRUE; if (layout_lock_monitor) { PR_EnterMonitor(layout_lock_monitor); bHolding = (layout_lock_owner != PR_CurrentThread()); PR_ExitMonitor(layout_lock_monitor); } return bHolding; #else return TRUE; #endif } /* * The mocha lock has been released, flush the blockage to see if * we can get our blocked entities evaluated now. */ static void lo_JSLockReleased(void * data) { MWContext * context = (MWContext *) data; lo_TopState * top_state; top_state = lo_FetchTopState(XP_DOCID(context)); if (!top_state) return; /* free the fake element we created earlier */ lo_FreeElement(context, top_state->layout_blocking_element, FALSE); /* flush the blockage */ lo_UnblockLayout(context, top_state); } /* * see if there are any &{} constructs for this tag. If so, * convert them now, if we can't convert them (since we couldn't * get the mocha lock, for example) return FALSE */ Bool lo_ConvertMochaEntities(MWContext * context, lo_DocState *state, PA_Tag * tag) { char *tptr; char *str; char *new_str; int32 len; char tchar; Bool has_mocha; char *start, *end; int32 cur_layer_id; PA_LOCK(str, char *, tag->data); len = tag->data_len; if (LM_CanDoJS(context) == FALSE) return TRUE; new_str = NULL; tptr = str; has_mocha = lo_FindMochaExpr(tptr, &start, &end); if (has_mocha == FALSE) return TRUE; /* attempt to lock JS */ if (!LM_AttemptLockJS(context, lo_JSLockReleased, context)) return FALSE; /* * Make sure that the current layer and all its ancestors have * been reflected and that the current layer has been set as the * active layer, since we want to ensure correct scoping and there's * no way to know that the reflect event has reached Mocha yet. * Note that reflecting the object multiple times is safe, since the * reflection code is idempotent. */ cur_layer_id = lo_CurrentLayerId(state); if (cur_layer_id != LO_DOCUMENT_LAYER_ID) { CL_Layer *layer, *parent; int32 child_layer_id = cur_layer_id; int32 parent_layer_id; do { layer = LO_GetLayerFromId(context, child_layer_id); parent = CL_GetLayerParent(layer); parent_layer_id = LO_GetIdFromLayer(context, parent); LM_ReflectLayer(context, cur_layer_id, parent_layer_id, NULL); child_layer_id = parent_layer_id; } while (child_layer_id != LO_DOCUMENT_LAYER_ID); } LM_SetActiveLayer(context, cur_layer_id); 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'; if (state->top_state && state->top_state->nurl) { LM_SetDecoderStream(context, NULL, state->top_state->nurl, JS_FALSE); } eval_str = LM_EvaluateAttribute(context, expr_start, tag->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_FindMochaExpr(tptr, &start, &end); } if (tptr != str) { new_str = lo_add_string(new_str, tptr); } PA_FREE(tag->data); tag->data = (PA_Block) new_str; tag->data_len = XP_STRLEN((char *) tag->data); PA_UNLOCK(tag->data); LM_UnlockJS(context); return TRUE; } /* Called from front ends as a threadsafe way to update values of text fields. */ void LO_UpdateTextData(lo_FormElementTextData * textData, const char * text) { LO_LockLayout(); if(textData->current_text) XP_FREE(textData->current_text); textData->current_text = (PA_Block) XP_STRDUP(text); LO_UnlockLayout(); } LO_TableStruct *lo_GetParentTable(MWContext *pContext, LO_Element *pElement) { LO_Element *tptr; if( !pElement ) return NULL; if( pElement->lo_any.type == LO_CELL ) { /* Supplied element is already a cell, start table search with it */ tptr = pElement; } else { if( !pContext ) return NULL; /* First find the cell that contains the given element */ tptr = (LO_Element*)lo_GetParentCell(pContext, pElement); } /* Simply search back from current cell to find table element */ while( tptr ) { if( tptr->lo_any.type == LO_TABLE ) { return (LO_TableStruct*)tptr; } tptr = tptr->lo_any.prev; } return NULL; } /* Recusively search for cell struct containing the given element * If table is found, it searches into that as well */ LO_CellStruct *lo_GetCellContainingElement( LO_Element *pElement, LO_CellStruct *pFirstCell ) { LO_CellStruct *pNestedCell = NULL; LO_CellStruct *pFirstNestedCell; LO_Element *cell_list_ptr; LO_CellStruct *pLoCell; pLoCell = pFirstCell; if( pLoCell == NULL ) return NULL; do { cell_list_ptr = pLoCell->cell_list; while( cell_list_ptr ) { if( cell_list_ptr == pElement ) { /* We found the element - return cell containing it */ return pLoCell; } if( cell_list_ptr->type == LO_TABLE ) { /* We found a nested table * Get first cell in it and search through it */ pFirstNestedCell = (LO_CellStruct*)cell_list_ptr->lo_any.next; XP_ASSERT(pFirstNestedCell->type == LO_CELL); pNestedCell = lo_GetCellContainingElement(pElement, pFirstNestedCell); if( pNestedCell ) { /* We found cell in nested table */ return pNestedCell; } } cell_list_ptr = cell_list_ptr->lo_any.next; } pLoCell = (LO_CellStruct*)pLoCell->next; } /* End when there's no more cells or we hit linefeed at end of line (top-level table)*/ while (pLoCell && pLoCell->type == LO_CELL); return NULL; } /* Search algorithm is very similar to CEditBuffer::GetTableHitRegion() * (which is similar to lo_XYtoDocumentElement except it finds only cells) * Search for the Cell that contains pElement */ LO_CellStruct *lo_GetParentCell(MWContext * pContext, LO_Element *pElement) { LO_Element *tptr; int32 doc_id; lo_TopState *top_state; lo_DocState *state; XP_Bool in_table; int32 line; LO_Element *end_ptr; LO_Element *tptrTable; int32 x, y, t_width, t_height; doc_id = XP_DOCID(pContext); top_state = lo_FetchTopState(doc_id); /* Supplied element is a cell - return it ??? */ if( pElement && pElement->type == LO_CELL ) return (LO_CellStruct*)pElement; if( pContext == NULL || pElement == NULL || top_state == NULL || top_state->doc_state == NULL ) return NULL; state = top_state->doc_state; tptrTable = NULL; in_table = FALSE; /* Find the line that contains the supplied element * This defines the top-level table */ x = pElement->lo_any.x+1; y = pElement->lo_any.y+1; line = lo_PointToLine(pContext, state, x, y); lo_GetLineEnds(pContext, state, line, &tptr, &end_ptr); while (tptr != end_ptr) { t_width = tptr->lo_any.width; t_height = tptr->lo_any.height; if (t_width <= 0) { t_width = 1; } if ((y >= tptr->lo_any.y) && (y < tptr->lo_any.y + tptr->lo_any.y_offset + t_height) && (x >= tptr->lo_any.x) && (x < (tptr->lo_any.x + tptr->lo_any.x_offset + t_width))) { /* We are only interested in finding cells */ if (tptr->type == LO_CELL) { /* Search through the cell's element list, * Including all nested tables in that cell */ LO_CellStruct * pLoCell = lo_GetCellContainingElement(pElement, (LO_CellStruct*)tptr); /* We found it - done */ if( pLoCell ) return pLoCell; } } tptr = tptr->lo_any.next; } /* Failed to find element */ return NULL; } static int lo_iColumnX = 0; static int lo_iRowY = 0; /* Find the first cell with with closest left border x-value <= than the given x * value or, if bGetColumn=FALSE, find the cell with closest top border * y-value <= than the given Y value. Also returns the pointer to the * last LO element so we can search for other cells until pEndElement is reached. */ LO_Element* lo_GetFirstCellInColumnOrRow(MWContext *pContext, LO_Element *pElement, int32 x, int32 y, XP_Bool bGetColumn, LO_Element **ppLastCellInTable) { LO_Element *pFirstCell = NULL; LO_Element *pLastCellInTable = NULL; LO_Element *tptr = NULL; int32 closest = 0; /* Save these values so future calls to lo_GetNextCellInColumnOrRow can use them */ if( bGetColumn ) lo_iColumnX = x; else lo_iRowY = y; if( pElement->type == LO_TABLE ) { /* Start at first element after the table */ tptr = pElement->lo_any.next; } else { if( pElement->type == LO_CELL ) { /* Element is a cell */ tptr = pElement; } else { /* Get parent cell of supplied element */ tptr = (LO_Element*)lo_GetParentCell(pContext, pElement); } if( !tptr ) return NULL; /* Search back until we are at first cell in table */ while ( (tptr->lo_any.prev)->type != LO_TABLE ) { tptr = tptr->lo_any.prev; } } /* Scan all cells to find first and last */ do { if( tptr->type == LO_CELL ) { if( bGetColumn && tptr->lo_any.x <= x && tptr->lo_any.x > closest ) { /* We found a cell candidate */ closest = tptr->lo_any.x; pFirstCell = tptr; } else if( !bGetColumn && tptr->lo_any.y <= y && tptr->lo_any.y > closest ) { closest = tptr->lo_any.y; pFirstCell = tptr; } pLastCellInTable = tptr; } tptr = tptr->lo_any.next; /* Table ends at linefeed (top level) or null element (nested table) */ } while (tptr && tptr->type != LO_LINEFEED ); /* Return last cell element encountered */ if( ppLastCellInTable ) *ppLastCellInTable = pLastCellInTable; return pFirstCell; } LO_Element* lo_GetNextCellInColumnOrRow(MWContext *pContext, int32 x, int32 y, LO_Element *pElement, XP_Bool bGetColumn) { LO_Element *tptr; if(!pElement) return NULL; /* Start with next element after the "current" one supplied */ tptr = pElement->lo_any.next; /* Be sure we are a cell */ /* (tptr = LO_LINEFEED if we were already on the last cell */ while ( tptr && tptr->type != LO_LINEFEED && tptr->type != LO_CELL ) tptr = tptr->lo_any.next; if(!tptr || tptr->type == LO_LINEFEED) return NULL; /* Set column X or row Y values that define a col or row respectively */ if( bGetColumn ) { if( x > 0 ) lo_iColumnX = x; } else { if( y > 0 ) lo_iRowY = y; } do { if( tptr->type == LO_CELL ) { /* Note that we return cell SPANNED by given ColumnX or RowY */ if( ( bGetColumn && tptr->lo_any.x <= lo_iColumnX && (tptr->lo_any.x + tptr->lo_any.width) > lo_iColumnX ) || ( !bGetColumn && tptr->lo_any.y <= lo_iRowY && (tptr->lo_any.y + tptr->lo_any.height) > lo_iRowY ) ) { return tptr; } } tptr = tptr->lo_any.next; /* Table ends at linefeed (top level) or null element (nested table) */ } while (tptr && tptr->type != LO_LINEFEED ); return NULL; } /* Assumptions; * Given a LO_CellStruct, the table element defines the begining of table * and the last cell is that whose "next" pointer is NULL (nested table) * or a linefeed element (top-level table) */ LO_Element *lo_GetFirstAndLastCellsInTable(MWContext *pContext, LO_Element *pElement, LO_Element **ppLastCell) { LO_Element *tptr = NULL; LO_Element *pFirstCell = NULL; LO_Element *pLastCell = NULL; LO_Element *pCurrentCell = NULL; if(pContext == NULL || pElement == NULL ) return NULL; if( pElement->type == LO_CELL ) { pCurrentCell = pElement; } else if( pElement->type == LO_TABLE ) { /* First cell SHOULD be next element, but let's be sure */ pCurrentCell = pElement->lo_table.next; while(pCurrentCell->type != LO_CELL) { pCurrentCell = pCurrentCell->lo_any.next; } } else { pCurrentCell = (LO_Element*)lo_GetParentCell(pContext, pElement); } if( pCurrentCell == NULL ) return NULL; /* Scan backwards until we hit the table element * that's before all the cells */ tptr = pCurrentCell; while (tptr && (tptr->lo_any.prev)->type != LO_TABLE ) { tptr = tptr->lo_any.prev; } /* Scan forward to find last cell -- next is null or line-feed element */ do { if( tptr->type == LO_CELL ) { if( !pFirstCell ) pFirstCell = tptr; pLastCell = tptr; } tptr = tptr->lo_any.next; } while (tptr && tptr->type != LO_LINEFEED); /* Return last cell element encountered */ if( ppLastCell ) *ppLastCell = pLastCell; return pFirstCell; } LO_Element *lo_GetFirstAndLastCellsInColumnOrRow(LO_Element *pCellElement, LO_Element **ppLastCell, XP_Bool bInColumn ) { LO_Element *tptr = NULL; LO_Element *pFirstCell = NULL; LO_Element *pLastCell = NULL; if(pCellElement == NULL || pCellElement->type != LO_CELL) return NULL; /* Scan backwards until we hit the table element * that's before all the cells */ tptr = pCellElement; while (tptr && (tptr->lo_any.prev)->type != LO_TABLE ) { tptr = tptr->lo_any.prev; } /* Scan forward to find first and last cells, * based on sharing left edge (column) or top (row) */ do { if( tptr->type == LO_CELL ) { if( (bInColumn && pCellElement->lo_any.x == tptr->lo_any.x) || (!bInColumn && pCellElement->lo_any.y == tptr->lo_any.y) ) { if( !pFirstCell ) pFirstCell = tptr; pLastCell = tptr; } } tptr = tptr->lo_any.next; } while (tptr && tptr->type != LO_LINEFEED); /* Return last cell element encountered */ if( ppLastCell ) *ppLastCell = pLastCell; return pFirstCell; } XP_Bool lo_AllCellsSelectedInColumnOrRow( LO_CellStruct *pCell, XP_Bool bInColumn ) { LO_Element *pFirstCell; LO_Element *pLastCell = NULL; LO_Element *tptr; /* Get first and last cells in row or column */ pFirstCell = lo_GetFirstAndLastCellsInColumnOrRow( (LO_Element*)pCell, &pLastCell, bInColumn ); if( !pFirstCell || !pLastCell ) return FALSE; tptr = pFirstCell; while( TRUE ) { if( tptr->type == LO_CELL ) { if( (bInColumn && pCell->x == tptr->lo_any.x) || (!bInColumn && pCell->y == tptr->lo_any.y) ) { /* If any cell is not selected, return FALSE */ if( (tptr->lo_cell.ele_attrmask & LO_ELE_SELECTED) ? FALSE : TRUE ) return FALSE; } } /* Done when we checked the last cell */ if( tptr == pLastCell ) break; tptr = tptr->lo_any.next; if( !tptr ) break; } return TRUE; } int32 lo_GetNumberOfCellsInTable(LO_TableStruct *pTable ) { int32 iCount = 0; if( pTable ) { LO_Element *tptr = (LO_Element*)pTable; while( tptr && tptr->type != LO_LINEFEED ) { if( tptr->type == LO_CELL) iCount++; tptr = tptr->lo_any.next; } } return iCount; } /* Subtract borders and inter-cell spacing to get the available table width * to use when cell width in HTML is "percent of table" */ int32 lo_CalcTableWidthForPercentMode(LO_Element *pCellElement) { LO_Element *pLastCell; LO_Element *pFirstCell; LO_Element *pCell; int32 iWidth; XP_Bool bDone; /* Never return 0 to avoid divide by zero errors */ if( !pCellElement || pCellElement->type != LO_CELL ) return -1; iWidth = 0; /* We calculate the width by simply adding up widths of all cells in the row */ pFirstCell = lo_GetFirstAndLastCellsInColumnOrRow(pCellElement, &pLastCell, FALSE ); pCell = pFirstCell; if( pCell && pLastCell ) { bDone = FALSE; while(!bDone) { if( pCell == pLastCell ) bDone = TRUE; /* Add widths of all cells in the row (must have same top) */ if( pCell->type == LO_CELL && (pCell->lo_any.y == pFirstCell->lo_any.y) ) { iWidth += pCell->lo_cell.width; } pCell = pCell->lo_any.next; } } return (iWidth > 0) ? iWidth : -1; } XP_Bool LO_IsEmptyCell(LO_CellStruct *cell) { if ( cell ) { LO_Element *element = cell->cell_list; /* Unlikely, but if nothing inside cell, we're empty! */ if(element) { while(element) { /* Any non-text element except linefeed is not "empty" * and check for text length * Length of any element is 1. Length of text = real length, * (but we don't want to consider linefeeds) */ if( element->type != LO_LINEFEED && lo_GetElementLength(element) > 0 ) { return FALSE; } element = element->lo_any.next; } } } return TRUE; } /* The LO_CellStruct.width includes border, cell padding etc and is complicated * by Column Span as well. Calculate the value to use for param that * would result in current pCellElement->lo_cell.width during the next layout */ int32 lo_GetCellTagWidth(LO_Element *pCellElement) { /* XP_ASSERT(pCellElement->type == LO_CELL); */ /* Why doesn't this compile! */ int32 iColSpan = lo_GetColSpan(pCellElement); int32 iPadding = iColSpan * 2 * (pCellElement->lo_cell.border_width + lo_GetCellPadding(pCellElement)); /* Cells spanning across a border include the inter-cell space as well */ if( iColSpan > 1 ) iPadding += (iColSpan - 1) * pCellElement->lo_cell.inter_cell_space; return pCellElement->lo_cell.width - iPadding; } /* Similar calculation for height. Unfortunately, there are differences from width. * e.g., the cell border must be subtracted from width, but not height! (a bug???) */ int32 lo_GetCellTagHeight(LO_Element *pCellElement) { /*XP_ASSERT(pCellElement->type == LO_CELL);*/ int32 iRowSpan = lo_GetRowSpan(pCellElement); int32 iPadding = iRowSpan * 2 * (/*pCellElement->lo_cell.border_width + */ lo_GetCellPadding(pCellElement)); if( iRowSpan > 1 ) iPadding += (iRowSpan - 1) * pCellElement->lo_cell.inter_cell_space; return pCellElement->lo_cell.height - iPadding; } /* Helpers to access the lo_TableCell members now accessible through the LO_CellStruct */ int32 lo_GetRowSpan(LO_Element *pCellElement) { XP_ASSERT(pCellElement->type == LO_CELL); if( pCellElement && pCellElement->lo_any.type == LO_CELL && pCellElement->lo_cell.table_cell ) { return ((lo_TableCell*)pCellElement->lo_cell.table_cell)->rowspan; } /* Should never fail, but since default value is 1, this will tell caller we failed */ return 0; } int32 lo_GetColSpan(LO_Element *pCellElement) { XP_ASSERT(pCellElement && pCellElement->type == LO_CELL); if( pCellElement && pCellElement->lo_any.type == LO_CELL && pCellElement->lo_cell.table_cell ) { return ((lo_TableCell*)pCellElement->lo_cell.table_cell)->colspan; } return 0; } /* TODO: CHANGE THIS TO GET LEFT, TOP, RIGHT, OR BOTTOM SEPARATELY? */ int32 lo_GetCellPadding(LO_Element *pCellElement) { XP_ASSERT(pCellElement && pCellElement->type == LO_CELL); if( pCellElement && pCellElement->type == LO_CELL && pCellElement->lo_cell.table ) { return ((lo_TableRec*)pCellElement->lo_cell.table)->inner_top_pad; } return 0; } LO_Element * lo_GetLastElementInList( LO_Element *eleList ) { LO_Element *retEle = NULL; if (eleList != NULL) { while (eleList->lo_any.next != NULL) eleList = eleList->lo_any.next; retEle = eleList; } return retEle; } #ifdef PROFILE #pragma profile off #endif