home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / lib / layout / layutil.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  77.0 KB  |  3,521 lines

  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19.  
  20.  
  21. #include "xp.h"
  22. #include "pa_parse.h"
  23. #include "layout.h"
  24. #include "laylayer.h"
  25. #include "laystyle.h"
  26. #include "libmocha.h"
  27. #include "stystruc.h"
  28. #include "stystack.h"
  29. #include "layers.h"
  30. #ifdef NSPR20
  31. #ifdef XP_MAC
  32. #include "prpriv.h"
  33. #else
  34. #include "private/prpriv.h"        /* for PR_NewNamedMonitor */
  35. #endif
  36. #endif /* NSPR20 */
  37. #include "intl_csi.h"
  38.  
  39. #ifdef EDITOR
  40. #include "edt.h"
  41. #endif
  42. #ifdef PROFILE
  43. #pragma profile on
  44. #endif
  45.  
  46. #ifdef XP_WIN16
  47. #define SIZE_LIMIT              32000
  48. #endif /* XP_WIN16 */
  49.  
  50. #define    FONT_FACE_INC    10
  51. #define    FONT_FACE_MAX    1000
  52.  
  53. void
  54. lo_GetElementBbox(LO_Element *element, XP_Rect *rect) 
  55. {
  56.     rect->left = element->lo_any.x + element->lo_any.x_offset;
  57.     rect->top = element->lo_any.y + element->lo_any.y_offset;
  58.     rect->right = rect->left + element->lo_any.width;
  59.     rect->bottom = rect->top + element->lo_any.height;
  60.  
  61.     switch(element->type)
  62.     {
  63.     case LO_IMAGE:
  64.     {
  65.         LO_ImageStruct *image = &element->lo_image;
  66.         rect->right  += 2 * (image->border_width + image->border_horiz_space);
  67.         rect->bottom += 2 * (image->border_width + image->border_vert_space);
  68.     }
  69.     break;
  70.  
  71.     case LO_SUBDOC:
  72.     {
  73.         LO_SubDocStruct *subdoc = &element->lo_subdoc;
  74.         rect->right  += 2 * (subdoc->border_width + subdoc->border_horiz_space);
  75.         rect->bottom += 2 * (subdoc->border_width + subdoc->border_vert_space);
  76.     }
  77.     break;
  78.  
  79.     case LO_TABLE:
  80.     {
  81.         LO_TableStruct *table = &element->lo_table;
  82.         rect->right  += 2 * (table->border_width + table->border_horiz_space);
  83.         rect->bottom += 2 * (table->border_width + table->border_vert_space);
  84.     }
  85.     break;
  86.  
  87.     case LO_CELL:
  88.     {
  89.         LO_CellStruct *cell = &element->lo_cell;
  90.         rect->right  += 2 * (cell->border_width + cell->border_horiz_space);
  91.         rect->bottom += 2 * (cell->border_width + cell->border_vert_space);
  92.     }
  93.     break;
  94.  
  95.     case LO_EMBED:
  96.     {
  97.         LO_EmbedStruct *embed = &element->lo_embed;
  98.         rect->right  += 2 * (embed->border_width + embed->border_horiz_space);
  99.         rect->bottom += 2 * (embed->border_width + embed->border_vert_space);
  100.     }
  101.     break;
  102.  
  103. #ifdef JAVA
  104.     case LO_JAVA:
  105.     {
  106.         LO_JavaAppStruct *java = &element->lo_java;
  107.         rect->right  += 2 * (java->border_width + java->border_horiz_space);
  108.         rect->bottom += 2 * (java->border_width + java->border_vert_space);
  109.     }
  110.     break;
  111. #endif
  112.  
  113.     default:
  114.         break;
  115.     }
  116. }
  117.  
  118. void
  119. lo_RefreshElement(LO_Element *element, CL_Layer *layer, Bool update_now)
  120. {
  121.     XP_Rect rect;
  122.     int32 x_offset, y_offset;
  123.     CL_Compositor *compositor = CL_GetLayerCompositor(layer);
  124.  
  125.     lo_GetElementBbox(element, &rect);
  126.     
  127.     lo_GetLayerXYShift(layer, &x_offset, &y_offset);
  128.     XP_OffsetRect(&rect, -x_offset, -y_offset);
  129.     CL_UpdateLayerRect(compositor, layer, &rect, (PRBool)update_now);
  130. }
  131.  
  132. /* Given a layout element, retrieve its anchor information. */
  133. LO_AnchorData *
  134. lo_GetElementAnchor(LO_Element *element)
  135. {
  136.     switch(element->type)
  137.     {
  138.         case LO_TEXT:
  139.             return element->lo_text.anchor_href;
  140.  
  141.         case LO_IMAGE:
  142.             return element->lo_image.anchor_href;
  143.  
  144.         case LO_TABLE:
  145.             return element->lo_table.anchor_href;
  146.  
  147.         case LO_SUBDOC:
  148.             return element->lo_subdoc.anchor_href;
  149.  
  150.         case LO_LINEFEED:
  151.             return element->lo_linefeed.anchor_href;
  152.  
  153.         default:
  154.             return NULL;
  155.     }
  156. }
  157.  
  158. /* Return an element's foreground color */
  159. void
  160. lo_GetElementFGColor(LO_Element *element, LO_Color *color)
  161. {
  162.     switch (element->type)
  163.     {
  164.     case LO_TEXT:
  165.         color->red   = element->lo_text.text_attr->fg.red;
  166.         color->green = element->lo_text.text_attr->fg.green;
  167.         color->blue  = element->lo_text.text_attr->fg.blue;
  168.         break;
  169.         
  170.     case LO_IMAGE:
  171.         color->red   = element->lo_image.text_attr->fg.red;
  172.         color->green = element->lo_image.text_attr->fg.green;
  173.         color->blue  = element->lo_image.text_attr->fg.blue;
  174.         break;
  175.  
  176.     case LO_SUBDOC:
  177.         color->red   = element->lo_subdoc.text_attr->fg.red;
  178.         color->green = element->lo_subdoc.text_attr->fg.green;
  179.         color->blue  = element->lo_subdoc.text_attr->fg.blue;
  180.         break;
  181.  
  182.     default:
  183.         XP_ASSERT(0);            /* Not implemented */
  184.         break;
  185.     }
  186. }
  187.  
  188. /* Set an element's foreground color */
  189. void
  190. lo_SetElementFGColor(LO_Element *element, LO_Color *color)
  191. {
  192.     switch (element->type)
  193.     {
  194.     case LO_TEXT:
  195.         element->lo_text.text_attr->fg.red = color->red;
  196.         element->lo_text.text_attr->fg.green = color->green;
  197.         element->lo_text.text_attr->fg.blue = color->blue;
  198.         break;
  199.         
  200.     case LO_IMAGE:
  201.         element->lo_image.text_attr->fg.red = color->red;
  202.         element->lo_image.text_attr->fg.green = color->green;
  203.         element->lo_image.text_attr->fg.blue = color->blue;
  204.         break;
  205.  
  206.     case LO_SUBDOC:
  207.         element->lo_subdoc.text_attr->fg.red = color->red;
  208.         element->lo_subdoc.text_attr->fg.green = color->green;
  209.         element->lo_subdoc.text_attr->fg.blue = color->blue;
  210.         break;
  211.  
  212.     default:
  213.         XP_ASSERT(0);            /* Not implemented */
  214.         break;
  215.     }
  216. }
  217.  
  218. void
  219. lo_ShiftElementList(LO_Element *e_list, int32 dx, int32 dy)
  220. {
  221.     LO_Element *eptr;
  222.  
  223.     if (e_list == NULL)
  224.     {
  225.         return;
  226.     }
  227.  
  228.     eptr = e_list;
  229.     while (eptr != NULL)
  230.     {
  231.         eptr->lo_any.x += dx;
  232.         eptr->lo_any.y += dy;
  233.         if (eptr->type == LO_CELL)
  234.         {
  235.             lo_ShiftCell((LO_CellStruct *)eptr, dx, dy);
  236.         }
  237.  
  238.         eptr = eptr->lo_any.next;
  239.     }
  240. }
  241.  
  242. NET_ReloadMethod
  243. LO_GetReloadMethod(MWContext *context)
  244. {
  245.     int32 doc_id;
  246.     lo_TopState *top_state;
  247.     lo_DocState *state;
  248.  
  249.     /*
  250.      * Get the unique document ID, and retreive this
  251.      * documents layout state.
  252.      */
  253.     doc_id = XP_DOCID(context);
  254.     top_state = lo_FetchTopState(doc_id);
  255.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  256.     {
  257.         return NET_NORMAL_RELOAD;
  258.     }
  259.     state = top_state->doc_state;
  260.  
  261.     return (FORCE_RELOAD_FLAG(state->top_state));
  262. }
  263.  
  264.  
  265. void
  266. LO_InvalidateFontData(MWContext *context)
  267. {
  268.     int32 i, doc_id;
  269.     lo_TopState *top_state;
  270.     lo_DocState *state;
  271.     LO_TextAttr **text_attr_hash;
  272.     LO_TextAttr *attr_ptr;
  273.  
  274.     /*
  275.      * Get the unique document ID, and retreive this
  276.      * documents layout state.
  277.      */
  278.     doc_id = XP_DOCID(context);
  279.     top_state = lo_FetchTopState(doc_id);
  280.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  281.     {
  282.         return;
  283.     }
  284.     state = top_state->doc_state;
  285.  
  286.     if (state->top_state->text_attr_hash == NULL)
  287.     {
  288.         return;
  289.     }
  290.  
  291.     XP_LOCK_BLOCK(text_attr_hash, LO_TextAttr **,
  292.         state->top_state->text_attr_hash);
  293.     for (i=0; i < FONT_HASH_SIZE; i++)
  294.     {
  295.         attr_ptr = text_attr_hash[i];
  296.         while (attr_ptr != NULL)
  297.         {
  298.             if (attr_ptr->FE_Data != NULL)
  299.             {
  300.                 FE_ReleaseTextAttrFeData(context, attr_ptr);
  301.             }
  302.             attr_ptr->FE_Data = NULL;
  303.             attr_ptr = attr_ptr->next;
  304.         }
  305.     }
  306. }
  307.  
  308.  
  309. static char *
  310. lo_find_face_in_array(char **face_list, intn len, char *face)
  311. {
  312.     intn i;
  313.     char *the_face;
  314.  
  315.     if (face == NULL)
  316.     {
  317.         return(NULL);
  318.     }
  319.  
  320.     the_face = NULL;
  321.     for (i=0; i<len; i++)
  322.     {
  323.         if ((face_list[i] != NULL)&&
  324.             (XP_STRCMP(face, face_list[i]) == 0))
  325.         {
  326.             the_face = face_list[i];
  327.             break;
  328.         }
  329.     }
  330.  
  331.     return(the_face);
  332. }
  333.  
  334.  
  335. Bool
  336. lo_EvalTrueOrFalse(char *str, Bool default_val)
  337. {
  338.     Bool ret_val;
  339.  
  340.     ret_val = default_val;
  341.  
  342.     if (str == NULL)
  343.     {
  344.         return(ret_val);
  345.     }
  346.  
  347.     if (pa_TagEqual("true", str))
  348.     {
  349.         ret_val = TRUE;
  350.     }
  351.     else if (pa_TagEqual("yes", str))
  352.     {
  353.         ret_val = TRUE;
  354.     }
  355.     else if (pa_TagEqual("no", str))
  356.     {
  357.         ret_val = FALSE;
  358.     }
  359.     else if (pa_TagEqual("false", str))
  360.     {
  361.         ret_val = FALSE;
  362.     }
  363.  
  364.     return(ret_val);
  365. }
  366.  
  367. char *
  368. lo_FetchFontFace(MWContext *context, lo_DocState *state, char *face)
  369. {
  370.     char **face_list;
  371.     char *new_face;
  372.  
  373.     /*
  374.      * If this is our first addition, allocate the array.
  375.      */
  376.     if (state->top_state->font_face_array == NULL)
  377.     {
  378.         PA_Block buff;
  379.  
  380.         buff = PA_ALLOC(FONT_FACE_INC * sizeof(char *));
  381.         if (buff == NULL)
  382.         {
  383.             state->top_state->out_of_memory = TRUE;
  384.             return(NULL);
  385.         }
  386.         state->top_state->font_face_array = buff;
  387.         state->top_state->font_face_array_size = FONT_FACE_INC;
  388.         state->top_state->font_face_array_len = 0;
  389.     }
  390.     /*
  391.      * Else if the list is full, grow the array.
  392.      */
  393.     else if (state->top_state->font_face_array_len >=
  394.             state->top_state->font_face_array_size)
  395.     {
  396.         PA_Block buff;
  397.         intn new_size;
  398.  
  399.         if (state->top_state->font_face_array_size >= FONT_FACE_MAX)
  400.         {
  401.             return(NULL);
  402.         }
  403.  
  404.         new_size = state->top_state->font_face_array_size +
  405.                 FONT_FACE_INC;
  406.         if (new_size > FONT_FACE_MAX)
  407.         {
  408.             new_size = FONT_FACE_MAX;
  409.         }
  410.         buff = XP_REALLOC_BLOCK(state->top_state->font_face_array,
  411.             (new_size * sizeof(char *)));
  412.         if (buff == NULL)
  413.         {
  414.             state->top_state->out_of_memory = TRUE;
  415.             return(NULL);
  416.         }
  417.         state->top_state->font_face_array = buff;
  418.         state->top_state->font_face_array_size = new_size;
  419.     }
  420.  
  421.     PA_LOCK(face_list, char **, state->top_state->font_face_array);
  422.     new_face = lo_find_face_in_array(face_list,
  423.         state->top_state->font_face_array_len, face);
  424.     if (new_face == NULL)
  425.     {
  426.         char *str;
  427.  
  428.         str = XP_STRDUP(face);
  429.         if (str == NULL)
  430.         {
  431.             PA_UNLOCK(state->top_state->font_face_array);
  432.             state->top_state->out_of_memory = TRUE;
  433.             return(NULL);
  434.         }
  435.         face_list[state->top_state->font_face_array_len] = str;
  436.         state->top_state->font_face_array_len++;
  437.         new_face = str;
  438.     }
  439.     PA_UNLOCK(state->top_state->font_face_array);
  440.     return(new_face);
  441. }
  442.  
  443.  
  444. Bool
  445. lo_IsValidTarget(char *target)
  446. {
  447.     if (target == NULL)
  448.     {
  449.         return(FALSE);
  450.     }
  451.     if ((XP_IS_ALPHA(target[0]) != FALSE)||
  452.         (XP_IS_DIGIT(target[0]) != FALSE)||
  453.         (target[0] == '_'))
  454.     {
  455.         return(TRUE);
  456.     }
  457.     return(FALSE);
  458. }
  459.  
  460.  
  461. int32
  462. lo_StripTextNewlines(char *text, int32 text_len)
  463. {
  464.     char *from_ptr;
  465.     char *to_ptr;
  466.     int32 len;
  467.  
  468.     if ((text == NULL)||(text_len < 1))
  469.     {
  470.         return(0);
  471.     }
  472.  
  473.     /*
  474.      * remove all non-space whitespace from the middle of the string.
  475.      */
  476.     from_ptr = text;
  477.     len = text_len;
  478.     to_ptr = text;
  479.     while (*from_ptr != '\0')
  480.     {
  481.         if (XP_IS_SPACE(*from_ptr) && (*from_ptr != ' '))
  482.         {
  483.             from_ptr++;
  484.             len--;
  485.         }
  486.         else
  487.         {
  488.             *to_ptr++ = *from_ptr++;
  489.         }
  490.     }
  491.     *to_ptr = '\0';
  492.  
  493.     return(len);
  494. }
  495.  
  496.  
  497. PA_Block
  498. lo_FEToNetLinebreaks(PA_Block text_block)
  499. {
  500.     PA_Block new_block;
  501.     char *new_text;
  502.     char *text;
  503.     char *from_ptr;
  504.     char *to_ptr;
  505.     char break_char;
  506.     int32 len, new_len;
  507.     int32 linebreak;
  508.  
  509.     if (text_block == NULL)
  510.     {
  511.         return(NULL);
  512.     }
  513.     PA_LOCK(text, char *, text_block);
  514.  
  515.     XP_BCOPY(LINEBREAK, (char *)(&break_char), 1);
  516.     len = 0;
  517.     linebreak = 0;
  518.     from_ptr = text;
  519.     while (*from_ptr != '\0')
  520.     {
  521.         if (*from_ptr == break_char)
  522.         {
  523.             if (strncmp(from_ptr, LINEBREAK, LINEBREAK_LEN) == 0)
  524.             {
  525.                 linebreak++;
  526.                 len += LINEBREAK_LEN;
  527.                 from_ptr = (char *)(from_ptr + LINEBREAK_LEN);
  528.             }
  529.             else
  530.             {
  531.                 len++;
  532.                 from_ptr++;
  533.             }
  534.         }
  535.         else
  536.         {
  537.             len++;
  538.             from_ptr++;
  539.         }
  540.     }
  541.  
  542.     new_len = len - (LINEBREAK_LEN * linebreak) + (2 * linebreak);
  543.     new_block = PA_ALLOC(new_len + 1);
  544.     if (new_block == NULL)
  545.     {
  546.         PA_UNLOCK(text_block);
  547.         return(NULL);
  548.     }
  549.     PA_LOCK(new_text, char *, new_block);
  550.     from_ptr = text;
  551.     to_ptr = new_text;
  552.     while (*from_ptr != '\0')
  553.     {
  554.         if (*from_ptr == break_char)
  555.         {
  556.             if (strncmp(from_ptr, LINEBREAK, LINEBREAK_LEN) == 0)
  557.             {
  558.                 from_ptr = (char *)(from_ptr + LINEBREAK_LEN);
  559.                 *to_ptr++ = CR;
  560.                 *to_ptr++ = LF;
  561.             }
  562.             else
  563.             {
  564.                 *to_ptr++ = *from_ptr++;
  565.             }
  566.         }
  567.         else
  568.         {
  569.             *to_ptr++ = *from_ptr++;
  570.         }
  571.     }
  572.     *to_ptr = '\0';
  573.  
  574.     new_text[new_len] = '\0';
  575.     PA_UNLOCK(new_block);
  576.     PA_UNLOCK(text_block);
  577.     return(new_block);
  578. }
  579.  
  580.  
  581. PA_Block
  582. lo_ConvertToFELinebreaks(char *text, int32 text_len, int32 *new_len_ptr)
  583. {
  584.     PA_Block new_block;
  585.     char *new_text;
  586.     char *from_ptr;
  587.     char *to_ptr;
  588.     char break_char;
  589.     int32 len, new_len;
  590.     int32 skip, linebreak;
  591.  
  592.     if (text == NULL)
  593.     {
  594.         return(NULL);
  595.     }
  596.  
  597.     break_char = '\0';
  598.     len = 0;
  599.     skip = 0;
  600.     linebreak = 0;
  601.     from_ptr = text;
  602.     to_ptr = text;
  603.     while (len < text_len)
  604.     {
  605.         if ((*from_ptr == CR)||(*from_ptr == LF))
  606.         {
  607.             if ((break_char != '\0')&&(break_char != *from_ptr))
  608.             {
  609.                 skip++;
  610.                 break_char = '\0';
  611.             }
  612.             else
  613.             {
  614.                 skip++;
  615.                 linebreak++;
  616.                 break_char = *from_ptr;
  617.             }
  618.         }
  619.         from_ptr++;
  620.         len++;
  621.     }
  622.  
  623.     new_len = len - skip + (LINEBREAK_LEN * linebreak);
  624.     new_block = PA_ALLOC(new_len + 1);
  625.     if (new_block == NULL)
  626.     {
  627.         return(NULL);
  628.     }
  629.     PA_LOCK(new_text, char *, new_block);
  630.     break_char = '\0';
  631.     len = 0;
  632.     from_ptr = text;
  633.     to_ptr = new_text;
  634.     while (len < text_len)
  635.     {
  636.         if ((*from_ptr == CR)||(*from_ptr == LF))
  637.         {
  638.             if ((break_char != '\0')&&(break_char != *from_ptr))
  639.             {
  640.                 break_char = '\0';
  641.             }
  642.             else
  643.             {
  644.                 XP_BCOPY(LINEBREAK, to_ptr, LINEBREAK_LEN);
  645.                 to_ptr = (char *)(to_ptr + LINEBREAK_LEN);
  646.                 break_char = *from_ptr;
  647.             }
  648.             from_ptr++;
  649.             len++;
  650.         }
  651.         else
  652.         {
  653.             *to_ptr++ = *from_ptr++;
  654.             len++;
  655.         }
  656.     }
  657.  
  658.     new_text[new_len] = '\0';
  659.     *new_len_ptr = new_len;
  660.     PA_UNLOCK(new_block);
  661.     return(new_block);
  662. }
  663.  
  664.  
  665. intn
  666. lo_EvalVAlignParam(char *str)
  667. {
  668.     intn alignment;
  669.  
  670.     alignment = LO_ALIGN_TOP;
  671.     if (pa_TagEqual("top", str))
  672.     {
  673.         alignment = LO_ALIGN_TOP;
  674.     }
  675.     else if (pa_TagEqual("bottom", str))
  676.     {
  677.         alignment = LO_ALIGN_BOTTOM;
  678.     }
  679.     else if ((pa_TagEqual("middle", str))||
  680.         (pa_TagEqual("center", str)))
  681.     {
  682.         alignment = LO_ALIGN_CENTER;
  683.     }
  684.     else if (pa_TagEqual("baseline", str))
  685.     {
  686.         alignment = LO_ALIGN_BASELINE;
  687.     }
  688.     return(alignment);
  689. }
  690.  
  691.  
  692. intn
  693. lo_EvalDivisionAlignParam(char *str)
  694. {
  695.     intn alignment;
  696.  
  697.     alignment = LO_ALIGN_LEFT;
  698.     if (pa_TagEqual("left", str))
  699.     {
  700.         alignment = LO_ALIGN_LEFT;
  701.     }
  702.     else if (pa_TagEqual("right", str))
  703.     {
  704.         alignment = LO_ALIGN_RIGHT;
  705.     }
  706.     else if (pa_TagEqual("center", str))
  707.     {
  708.         alignment = LO_ALIGN_CENTER;
  709.     }
  710.     else if (pa_TagEqual("justify", str))
  711.     {
  712.         alignment = LO_ALIGN_JUSTIFY;
  713.     }
  714.     return(alignment);
  715. }
  716.  
  717.  
  718. intn
  719. lo_EvalCellAlignParam(char *str)
  720. {
  721.     intn alignment;
  722.  
  723.     alignment = LO_ALIGN_LEFT;
  724.     if (pa_TagEqual("left", str))
  725.     {
  726.         alignment = LO_ALIGN_LEFT;
  727.     }
  728.     else if (pa_TagEqual("right", str))
  729.     {
  730.         alignment = LO_ALIGN_RIGHT;
  731.     }
  732.     else if ((pa_TagEqual("middle", str))||
  733.         (pa_TagEqual("center", str)))
  734.     {
  735.         alignment = LO_ALIGN_CENTER;
  736.     }
  737.     return(alignment);
  738. }
  739.  
  740. #ifdef EDITOR
  741. /* this is a replacement routine for the non-editor version and should eventually
  742.  *    replace the non-edit version.  It uses an array of values. instead of an 
  743.  *    unrolled loop.  
  744. */
  745.  
  746. /*
  747.  * Indexed by LO_ALIGN_ETC
  748. */
  749. char* lo_alignStrings[] = {
  750.     "abscenter",        /* LO_ALIGN_CENTER */
  751.     "left",                /* LO_ALIGN_LEFT */
  752.     "right",            /* LO_ALIGN_RIGHT */
  753.     "texttop",            /* LO_ALIGN_TOP */
  754.     "absbottom",        /* LO_ALIGN_BOTTOM */
  755.     "baseline",            /* LO_ALIGN_BASELINE */
  756.     "center",            /* LO_ALIGN_NCSA_CENTER */
  757.     "bottom",            /* LO_ALIGN_NCSA_BOTTOM */
  758.     "top",                /* LO_ALIGN_NCSA */
  759.     0
  760. };
  761.  
  762. intn
  763. lo_EvalAlignParam(char *str, Bool *floating)
  764. {
  765.     intn alignment;
  766.  
  767.     *floating = FALSE;
  768.     alignment = LO_ALIGN_BASELINE;
  769.     if (pa_TagEqual("middle", str))
  770.     {
  771.         alignment = LO_ALIGN_NCSA_CENTER;
  772.     }
  773.     else if (pa_TagEqual("absmiddle", str))
  774.     {
  775.         alignment = LO_ALIGN_CENTER;
  776.     }
  777.     else 
  778.     {
  779.         intn i = 0;
  780.         Bool bFound = FALSE;
  781.  
  782.         while( lo_alignStrings[i] != NULL  && bFound == FALSE )
  783.         {
  784.             if( pa_TagEqual( lo_alignStrings[i], str) )
  785.             {
  786.                 bFound = TRUE;
  787.                 alignment = i;
  788.             }
  789.             i++;
  790.         }     
  791.     }
  792.     if( alignment == LO_ALIGN_LEFT || alignment == LO_ALIGN_RIGHT)
  793.     {
  794.         *floating = TRUE;
  795.     }
  796.     return alignment;
  797.  
  798. #else
  799.  
  800. intn
  801. lo_EvalAlignParam(char *str, Bool *floating)
  802. {
  803.     intn alignment;
  804.  
  805.     *floating = FALSE;
  806.     alignment = LO_ALIGN_BASELINE;
  807.     if (pa_TagEqual("top", str))
  808.     {
  809.         alignment = LO_ALIGN_NCSA_TOP;
  810.     }
  811.     else if (pa_TagEqual("texttop", str))
  812.     {
  813.         alignment = LO_ALIGN_TOP;
  814.     }
  815.     else if (pa_TagEqual("bottom", str))
  816.     {
  817.         alignment = LO_ALIGN_NCSA_BOTTOM;
  818.     }
  819.     else if (pa_TagEqual("absbottom", str))
  820.     {
  821.         alignment = LO_ALIGN_BOTTOM;
  822.     }
  823.     else if (pa_TagEqual("baseline", str))
  824.     {
  825.         alignment = LO_ALIGN_BASELINE;
  826.     }
  827.     else if ((pa_TagEqual("middle", str))||
  828.         (pa_TagEqual("center", str)))
  829.     {
  830.         alignment = LO_ALIGN_NCSA_CENTER;
  831.     }
  832.     else if ((pa_TagEqual("absmiddle", str))||
  833.         (pa_TagEqual("abscenter", str)))
  834.     {
  835.         alignment = LO_ALIGN_CENTER;
  836.     }
  837.     else if (pa_TagEqual("left", str))
  838.     {
  839.         alignment = LO_ALIGN_LEFT;
  840.         *floating = TRUE;
  841.     }
  842.     else if (pa_TagEqual("right", str))
  843.     {
  844.         alignment = LO_ALIGN_RIGHT;
  845.         *floating = TRUE;
  846.     }
  847.     return(alignment);
  848. }
  849. #endif
  850.  
  851. /* set *alignment and *floating iff there
  852.  * is a valid stylesheet alignment property.
  853.  */
  854. MODULE_PRIVATE void
  855. lo_EvalStyleSheetAlignment(StyleStruct *style_struct, 
  856.                             intn *alignment,
  857.                                Bool *floating)
  858. {
  859.     char *valign, *halign;
  860.  
  861.     if(!style_struct)
  862.         return;
  863.  
  864.     valign = STYLESTRUCT_GetString(style_struct,VERTICAL_ALIGN_STYLE);
  865.     halign = STYLESTRUCT_GetString(style_struct,HORIZONTAL_ALIGN_STYLE);
  866.  
  867.     if(valign)
  868.     {
  869.         if (pa_TagEqual("top", valign))
  870.         {
  871.             *alignment = LO_ALIGN_NCSA_TOP;
  872.             *floating = FALSE;
  873.         }
  874.         else if (pa_TagEqual("texttop", valign) 
  875.                  || pa_TagEqual("text-top", valign))
  876.         {
  877.             *alignment = LO_ALIGN_TOP;
  878.             *floating = FALSE;
  879.         }
  880.         else if (pa_TagEqual("bottom", valign))
  881.         {
  882.             *alignment = LO_ALIGN_NCSA_BOTTOM;
  883.             *floating = FALSE;
  884.         }
  885.         else if (pa_TagEqual("absbottom", valign)
  886.                  || pa_TagEqual("text-bottom", valign))
  887.         {
  888.             *alignment = LO_ALIGN_BOTTOM;
  889.             *floating = FALSE;
  890.         }
  891.         else if (pa_TagEqual("baseline", valign))
  892.         {
  893.             *alignment = LO_ALIGN_BASELINE;
  894.             *floating = FALSE;
  895.         }
  896.         else if ((pa_TagEqual("middle", valign))||
  897.             (pa_TagEqual("center", valign)))
  898.         {
  899.             *alignment = LO_ALIGN_NCSA_CENTER;
  900.             *floating = FALSE;
  901.         }
  902.         else if ((pa_TagEqual("absmiddle", valign))||
  903.             (pa_TagEqual("abscenter", valign)))
  904.         {
  905.             *alignment = LO_ALIGN_CENTER;
  906.             *floating = FALSE;
  907.         }
  908.     }
  909.  
  910.     if(halign)
  911.     {
  912.         if (pa_TagEqual("left", halign))
  913.         {
  914.             *alignment = LO_ALIGN_LEFT;
  915.             *floating = TRUE;
  916.         }
  917.         else if (pa_TagEqual("right", halign))
  918.         {
  919.             *alignment = LO_ALIGN_RIGHT;
  920.             *floating = TRUE;
  921.         }
  922.     }
  923.  
  924.     XP_FREEIF(valign);
  925.     XP_FREEIF(halign);
  926.  
  927.     return;
  928. }
  929.  
  930. void
  931. lo_CalcAlignOffsets(lo_DocState *state, LO_TextInfo *text_info, intn alignment,
  932.     int32 width, int32 height, int16 *x_offset, int32 *y_offset,
  933.     int32 *line_inc, int32 *baseline_inc)
  934. {
  935.     *line_inc = 0;
  936.     *baseline_inc = 0;
  937.     switch (alignment)
  938.     {
  939.         case LO_ALIGN_CENTER:
  940.             *x_offset = 0;
  941.             /*
  942.              * Center after current text.
  943.              */
  944.             /*
  945.              * If the image is shorter than the height
  946.              * of this line, just increase y_offset
  947.              * to get it centered.
  948.              */
  949.             if (height < state->line_height)
  950.             {
  951.                 *y_offset += ((state->line_height - height)
  952.                         / 2);
  953.             }
  954.             /*
  955.              * Else for images taller than the line
  956.              * we will need to move all their
  957.              * baselines down, plus increase the rest
  958.              * of their lineheights to reflect the
  959.              * big centered image.
  960.              */
  961.             else
  962.             {
  963.                 *y_offset = 0;
  964.                 *baseline_inc = (height - state->line_height)
  965.                         / 2;
  966.                 *line_inc = height - state->line_height -
  967.                     *baseline_inc;
  968.             }
  969.             break;
  970.         case LO_ALIGN_NCSA_CENTER:
  971.             *x_offset = 0;
  972.             /*
  973.              * Center on baseline of current text.
  974.              */
  975.             /*
  976.              * If the image centered on the baseline will not
  977.              * extend below or above the current line,
  978.              * just increase y_offset to get it centered.
  979.              */
  980.             if (((height / 2) <= state->baseline)&&
  981.                 ((height / 2) <= (state->line_height - 
  982.                     state->baseline)))
  983.             {
  984.                 *y_offset = state->baseline -
  985.                     (height / 2);
  986.             }
  987.             /*
  988.              * Else the image extends over the bottom, but
  989.              * not off the top.
  990.              */
  991.             else if ((height / 2) <= state->baseline)
  992.             {
  993.                 *y_offset = state->baseline -
  994.                     (height / 2);
  995.                 *line_inc = *y_offset + height -
  996.                     state->line_height;
  997.             }
  998.             /*
  999.              * Else for images taller than the line
  1000.              * we will need to move all their
  1001.              * baselines down, plus and maybe increase the rest
  1002.              * of their lineheights to reflect the
  1003.              * big centered image.
  1004.              */
  1005.             else
  1006.             {
  1007.                 *y_offset = 0;
  1008.                 *baseline_inc = (height / 2) - state->baseline;
  1009.                 if ((state->baseline + (height / 2)) >
  1010.                     state->line_height)
  1011.                 {
  1012.                     *line_inc = state->baseline +
  1013.                         (height / 2) -
  1014.                         state->line_height;
  1015.                 }
  1016.             }
  1017.             break;
  1018.         case LO_ALIGN_TOP:
  1019.             *x_offset = 0;
  1020.             /*
  1021.              * Move the top of the image to the top of the
  1022.              * last text placed.  On negative offsets
  1023.              * (such as at the start of a line) just
  1024.              * start the image at the top.
  1025.              */
  1026.             *y_offset = state->baseline - text_info->ascent;
  1027.             if (*y_offset < 0)
  1028.             {
  1029.                 *y_offset = 0;
  1030.             }
  1031.  
  1032.             /*
  1033.              * Top aligned images can easily hang down and make
  1034.              * all the rest of the line taller.  Set line_inc
  1035.              * to properly increase the height of all previous
  1036.              * elements.
  1037.              */
  1038.             if ((*y_offset + height) > state->line_height)
  1039.             {
  1040.                 *line_inc = *y_offset + height -
  1041.                     state->line_height;
  1042.             }
  1043.             break;
  1044.         case LO_ALIGN_NCSA_TOP:
  1045.             *x_offset = 0;
  1046.             /*
  1047.              * Move the top of the image to the top of the
  1048.              * line.
  1049.              */
  1050.             *y_offset = 0;
  1051.  
  1052.             /*
  1053.              * Top aligned images can easily hang down and make
  1054.              * all the rest of the line taller.  Set line_inc
  1055.              * to properly increase the height of all previous
  1056.              * elements.
  1057.              */
  1058.             if ((*y_offset + height) > state->line_height)
  1059.             {
  1060.                 *line_inc = *y_offset + height -
  1061.                     state->line_height;
  1062.             }
  1063.             break;
  1064.         case LO_ALIGN_BOTTOM:
  1065.             *x_offset = 0;
  1066.             /*
  1067.              * Like centered images, images shorter than
  1068.              * the line just get move with y_offset.
  1069.              */
  1070.             if (height < state->line_height)
  1071.             {
  1072.                 *y_offset = state->line_height - height;
  1073.             }
  1074.             /*
  1075.              * Else since they are bottom aligned.
  1076.              * they move everyone's baselines and can't
  1077.              * change the height below the baseline.
  1078.              */
  1079.             else
  1080.             {
  1081.                 *y_offset = 0;
  1082.                 *baseline_inc = height - state->line_height;
  1083.             }
  1084.             break;
  1085.         case LO_ALIGN_NCSA_BOTTOM:
  1086.         case LO_ALIGN_BASELINE:
  1087.         default:
  1088.             *x_offset = 0;
  1089.             /*
  1090.              * Like centered images, images shorter than
  1091.              * the baseline just get move with y_offset.
  1092.              */
  1093.             if (height < state->baseline)
  1094.             {
  1095.                 *y_offset = state->baseline - height;
  1096.             }
  1097.             /*
  1098.              * Else since they are baseline aligned.
  1099.              * they move everyone's baselines and can't
  1100.              * change the height below the baseline.
  1101.              */
  1102.             else
  1103.             {
  1104.                 *y_offset = 0;
  1105.                 *baseline_inc = height - state->baseline;
  1106.             }
  1107.             break;
  1108.     }
  1109. }
  1110.  
  1111.  
  1112. int32
  1113. lo_ValueOrPercent(char *str, Bool *is_percent)
  1114. {
  1115.     int32 val;
  1116.     char *tptr;
  1117.  
  1118.     *is_percent = FALSE;
  1119.  
  1120.     if (str == NULL)
  1121.     {
  1122.         return(0);
  1123.     }
  1124.  
  1125.     tptr = str;
  1126.     while (*tptr != '\0')
  1127.     {
  1128.         if (*tptr == '%')
  1129.         {
  1130.             *is_percent = TRUE;
  1131.             *tptr = '\0';
  1132.             break;
  1133.         }
  1134.         tptr++;
  1135.     }
  1136.     val = XP_ATOI(str);
  1137.         if (*is_percent)
  1138.             *tptr = '%';            /* Restore original string */
  1139.     return(val);
  1140. }
  1141.  
  1142.  
  1143. void
  1144. lo_SetElementTextAttr(LO_Element *element, LO_TextAttr *attr)
  1145. {
  1146.     switch(element->type)
  1147.     {
  1148.     case LO_TEXT:
  1149.         element->lo_text.text_attr = attr;
  1150.         break;
  1151.  
  1152.     case LO_IMAGE:
  1153.         element->lo_image.text_attr = attr;
  1154.         break;
  1155.  
  1156.     case LO_SUBDOC:
  1157.         element->lo_subdoc.text_attr = attr;
  1158.         break;
  1159.  
  1160.     case LO_LINEFEED:
  1161.         element->lo_linefeed.text_attr = attr;
  1162.         break;
  1163.  
  1164.     case LO_FORM_ELE:
  1165.         element->lo_form.text_attr = attr;
  1166.         break;
  1167.  
  1168.     case LO_BULLET:
  1169.         element->lo_bullet.text_attr = attr;
  1170.         break;
  1171.  
  1172.     default:
  1173.         XP_ASSERT(0);
  1174.     }
  1175. }
  1176.  
  1177. LO_TextAttr *
  1178. lo_GetElementTextAttr(LO_Element *element)
  1179. {
  1180.     switch(element->type)
  1181.     {
  1182.     case LO_TEXT:
  1183.         return element->lo_text.text_attr;
  1184.  
  1185.     case LO_IMAGE:
  1186.         return element->lo_image.text_attr;
  1187.  
  1188.     case LO_SUBDOC:
  1189.         return element->lo_subdoc.text_attr;
  1190.  
  1191.     case LO_LINEFEED:
  1192.         return element->lo_linefeed.text_attr;
  1193.  
  1194.     case LO_FORM_ELE:
  1195.         return element->lo_form.text_attr;
  1196.  
  1197.     case LO_BULLET:
  1198.         return element->lo_bullet.text_attr;
  1199.  
  1200.     default:
  1201.         return NULL;
  1202.     }
  1203. }
  1204.  
  1205. void
  1206. lo_CopyTextAttr(LO_TextAttr *old_attr, LO_TextAttr *new_attr)
  1207. {
  1208.     if ((old_attr == NULL)||(new_attr == NULL))
  1209.     {
  1210.         return;
  1211.     }
  1212.  
  1213.     new_attr->size = old_attr->size;
  1214.     new_attr->fontmask = old_attr->fontmask;
  1215.  
  1216.     new_attr->fg.red = old_attr->fg.red;
  1217.     new_attr->fg.green = old_attr->fg.green;
  1218.     new_attr->fg.blue = old_attr->fg.blue;
  1219.  
  1220.     new_attr->bg.red = old_attr->bg.red;
  1221.     new_attr->bg.green = old_attr->bg.green;
  1222.     new_attr->bg.blue = old_attr->bg.blue;
  1223.  
  1224.     new_attr->no_background = old_attr->no_background;
  1225.     new_attr->attrmask = old_attr->attrmask;
  1226.     new_attr->font_face = old_attr->font_face;
  1227.     /*
  1228.      * FE_Data is not copied, it belongs to the FE,
  1229.      * And will need to be generated by the FE the first time this
  1230.      * new TextAttr is used.
  1231.      */
  1232.     new_attr->FE_Data = NULL;
  1233.  
  1234.     new_attr->charset = old_attr->charset;
  1235.     new_attr->point_size = old_attr->point_size;
  1236.     new_attr->font_weight = old_attr->font_weight;
  1237. }
  1238.  
  1239.  
  1240. LO_TextAttr *
  1241. lo_FetchTextAttr(lo_DocState *state, LO_TextAttr *old_attr)
  1242. {
  1243.     LO_TextAttr **text_attr_hash;
  1244.     LO_TextAttr *attr_ptr;
  1245.     int32 hash_index;
  1246.  
  1247.     if (old_attr == NULL)
  1248.     {
  1249.         return(NULL);
  1250.     }
  1251.  
  1252.     hash_index = (old_attr->fontmask & old_attr->attrmask) % FONT_HASH_SIZE;
  1253.  
  1254.     XP_LOCK_BLOCK(text_attr_hash, LO_TextAttr **,
  1255.         state->top_state->text_attr_hash);
  1256.     attr_ptr = text_attr_hash[hash_index];
  1257.  
  1258.     while (attr_ptr != NULL)
  1259.     {
  1260.         if ((attr_ptr->size == old_attr->size)&&
  1261.             (attr_ptr->fontmask == old_attr->fontmask)&&
  1262.             (attr_ptr->attrmask == old_attr->attrmask)&&
  1263.             (attr_ptr->fg.red == old_attr->fg.red)&&
  1264.             (attr_ptr->fg.green == old_attr->fg.green)&&
  1265.             (attr_ptr->fg.blue == old_attr->fg.blue)&&
  1266.             (attr_ptr->bg.red == old_attr->bg.red)&&
  1267.             (attr_ptr->bg.green == old_attr->bg.green)&&
  1268.             (attr_ptr->bg.blue == old_attr->bg.blue)&&
  1269.             (attr_ptr->font_face == old_attr->font_face)&&
  1270.             (attr_ptr->charset == old_attr->charset)&&
  1271.             (attr_ptr->point_size == old_attr->point_size)&&
  1272.             (attr_ptr->font_weight == old_attr->font_weight))
  1273.         {
  1274.             break;
  1275.         }
  1276.         attr_ptr = attr_ptr->next;
  1277.     }
  1278.     if (attr_ptr == NULL)
  1279.     {
  1280.         LO_TextAttr *new_attr;
  1281.  
  1282.         new_attr = XP_NEW(LO_TextAttr);
  1283.         if (new_attr != NULL)
  1284.         {
  1285.             lo_CopyTextAttr(old_attr, new_attr);
  1286.             new_attr->next = text_attr_hash[hash_index];
  1287.             text_attr_hash[hash_index] = new_attr;
  1288.         }
  1289.         else
  1290.         {
  1291.             state->top_state->out_of_memory = TRUE;
  1292.         }
  1293.         attr_ptr = new_attr;
  1294.     }
  1295.  
  1296.     XP_UNLOCK_BLOCK(state->top_state->text_attr_hash);
  1297.     return(attr_ptr);
  1298. }
  1299.  
  1300.  
  1301. void
  1302. lo_AddMarginStack(lo_DocState *state, int32 x, int32 y,
  1303.         int32 width, int32 height,
  1304.         int32 border_width,
  1305.         int32 border_vert_space, int32 border_horiz_space,
  1306.         intn alignment)
  1307. {
  1308.     lo_MarginStack *mptr;
  1309.  
  1310.     mptr = XP_NEW(lo_MarginStack);
  1311.     if (mptr == NULL)
  1312.     {
  1313.         state->top_state->out_of_memory = TRUE;
  1314.         return;
  1315.     }
  1316.  
  1317.     mptr->y_min = y;
  1318.     if (y >= 0)
  1319.     {
  1320.         mptr->y_max = y + height + (2 * border_width) +
  1321.             (2 * border_vert_space);
  1322.     }
  1323.     else
  1324.     {
  1325.         mptr->y_max = height + (2 * border_width) +
  1326.             (2 * border_vert_space);
  1327.     }
  1328.  
  1329.     if (alignment == LO_ALIGN_RIGHT)
  1330.     {
  1331.         if (state->right_margin_stack == NULL)
  1332.         {
  1333.             mptr->margin = state->right_margin - width -
  1334.                 (2 * border_width) - (2 * border_horiz_space);
  1335.         }
  1336.         else
  1337.         {
  1338.             mptr->margin = state->right_margin_stack->margin -
  1339.                 width - (2 * border_width) -
  1340.                 (2 * border_horiz_space);
  1341.         }
  1342.         mptr->next = state->right_margin_stack;
  1343.         state->right_margin_stack = mptr;
  1344.     }
  1345.     else
  1346.     {
  1347.         mptr->margin = state->left_margin + width +
  1348.             (2 * border_width) + (2 * border_horiz_space);
  1349.         mptr->next = state->left_margin_stack;
  1350.         state->left_margin_stack = mptr;
  1351.     }
  1352. }
  1353.  
  1354.  
  1355. /*
  1356.  * When clipping lines we have essentially scrolled the document up.
  1357.  * Move the margin blocks up too.
  1358.  */
  1359. void
  1360. lo_ShiftMarginsUp(MWContext *context, lo_DocState *state, int32 dy)
  1361. {
  1362.     lo_MarginStack *mptr;
  1363.     int32 y;
  1364.  
  1365.     mptr = state->left_margin_stack;
  1366.     while (mptr != NULL)
  1367.     {
  1368.         y = mptr->y_min - dy;
  1369.         if (y < 0)
  1370.         {
  1371.             y = 0;
  1372.         }
  1373.         mptr->y_min = y;
  1374.  
  1375.         y = mptr->y_max - dy;
  1376.         if (y < 0)
  1377.         {
  1378.             y = 0;
  1379.         }
  1380.         mptr->y_max = y;
  1381.         mptr = mptr->next;
  1382.     }
  1383.  
  1384.     mptr = state->right_margin_stack;
  1385.     while (mptr != NULL)
  1386.     {
  1387.         y = mptr->y_min - dy;
  1388.         if (y < 0)
  1389.         {
  1390.             y = 0;
  1391.         }
  1392.         mptr->y_min = y;
  1393.  
  1394.         y = mptr->y_max - dy;
  1395.         if (y < 0)
  1396.         {
  1397.             y = 0;
  1398.         }
  1399.         mptr->y_max = y;
  1400.         mptr = mptr->next;
  1401.     }
  1402. }
  1403.  
  1404.  
  1405. void
  1406. lo_ClearToLeftMargin(MWContext *context, lo_DocState *state)
  1407. {
  1408.     lo_MarginStack *mptr;
  1409.     int32 y;
  1410.  
  1411.     mptr = state->left_margin_stack;
  1412.     if (mptr == NULL)
  1413.     {
  1414.         return;
  1415.     }
  1416.     y = state->y;
  1417.  
  1418.     if (mptr->y_max > y)
  1419.     {
  1420.         y = mptr->y_max;
  1421.     }
  1422.     while (mptr->next != NULL)
  1423.     {
  1424.         lo_MarginStack *margin;
  1425.  
  1426.         if (mptr->y_max > y)
  1427.         {
  1428.             y = mptr->y_max;
  1429.         }
  1430.         margin = mptr;
  1431.         mptr = mptr->next;
  1432.         XP_DELETE(margin);
  1433.     }
  1434.     XP_DELETE(mptr);
  1435.     state->y = y;
  1436.     state->left_margin_stack = NULL;
  1437.     state->left_margin = state->win_left;
  1438.     state->x = state->left_margin;
  1439.  
  1440.     /*
  1441.      * Find where we hit the right margin, popping old
  1442.      * obsolete elements as we go.
  1443.      */
  1444.     mptr = state->right_margin_stack;
  1445.     while ((mptr != NULL)&&(state->y > mptr->y_max))
  1446.     {
  1447.         lo_MarginStack *margin;
  1448.  
  1449.         margin = mptr;
  1450.         mptr = mptr->next;
  1451.         XP_DELETE(margin);
  1452.     }
  1453.     if (mptr != NULL)
  1454.     {
  1455.         state->right_margin_stack = mptr;
  1456.         state->right_margin = mptr->margin;
  1457.     }
  1458.     else
  1459.     {
  1460.         state->right_margin_stack = NULL;
  1461.         state->right_margin = (state->list_stack != NULL)
  1462.                             ? state->list_stack->old_right_margin
  1463.                             : 0;
  1464.     }
  1465. }
  1466.  
  1467.  
  1468. void
  1469. lo_ClearToRightMargin(MWContext *context, lo_DocState *state)
  1470. {
  1471.     lo_MarginStack *mptr;
  1472.     int32 y;
  1473.  
  1474.     mptr = state->right_margin_stack;
  1475.     if (mptr == NULL)
  1476.     {
  1477.         return;
  1478.     }
  1479.     y = state->y;
  1480.  
  1481.     if (mptr->y_max > y)
  1482.     {
  1483.         y = mptr->y_max;
  1484.     }
  1485.     while (mptr->next != NULL)
  1486.     {
  1487.         lo_MarginStack *margin;
  1488.  
  1489.         if (mptr->y_max > y)
  1490.         {
  1491.             y = mptr->y_max;
  1492.         }
  1493.         margin = mptr;
  1494.         mptr = mptr->next;
  1495.         XP_DELETE(margin);
  1496.     }
  1497.     XP_DELETE(mptr);
  1498.     state->y = y;
  1499.     state->right_margin_stack = NULL;
  1500.     state->right_margin = state->win_width - state->win_right;
  1501.  
  1502.     /*
  1503.      * Find where we hit the left margin, popping old
  1504.      * obsolete elements as we go.
  1505.      */
  1506.     mptr = state->left_margin_stack;
  1507.     while ((mptr != NULL)&&(state->y > mptr->y_max))
  1508.     {
  1509.         lo_MarginStack *margin;
  1510.  
  1511.         margin = mptr;
  1512.         mptr = mptr->next;
  1513.         XP_DELETE(margin);
  1514.     }
  1515.     if (mptr != NULL)
  1516.     {
  1517.         state->left_margin_stack = mptr;
  1518.         state->left_margin = mptr->margin;
  1519.     }
  1520.     else
  1521.     {
  1522.         state->left_margin_stack = NULL;
  1523.         state->left_margin = (state->list_stack != NULL)
  1524.                            ? state->list_stack->old_left_margin
  1525.                            : 0;
  1526.     }
  1527. }
  1528.  
  1529.  
  1530. void
  1531. lo_ClearToBothMargins(MWContext *context, lo_DocState *state)
  1532. {
  1533.     lo_MarginStack *mptr;
  1534.     int32 y;
  1535.  
  1536.     y = state->y;
  1537.  
  1538.     mptr = state->right_margin_stack;
  1539.     if (mptr != NULL)
  1540.     {
  1541.         if (mptr->y_max > y)
  1542.         {
  1543.             y = mptr->y_max;
  1544.         }
  1545.         while (mptr->next != NULL)
  1546.         {
  1547.             lo_MarginStack *margin;
  1548.  
  1549.             if (mptr->y_max > y)
  1550.             {
  1551.                 y = mptr->y_max;
  1552.             }
  1553.             margin = mptr;
  1554.             mptr = mptr->next;
  1555.             XP_DELETE(margin);
  1556.         }
  1557.         XP_DELETE(mptr);
  1558.         state->y = y;
  1559.         state->right_margin_stack = NULL;
  1560.  
  1561.         state->right_margin = state->win_width - state->win_right;
  1562.     }
  1563.  
  1564.     mptr = state->left_margin_stack;
  1565.     if (mptr != NULL)
  1566.     {
  1567.         if (mptr->y_max > y)
  1568.         {
  1569.             y = mptr->y_max;
  1570.         }
  1571.         while (mptr->next != NULL)
  1572.         {
  1573.             lo_MarginStack *margin;
  1574.  
  1575.             if (mptr->y_max > y)
  1576.             {
  1577.                 y = mptr->y_max;
  1578.             }
  1579.             margin = mptr;
  1580.             mptr = mptr->next;
  1581.             XP_DELETE(margin);
  1582.         }
  1583.         XP_DELETE(mptr);
  1584.         if (y > state->y)
  1585.         {
  1586.             state->y = y;
  1587.         }
  1588.         state->left_margin_stack = NULL;
  1589.  
  1590.         state->left_margin = state->win_left;
  1591.         state->x = state->left_margin;
  1592.     }
  1593. }
  1594.  
  1595.  
  1596. void
  1597. lo_FindLineMargins(MWContext *context, lo_DocState *state, Bool updateFE)
  1598. {
  1599.     lo_MarginStack *mptr;
  1600.     LO_Element *eptr;
  1601.  
  1602.     eptr = state->float_list;
  1603.     /*
  1604.     while ((eptr != NULL)&&(eptr->lo_any.y < 0))
  1605.     */
  1606.     while (eptr != NULL)
  1607.     {
  1608.         LO_Element *next_eptr = NULL;
  1609.  
  1610.         if (eptr->lo_any.y < 0) 
  1611.         {
  1612.             int32 expose_y;
  1613.             int32 expose_height;
  1614.             /* LO_Element *next_eptr; */
  1615.  
  1616.             next_eptr = NULL;
  1617.             /*
  1618.              * Float the y to our current position.
  1619.              */
  1620.             eptr->lo_any.y = state->y;
  1621.  
  1622.             /*
  1623.              * These get coppied into special variables because they
  1624.              * may get changed by a table in the floating list.
  1625.              */
  1626.             expose_y = eptr->lo_any.y;
  1627.             expose_height = eptr->lo_any.height;
  1628.  
  1629.             /*
  1630.              * Special case:  If we have just placed a TABLE element,
  1631.              * we need to properly offset all the CELLs inside it.
  1632.              */
  1633.             if (eptr->type == LO_TABLE)
  1634.             {
  1635.                 int32 y2;
  1636.                 int32 change_y;
  1637.                 LO_Element *tptr;
  1638.  
  1639.                 /*
  1640.                  * We need to know the bottom and top extents of
  1641.                  * the table to know big an area to refresh.
  1642.                  */
  1643.                 y2 = expose_y + eptr->lo_any.y_offset + expose_height;
  1644.  
  1645.                 /*
  1646.                  * This is how far to move the cells based on
  1647.                  * where the table thought it was placed.
  1648.                  */
  1649.                 change_y = state->y - eptr->lo_table.expected_y;
  1650.  
  1651.                 /*
  1652.                  * set tptr to the start of the cell list.
  1653.                  */
  1654.                 tptr = eptr->lo_any.next;
  1655.                 while ((tptr != NULL)&&(tptr->type == LO_CELL))
  1656.                 {
  1657.                     /*
  1658.                      * Only do this work if the table has moved.
  1659.                      */
  1660.                     if (change_y != 0)
  1661.                     {
  1662.                         tptr->lo_any.y += change_y;
  1663.                         lo_ShiftCell((LO_CellStruct *)tptr,
  1664.                             0, change_y);
  1665.                     }
  1666.  
  1667.                     /*
  1668.                      * Find upper and lower bounds of the
  1669.                      * cells.
  1670.                      */
  1671.                     if (tptr->lo_any.y < expose_y)
  1672.                     {
  1673.                         expose_y = tptr->lo_any.y;
  1674.                     }
  1675.                     if ((tptr->lo_any.y + tptr->lo_any.y_offset +
  1676.                         tptr->lo_any.height) > y2)
  1677.                     {
  1678.                         y2 = tptr->lo_any.y +
  1679.                             tptr->lo_any.y_offset +
  1680.                             tptr->lo_any.height;
  1681.                     }
  1682.  
  1683.                     tptr = tptr->lo_any.next;
  1684.                 }
  1685.  
  1686.                 /*
  1687.                  * Whatever is after all the table cells will
  1688.                  * really be the next eptr.
  1689.                  */
  1690.                 if (tptr != NULL)
  1691.                 {
  1692.                     next_eptr = tptr;
  1693.                 }
  1694.  
  1695.                 /*
  1696.                  * Adjust exposed area height for lower bound.
  1697.                  * Upper bound already possibly moved.
  1698.                  */
  1699.                 if ((y2 - expose_y - eptr->lo_any.y_offset) >
  1700.                     expose_height)
  1701.                 {
  1702.                     expose_height = y2 - expose_y -
  1703.                         eptr->lo_any.y_offset;
  1704.                 }
  1705.             }
  1706.  
  1707.             if (updateFE) 
  1708.             {
  1709.                 /*
  1710.                  * We may have been blocking display on this floating image.
  1711.                  * If so deal with it here.
  1712.                  */
  1713.                 if ((state->display_blocked != FALSE)&&
  1714.                     (state->is_a_subdoc == SUBDOC_NOT)&&
  1715.                     (state->display_blocking_element_y == 0)&&
  1716.                     (state->display_blocking_element_id != -1)&&
  1717.                     (eptr->lo_any.ele_id >=
  1718.                     state->display_blocking_element_id))
  1719.                 {
  1720.                     state->display_blocking_element_y =
  1721.                         state->y;
  1722.                     /*
  1723.                      * Special case, if the blocking element
  1724.                      * is on the first line, no blocking
  1725.                      * at all needs to happen.
  1726.                      */
  1727.                     if (state->y == state->win_top)
  1728.                     {
  1729.                         state->display_blocked = FALSE;
  1730.                         FE_SetDocPosition(context,
  1731.                             FE_VIEW, 0, state->base_y);
  1732.  
  1733.                         if (context->compositor)
  1734.                         {
  1735.                             XP_Rect rect;
  1736.                     
  1737.                             rect.left = 0;
  1738.                             rect.top = 0;
  1739.                             rect.right = state->win_width;
  1740.                             rect.bottom = state->win_height;
  1741.                             CL_UpdateDocumentRect(context->compositor,
  1742.                                                   &rect, (PRBool)FALSE);
  1743.                         }
  1744.                     }
  1745.                 }
  1746.  
  1747.             }    /* end if (updateFE) */
  1748.  
  1749.  
  1750.  
  1751.             if (context->compositor)
  1752.             {
  1753.                 XP_Rect rect;
  1754.            
  1755.                 rect.left = eptr->lo_any.x + eptr->lo_any.x_offset;
  1756.                 rect.top = expose_y + eptr->lo_any.y_offset;
  1757.                 rect.right = rect.left + eptr->lo_any.width;
  1758.                 rect.bottom = rect.top + expose_height;
  1759.                 CL_UpdateDocumentRect(context->compositor,
  1760.                                           &rect, (PRBool)FALSE);
  1761.  
  1762.             }            
  1763.  
  1764.             /*
  1765.              * In case these floating elements are the only thing in the
  1766.              * doc (or subdoc) we need to set the state min and max widths
  1767.              * when we place them.
  1768.              */
  1769.         {
  1770.             int32 width;
  1771.             XP_Rect rect;
  1772.  
  1773.             rect.left = 0;
  1774.             rect.right = 0;
  1775.             lo_GetElementBbox(eptr, &rect);
  1776.             width = rect.right - rect.left;
  1777.             if (width > 0)
  1778.             {
  1779.                 if (width > state->min_width)
  1780.                 {
  1781.                     state->min_width = width;
  1782.                 }
  1783.  
  1784.                 /*
  1785.                  * Only do this if not in a table
  1786.                  */
  1787.                 if (state->is_a_subdoc == SUBDOC_NOT)
  1788.                 {
  1789.                     width += rect.left;
  1790.                 }
  1791.                 if (width > state->max_width)
  1792.                 {
  1793.                     state->max_width = width;
  1794.                 }
  1795.             }
  1796.         
  1797.         }
  1798.             /*
  1799.             eptr = eptr->lo_any.next;
  1800.             */
  1801.             /*
  1802.              * If we are skipping a table's cells, set eptr
  1803.              * properly now.
  1804.              */
  1805.             /*
  1806.             if (next_eptr != NULL)
  1807.             {
  1808.                 eptr = next_eptr;
  1809.             }
  1810.             */
  1811.         }  /* End if (eptr->lo_any.y < 0) */
  1812.  
  1813.         eptr = eptr->lo_any.next;
  1814.  
  1815.         /*
  1816.          * If we are skipping a table's cells, set eptr
  1817.          * properly now.
  1818.          */
  1819.         if (next_eptr != NULL)
  1820.         {
  1821.             eptr = next_eptr;
  1822.         }
  1823.     }  /* End while */
  1824.     
  1825.     /*
  1826.      * Find floating elements just added to the left margin stack
  1827.      * from the last line, and set their absolute
  1828.      * coordinates.
  1829.      */
  1830.     mptr = state->left_margin_stack;
  1831.     while ((mptr != NULL)&&(mptr->y_min < 0))
  1832.     {
  1833.         mptr->y_min = state->y;
  1834.         mptr->y_max = mptr->y_min + mptr->y_max;
  1835.         mptr = mptr->next;
  1836.     }
  1837.  
  1838.     /*
  1839.      * Find where we hit the left margin, popping old
  1840.      * obsolete elements as we go.
  1841.      */
  1842.     mptr = state->left_margin_stack;
  1843.     while ((mptr != NULL)&&(state->y > mptr->y_max))
  1844.     {
  1845.         lo_MarginStack *margin;
  1846.  
  1847.         margin = mptr;
  1848.         mptr = mptr->next;
  1849.         XP_DELETE(margin);
  1850.     }
  1851.     if (mptr != NULL)
  1852.     {
  1853.         state->left_margin_stack = mptr;
  1854.         state->left_margin = mptr->margin;
  1855.         /*
  1856.          * For a list wrapped around a floating image we may
  1857.          * need to be indented from the image edge.
  1858.          */
  1859.         if (state->list_stack->old_left_margin > mptr->margin)
  1860.         {
  1861.             state->left_margin = state->list_stack->old_left_margin;
  1862.         }
  1863.     }
  1864.     else
  1865.     {
  1866.         state->left_margin_stack = NULL;
  1867.         state->left_margin = (state->list_stack != NULL)
  1868.                            ? state->list_stack->old_left_margin
  1869.                            : 0;
  1870.     }
  1871.  
  1872.     /*
  1873.      * Find floating elements just added to the right margin stack
  1874.      * from the last line, and set their absolute
  1875.      * coordinates.
  1876.      */
  1877.     mptr = state->right_margin_stack;
  1878.     while ((mptr != NULL)&&(mptr->y_min < 0))
  1879.     {
  1880.         mptr->y_min = state->y;
  1881.         mptr->y_max = mptr->y_min + mptr->y_max;
  1882.         mptr = mptr->next;
  1883.     }
  1884.  
  1885.     /*
  1886.      * Find where we hit the right margin, popping old
  1887.      * obsolete elements as we go.
  1888.      */
  1889.     mptr = state->right_margin_stack;
  1890.     while ((mptr != NULL)&&(state->y > mptr->y_max))
  1891.     {
  1892.         lo_MarginStack *margin;
  1893.  
  1894.         margin = mptr;
  1895.         mptr = mptr->next;
  1896.         XP_DELETE(margin);
  1897.     }
  1898.     if (mptr != NULL)
  1899.     {
  1900.         state->right_margin_stack = mptr;
  1901.         state->right_margin = mptr->margin;
  1902.     }
  1903.     else
  1904.     {
  1905.         state->right_margin_stack = NULL;
  1906.         state->right_margin = (state->list_stack != NULL)
  1907.                             ? state->list_stack->old_right_margin
  1908.                             : 0;
  1909.     }
  1910. }
  1911.  
  1912. void
  1913. lo_RecycleElements(MWContext *context, lo_DocState *state, LO_Element *elements)
  1914. {
  1915.     LO_Element *eptr;
  1916.  
  1917.     if (elements == NULL)
  1918.     {
  1919.         return;
  1920.     }
  1921.  
  1922.     eptr = elements;
  1923.     while(eptr->lo_any.next != NULL)
  1924.     {
  1925.         lo_ScrapeElement(context, eptr);
  1926.         eptr = eptr->lo_any.next;
  1927.     }
  1928.     lo_ScrapeElement(context, eptr);
  1929. #ifdef MEMORY_ARENAS
  1930.     if ( EDT_IS_EDITOR(context) ) {
  1931.         eptr->lo_any.next = state->top_state->recycle_list;
  1932.         state->top_state->recycle_list = elements;
  1933.     }
  1934. #else
  1935.     eptr->lo_any.next = state->top_state->recycle_list;
  1936.     state->top_state->recycle_list = elements;
  1937. #endif /* MEMORY_ARENAS */
  1938. }
  1939.  
  1940.  
  1941. LO_Element *
  1942. lo_NewElement(MWContext *context, lo_DocState *state, intn type, 
  1943.         ED_Element *edit_element, intn edit_offset)
  1944. {
  1945.     LO_Element *eptr;
  1946.  
  1947. #ifdef MEMORY_ARENAS
  1948.     int32 size;
  1949.  
  1950.     switch(type)
  1951.     {
  1952.         case LO_TEXT:
  1953.             size = sizeof(LO_TextStruct);
  1954.             break;
  1955.         case LO_LINEFEED:
  1956.             size = sizeof(LO_LinefeedStruct);
  1957.             break;
  1958.         case LO_HRULE:
  1959.             size = sizeof(LO_HorizRuleStruct);
  1960.             break;
  1961.         case LO_IMAGE:
  1962.             size = sizeof(LO_ImageStruct);
  1963.             break;
  1964.         case LO_BULLET:
  1965.             size = sizeof(LO_BulletStruct);
  1966.             break;
  1967.         case LO_FORM_ELE:
  1968.             size = sizeof(LO_FormElementStruct);
  1969.             break;
  1970.         case LO_SUBDOC:
  1971.             size = sizeof(LO_SubDocStruct);
  1972.             break;
  1973.         case LO_TABLE:
  1974.             size = sizeof(LO_TableStruct);
  1975.             break;
  1976.         case LO_CELL:
  1977.             size = sizeof(LO_CellStruct);
  1978.             break;
  1979.         case LO_EMBED:
  1980.             size = sizeof(LO_EmbedStruct);
  1981.             break;
  1982. #ifdef JAVA
  1983.         case LO_JAVA:
  1984.             size = sizeof(LO_JavaAppStruct);
  1985.             break;
  1986. #endif
  1987.         case LO_EDGE:
  1988.             size = sizeof(LO_EdgeStruct);
  1989.             break;
  1990.         case LO_OBJECT:
  1991.             size = sizeof(LO_ObjectStruct);
  1992.             break;
  1993.         case LO_PARAGRAPH:
  1994.             size = sizeof(LO_ParagraphStruct);
  1995.             break;
  1996.         case LO_CENTER:
  1997.             size = sizeof(LO_CenterStruct);
  1998.             break;
  1999.         case LO_MULTICOLUMN:
  2000.             size = sizeof(LO_MulticolumnStruct);
  2001.             break;
  2002.         case LO_TEXTBLOCK:
  2003.             size = sizeof(LO_TextBlock);
  2004.             break;
  2005.         case LO_LIST:
  2006.             size = sizeof(LO_ListStruct);
  2007.             break;
  2008.         case LO_DESCTITLE:
  2009.             size = sizeof(LO_DescTitleStruct);
  2010.             break;
  2011.         case LO_DESCTEXT:
  2012.             size = sizeof(LO_DescTextStruct);
  2013.             break;
  2014.         case LO_BLOCKQUOTE:
  2015.             size = sizeof(LO_BlockQuoteStruct);
  2016.             break;
  2017.         case LO_LAYER:
  2018.             size = sizeof(LO_LayerStruct);
  2019.             break;
  2020.         case LO_SPACER:
  2021.             size = sizeof(LO_SpacerStruct);
  2022.             break;
  2023.         default:
  2024.             size = sizeof(LO_Any);
  2025.             break;
  2026.     }
  2027.     if ( ! EDT_IS_EDITOR(context) ) {
  2028.         eptr = (LO_Element *)lo_MemoryArenaAllocate(state->top_state, size);
  2029.     }
  2030.     else {
  2031.         if (state->top_state->recycle_list != NULL)
  2032.         {
  2033.             eptr = state->top_state->recycle_list;
  2034.             state->top_state->recycle_list = eptr->lo_any.next;
  2035.         }
  2036.         else
  2037.         {
  2038.             eptr = XP_NEW(LO_Element);
  2039.         }
  2040.     }
  2041. #else
  2042.  
  2043.     /* sorry about all the asserts, in my debug version somehow state
  2044.      * becomes nil after a malloc failure. */
  2045. #ifdef DEBUG
  2046.     assert (state);
  2047. #endif
  2048.     
  2049.     if (state->top_state->recycle_list != NULL)
  2050.     {
  2051.         eptr = state->top_state->recycle_list;
  2052.         state->top_state->recycle_list = eptr->lo_any.next;
  2053.     }
  2054.     else
  2055.     {
  2056.         eptr = XP_NEW(LO_Element);
  2057. #ifdef DEBUG
  2058.         assert (state);
  2059. #endif
  2060.     } 
  2061. #endif /* MEMORY_ARENAS */
  2062.     
  2063.     if (eptr == NULL)
  2064.     {
  2065. #ifdef DEBUG
  2066.         assert (state);
  2067. #endif
  2068.         state->top_state->out_of_memory = TRUE;
  2069.     }
  2070.     else
  2071.     {
  2072.         eptr->lo_any.next = NULL;
  2073.         eptr->lo_any.prev = NULL;
  2074. #ifdef EDITOR
  2075.         eptr->lo_any.edit_element = NULL;
  2076.         eptr->lo_any.edit_offset = 0;
  2077.  
  2078.         if( EDT_IS_EDITOR(context) )
  2079.         {
  2080.             if( lo_EditableElement( type ) )
  2081.             {
  2082.                 if( edit_element == 0 ){
  2083.                     edit_element = state->edit_current_element;
  2084.                     edit_offset = state->edit_current_offset;
  2085.                 }
  2086.                 EDT_SetLayoutElement( edit_element, 
  2087.                                         edit_offset,
  2088.                                         type,
  2089.                                         eptr );
  2090.             } else if( edit_element && (type == LO_TABLE || type == LO_CELL) )
  2091.             {
  2092.                 /* _TABLE_EXPERIMENT_  SAVE POINTER TO EDIT ELEMENT IN LO STRUCT */
  2093.                 eptr->lo_any.edit_element = edit_element;
  2094.  
  2095.             }
  2096.         }
  2097.         state->edit_force_offset = FALSE;
  2098. #endif
  2099.     }
  2100.  
  2101.     return(eptr);
  2102. }
  2103.  
  2104.  
  2105. /* Add a named anchor to the global list of named anchors.  This does not
  2106.    associate the anchor with a layout element.  lo_SetNamedAnchor should be
  2107.    followed by a call to lo_BindNamedAnchorToElement in order to associate
  2108.    the named anchor with a layout element. */
  2109. Bool
  2110. lo_SetNamedAnchor(lo_DocState *state, PA_Block name)
  2111. {
  2112.     lo_NameList *name_rec = NULL;
  2113.     lo_DocLists *doc_lists;
  2114.  
  2115.     /*
  2116.      * No named anchors are allowed within a
  2117.      * hacked scrolling content layout document.
  2118.      */
  2119.     if (state->top_state->scrolling_doc != FALSE)
  2120.     {
  2121.         if (name != NULL)
  2122.         {
  2123.             PA_FREE(name);
  2124.         }
  2125.         return FALSE;
  2126.     }
  2127.  
  2128.     doc_lists = lo_GetCurrentDocLists(state);
  2129.     
  2130. #ifdef MOCHA
  2131.     if (state->in_relayout) {
  2132.         /* Look for the old lo_NameList.  Its mocha_object is still valid. */
  2133.         lo_NameList *name_list = doc_lists->name_list;
  2134.         lo_NameList *prev;
  2135.         Bool no_match;
  2136.         char *s1, *s2;
  2137.         
  2138.         XP_ASSERT(name_list);   /* If in relayout, this cannot be empty. */
  2139.         
  2140.         prev = name_list;
  2141.         name_rec = name_list;
  2142.         while (name_rec != NULL) {
  2143.             PA_LOCK(s1, char*, name);
  2144.             PA_LOCK(s2, char*, name_rec->name);
  2145.             no_match = XP_STRCMP(s1, s2);
  2146.             PA_UNLOCK(name_rec->name);
  2147.             PA_UNLOCK(name);
  2148.             if (!no_match) {
  2149.                 if (name_rec == name_list)
  2150.                     doc_lists->name_list = name_list->next;
  2151.                 else
  2152.                     prev->next = name_rec->next;
  2153.                 PA_FREE(name_rec->name); /* No need for this, use new one. */
  2154.                 break;
  2155.             }
  2156.             prev = name_rec;
  2157.             name_rec = name_rec->next;
  2158.         }
  2159.     }
  2160.     if (!name_rec || !state->in_relayout) {
  2161. #endif /* MOCHA */
  2162.         name_rec = XP_NEW(lo_NameList);
  2163.         if (name_rec == NULL)
  2164.             {
  2165.                 state->top_state->out_of_memory = TRUE;
  2166.                 PA_FREE(name);
  2167.                 return FALSE;
  2168.             }
  2169. #ifdef MOCHA
  2170.         name_rec->mocha_object = NULL;
  2171.     }
  2172. #endif /* MOCHA */
  2173.     name_rec->x = 0;
  2174.     name_rec->y = 0;
  2175.     name_rec->name = name;
  2176.     name_rec->element = NULL;   /* Set by lo_BindNamedAnchorToElement. */
  2177.  
  2178.     name_rec->next = doc_lists->name_list;
  2179.     doc_lists->name_list = name_rec;
  2180.     return TRUE;
  2181. }
  2182.  
  2183. /* Associate a named anchor with a layout element.  The named anchor should
  2184.    be the first one in the list of named anchors.*/
  2185. Bool
  2186. lo_BindNamedAnchorToElement(lo_DocState *state, PA_Block name,
  2187.                            LO_Element *element)
  2188. {
  2189.     Bool no_match;
  2190.     LO_Element *eptr;
  2191.     lo_NameList *name_rec;
  2192.     char *s1, *s2;
  2193.     lo_DocLists *doc_lists;
  2194.  
  2195.     doc_lists = lo_GetCurrentDocLists(state);
  2196.     
  2197.     if (element == NULL)
  2198.     {
  2199.         if (state->line_list == NULL)
  2200.         {
  2201.             eptr = state->end_last_line;
  2202.         }
  2203.         else
  2204.         {
  2205.             eptr = state->line_list;
  2206.             while (eptr->lo_any.next != NULL)
  2207.             {
  2208.                 eptr = eptr->lo_any.next;
  2209.             }
  2210.         }
  2211.     }
  2212.     else
  2213.     {
  2214.         eptr = element;
  2215.     }
  2216.  
  2217.     /* Find the matching name_rec in the name_list */
  2218.     name_rec = doc_lists->name_list;
  2219.     PA_LOCK(s1, char*, name);
  2220.     while (name_rec != NULL)
  2221.     {
  2222.     PA_LOCK(s2, char*, name_rec->name);
  2223.     no_match = XP_STRCMP(s1, s2);
  2224.     PA_UNLOCK(name_rec->name);
  2225.     if (!no_match)
  2226.         break;
  2227.     name_rec = name_rec->next;
  2228.     }
  2229.     PA_UNLOCK(name);
  2230.     if (name_rec == NULL)
  2231.         return FALSE;
  2232.  
  2233.     if (eptr == NULL)
  2234.     {
  2235.         name_rec->x = 0;
  2236.         name_rec->y = 0;
  2237.     }
  2238.     else
  2239.     {
  2240.         name_rec->x = eptr->lo_any.x;
  2241.         name_rec->y = eptr->lo_any.y;
  2242.     }
  2243.  
  2244.     name_rec->element = eptr;
  2245.     return TRUE;
  2246. }
  2247.  
  2248.  
  2249. void
  2250. lo_AddNameList(lo_DocState *state, lo_DocState *old_state)
  2251. {
  2252. /*
  2253.  * Now named anchors are added directly to the
  2254.  * top_state (or layer's) name_list, so we don't have to push
  2255.  * them up. We do, however, need to update their positions, 
  2256.  * though it seems like this is done at the end of table 
  2257.  * processing. Hence this code is no longer necessary.
  2258.  */
  2259. #if 0
  2260.     lo_NameList *name_list;
  2261.     lo_NameList *nptr;
  2262.  
  2263.     name_list = old_state->name_list;
  2264.     while (name_list != NULL)
  2265.     {
  2266.         /*
  2267.          * Extract the current record from the list.
  2268.          */
  2269.         nptr = name_list;
  2270.         name_list = name_list->next;
  2271.  
  2272.         /*
  2273.          * The element almost certainly has been moved
  2274.          * from subdoc to main cell, correct its position.
  2275.          */
  2276.         if (nptr->element != NULL)
  2277.         {
  2278.             nptr->x = nptr->element->lo_any.x;
  2279.             nptr->y = nptr->element->lo_any.y;
  2280.         }
  2281.  
  2282.         /*
  2283.          * Stick it into the main name list.
  2284.          */
  2285.         nptr->next = state->name_list;
  2286.         state->name_list = nptr;
  2287.     }
  2288.     old_state->name_list = name_list;
  2289. #endif /* 0 */
  2290. }
  2291.  
  2292.  
  2293. void
  2294. lo_CheckNameList(MWContext *context, lo_DocState *state, int32 min_id)
  2295. {
  2296.     lo_NameList *nptr;
  2297.     lo_DocLists *doc_lists;
  2298.  
  2299.     /*
  2300.      * Positions of named elements are only accurate if
  2301.      * we are not in a nested subdoc.
  2302.      */
  2303.     if (state->is_a_subdoc != SUBDOC_NOT)
  2304.     {
  2305.         return;
  2306.     }
  2307.  
  2308.     doc_lists = lo_GetCurrentDocLists(state);
  2309.     
  2310.     nptr = doc_lists->name_list;
  2311.     while (nptr != NULL)
  2312.     {
  2313.         /*
  2314.          * If the element's id is greater
  2315.          * than the passed minimum, then
  2316.          * the element almost certainly has been moved
  2317.          * during table processing,
  2318.          * correct its position.
  2319.          */
  2320.         if ((nptr->element != NULL)&&
  2321.             (nptr->element->lo_any.ele_id > min_id))
  2322.         {
  2323.             nptr->x = nptr->element->lo_any.x;
  2324.             nptr->y = nptr->element->lo_any.y;
  2325.         }
  2326.  
  2327.         /*
  2328.          * If we are blocked on a named element.
  2329.          * Check if this name is one we are waiting on.
  2330.          */
  2331.         if ((state->display_blocking_element_id == -1)&&
  2332.             (nptr->name != NULL))
  2333.         {
  2334.             char *name;
  2335.  
  2336.             PA_LOCK(name, char *, nptr->name);
  2337.             if (XP_STRCMP(name,
  2338.                 (char *)(state->top_state->name_target + 1)) == 0)
  2339.             {
  2340.                 XP_FREE(state->top_state->name_target);
  2341.                 state->top_state->name_target = NULL;
  2342.                 if (nptr->element != NULL)
  2343.                 {
  2344.                     state->display_blocking_element_id =
  2345.                         nptr->element->lo_any.ele_id;
  2346.                 }
  2347.                 else
  2348.                 {
  2349.                     state->display_blocking_element_id =
  2350.                         state->top_state->element_id;
  2351.                 }
  2352.                 /*
  2353.                  * If the display is blocked for an element
  2354.                  * we havn't reached yet, check to see if
  2355.                  * it is in this line, and if so, save its
  2356.                  * y position.
  2357.                  */
  2358.                 if ((state->display_blocked != FALSE)&&
  2359.                     (state->is_a_subdoc == SUBDOC_NOT)&&
  2360.                     (state->display_blocking_element_y == 0)&&
  2361.                     (state->display_blocking_element_id != -1))
  2362.                 {
  2363.                     state->display_blocking_element_y = nptr->y;
  2364.                 }
  2365.                 PA_UNLOCK(nptr->name);
  2366.             }
  2367.             else
  2368.             {
  2369.                 PA_UNLOCK(nptr->name);
  2370.             }
  2371.         }
  2372.  
  2373.         nptr = nptr->next;
  2374.     }
  2375. }
  2376.  
  2377.  
  2378. void
  2379. lo_AppendToLineList(MWContext *context, lo_DocState *state,
  2380.     LO_Element *element, int32 baseline_inc)
  2381. {
  2382.     LO_Element *eptr;
  2383.  
  2384.     if (state->current_named_anchor != NULL) {
  2385.         if (!lo_BindNamedAnchorToElement(state, state->current_named_anchor,
  2386.                                          element)) {
  2387.             XP_ASSERT(FALSE);
  2388.         }
  2389.     state->current_named_anchor = NULL;
  2390.     }
  2391. #ifdef MOCHA
  2392.     else if (state->current_anchor) {
  2393.         state->current_anchor->element = element;
  2394.     }
  2395. #endif /* MOCHA */
  2396.  
  2397.     element->lo_any.next = NULL;
  2398.     element->lo_any.prev = NULL;
  2399.     eptr = state->line_list;
  2400.     if (eptr == NULL)
  2401.     {
  2402.         state->line_list = element;
  2403.     }
  2404.     else
  2405.     {
  2406.         while (eptr->lo_any.next != NULL)
  2407.         {
  2408.             eptr->lo_any.y_offset += baseline_inc;
  2409.             eptr = eptr->lo_any.next;
  2410.         }
  2411.         eptr->lo_any.y_offset += baseline_inc;
  2412.         eptr->lo_any.next = element;
  2413.         element->lo_any.prev = eptr;
  2414.     }
  2415. }
  2416.  
  2417. void
  2418. lo_SetLineArrayEntry(lo_DocState *state, LO_Element *eptr, int32 line)
  2419. {
  2420.     LO_Element **line_array;
  2421.  
  2422. #ifdef XP_WIN16
  2423.         intn a_size;
  2424.         intn a_indx;
  2425.         intn a_line;
  2426.         XP_Block *larray_array;
  2427. #endif /* XP_WIN16 */
  2428.  
  2429.     if ((line < 0) || (line >= (state->line_num - 1)))
  2430.     {
  2431.         return;
  2432.     }
  2433.  
  2434. #ifdef XP_WIN16
  2435.         a_size = SIZE_LIMIT / sizeof(LO_Element *);
  2436.         a_indx = (intn)(line / a_size);
  2437.         a_line = (intn)(line - (a_indx * a_size));
  2438.  
  2439.         XP_LOCK_BLOCK(larray_array, XP_Block *, state->larray_array);
  2440.         state->line_array = larray_array[a_indx];
  2441.         XP_LOCK_BLOCK(line_array, LO_Element **, state->line_array);
  2442.     line_array[a_line] = eptr;
  2443.  
  2444.         XP_UNLOCK_BLOCK(state->line_array);
  2445.         XP_UNLOCK_BLOCK(state->larray_array);
  2446. #else
  2447.         XP_LOCK_BLOCK(line_array, LO_Element **, state->line_array);
  2448.     line_array[line] = eptr;
  2449.         XP_UNLOCK_BLOCK(state->line_array);
  2450. #endif /* XP_WIN16 */
  2451. }
  2452.  
  2453. #define POSITIVE_SCALE_FACTOR 1.10 /* 10% */
  2454. #define NEGATIVE_SCALE_FACTOR .90  /* 10% */
  2455.  
  2456.  
  2457. /*
  2458.  * Return the scaling percentage given a font scaler
  2459.  *
  2460.  */
  2461. double
  2462. LO_GetScalingFactor(int32 scaler)
  2463. {
  2464.     double scale=1.0;
  2465.     double mult;
  2466.     int32 count;
  2467.  
  2468.     if(scaler < 0)
  2469.     {
  2470.         count = -scaler;
  2471.         mult = NEGATIVE_SCALE_FACTOR;
  2472.     }
  2473.     else
  2474.     {
  2475.        count = scaler;
  2476.        mult = POSITIVE_SCALE_FACTOR;
  2477.     }
  2478.  
  2479.     /* use the percentage scaling factor to the power of the pref */
  2480.     while(count--)
  2481.         scale *= mult;
  2482.  
  2483.     return scale;
  2484. }
  2485.  
  2486.  
  2487. /*
  2488.  * Return the width of the window for this context in chars in the default
  2489.  * fixed width font.  Return -1 on error.
  2490.  */
  2491. int16
  2492. LO_WindowWidthInFixedChars(MWContext *context)
  2493. {
  2494.     int32 doc_id;
  2495.     lo_TopState *top_state;
  2496.     lo_DocState *state;
  2497.     LO_TextInfo tmp_info;
  2498.     LO_TextAttr tmp_attr;
  2499.     LO_TextStruct tmp_text;
  2500.     PA_Block buff;
  2501.     char *str;
  2502.     int32 width;
  2503.     int16 char_cnt;
  2504.  
  2505.     /*
  2506.      * Get the unique document ID, and retreive this
  2507.      * documents layout state.
  2508.      */
  2509.     doc_id = XP_DOCID(context);
  2510.     top_state = lo_FetchTopState(doc_id);
  2511.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  2512.     {
  2513.         return((int16)-1);
  2514.     }
  2515.     state = top_state->doc_state;
  2516.  
  2517.     /*
  2518.      * Create a fake text buffer to measure an 'M'.
  2519.      */
  2520.     memset (&tmp_text, 0, sizeof (tmp_text));
  2521.     buff = PA_ALLOC(1);
  2522.     if (buff == NULL)
  2523.     {
  2524.         return((int16)-1);
  2525.     }
  2526.     PA_LOCK(str, char *, buff);
  2527.     str[0] = 'M';
  2528.     PA_UNLOCK(buff);
  2529.     tmp_text.text = buff;
  2530.     tmp_text.text_len = 1;
  2531.  
  2532.     /*
  2533.      * Fill in default fixed font information.
  2534.      */
  2535.     lo_SetDefaultFontAttr(state, &tmp_attr, context);
  2536.     tmp_attr.fontmask |= LO_FONT_FIXED;
  2537.     tmp_text.text_attr = &tmp_attr;
  2538.  
  2539.     /*
  2540.      * Get the width of that 'M'
  2541.      */
  2542.     FE_GetTextInfo(context, &tmp_text, &tmp_info);
  2543.     PA_FREE(buff);
  2544.  
  2545.     /*
  2546.      * Error if bad character width
  2547.      */
  2548.     if (tmp_info.max_width <= 0)
  2549.     {
  2550.         return((int16)-1);
  2551.     }
  2552.  
  2553.     width = state->win_width - state->win_left - state->win_right;
  2554.     char_cnt = (int16)(width / tmp_info.max_width);
  2555.     return(char_cnt);
  2556. }
  2557.  
  2558.  
  2559. LO_Element *
  2560. lo_FirstElementOfLine(MWContext *context, lo_DocState *state, int32 line)
  2561. {
  2562.     LO_Element *eptr;
  2563.     LO_Element **line_array;
  2564.  
  2565.     if ((line < 0)||(line > (state->line_num - 2)))
  2566.     {
  2567.         return(NULL);
  2568.     }
  2569.  
  2570. #ifdef XP_WIN16
  2571.     {
  2572.         intn a_size;
  2573.         intn a_indx;
  2574.         intn a_line;
  2575.         XP_Block *larray_array;
  2576.  
  2577.         a_size = SIZE_LIMIT / sizeof(LO_Element *);
  2578.         a_indx = (intn)(line / a_size);
  2579.         a_line = (intn)(line - (a_indx * a_size));
  2580.         XP_LOCK_BLOCK(larray_array, XP_Block *,
  2581.                 state->larray_array);
  2582.         state->line_array = larray_array[a_indx];
  2583.         XP_LOCK_BLOCK(line_array, LO_Element **,
  2584.                 state->line_array);
  2585.         eptr = line_array[a_line];
  2586.         XP_UNLOCK_BLOCK(state->line_array);
  2587.         XP_UNLOCK_BLOCK(state->larray_array);
  2588.     }
  2589. #else
  2590.     {
  2591.         XP_LOCK_BLOCK(line_array, LO_Element **,
  2592.                 state->line_array);
  2593.         eptr = line_array[line];
  2594.         XP_UNLOCK_BLOCK(state->line_array);
  2595.     }
  2596. #endif /* XP_WIN16 */
  2597.     return(eptr);
  2598. }
  2599.  
  2600.  
  2601. Bool
  2602. lo_FindMochaExpr(char *str, char **expr_start, char **expr_end)
  2603. {
  2604.     char *tptr;
  2605.     char *start;
  2606.     char *end;
  2607.  
  2608.     if (str == NULL)
  2609.     {
  2610.         return(FALSE);
  2611.     }
  2612.  
  2613.     start = NULL;
  2614.     end = NULL;
  2615.     tptr = str;
  2616.     while (*tptr != '\0')
  2617.     {
  2618.         if ((*tptr == '&')&&(*(char *)(tptr + 1) == '{'))
  2619.         {
  2620.             start = tptr;
  2621.             break;
  2622.         }
  2623.         tptr++;
  2624.     }
  2625.  
  2626.     if (start == NULL)
  2627.     {
  2628.         return(FALSE);
  2629.     }
  2630.  
  2631.     tptr = (char *)(start + 2);
  2632.     while (*tptr != '\0')
  2633.     {
  2634.         if ((*tptr == '}')&&(*(char *)(tptr + 1) == ';'))
  2635.         {
  2636.             end = tptr;
  2637.             break;
  2638.         }
  2639.         tptr++;
  2640.     }
  2641.  
  2642.     if (end == NULL)
  2643.     {
  2644.         return(FALSE);
  2645.     }
  2646.  
  2647.     end = (char *)(end + 1);
  2648.     *expr_start = start;
  2649.     *expr_end = end;
  2650.     return(TRUE);
  2651. }
  2652.  
  2653.  
  2654. static char *
  2655. lo_add_string(char *base, char *add)
  2656. {
  2657.     char *new;
  2658.     int32 base_len, add_len;
  2659.  
  2660.     if (add == NULL)
  2661.     {
  2662.         return(base);
  2663.     }
  2664.  
  2665.     if (base == NULL)
  2666.     {
  2667.         new = XP_STRDUP(add);
  2668.         return(new);
  2669.     }
  2670.  
  2671.     base_len = XP_STRLEN(base);
  2672.     add_len = XP_STRLEN(add);
  2673.     new = (char *)XP_ALLOC(base_len + add_len + 1);
  2674.     if (new == NULL)
  2675.     {
  2676.         return(base);
  2677.     }
  2678.  
  2679.     XP_STRCPY(new, base);
  2680.     XP_STRCAT(new, add);
  2681.     XP_FREE(base);
  2682.     return(new);
  2683. }
  2684.  
  2685.  
  2686. static char *
  2687. lo_eval_javascript_entities(MWContext *context, char *orig_value,
  2688.                             uint newline_count)
  2689. {
  2690.     char *tptr;
  2691.     char *str;
  2692.     char *new_str;
  2693.     int32 len;
  2694.     char tchar;
  2695.     Bool has_mocha;
  2696.     char *start, *end;
  2697.  
  2698.     if (orig_value == NULL)
  2699.     {
  2700.         return(NULL);
  2701.     }
  2702.  
  2703.     str = orig_value;
  2704.     len = XP_STRLEN(str);
  2705.  
  2706.     new_str = NULL;
  2707.     tptr = str;
  2708.     has_mocha = lo_FindMochaExpr(tptr, &start, &end);
  2709.     while (has_mocha != FALSE)
  2710.     {
  2711.         char *expr_start, *expr_end;
  2712.  
  2713.         if (start > tptr)
  2714.         {
  2715.             tchar = *start;
  2716.             *start = '\0';
  2717.             new_str = lo_add_string(new_str, tptr);
  2718.             *start = tchar;
  2719.         }
  2720.  
  2721.         expr_start = (char *)(start + 2);
  2722.         expr_end = (char *)(end - 1);
  2723.         if (expr_end > expr_start)
  2724.         {
  2725.             char *eval_str;
  2726.  
  2727.             tchar = *expr_end;
  2728.             *expr_end = '\0';
  2729.             eval_str = LM_EvaluateAttribute(context, expr_start,
  2730.                                             newline_count + 1);
  2731.             *expr_end = tchar;
  2732.             new_str = lo_add_string(new_str, eval_str);
  2733.             if (eval_str != NULL)
  2734.             {
  2735.                 XP_FREE(eval_str);
  2736.             }
  2737.         }
  2738.  
  2739.         tptr = (char *)(end + 1);
  2740.         has_mocha = lo_FindMochaExpr(tptr, &start, &end);
  2741.     }
  2742.     if (tptr != str)
  2743.     {
  2744.         new_str = lo_add_string(new_str, tptr);
  2745.     }
  2746.  
  2747.     if (new_str == NULL)
  2748.     {
  2749.         new_str = str;
  2750.     }
  2751.  
  2752.     return(new_str);
  2753. }
  2754.  
  2755.  
  2756. void
  2757. lo_ConvertAllValues(MWContext *context, char **value_list, int32 list_len,
  2758.                     uint newline_count)
  2759. {
  2760.     int32 i;
  2761.  
  2762.     if (LM_CanDoJS(context) == FALSE)
  2763.     {
  2764.         return;
  2765.     }
  2766.  
  2767.     for (i=0; i < list_len; i++)
  2768.     {
  2769.         char *str;
  2770.         char *new_str;
  2771.  
  2772.         str = value_list[i];
  2773.         if (str != NULL)
  2774.         {
  2775.             new_str = lo_eval_javascript_entities(context, str, newline_count);
  2776.             if (new_str != str)
  2777.             {
  2778.                 XP_FREE(str);
  2779.                 value_list[i] = new_str;
  2780.             }
  2781.         }
  2782.     }
  2783. }
  2784.  
  2785.  
  2786. PA_Block
  2787. lo_FetchParamValue(MWContext *context, PA_Tag *tag, char *param_name)
  2788. {
  2789.     PA_Block buff;
  2790.     INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(context);
  2791.     buff = PA_FetchParamValue(tag, param_name, INTL_GetCSIWinCSID(c));
  2792.     return(buff);
  2793. }
  2794.  
  2795. #include "prthread.h"
  2796. #include "prmon.h"
  2797.  
  2798. static PRMonitor * layout_lock_monitor = NULL;
  2799. static PRThread  * layout_lock_owner = NULL;
  2800. static int layout_lock_count = 0;
  2801.  
  2802. /*
  2803.  * Grab the lock around layout so that we can delete things / manipulate
  2804.  *   layout owned data structures in a separate thread
  2805.  */
  2806. void
  2807. LO_LockLayout()
  2808. {
  2809.     if(!layout_lock_monitor)
  2810.     layout_lock_monitor = PR_NewNamedMonitor("layout-lock");
  2811.  
  2812.     PR_EnterMonitor(layout_lock_monitor);
  2813.  
  2814.     while(TRUE) {
  2815.  
  2816.     /* no current owner or owned by this thread */
  2817.     if(layout_lock_owner == NULL ||
  2818.        layout_lock_owner == PR_CurrentThread()) {
  2819.         layout_lock_owner = PR_CurrentThread();
  2820.         layout_lock_count++;
  2821.  
  2822.         PR_ExitMonitor(layout_lock_monitor);
  2823.         return;
  2824.     }
  2825.  
  2826.     /* owned by someone else -- wait till we can get it */
  2827.     PR_Wait(layout_lock_monitor, PR_INTERVAL_NO_TIMEOUT);
  2828.  
  2829.     }
  2830. }
  2831.  
  2832. /*
  2833.  * Release the lock and wake up anyone who was waiting to get it if
  2834.  *   we are the last release of the lock from this thread
  2835.  */
  2836. void
  2837. LO_UnlockLayout()
  2838. {
  2839.  
  2840.     PR_EnterMonitor(layout_lock_monitor);
  2841.  
  2842. #ifdef DEBUG
  2843.     /* make sure someone doesn't try to free a lock they don't own */
  2844.     XP_ASSERT(layout_lock_owner == PR_CurrentThread());
  2845. #endif
  2846.  
  2847.     layout_lock_count--;
  2848.  
  2849.     if(layout_lock_count == 0) {
  2850.     layout_lock_owner = NULL;
  2851.     PR_Notify(layout_lock_monitor);
  2852.     }
  2853.     PR_ExitMonitor(layout_lock_monitor);
  2854.  
  2855. }
  2856.  
  2857.  
  2858. /*
  2859.  * A (debugging) way to make sure the current thread doesn't have the
  2860.  *   layout lock
  2861.  */
  2862. Bool
  2863. LO_VerifyUnlockedLayout()
  2864. {
  2865. #ifdef DEBUG
  2866.     Bool bHolding = TRUE;
  2867.     
  2868.     if (layout_lock_monitor) {
  2869.     PR_EnterMonitor(layout_lock_monitor);
  2870.     bHolding = (layout_lock_owner != PR_CurrentThread());
  2871.     PR_ExitMonitor(layout_lock_monitor);
  2872.     }
  2873.  
  2874.     return bHolding;
  2875.  
  2876. #else
  2877.     return TRUE;
  2878. #endif
  2879. }
  2880.  
  2881.  
  2882. /*
  2883.  * The mocha lock has been released, flush the blockage to see if
  2884.  *   we can get our blocked entities evaluated now.
  2885.  */
  2886. static void
  2887. lo_JSLockReleased(void * data)
  2888. {
  2889.     MWContext * context = (MWContext *) data;
  2890.     lo_TopState * top_state;
  2891.  
  2892.     top_state = lo_FetchTopState(XP_DOCID(context));
  2893.     if (!top_state)
  2894.     return;
  2895.  
  2896.     /* free the fake element we created earlier */
  2897.     lo_FreeElement(context, top_state->layout_blocking_element, FALSE);
  2898.  
  2899.     /* flush the blockage */
  2900.     lo_UnblockLayout(context, top_state);
  2901. }
  2902.  
  2903. /* 
  2904.  * see if there are any &{} constructs for this tag.  If so,
  2905.  *   convert them now, if we can't convert them (since we couldn't
  2906.  *   get the mocha lock, for example) return FALSE
  2907.  */
  2908. Bool
  2909. lo_ConvertMochaEntities(MWContext * context, lo_DocState *state, PA_Tag * tag)
  2910. {
  2911.  
  2912.         char *tptr;
  2913.     char *str;
  2914.     char *new_str;
  2915.     int32 len;
  2916.     char tchar;
  2917.     Bool has_mocha;
  2918.     char *start, *end;
  2919.     int32 cur_layer_id;
  2920.  
  2921.     PA_LOCK(str, char *, tag->data);
  2922.     len = tag->data_len;
  2923.  
  2924.     if (LM_CanDoJS(context) == FALSE)
  2925.         return TRUE;
  2926.  
  2927.     new_str = NULL;
  2928.     tptr = str;
  2929.     has_mocha = lo_FindMochaExpr(tptr, &start, &end);
  2930.  
  2931.     if (has_mocha == FALSE)
  2932.         return TRUE;
  2933.  
  2934.     /* attempt to lock JS */
  2935.     if (!LM_AttemptLockJS(lo_JSLockReleased, context))
  2936.         return FALSE;
  2937.  
  2938.     /* 
  2939.      * Make sure that the current layer and all its ancestors have
  2940.      * been reflected and that the current layer has been set as the 
  2941.      * active layer, since we want to ensure correct scoping and there's 
  2942.      * no way to know that the reflect event has reached Mocha yet. 
  2943.      * Note that reflecting the object multiple times is safe, since the 
  2944.      * reflection code is idempotent.
  2945.      */
  2946.     cur_layer_id = lo_CurrentLayerId(state);
  2947.     if (cur_layer_id != LO_DOCUMENT_LAYER_ID) {
  2948.         CL_Layer *layer, *parent;
  2949.     int32 child_layer_id = cur_layer_id;
  2950.         int32 parent_layer_id;
  2951.         
  2952.     do {
  2953.         layer = LO_GetLayerFromId(context, child_layer_id);
  2954.         parent = CL_GetLayerParent(layer);
  2955.         parent_layer_id = LO_GetIdFromLayer(context, parent);
  2956.         LM_ReflectLayer(context, cur_layer_id, parent_layer_id, NULL);
  2957.         child_layer_id = parent_layer_id;
  2958.     } while (child_layer_id != LO_DOCUMENT_LAYER_ID);
  2959.  
  2960.     }
  2961.     LM_SetActiveLayer(context, cur_layer_id);
  2962.     while (has_mocha != FALSE)
  2963.     {
  2964.         char *expr_start, *expr_end;
  2965.  
  2966.         if (start > tptr)
  2967.         {
  2968.             tchar = *start;
  2969.             *start = '\0';
  2970.             new_str = lo_add_string(new_str, tptr);
  2971.             *start = tchar;
  2972.         }
  2973.  
  2974.         expr_start = (char *)(start + 2);
  2975.         expr_end = (char *)(end - 1);
  2976.         if (expr_end > expr_start)
  2977.         {
  2978.             char *eval_str;
  2979.  
  2980.             tchar = *expr_end;
  2981.             *expr_end = '\0';
  2982.             if (state->top_state && state->top_state->nurl) {
  2983.                 LM_SetDecoderStream(context, NULL, 
  2984.                                     state->top_state->nurl,
  2985.                                     JS_FALSE);
  2986.             }
  2987.             eval_str = LM_EvaluateAttribute(context, expr_start,
  2988.                             tag->newline_count + 1);
  2989.             *expr_end = tchar;
  2990.             new_str = lo_add_string(new_str, eval_str);
  2991.             if (eval_str != NULL)
  2992.             {
  2993.                 XP_FREE(eval_str);
  2994.             }
  2995.         }
  2996.  
  2997.         tptr = (char *)(end + 1);
  2998.         has_mocha = lo_FindMochaExpr(tptr, &start, &end);
  2999.     }
  3000.     if (tptr != str)
  3001.     {
  3002.         new_str = lo_add_string(new_str, tptr);
  3003.     }
  3004.  
  3005.     PA_FREE(tag->data);
  3006.     tag->data = (PA_Block) new_str;
  3007.     tag->data_len = XP_STRLEN((char *) tag->data);
  3008.     PA_UNLOCK(tag->data);
  3009.  
  3010.     LM_UnlockJS();
  3011.  
  3012.     return TRUE;
  3013.  
  3014. }
  3015.  
  3016. /* Called from front ends as a threadsafe way to update values of text fields. */
  3017. void
  3018. LO_UpdateTextData(lo_FormElementTextData * textData, const char * text) {
  3019.  
  3020.     LO_LockLayout();
  3021.  
  3022.     if(textData->current_text)
  3023.     XP_FREE(textData->current_text);
  3024.             
  3025.     textData->current_text = (PA_Block) XP_STRDUP(text);
  3026.  
  3027.     LO_UnlockLayout();
  3028. }
  3029.  
  3030. LO_TableStruct *lo_GetParentTable(MWContext *pContext, LO_Element *pElement)
  3031. {
  3032.     LO_Element *tptr;
  3033.     if( !pElement )
  3034.         return NULL;
  3035.  
  3036.     if( pElement->lo_any.type == LO_CELL )
  3037.     {
  3038.         /* Supplied element is already a cell, start table search with it */
  3039.         tptr = pElement;
  3040.     } else {
  3041.         if( !pContext )
  3042.             return NULL;
  3043.  
  3044.         /* First find the cell that contains the given element */
  3045.         tptr = (LO_Element*)lo_GetParentCell(pContext, pElement);
  3046.     }
  3047.  
  3048.     /* Simply search back from current cell to find table element */
  3049.     while( tptr )
  3050.     {
  3051.         if( tptr->lo_any.type == LO_TABLE )
  3052.         {
  3053.             return (LO_TableStruct*)tptr;
  3054.         }
  3055.         tptr = tptr->lo_any.prev;
  3056.     } 
  3057.     return NULL;
  3058. }
  3059.  
  3060.  
  3061. /* Recusively search for cell struct containing the given element 
  3062.  * If table is found, it searches into that as well
  3063.  */
  3064. LO_CellStruct *lo_GetCellContainingElement( LO_Element *pElement, LO_CellStruct *pFirstCell )
  3065. {
  3066.     LO_CellStruct *pNestedCell = NULL;
  3067.     LO_CellStruct *pFirstNestedCell;
  3068.     LO_Element    *cell_list_ptr;
  3069.     LO_CellStruct *pLoCell;
  3070.  
  3071.     pLoCell = pFirstCell;
  3072.     if( pLoCell == NULL )
  3073.         return NULL;
  3074.  
  3075.     do {
  3076.         cell_list_ptr = pLoCell->cell_list;
  3077.         while( cell_list_ptr )
  3078.         {
  3079.             if( cell_list_ptr == pElement )
  3080.             {
  3081.                 /* We found the element - return cell containing it */
  3082.                 return pLoCell;
  3083.             }
  3084.             if( cell_list_ptr->type == LO_TABLE )
  3085.             {
  3086.                 /* We found a nested table
  3087.                  * Get first cell in it and search through it
  3088.                 */
  3089.                 pFirstNestedCell = (LO_CellStruct*)cell_list_ptr->lo_any.next;
  3090.                 XP_ASSERT(pFirstNestedCell->type == LO_CELL);
  3091.                 pNestedCell = lo_GetCellContainingElement(pElement, pFirstNestedCell);
  3092.                 if( pNestedCell )
  3093.                 {
  3094.                    /* We found cell in nested table */
  3095.                    return pNestedCell;
  3096.                 }
  3097.             }
  3098.             cell_list_ptr = cell_list_ptr->lo_any.next;
  3099.         }
  3100.         pLoCell = (LO_CellStruct*)pLoCell->next;
  3101.     }  /* End when there's no more cells or we hit linefeed at end of line (top-level table)*/
  3102.     while (pLoCell && pLoCell->type == LO_CELL);
  3103.  
  3104.     return NULL;
  3105. }
  3106.  
  3107. /* Search algorithm is very similar to CEditBuffer::GetTableHitRegion()
  3108.  *   (which is similar to lo_XYtoDocumentElement except it finds only cells)
  3109.  * Search for the Cell that contains pElement
  3110. */
  3111. LO_CellStruct *lo_GetParentCell(MWContext * pContext, LO_Element *pElement)
  3112. {
  3113.     LO_Element *tptr;
  3114.     int32 doc_id;
  3115.     lo_TopState *top_state;
  3116.     lo_DocState *state;
  3117.     XP_Bool in_table;
  3118.     int32 line;
  3119.     LO_Element *end_ptr;
  3120.     LO_Element *tptrTable;
  3121.     int32 x, y, t_width, t_height;
  3122.  
  3123.     doc_id = XP_DOCID(pContext);
  3124.     top_state = lo_FetchTopState(doc_id);
  3125.  
  3126.     /* Supplied element is a cell - return it ??? */
  3127.     if( pElement && pElement->type == LO_CELL )
  3128.         return (LO_CellStruct*)pElement;
  3129.  
  3130.     if( pContext == NULL || pElement == NULL || top_state == NULL || top_state->doc_state == NULL )
  3131.         return NULL;
  3132.     
  3133.     state = top_state->doc_state;
  3134.     tptrTable = NULL;
  3135.     in_table = FALSE;
  3136.     
  3137.     /* Find the line that contains the supplied element
  3138.      * This defines the top-level table
  3139.     */
  3140.     x = pElement->lo_any.x+1;
  3141.     y = pElement->lo_any.y+1;
  3142.  
  3143.     line = lo_PointToLine(pContext, state, x, y);
  3144.     lo_GetLineEnds(pContext, state, line, &tptr, &end_ptr);
  3145.  
  3146.     while (tptr != end_ptr)
  3147.     {
  3148.            t_width = tptr->lo_any.width;
  3149.            t_height = tptr->lo_any.height;
  3150.  
  3151.         if (t_width <= 0)
  3152.         {
  3153.             t_width = 1;
  3154.         }
  3155.  
  3156.         if ((y >= tptr->lo_any.y) &&
  3157.             (y < tptr->lo_any.y + tptr->lo_any.y_offset + t_height) &&
  3158.             (x >= tptr->lo_any.x) &&
  3159.             (x < (tptr->lo_any.x + tptr->lo_any.x_offset + t_width)))
  3160.         {
  3161.             /* We are only interested in finding cells */
  3162.             if (tptr->type == LO_CELL)
  3163.             {
  3164.                 /* Search through the cell's element list,
  3165.                  * Including all nested tables in that cell
  3166.                 */
  3167.                 LO_CellStruct * pLoCell = lo_GetCellContainingElement(pElement, (LO_CellStruct*)tptr);
  3168.                 /* We found it - done */
  3169.                 if( pLoCell )
  3170.                     return pLoCell;
  3171.             }
  3172.         }
  3173.         tptr = tptr->lo_any.next;
  3174.     }
  3175.  
  3176.     /* Failed to find element */
  3177.     return NULL;
  3178. }
  3179.  
  3180.  
  3181. /* Find the first cell with with closest left border x-value <= than the given x
  3182.  * value or, if bGetColumn=FALSE, find the cell with closest top border 
  3183.  *   y-value <= than the given Y value. Also returns the pointer to the
  3184.  *    last LO element so we can search for other cells until pEndElement is reached.
  3185. */
  3186. LO_Element* lo_GetFirstCellInColumnOrRow(MWContext *pContext, LO_Element *pElement, int32 x, int32 y, XP_Bool bGetColumn, LO_Element **ppLastCellInTable)
  3187. {
  3188.     LO_Element *pFirstCell = NULL;
  3189.     LO_Element *pLastCellInTable = NULL;
  3190.     LO_Element *tptr = NULL;
  3191.     int32 closest = 0;
  3192.     lo_DocState *state;
  3193.  
  3194.  
  3195.     if( pElement->type == LO_TABLE )
  3196.     {
  3197.         /* Start at first element after the table */
  3198.         tptr = pElement->lo_any.next;
  3199.     } else {
  3200.     
  3201.         if( pElement->type == LO_CELL )
  3202.         {
  3203.             /* Element is a cell */
  3204.             tptr = pElement;
  3205.         } else {
  3206.             /* Get parent cell of supplied element */
  3207.             tptr = (LO_Element*)lo_GetParentCell(pContext, pElement);
  3208.         }
  3209.         if( !tptr )
  3210.             return NULL;
  3211.     
  3212.         /* Search back until we are at first cell in table */
  3213.         while ( (tptr->lo_any.prev)->type != LO_TABLE )
  3214.         {
  3215.             tptr = tptr->lo_any.prev;
  3216.         }
  3217.     }
  3218.     
  3219.     /* Scan all cells to find first and last */
  3220.     do {
  3221.         if( tptr->type == LO_CELL )
  3222.         {
  3223.             if( bGetColumn && tptr->lo_any.x <= x &&
  3224.                 tptr->lo_any.x > closest  )
  3225.             {
  3226.                 /* We found a cell candidate */
  3227.                 closest = tptr->lo_any.x;
  3228.                 pFirstCell = tptr;
  3229.             } else if( !bGetColumn && tptr->lo_any.y <= y &&
  3230.                        tptr->lo_any.y > closest )
  3231.             {
  3232.                 closest = tptr->lo_any.y;
  3233.                 pFirstCell = tptr;
  3234.             }
  3235.             pLastCellInTable = tptr;
  3236.         }
  3237.         tptr = tptr->lo_any.next;
  3238.  
  3239.        /* Table ends at linefeed (top level) or null element (nested table) */
  3240.     } while (tptr && tptr->type != LO_LINEFEED ); 
  3241.  
  3242.     /* Return last cell element encountered */
  3243.     if( ppLastCellInTable )
  3244.         *ppLastCellInTable = pLastCellInTable;
  3245.  
  3246.     return pFirstCell;    
  3247. }
  3248.  
  3249. /* Assumptions;
  3250.  * Given a LO_CellStruct, the table element defines the begining of table
  3251.  *   and the last cell is that whose "next" pointer is NULL (nested table)
  3252.  *   or a linefeed element (top-level table)
  3253. */
  3254. LO_Element *lo_GetFirstAndLastCellsInTable(MWContext *pContext, LO_Element *pElement, LO_Element **ppLastCell)
  3255. {
  3256.     LO_Element *tptr = NULL;
  3257.     LO_Element *pFirstCell = NULL;
  3258.     LO_Element *pLastCell = NULL;
  3259.     LO_Element *pCurrentCell = NULL;
  3260.  
  3261.     if(pContext == NULL || pElement == NULL )
  3262.         return NULL;
  3263.  
  3264.     if( pElement->type == LO_CELL )
  3265.     {
  3266.         pCurrentCell = pElement;
  3267.     }
  3268.     else if( pElement->type == LO_TABLE )
  3269.     {
  3270.         /* First cell SHOULD be next element, but let's be sure */
  3271.         pCurrentCell = pElement->lo_table.next;
  3272.         while(pCurrentCell->type != LO_CELL)
  3273.         {
  3274.             pCurrentCell = pCurrentCell->lo_any.next;
  3275.         }
  3276.     } else {
  3277.         pCurrentCell = (LO_Element*)lo_GetParentCell(pContext, pElement);
  3278.     }
  3279.  
  3280.     if( pCurrentCell == NULL )
  3281.         return NULL;
  3282.  
  3283.     /* Scan backwards until we hit the table element
  3284.      *  that's before all the cells
  3285.     */
  3286.     tptr = pCurrentCell;
  3287.     while (tptr && (tptr->lo_any.prev)->type != LO_TABLE )
  3288.     {
  3289.         tptr = tptr->lo_any.prev;
  3290.     }
  3291.     
  3292.     /* Scan forward to find last cell -- next is null or line-feed element */
  3293.     do {
  3294.         if( tptr->type == LO_CELL )
  3295.         {
  3296.             if( !pFirstCell )
  3297.                 pFirstCell = tptr;
  3298.  
  3299.             pLastCell = tptr;
  3300.         }
  3301.         tptr = tptr->lo_any.next;
  3302.     } while (tptr && tptr->type != LO_LINEFEED); 
  3303.  
  3304.     /* Return last cell element encountered */
  3305.     if( ppLastCell )
  3306.         *ppLastCell = pLastCell;
  3307.  
  3308.     return pFirstCell;    
  3309. }
  3310.  
  3311. LO_Element *lo_GetFirstAndLastCellsInColumnOrRow(LO_Element *pCellElement, LO_Element **ppLastCell, XP_Bool bInColumn )
  3312. {
  3313.     LO_Element *tptr = NULL;
  3314.     LO_Element *pFirstCell = NULL;
  3315.     LO_Element *pLastCell = NULL;
  3316.  
  3317.     if(pCellElement == NULL || pCellElement->type != LO_CELL)
  3318.         return NULL;
  3319.  
  3320.     /* Scan backwards until we hit the table element
  3321.      *  that's before all the cells
  3322.     */
  3323.     tptr = pCellElement;
  3324.     while (tptr && (tptr->lo_any.prev)->type != LO_TABLE )
  3325.     {
  3326.         tptr = tptr->lo_any.prev;
  3327.     }
  3328.     
  3329.     /* Scan forward to find first and last cells,
  3330.      *   based on sharing left edge (column) or top (row)
  3331.      */
  3332.     do {
  3333.         if( tptr->type == LO_CELL )
  3334.         {
  3335.             if( (bInColumn && pCellElement->lo_any.x == tptr->lo_any.x) ||
  3336.                 (!bInColumn && pCellElement->lo_any.y == tptr->lo_any.y) )
  3337.             {
  3338.                 if( !pFirstCell )
  3339.                     pFirstCell = tptr;
  3340.  
  3341.                 pLastCell = tptr;
  3342.             }
  3343.         }
  3344.         tptr = tptr->lo_any.next;
  3345.     } while (tptr && tptr->type != LO_LINEFEED); 
  3346.  
  3347.     /* Return last cell element encountered */
  3348.     if( ppLastCell )
  3349.         *ppLastCell = pLastCell;
  3350.  
  3351.     return pFirstCell;    
  3352. }
  3353.  
  3354. XP_Bool lo_AllCellsSelectedInColumnOrRow( LO_CellStruct *pCell, XP_Bool bInColumn )
  3355. {
  3356.     LO_Element *pFirstCell;
  3357.     LO_Element *pLastCell;
  3358.     LO_Element *tptr;
  3359.  
  3360.     /* Get first and last cells in row or column */
  3361.     pFirstCell = lo_GetFirstAndLastCellsInColumnOrRow( (LO_Element*)pCell, &pLastCell, bInColumn );
  3362.     if( !pFirstCell || !pLastCell )
  3363.         return FALSE;
  3364.  
  3365.     tptr = pFirstCell;
  3366.  
  3367.     do {
  3368.         if( tptr->type == LO_CELL )
  3369.         {
  3370.             if( (bInColumn && pCell->x == tptr->lo_any.x) ||
  3371.                 (!bInColumn && pCell->y == tptr->lo_any.y) )
  3372.             {
  3373.                 /* If any cell is not selected, return FALSE */
  3374.                 if( !(pCell->ele_attrmask & LO_ELE_SELECTED) )
  3375.                     return FALSE;
  3376.             }
  3377.         }
  3378.         tptr = tptr->lo_any.next;
  3379.     } while (tptr != pLastCell); 
  3380.  
  3381.     return TRUE;
  3382. }
  3383.  
  3384. int32 lo_GetNumberOfCellsInTable(LO_TableStruct *pTable )
  3385. {
  3386.     int32 iCount = 0;
  3387.     if( pTable )
  3388.     {
  3389.         LO_Element *tptr = (LO_Element*)pTable;
  3390.         while( tptr && tptr->type != LO_LINEFEED )
  3391.         {
  3392.             if( tptr->type == LO_CELL)
  3393.                 iCount++;
  3394.             
  3395.             tptr = tptr->lo_any.next;
  3396.         }
  3397.     }
  3398.     return iCount;
  3399. }
  3400.  
  3401. /* Subtract borders and inter-cell spacing to get the available table width
  3402.  *   to use when cell width in HTML is "percent of table"
  3403. */
  3404. int32 lo_CalcTableWidthForPercentMode(LO_Element *pCellElement)
  3405. {
  3406.     LO_Element     *pLastCell;
  3407.     LO_Element     *pFirstCell;
  3408.     LO_Element     *pCell;
  3409.     int32          iWidth;
  3410.     XP_Bool        bDone;
  3411.  
  3412.     /* Never return 0 to avoid divide by zero errors */
  3413.     if( !pCellElement || pCellElement->type != LO_CELL )
  3414.         return -1;
  3415.  
  3416.     iWidth = 0;
  3417.  
  3418.     /* We calculate the width by simply adding up widths of all cells in the row */
  3419.     pFirstCell = lo_GetFirstAndLastCellsInColumnOrRow(pCellElement, &pLastCell, FALSE );
  3420.     pCell = pFirstCell;
  3421.  
  3422.     if( pCell && pLastCell )
  3423.     {
  3424.         bDone = FALSE;
  3425.         while(!bDone) 
  3426.         {
  3427.             if( pCell == pLastCell )
  3428.                 bDone = TRUE;
  3429.  
  3430.             /* Add widths of all cells in the row (must have same top) */
  3431.             if( pCell->type == LO_CELL && 
  3432.                 (pCell->lo_any.y == pFirstCell->lo_any.y) )
  3433.             {
  3434.                 iWidth += pCell->lo_cell.width;            
  3435.             }
  3436.             pCell = pCell->lo_any.next;
  3437.         }
  3438.     }    
  3439.     return (iWidth > 0) ? iWidth : -1;
  3440. }
  3441.  
  3442. XP_Bool LO_IsEmptyCell(LO_CellStruct *cell)
  3443. {
  3444.     if ( cell )
  3445.     {
  3446.         LO_Element *element = cell->cell_list;
  3447.  
  3448.         /* Unlikely, but if nothing inside cell, we're empty! */
  3449.         if(element)
  3450.         {
  3451.             LO_Element *pNext = element->lo_any.next;
  3452.             while(element)
  3453.             {
  3454.                 /* Any non-text element except linefeed is not "empty"
  3455.                  *  and check for text length
  3456.                  * Length of any element is 1. Length of text = real length,
  3457.                  * (but we don't want to consider linefeeds)
  3458.                 */
  3459.                 if( element->type != LO_LINEFEED && lo_GetElementLength(element) > 0 )
  3460.                 {
  3461.                     return FALSE;
  3462.                 }
  3463.                 element = element->lo_any.next;
  3464.             }
  3465.         }
  3466.     }
  3467.     return TRUE;
  3468. }
  3469.  
  3470. LO_Element * lo_GetLastElementInList( LO_Element *eleList )
  3471. {
  3472.     LO_Element *retEle = NULL;
  3473.  
  3474.     if (eleList != NULL)
  3475.     {
  3476.         while (eleList->lo_any.next != NULL)
  3477.             eleList = eleList->lo_any.next;
  3478.  
  3479.         retEle = eleList;
  3480.     }
  3481.  
  3482.     return retEle;
  3483.         
  3484. }
  3485.  
  3486. #ifdef DOM
  3487. /* Checks the layout element to see if the element is enclosed within <SPAN> */
  3488. Bool LO_IsWithinSpan( LO_Element *ele )
  3489. {
  3490.     Bool isInSpan = FALSE;
  3491.  
  3492.     switch (ele->type) {
  3493.  
  3494.     case LO_TEXT:
  3495.         isInSpan = ele->lo_text.ele_attrmask & LO_ELE_IN_SPAN ? TRUE : FALSE;
  3496.         break;
  3497.     case LO_IMAGE:
  3498.         isInSpan = ele->lo_image.ele_attrmask & LO_ELE_IN_SPAN ? TRUE : FALSE;
  3499.         break;
  3500.     case LO_FORM_ELE:
  3501.         isInSpan = ele->lo_form.ele_attrmask & LO_ELE_IN_SPAN ? TRUE : FALSE;
  3502.         break;
  3503.     case LO_EMBED:
  3504.         isInSpan = ele->lo_embed.ele_attrmask & LO_ELE_IN_SPAN ? TRUE : FALSE;
  3505.         break;
  3506. #ifdef JAVA
  3507.     case LO_JAVA:
  3508.         isInSpan = ele->lo_java.ele_attrmask & LO_ELE_IN_SPAN ? TRUE : FALSE;
  3509.         break;
  3510. #endif
  3511.     }
  3512.  
  3513.     return isInSpan;
  3514. }
  3515. #endif
  3516.  
  3517. #ifdef PROFILE
  3518. #pragma profile off
  3519. #endif
  3520.