home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / lib / layout / layedit.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  56.8 KB  |  2,144 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. /* Moved here so we can expose 2 functions outside of ifdef EDITOR */
  21. #include "xp.h"
  22. #include "layout.h"
  23. #include "layers.h"
  24.  
  25. #ifdef EDITOR
  26.  
  27. /* define LAYEDIT so LO_RelayoutData is not defined. */
  28. #define LAYEDIT        1
  29. typedef struct LO_RelayoutData_struct LO_RelayoutData;
  30.  
  31. #include "pa_parse.h"
  32. #include "edt.h"
  33. #include "libi18n.h"
  34.  
  35. #ifdef max
  36. #undef max
  37. #endif
  38. #define max(a, b) (((a) > (b)) ? (a) : (b))
  39.  
  40. struct LO_RelayoutData_struct {
  41.     MWContext *context;
  42.     ED_TagCursor* pTagCursor;
  43.     lo_TopState* top_state;
  44.     lo_DocState* old_state;
  45.     lo_DocState* new_state;
  46.     int32 iStartLine;
  47.     int iStartEditOffset;
  48. };
  49.  
  50. #define LINE_INC        100
  51.  
  52. #ifdef DEBUG
  53.  
  54. PRIVATE
  55. Bool
  56. lo_VerifyStateLayoutImplementation( MWContext *pContext, lo_TopState *top_state, lo_DocState *state, Bool print);
  57.  
  58. #endif
  59.  
  60. #ifdef XP_WIN16
  61.  
  62. #define SIZE_LIMIT        32000
  63.  
  64. void lo_GrowLineArrayByOneForWin16( lo_DocState *state, intn lineNum) 
  65. {
  66.     /* This code is a modified version of the lo_FlushLineList
  67.      * that grows the line array to hold more lines.
  68.     */
  69.  
  70.     intn a_size;
  71.     intn a_indx;
  72.     intn a_line;
  73.     XP_Block *larray_array;
  74.     LO_Element **line_array;
  75.  
  76.     a_size = SIZE_LIMIT / sizeof(LO_Element *);
  77.     a_indx = (lineNum - 1) / a_size;
  78.     a_line = lineNum - (a_indx * a_size);
  79.  
  80.     XP_LOCK_BLOCK(larray_array, XP_Block *, state->larray_array);
  81.     state->line_array = larray_array[a_indx];
  82.  
  83.     if (a_line == a_size)
  84.     {
  85.         state->line_array = XP_ALLOC_BLOCK(LINE_INC *
  86.                     sizeof(LO_Element *));
  87.         if (state->line_array == NULL)
  88.         {
  89.             XP_UNLOCK_BLOCK(state->larray_array);
  90.             state->top_state->out_of_memory = TRUE;
  91.             return;
  92.         }
  93.         XP_LOCK_BLOCK(line_array, LO_Element **, state->line_array);
  94.         line_array[0] = NULL;
  95.         XP_UNLOCK_BLOCK(state->line_array);
  96.         state->line_array_size = LINE_INC;
  97.  
  98.         state->larray_array_size++;
  99.         XP_UNLOCK_BLOCK(state->larray_array);
  100.         state->larray_array = XP_REALLOC_BLOCK(
  101.             state->larray_array, (state->larray_array_size
  102.             * sizeof(XP_Block)));
  103.         if (state->larray_array == NULL)
  104.         {
  105.             state->top_state->out_of_memory = TRUE;
  106.             return;
  107.         }
  108.         XP_LOCK_BLOCK(larray_array, XP_Block *, state->larray_array);
  109.         larray_array[state->larray_array_size - 1] = state->line_array;
  110.         state->line_array = larray_array[a_indx];
  111.     }
  112.     else if (a_line >= state->line_array_size)
  113.     {
  114.         state->line_array_size += LINE_INC;
  115.         if (state->line_array_size > a_size)
  116.         {
  117.             state->line_array_size = (intn)a_size;
  118.         }
  119.         state->line_array = XP_REALLOC_BLOCK(state->line_array,
  120.             (state->line_array_size * sizeof(LO_Element *)));
  121.         if (state->line_array == NULL)
  122.         {
  123.             XP_UNLOCK_BLOCK(state->larray_array);
  124.             state->top_state->out_of_memory = TRUE;
  125.             return;
  126.         }
  127.         larray_array[a_indx] = state->line_array;
  128.     }
  129.  
  130.     /*
  131.      * Place this line of elements into the line array.
  132.      */
  133.     XP_LOCK_BLOCK(line_array, LO_Element **, state->line_array);
  134.     line_array[a_line - 1] = state->line_list;
  135.     XP_UNLOCK_BLOCK(state->line_array);
  136.  
  137.     XP_UNLOCK_BLOCK(state->larray_array);
  138. }
  139.  
  140. #endif
  141.  
  142. PRIVATE
  143. Bool lo_GrowLineArray( lo_DocState *state, int32 iMaxLines )
  144. {
  145.     /* This code is a modified version of the lo_FlushLineList
  146.      * that grows the line array to hold more lines.
  147.     */
  148.  
  149. #ifdef XP_WIN16
  150.     intn linenum = state->line_array_size;
  151.     while ( iMaxLines > linenum){
  152.         lo_GrowLineArrayByOneForWin16(state, linenum);
  153.         if ( state->top_state->out_of_memory == TRUE)
  154.         {
  155.             return FALSE;
  156.         }
  157.         linenum++; 
  158.     }
  159. #else
  160.     int32 line_inc = 0;
  161.  
  162.     while ( iMaxLines > state->line_array_size + line_inc)
  163.     {
  164.  
  165.         if (state->line_array_size > (LINE_INC * 10))
  166.         {
  167.             line_inc += state->line_array_size / 10;
  168.         }
  169.         else
  170.         {
  171.             line_inc += LINE_INC;
  172.         }
  173.     }
  174.  
  175.     if ( line_inc != 0 )
  176.     {
  177.         state->line_array = XP_REALLOC_BLOCK(state->line_array,
  178.             ((state->line_array_size + line_inc) *
  179.             sizeof(LO_Element *)));
  180.         if (state->line_array == NULL)
  181.         {
  182.             state->top_state->out_of_memory = TRUE;
  183.             return FALSE;
  184.         }
  185.         state->line_array_size += line_inc;
  186.     }
  187.  
  188. #endif
  189.  
  190.     return TRUE;
  191. }
  192.  
  193. void lo_MergeStateMoveElement(LO_Element* eptr, int32 yDelta, int32* pEle_id);
  194.  
  195. PRIVATE
  196. void lo_MergeStateMoveCell(LO_CellStruct* cellPtr, int32 yDelta, int32* pEle_id)
  197. {
  198.     /* Like lo_ShiftCell, only we also renumber the element ids. */
  199.     LO_Element* eptr;
  200.     if (cellPtr->cell_bg_layer)
  201.         CL_MoveLayer(cellPtr->cell_bg_layer,
  202.                      cellPtr->x, cellPtr->y);
  203.     for( eptr = cellPtr->cell_list;
  204.         eptr;
  205.         eptr = eptr->lo_any.next)
  206.     {
  207.         lo_MergeStateMoveElement(eptr, yDelta, pEle_id);
  208.     }
  209.     for( eptr = cellPtr->cell_float_list;
  210.         eptr;
  211.         eptr = eptr->lo_any.next)
  212.     {
  213.         lo_MergeStateMoveElement(eptr, yDelta, pEle_id);
  214.     }
  215. }
  216.  
  217. void lo_MergeStateMoveElement(LO_Element* eptr, int32 yDelta, int32* pEle_id)
  218. {
  219.     eptr->lo_any.ele_id = (*pEle_id)++;
  220.     eptr->lo_any.y += yDelta;
  221.     /* Descend into cells */
  222.     if ( eptr->type == LO_CELL )
  223.     {
  224.         lo_MergeStateMoveCell(&eptr->lo_cell, yDelta, pEle_id);
  225.     }
  226.     else if (eptr->type == LO_IMAGE)
  227.     {
  228.         if (eptr->lo_image.layer)
  229.         {
  230.             CL_OffsetLayer(eptr->lo_image.layer, 0, yDelta);
  231.         }
  232.     }
  233. }
  234.  
  235. PRIVATE
  236. void lo_MergeState( MWContext *context, lo_DocState *old_state, int32 iStartLine, 
  237.         int32 iEndLine, lo_DocState *new_state, int32* pRetY, int32* pRetHeight )
  238. {
  239.     LO_Element **old_line_array, **new_line_array;
  240.     LO_Element *prev_element, *start_element, *eptr, *end_element, *new_end_element;
  241.     int32 yDelta;
  242.     int32 ele_id;
  243.     int32 new_line_num;
  244.     int32 new_changed_line_count;
  245.     int32 old_changed_line_count;
  246.     int32 old_delta;
  247.     int32 old_lines_to_move;
  248.     Bool relayout_to_end;
  249.     Bool old_state_empty = (old_state->line_num < 2);
  250.  
  251.     new_changed_line_count = new_state->line_num-1;
  252.     old_changed_line_count = iEndLine-iStartLine;
  253.  
  254.     new_line_num = old_state->line_num - old_changed_line_count + 
  255.             new_changed_line_count;
  256.  
  257.     /*
  258.      *  Grow the line array if we need to.
  259.     */    
  260.     if( !lo_GrowLineArray( old_state, new_line_num+1 ) )
  261.     {
  262.         return;        /* out of memory */
  263.     }
  264.         
  265.  
  266.     /* lock down the line array and copy the lines from the new_state into the
  267.      * old state.
  268.     */
  269.     XP_LOCK_BLOCK(old_line_array, LO_Element **, old_state->line_array);
  270.     XP_LOCK_BLOCK(new_line_array, LO_Element **, new_state->line_array);
  271.  
  272.     if( iEndLine == old_state->line_num-1 ){
  273.         /* ERIC: help! */
  274.         /* There is something very wrong here.  We the layout engine is 
  275.          * telling us the wrong line count.  Force the end by stuffing
  276.          * a zero
  277.         */
  278.  
  279.         /*XP_ASSERT( old_line_array[iEndLine] == 0 );*/
  280.         old_line_array[iEndLine] = 0;
  281.     }
  282.  
  283.     start_element = old_line_array[iStartLine];
  284.     end_element = old_line_array[iEndLine];
  285.     relayout_to_end = ( end_element == 0 );
  286.  
  287.     /*
  288.      * Break the chain so when we recycle these elements they don't continue
  289.      *  and deallocate the entire tree.
  290.     */
  291.     if( end_element && end_element->lo_any.prev){
  292.         end_element->lo_any.prev->lo_any.next = 0;
  293.     }
  294.  
  295.     /*
  296.      * Shrink or grow the the old line array.  XP_BCOPY is supposed to handle
  297.      *  overlaps.
  298.     */
  299.     old_delta = new_changed_line_count - old_changed_line_count;
  300.     old_lines_to_move = (old_state->line_num-1) - iEndLine;
  301.  
  302.     XP_BCOPY( &old_line_array[iEndLine], &old_line_array[iEndLine+old_delta],
  303.             sizeof(LO_Element*)*old_lines_to_move );
  304.  
  305.     old_state->line_num = new_line_num;
  306.  
  307.     /* if we shrunk the line array, make sure it is 0 filled
  308.     */
  309.     if( old_delta < 0 ){
  310.         XP_BZERO( &old_line_array[iEndLine+old_delta+old_lines_to_move],
  311.             sizeof(LO_Element**)*(old_delta * -1) );
  312.     }
  313.  
  314.     if( !old_state_empty ) {
  315.         *pRetY = start_element->lo_any.y;
  316.     }
  317.     else {
  318.         *pRetY = 0;
  319.     }
  320.  
  321.     if( iStartLine != 0 )
  322.     {
  323.         prev_element = start_element->lo_any.prev;
  324.     }
  325.     else {
  326.         prev_element = 0;
  327.     }
  328.  
  329.     if( new_changed_line_count ){
  330.         if( !old_state_empty )
  331.         {
  332.             yDelta = start_element->lo_any.y - new_line_array[0]->lo_any.y;
  333.             ele_id = start_element->lo_any.ele_id;
  334.         }
  335.         else {
  336.             yDelta = 0;
  337.             ele_id = 0;
  338.         }
  339.  
  340.         XP_BCOPY( &new_line_array[0], &old_line_array[iStartLine], 
  341.                 sizeof(LO_Element*)*new_changed_line_count );
  342.  
  343.         if( prev_element )
  344.         {
  345.             prev_element->lo_any.next = old_line_array[iStartLine];
  346.             old_line_array[iStartLine]->lo_any.prev = prev_element;
  347.         }
  348.  
  349.         eptr = old_line_array[iStartLine];
  350.         while( eptr != NULL)
  351.         {
  352.             lo_MergeStateMoveElement(eptr, yDelta, &ele_id);
  353.             new_end_element = eptr;
  354.             eptr = eptr->lo_any.next;
  355.         }
  356.     }
  357.     else {
  358.         yDelta = 0;
  359.         if( !old_state_empty )
  360.         {
  361.             ele_id = start_element->lo_any.ele_id;
  362.         }
  363.         else 
  364.         {
  365.             ele_id = 0;
  366.         }
  367.         new_end_element = prev_element;
  368.     }
  369.  
  370.     /*
  371.      * We now have to patch the the bottom of the newly generated tags into
  372.      *  the old tags.
  373.      *
  374.     */
  375.     if( relayout_to_end == FALSE )
  376.     {
  377.         end_element->lo_any.prev = new_end_element;
  378.         new_end_element->lo_any.next = end_element;
  379.  
  380.         yDelta = (new_end_element->lo_any.y+new_end_element->lo_any.line_height)
  381.                      - end_element->lo_any.y;
  382.         ele_id = new_end_element->lo_any.ele_id+1;
  383.  
  384.         eptr = end_element;
  385.         while( eptr != NULL)
  386.         {
  387.             lo_MergeStateMoveElement(eptr, yDelta, &ele_id);
  388.             eptr = eptr->lo_any.next;
  389.         }
  390.     }
  391.     else
  392.     {
  393.         /* trivia: The -2 is because lines are 1 based, and new_line_num is one
  394.          * more than the allocated number of lines. The array is zero based.
  395.          * So subtract 1 to get the count zero based, and another 1 to get the
  396.          * last allocated line rather than the first un-allocated line.
  397.          */
  398.         if( !old_state_empty )
  399.         {
  400.             eptr = old_line_array[new_line_num - 2];
  401.         }
  402.         else
  403.         {
  404.             eptr = new_end_element;
  405.         }
  406.         /* move to the last element of the last line line */
  407.         while( eptr != NULL && eptr->lo_any.next != NULL)
  408.         {
  409.             eptr = eptr->lo_any.next;
  410.         }
  411.         old_state->end_last_line = eptr;
  412.     }
  413.     
  414.     if( new_changed_line_count )
  415.     {
  416.         if( yDelta == 0 && !relayout_to_end )
  417.         {
  418.             eptr = new_end_element;
  419.             *pRetHeight = eptr->lo_any.y - *pRetY + eptr->lo_any.line_height;
  420.         }
  421.         else
  422.         {
  423.             /* the last line in the document. */
  424.             *pRetHeight = -1;
  425.         }
  426.         eptr = old_line_array[new_line_num-2];
  427.  
  428.         /* We need to add a few pixels to the new line width so that
  429.          * there's always room for the cursor at the end of the line,
  430.          * even if there's a scroll bar.
  431.          *
  432.          * JPNote: We never, ever, shrink the width of the document once
  433.          * we've enlarged it. This is bad.
  434.          */
  435.  
  436. #ifdef XP_MAC
  437. #define INCREMENT_WIDTH_AMT    15
  438. #else
  439. #define INCREMENT_WIDTH_AMT    20
  440. #endif
  441.  
  442. #define max(a, b) (((a) > (b)) ? (a) : (b))
  443.         old_state->max_width = max( old_state->max_width, new_state->max_width + INCREMENT_WIDTH_AMT );
  444.         old_state->y = eptr->lo_any.y +eptr->lo_any.line_height;
  445. #undef max
  446.  
  447.         LO_SetDocumentDimensions(context, old_state->max_width, 
  448.                                  eptr->lo_any.y +eptr->lo_any.line_height);
  449.     }
  450.     else
  451.     {
  452.         eptr = start_element;
  453.         if( eptr )
  454.         {
  455.             *pRetHeight = eptr->lo_any.y - *pRetY + eptr->lo_any.line_height;
  456.         }
  457.         else 
  458.         {
  459.             *pRetHeight = -1;
  460.         }
  461.     }
  462.  
  463. #if 0
  464.     XP_TRACE(( "line count = %d, end pointer = %d\n", 
  465.             new_line_num, old_line_array[new_line_num-2] ));
  466.  
  467.     XP_ASSERT( old_line_array[new_line_num-2] != 0 );
  468. #endif
  469.  
  470.     XP_UNLOCK_BLOCK(new_state->line_array);
  471.     XP_UNLOCK_BLOCK(old_state->line_array);
  472.  
  473.     /* LTNOTE: need to recycle element chain from start_element
  474.     */
  475.     if( start_element )
  476.     {
  477.         lo_relayout_recycle(context, old_state, start_element);
  478.     }
  479.  
  480.     /* 
  481.      * XXX BUGBUG We don't deal with layers yet, but we will eventually have
  482.      * to worry about layers that are in the old state.
  483.      */
  484.  
  485. #ifdef DEBUG
  486.     lo_VerifyLayout(context);
  487. #endif /* DEBUG */
  488. }
  489.  
  490. PRIVATE
  491. void lo_MergeElements( MWContext *context, lo_DocState *old_state, int32 iStartLine, 
  492.         int32 iEndLine, lo_DocState *new_state, int32* pRetY, int32* pRetHeight )
  493. {
  494.     LO_Element **old_line_array, **new_line_array;
  495.     LO_Element *prev_element, *eptr, *end_element, *new_end_element;
  496.     int32 yDelta;
  497.     int32 ele_id;
  498.     int32 new_line_num;
  499.     int32 new_changed_line_count;
  500.     int32 old_changed_line_count;
  501.     int32 old_delta;
  502.     int32 old_lines_to_move;
  503.     Bool relayout_to_end;
  504.     Bool old_state_empty = (old_state->line_num < 2);
  505.  
  506.     new_changed_line_count = new_state->line_num-1;
  507.     old_changed_line_count = iEndLine-iStartLine;
  508.  
  509.     new_line_num = old_state->line_num - old_changed_line_count + 
  510.             new_changed_line_count;
  511.  
  512.     /*
  513.      *  Grow the line array if we need to.
  514.     */    
  515.     if( !lo_GrowLineArray( old_state, new_line_num+1 ) )
  516.     {
  517.         return;        /* out of memory */
  518.     }
  519.         
  520.  
  521.     /* lock down the line array and copy the lines from the new_state into the
  522.      * old state.
  523.     */
  524.     XP_LOCK_BLOCK(old_line_array, LO_Element **, old_state->line_array);
  525.     XP_LOCK_BLOCK(new_line_array, LO_Element **, new_state->line_array);
  526.  
  527.     if( iEndLine == old_state->line_num-1 ){
  528.         /* ERIC: help! */
  529.         /* There is something very wrong here.  We the layout engine is 
  530.          * telling us the wrong line count.  Force the end by stuffing
  531.          * a zero
  532.         */
  533.  
  534.         /*XP_ASSERT( old_line_array[iEndLine] == 0 );*/
  535.         old_line_array[iEndLine] = 0;
  536.     }
  537.  
  538.     end_element = old_line_array[iEndLine];
  539.     relayout_to_end = ( end_element == 0 );
  540.  
  541.     /*
  542.      * Break the chain so when we recycle these elements they don't continue
  543.      *  and deallocate the entire tree.
  544.     */
  545.     if( end_element && end_element->lo_any.prev){
  546.         end_element->lo_any.prev->lo_any.next = 0;
  547.     }
  548.  
  549.     /*
  550.      * Shrink or grow the the old line array.  XP_BCOPY is supposed to handle
  551.      *  overlaps.
  552.     */
  553.     old_delta = new_changed_line_count - old_changed_line_count;
  554.     old_lines_to_move = (old_state->line_num-1) - iEndLine;
  555.  
  556.     XP_BCOPY( &old_line_array[iEndLine], &old_line_array[iEndLine+old_delta],
  557.             sizeof(LO_Element*)*old_lines_to_move );
  558.  
  559.     old_state->line_num = new_line_num;
  560.  
  561.     /* if we shrunk the line array, make sure it is 0 filled
  562.     */
  563.     if( old_delta < 0 ){
  564.         XP_BZERO( &old_line_array[iEndLine+old_delta+old_lines_to_move],
  565.             sizeof(LO_Element**)*(old_delta * -1) );
  566.     }
  567.  
  568.     if( iStartLine != 0 )
  569.     {
  570.         /* find the last element on the previous line */
  571.         prev_element = old_line_array[iStartLine - 1];
  572.         while ( prev_element != NULL )
  573.         {
  574.             if ( prev_element->lo_any.next == NULL )
  575.                 break;
  576.             
  577.             prev_element = prev_element->lo_any.next;
  578.         }
  579.     }
  580.     else {
  581.         prev_element = 0;
  582.     }
  583.     
  584.     if( old_state_empty ) {
  585.         *pRetY = 0;
  586.     }
  587.  
  588.     ele_id = 0;
  589.     
  590.     if( new_changed_line_count ){
  591.         /* copy in new lines */
  592.         XP_BCOPY( &new_line_array[0], &old_line_array[iStartLine], sizeof(LO_Element*)*new_changed_line_count );
  593.  
  594.         if( prev_element )
  595.         {
  596.             prev_element->lo_any.next = old_line_array[iStartLine];
  597.             old_line_array[iStartLine]->lo_any.prev = prev_element;
  598.             
  599.             ele_id = prev_element->lo_any.ele_id + 1;
  600.         }
  601.  
  602.         eptr = old_line_array[iStartLine];
  603.         
  604.         while( eptr != NULL)
  605.         {
  606.             lo_MergeStateMoveElement(eptr, 0, &ele_id);
  607.             new_end_element = eptr;
  608.             eptr = eptr->lo_any.next;
  609.         }
  610.     }
  611.     else {
  612.         /* no new lines to copy in, so get ready to delete the old ones */
  613.         yDelta = 0;
  614.         if( prev_element )
  615.         {
  616.             ele_id = prev_element->lo_any.ele_id + 1;
  617.         }
  618.         else 
  619.         {
  620.             ele_id = 0;
  621.         }
  622.         new_end_element = prev_element;
  623.     }
  624.  
  625.     /*
  626.      * We now have to patch the the bottom of the newly generated tags into
  627.      *  the old tags.
  628.      *
  629.     */
  630.     if( relayout_to_end == FALSE )
  631.     {
  632.         end_element->lo_any.prev = new_end_element;
  633.         new_end_element->lo_any.next = end_element;
  634.  
  635.         yDelta = (new_end_element->lo_any.y+new_end_element->lo_any.line_height)
  636.                      - end_element->lo_any.y;
  637.         ele_id = new_end_element->lo_any.ele_id+1;
  638.  
  639.         eptr = end_element;
  640.         while( eptr != NULL)
  641.         {
  642.             lo_MergeStateMoveElement(eptr, yDelta, &ele_id);
  643.             eptr = eptr->lo_any.next;
  644.         }
  645.     }
  646.     else
  647.     {
  648.         /* trivia: The -2 is because lines are 1 based, and new_line_num is one
  649.          * more than the allocated number of lines. The array is zero based.
  650.          * So subtract 1 to get the count zero based, and another 1 to get the
  651.          * last allocated line rather than the first un-allocated line.
  652.          */
  653.         if( !old_state_empty )
  654.         {
  655.             eptr = old_line_array[new_line_num - 2];
  656.         }
  657.         else
  658.         {
  659.             eptr = new_end_element;
  660.         }
  661.         /* move to the last element of the last line line */
  662.         while( eptr != NULL && eptr->lo_any.next != NULL)
  663.         {
  664.             eptr = eptr->lo_any.next;
  665.         }
  666.         old_state->end_last_line = eptr;
  667.     }
  668.     
  669.     if( new_changed_line_count )
  670.     {
  671.         if( yDelta == 0 && !relayout_to_end )
  672.         {
  673.             eptr = new_end_element;
  674.             *pRetHeight = eptr->lo_any.y - *pRetY + eptr->lo_any.line_height;
  675.         }
  676.         else
  677.         {
  678.             /* the last line in the document. */
  679.             *pRetHeight = -1;
  680.         }
  681.         eptr = old_line_array[new_line_num-2];
  682.  
  683.         /* We need to add a few pixels to the new line width so that
  684.          * there's always room for the cursor at the end of the line,
  685.          * even if there's a scroll bar.
  686.          *
  687.          * JPNote: We never, ever, shrink the width of the document once
  688.          * we've enlarged it. This is bad.
  689.          */
  690.  
  691. #ifdef XP_MAC
  692. #define INCREMENT_WIDTH_AMT    15
  693. #else
  694. #define INCREMENT_WIDTH_AMT    20
  695. #endif
  696.  
  697. #define max(a, b) (((a) > (b)) ? (a) : (b))
  698.         old_state->max_width = max( old_state->max_width, new_state->max_width + INCREMENT_WIDTH_AMT );
  699.         old_state->y = eptr->lo_any.y +eptr->lo_any.line_height;
  700. #undef max
  701.  
  702.         LO_SetDocumentDimensions(context, old_state->max_width, 
  703.                                  eptr->lo_any.y +eptr->lo_any.line_height);
  704.     }
  705.     else
  706.     {
  707. #if 0
  708.         eptr = start_element;
  709.         if( eptr )
  710.         {
  711.             *pRetHeight = eptr->lo_any.y - *pRetY + eptr->lo_any.line_height;
  712.         }
  713.         else 
  714.         {
  715.             *pRetHeight = -1;
  716.         }
  717. #else
  718.         XP_ASSERT(0);
  719.         *pRetHeight = -1;
  720. #endif
  721.     }
  722.  
  723. #if 0
  724.     XP_TRACE(( "line count = %d, end pointer = %d\n", 
  725.             new_line_num, old_line_array[new_line_num-2] ));
  726.  
  727.     XP_ASSERT( old_line_array[new_line_num-2] != 0 );
  728. #endif
  729.  
  730.     XP_UNLOCK_BLOCK(new_state->line_array);
  731.     XP_UNLOCK_BLOCK(old_state->line_array);
  732.  
  733.     /* 
  734.      * XXX BUGBUG We don't deal with layers yet, but we will eventually have
  735.      * to worry about layers that are in the old state.
  736.      */
  737.  
  738. #ifdef DEBUG
  739.     lo_VerifyLayout(context);
  740. #endif /* DEBUG */
  741. }
  742.  
  743. PRIVATE
  744. LO_RelayoutData* lo_NewRelayoutData( MWContext* context, ED_TagCursor* pCursor, 
  745.             int32 iStartLine, int iStartEditOffset )
  746. {
  747.     int32 doc_id;
  748.     LO_RelayoutData *pRd;
  749.  
  750.     pRd = XP_NEW( LO_RelayoutData );
  751.     if( pRd == 0 )
  752.     {
  753.         return 0;
  754.     }
  755.  
  756.     pRd->pTagCursor = pCursor;
  757.     pRd->context = context;
  758.  
  759.     /*
  760.      * Get the unique document ID, and retreive this
  761.      * documents layout state.
  762.      */
  763.     doc_id = XP_DOCID(context);
  764.     pRd->top_state = lo_FetchTopState(doc_id);
  765.     if ((pRd->top_state == NULL)||(pRd->top_state->doc_state == NULL))
  766.     {
  767.         return 0;
  768.     }
  769.     pRd->old_state = pRd->top_state->doc_state;
  770.  
  771.     pRd->iStartLine = iStartLine;
  772.     pRd->iStartEditOffset = iStartEditOffset;
  773.  
  774.     pRd->new_state = lo_NewLayout( context, pRd->old_state->win_width, 
  775.                     pRd->old_state->win_height, pRd->old_state->win_left, 
  776.                     pRd->old_state->win_top, pRd->old_state );
  777.     pRd->new_state->display_blocked = TRUE;
  778.     pRd->new_state->edit_relayout_display_blocked = TRUE;
  779.     
  780.     return pRd;
  781. }
  782.  
  783. PRIVATE
  784. void lo_FreeRelayoutData( MWContext* context, LO_RelayoutData* pRd)
  785. {
  786.     if ( pRd ) {
  787.         if ( pRd->new_state ) {
  788.             lo_DocState* state = pRd->new_state;
  789.             
  790.             /*
  791.              * If there is a blocking element, we'll get into trouble later.
  792.              * Check if CEditImageElement::FinishedLoad is being called.
  793.              */
  794.  
  795.             XP_ASSERT ( state->top_state->layout_blocking_element == NULL );
  796.  
  797.             lo_FreeLayoutData(context, state);
  798.             lo_InternalDiscardDocument(context, state, NULL, FALSE);
  799.         }
  800.         XP_FREE(pRd);
  801.     }
  802. }
  803.  
  804. /* Remove all mquote bullets from elist, returning the list of mquote
  805.    bullets. */ 
  806. PRIVATE 
  807. LO_Element *lo_strip_mquotes(LO_Element **elist)
  808. {
  809.   /* Return value. */
  810.   LO_Element *mquotes = NULL;
  811.   LO_Element *mquotesTail = NULL;
  812.  
  813.   /* For traversing the elist. */
  814.   LO_Element *eptr = *elist;
  815.  
  816.   if (!elist || !*elist) {
  817.     return NULL;
  818.   }
  819.  
  820.   LO_LockLayout();
  821.  
  822.   while (eptr) {
  823.     /* Found a mquote bullet. */
  824.     if (eptr->type == LO_BULLET && 
  825.         eptr->lo_bullet.bullet_type == BULLET_MQUOTE) {
  826.  
  827.       /* Delete from the passed-in element list. */
  828.       if (eptr->lo_any.prev) {
  829.         XP_ASSERT(eptr->lo_any.prev->lo_any.next == eptr);
  830.         eptr->lo_any.prev->lo_any.next = eptr->lo_any.next;
  831.       }
  832.       else {
  833.         /* beginning of list, change the head. */
  834.         XP_ASSERT(*elist == eptr);
  835.         *elist = eptr->lo_any.next;
  836.       }
  837.       if (eptr->lo_any.next) {
  838.         eptr->lo_any.next->lo_any.prev = eptr->lo_any.prev;
  839.       }
  840.       eptr->lo_any.prev = mquotesTail;
  841.  
  842.  
  843.       /* Add to mquotes list. */
  844.       if (mquotes) {
  845.         XP_ASSERT(mquotesTail);
  846.         mquotesTail->lo_any.next = eptr;
  847.       }
  848.       else {
  849.         /* Start the mquotes list. */
  850.         mquotes = eptr;
  851.       }
  852.       mquotesTail = eptr;
  853.  
  854.       /* Move eptr to next element, eptrPrev stays the same. */
  855.       eptr = eptr->lo_any.next;
  856.  
  857.       /* NULL-terminate mquotes list. */
  858.       mquotesTail->lo_any.next = NULL;
  859.     }
  860.  
  861.     /* Not a mquote bullet, just skip it. */
  862.     else {
  863.       eptr = eptr->lo_any.next;
  864.     }
  865.   }
  866.  
  867.   LO_UnlockLayout();
  868.   return mquotes;
  869. }
  870.  
  871. void LO_EditorReflow(MWContext *context, ED_TagCursor *pCursor, 
  872.             int32 iStartLine, int iStartEditOffset, XP_Bool bDisplayTables)
  873. {
  874.     PA_Tag *pTag;
  875.     PA_Tag *pNextTag = 0;
  876.     LO_Element *eptr;
  877.     LO_Element **line_array;
  878.     int32 changedY, changedHeight;
  879.     LO_RelayoutData* pRd;
  880.     Bool bFoundBreak, bBreakIsEndTag;
  881.     int32 iEndLine = -1;
  882.     LO_Element *leadingMquotes = NULL;
  883.     ED_Element *pTagElement;
  884.     Bool bItemComplete;
  885.     LO_Element * element;
  886.     ED_ElementType    elementType;
  887.     LO_Element * startElement;
  888.     LO_Element * endElement;
  889.     LO_Element * prevElement;
  890.     LO_Element ** old_line_array;
  891.     
  892.     context->is_editor |= EDT_RELAYOUT_FLAG; /* Relayout flag */
  893.     pRd = lo_NewRelayoutData( context, pCursor, iStartLine, iStartEditOffset );
  894.  
  895.     /* save the floating element list for later deletion. */
  896.     if( pRd->old_state->float_list != 0 )
  897.     {
  898.         lo_relayout_recycle(context, pRd->new_state, pRd->old_state->float_list);
  899.         pRd->old_state->float_list = NULL;
  900.     }
  901.     
  902.     /* unhook the line array at our start line */
  903.     XP_LOCK_BLOCK(old_line_array, LO_Element **, pRd->old_state->line_array);
  904.     startElement = old_line_array[ iStartLine ];
  905. #ifdef UNHOOK
  906.     if ( ( startElement != NULL ) && ( startElement->lo_any.prev != NULL ) )
  907.     {
  908.         startElement->lo_any.prev->lo_any.next = NULL;
  909.         startElement->lo_any.prev = NULL;
  910.     }
  911. #endif
  912.     
  913.     prevElement = NULL;
  914.     
  915.     if ( startElement != NULL  )
  916.     {
  917.         prevElement = startElement->lo_any.prev;
  918.     }
  919.  
  920.     /* assume we'll layout to the end */
  921.     endElement = NULL;
  922.     
  923.     if ( startElement != NULL )
  924.     {
  925.         changedY = startElement->lo_any.y;
  926.     }
  927.     else
  928.     {
  929.         changedY = 0;
  930.     }
  931.  
  932.     /* set up our state's y position so we don't have to shift the new elements down */
  933.     pRd->new_state->y = startElement->lo_any.y;
  934.     pRd->top_state->element_id = startElement->lo_any.ele_id;
  935.     
  936.     /* set up any external state (such as lists) */
  937.     while( (pTag = pNextTag) != 0 
  938.             || (pTag = EDT_TagCursorGetNextState(pCursor)) != 0 )
  939.     {
  940.         lo_LayoutTag(pRd->context, pRd->new_state, pTag);
  941.         pNextTag = pTag->next;
  942.         PA_FreeTag(pTag);
  943.     }
  944.     
  945.     eptr = NULL;
  946.     XP_LOCK_BLOCK(line_array, LO_Element **, pRd->new_state->line_array);
  947.     eptr = line_array[0];
  948.     if (eptr != NULL )
  949.     {
  950.         lo_relayout_recycle(context, pRd->new_state, eptr);     
  951.     }
  952.     XP_UNLOCK_BLOCK(pRd->old_state->line_array);
  953.  
  954.   /* 
  955.      Get list of any mailing quote bullets, removing them from line_list.
  956.      These are the only elements that we want to preserve. 
  957.      lo_strip_mquotes deals properly with a NULL input.
  958.      Note that this can change the value of pRd->new_state->line_list.
  959.   */
  960.   leadingMquotes = lo_strip_mquotes(&pRd->new_state->line_list);
  961.   if ( pRd->new_state->line_list ) {
  962.         lo_relayout_recycle(context, pRd->new_state, pRd->new_state->line_list);
  963.   }
  964.  
  965.   /* Only leave the leading mquote bullets, if any. */
  966.     pRd->new_state->line_list = leadingMquotes; 
  967.     pRd->new_state->line_num = 1;
  968.  
  969.     /* hack to find the endline */
  970.     /* run through the tags with the tag cursor until we get to the end line */
  971.     bFoundBreak = FALSE;
  972.     pNextTag = NULL;
  973.     while( !bFoundBreak )
  974.     {
  975.         if( pNextTag == NULL )
  976.         {
  977.             pTag = EDT_TagCursorGetNext(pRd->pTagCursor);
  978.         }
  979.         else 
  980.         {
  981.             pTag = pNextTag;
  982.         }
  983.         
  984.         if( pTag == NULL )
  985.         {
  986.             break;
  987.         }
  988.         
  989.         PA_FreeTag(pTag);
  990.  
  991.         if( pNextTag == 0 )
  992.         {
  993.             bFoundBreak = EDT_TagCursorAtBreak( pRd->pTagCursor, &bBreakIsEndTag );
  994.         }
  995.     }
  996.  
  997.     iEndLine = EDT_TagCursorCurrentLine( pRd->pTagCursor );
  998.     if( iEndLine == -1 )
  999.     {
  1000.         /* BRAIN DAMAGE: figger this out - why do we need -2? */
  1001.         iEndLine = pRd->old_state->line_num - 2;
  1002.         if ( iEndLine < 0 )
  1003.         {
  1004.             iEndLine = pRd->old_state->line_num - 1;
  1005.         }
  1006.     }
  1007.     
  1008.     endElement = old_line_array[ iEndLine ];
  1009.     
  1010.     if ( endElement != NULL )
  1011.     {
  1012.         if ( endElement->lo_any.prev != NULL )
  1013.         {
  1014.             endElement->lo_any.prev->lo_any.next = NULL;
  1015.             endElement->lo_any.prev = NULL;
  1016.         }
  1017.     }
  1018.     
  1019.     /* unhook our elements from the old state record */
  1020.     if ( pRd->old_state->line_list == startElement )
  1021.     {
  1022.         pRd->old_state->line_list = NULL;
  1023.     }
  1024.     
  1025.     if ( pRd->old_state->end_last_line == startElement )
  1026.     {
  1027.         pRd->old_state->end_last_line = NULL;
  1028.     }
  1029.         
  1030.     /* reflow from our start to our end element */
  1031.     LO_Reflow(pRd->context, pRd->new_state, startElement, endElement );
  1032.     
  1033.     /* layout any cleanup tags */
  1034.     if( bFoundBreak )
  1035.     {
  1036.         if( bBreakIsEndTag ){
  1037.             pTag = EDT_TagCursorGetNext(pRd->pTagCursor);
  1038.             lo_LayoutTag(pRd->context, pRd->new_state, pTag);
  1039.         }
  1040.         else {
  1041.             pTag = EDT_TagCursorGetNext(pRd->pTagCursor);
  1042.             if ( pTag->type == P_TABLE ) {
  1043.                 lo_CloseOutLayout( pRd->context, pRd->new_state);
  1044.             }
  1045.             else {
  1046.                 lo_LayoutTag(pRd->context, pRd->new_state, pTag);
  1047.             }
  1048.         }
  1049.         EDT_DeleteTagChain(pTag);
  1050.  
  1051.         /* don't close layout.  We just flushed this line to the proper
  1052.          *  height, there is a start of a new tag in the buffer.
  1053.         */
  1054.     }
  1055.     
  1056.     /* cleanup the element list for the old elements before the reflow */
  1057.     if ( prevElement )
  1058.     {
  1059.         prevElement->lo_any.next = NULL;
  1060.     }
  1061.     
  1062. #if defined( DEBUG_shannon )
  1063.     XP_TRACE(("\n\nELEMENTS TO MERGE - new reflow"));
  1064.     lo_VerifyStateLayoutImplementation( context, pRd->top_state, pRd->new_state, TRUE);
  1065. #endif
  1066.     
  1067.     lo_MergeElements( context, pRd->old_state, iStartLine, iEndLine,
  1068.                 pRd->new_state, &changedY, &changedHeight );
  1069.  
  1070.     /* Free the layout */
  1071.     lo_FreeRelayoutData(context, pRd);
  1072.     
  1073.     context->is_editor &= ~EDT_RELAYOUT_FLAG;
  1074.     FE_DocumentChanged( context, changedY, changedHeight );
  1075. }
  1076.  
  1077. void LO_Relayout(MWContext *context, ED_TagCursor *pCursor, 
  1078.             int32 iStartLine, int iStartEditOffset, XP_Bool bDisplayTables)
  1079. {
  1080.     PA_Tag *pTag;
  1081.     PA_Tag *pNextTag = 0;
  1082.     LO_Element *eptr;
  1083.     LO_Element **line_array;
  1084.     int32 changedY, changedHeight;
  1085.     LO_RelayoutData* pRd;
  1086.     Bool bFoundBreak, bBreakIsEndTag;
  1087.     int32 iEndLine = -1;
  1088.     LO_Element *leadingMquotes = NULL;
  1089.     context->is_editor |= EDT_RELAYOUT_FLAG; /* Relayout flag */
  1090.     pRd = lo_NewRelayoutData( context, pCursor, iStartLine, iStartEditOffset );
  1091.  
  1092.     /* We need to keep images loaded during relayout. Images are reference counted.
  1093.      * When the total number of layout elements that use an image drops to zero,
  1094.      * the image is kicked out of the cache. We have to have the images in the cache,
  1095.      * or else the layout engine will start an asynchronous load. The asynchronous load
  1096.      * is bad because we don't have any way of waiting for it to complete. And that in
  1097.      * turn means we would have to leave LO_Relayout with an inconsistent CEditElement /
  1098.      * LO_Element state.
  1099.      *
  1100.      * Here's how there might come to be elements on the float list: When the document has
  1101.      * right-aligned images, they are placed on the float list during the original load.
  1102.      *
  1103.      * Happily, there's a way around this problem: We make sure we don't delete the old
  1104.      * image tags until we've created the new image tags. That's why we use lo_relayout_recycle,
  1105.      * which puts the image elements on the "trash" list of pRd->new_state. Then we clean the
  1106.      * trash in lo_InternalDiscardDocument.
  1107.      *
  1108.      */
  1109.  
  1110.     /* save the floating element list for later deletion. */
  1111.     if( pRd->old_state->float_list != 0 )
  1112.     {
  1113.         lo_relayout_recycle(context, pRd->new_state, pRd->old_state->float_list);
  1114.         pRd->old_state->float_list = NULL;
  1115.     }
  1116.  
  1117.     while( (pTag = pNextTag) != 0 
  1118.             || (pTag = EDT_TagCursorGetNextState(pCursor)) != 0 )
  1119.     {
  1120.         lo_LayoutTag(pRd->context, pRd->new_state, pTag);
  1121.         pNextTag = pTag->next;
  1122.         PA_FreeTag(pTag);
  1123.     }
  1124.     
  1125.     eptr = NULL;
  1126.     XP_LOCK_BLOCK(line_array, LO_Element **, pRd->new_state->line_array);
  1127.     eptr = line_array[0];
  1128.     if (eptr != NULL )
  1129.     {
  1130.         lo_relayout_recycle(context, pRd->new_state, eptr);     
  1131.     }
  1132.     XP_UNLOCK_BLOCK(pRd->old_state->line_array);
  1133.  
  1134.   /* 
  1135.      Get list of any mailing quote bullets, removing them from line_list.
  1136.      These are the only elements that we want to preserve. 
  1137.      lo_strip_mquotes deals properly with a NULL input.
  1138.      Note that this can change the value of pRd->new_state->line_list.
  1139.   */
  1140.   leadingMquotes = lo_strip_mquotes(&pRd->new_state->line_list);
  1141.   if ( pRd->new_state->line_list ) {
  1142.         lo_relayout_recycle(context, pRd->new_state, pRd->new_state->line_list);
  1143.   }
  1144.  
  1145.   /* Only leave the leading mquote bullets, if any. */
  1146.     pRd->new_state->line_list = leadingMquotes; 
  1147.     pRd->new_state->line_num = 1;
  1148.  
  1149.     /* while there are tags to parse... */
  1150.     bFoundBreak = FALSE;
  1151.     pNextTag = NULL;
  1152.     while( !bFoundBreak )
  1153.     {
  1154.         if( pNextTag == NULL )
  1155.         {
  1156.             pTag = EDT_TagCursorGetNext(pRd->pTagCursor);
  1157.         }
  1158.         else 
  1159.         {
  1160.             pTag = pNextTag;
  1161.         }
  1162.         if( pTag == NULL ){
  1163.             break;
  1164.         }
  1165.  
  1166.         pRd->new_state->display_blocked = TRUE;
  1167.         pNextTag = pTag->next;
  1168.         if( iStartEditOffset && pTag->type == P_TEXT )
  1169.         {
  1170.             pRd->new_state->edit_current_offset = iStartEditOffset;
  1171.             pRd->new_state->edit_force_offset = TRUE;
  1172.             lo_LayoutTag(pRd->context, pRd->new_state, pTag);
  1173.             iStartEditOffset = 0;
  1174.             pRd->new_state->edit_force_offset = FALSE;
  1175.             PA_FreeTag(pTag);
  1176.         }
  1177.         else
  1178.         {
  1179. /*            lo_LayoutTag(pRd->context, pRd->new_state, pTag);*/
  1180.             /* Matches code at end of LO_ProcessTag */
  1181.             lo_DocState *state = pRd->new_state;
  1182.             lo_DocState *orig_state;
  1183.             lo_DocState *up_state;
  1184.             PA_Tag* tag = pTag;
  1185.  
  1186.             /*
  1187.              * Divert all tags to the current sub-document if there is one.
  1188.              */
  1189.             up_state = NULL;
  1190.             orig_state = state;
  1191.             if ( bDisplayTables ) {
  1192.                 while (state->sub_state != NULL)
  1193.                 {
  1194.                     lo_DocState *new_state;
  1195.  
  1196.                     up_state = state;
  1197.                     new_state = state->sub_state;
  1198.                     state = new_state;
  1199.                 }
  1200.             }
  1201.  
  1202.             /* orig_state->top_state->layout_status = status; */
  1203.  
  1204.             {
  1205.                 lo_DocState *tmp_state;
  1206.                 Bool may_save;
  1207.  
  1208.                 if ((state->is_a_subdoc == SUBDOC_CELL)||
  1209.                     (state->is_a_subdoc == SUBDOC_CAPTION))
  1210.                 {
  1211.                     may_save = TRUE;
  1212.                 }
  1213.                 else
  1214.                 {
  1215.                     may_save = FALSE;
  1216.                 }
  1217.  
  1218.                 /* Some table routines reach out to find the top doc state.
  1219.                  * So we replace it for the duration of this call.
  1220.                  */
  1221.                 pRd->top_state->doc_state = pRd->new_state;
  1222.                 state->edit_relayout_display_blocked = TRUE;
  1223.                 state->display_blocked = TRUE;
  1224.  
  1225.                 lo_LayoutTag(context, state, tag);
  1226.                 pRd->top_state->doc_state = pRd->old_state;
  1227.                 tmp_state = lo_CurrentSubState(orig_state);
  1228.  
  1229.                 if (may_save != FALSE)
  1230.                 {
  1231.                     /*
  1232.                      * That tag popped us up one state level.  If this new
  1233.                      * state is still a subdoc, save the tag there.
  1234.                      */
  1235.                     if (tmp_state == up_state)
  1236.                     {
  1237.                         if ((tmp_state->is_a_subdoc == SUBDOC_CELL)||
  1238.                             (tmp_state->is_a_subdoc == SUBDOC_CAPTION))
  1239.                         {
  1240.                             lo_SaveSubdocTags(context, tmp_state, tag);
  1241.                         }
  1242.                         else
  1243.                         {
  1244.                             PA_FreeTag(tag);
  1245.                         }
  1246.                     }
  1247.                     /*
  1248.                      * Else that tag put us in a new subdoc on the same
  1249.                      * level.  It needs to be saved one level up,
  1250.                      * if the parent is also a subdoc.
  1251.                      */
  1252.                     else if ((up_state != NULL)&&
  1253.                         (tmp_state == up_state->sub_state)&&
  1254.                         (tmp_state != state))
  1255.                     {
  1256.                         if ((up_state->is_a_subdoc == SUBDOC_CELL)||
  1257.                              (up_state->is_a_subdoc == SUBDOC_CAPTION))
  1258.                         {
  1259.                             lo_SaveSubdocTags(context, up_state, tag);
  1260.                         }
  1261.                         else
  1262.                         {
  1263.                             PA_FreeTag(tag);
  1264.                         }
  1265.                     }
  1266.                     /*
  1267.                      * Else we are still in the same subdoc
  1268.                      */
  1269.                     else if (tmp_state == state)
  1270.                     {
  1271.                         lo_SaveSubdocTags(context, state, tag);
  1272.                     }
  1273.                     /*
  1274.                      * Else that tag started a new, nested subdoc.
  1275.                      * Add the starting tag to the parent.
  1276.                      */
  1277.                     else if (tmp_state == state->sub_state)
  1278.                     {
  1279.                         lo_SaveSubdocTags(context, state, tag);
  1280.                         /*
  1281.                          * Since we have extended the parent chain,
  1282.                          * we need to reset the child to the new
  1283.                          * parent end-chain.
  1284.                          */
  1285.                         if ((tmp_state->is_a_subdoc == SUBDOC_CELL)||
  1286.                             (tmp_state->is_a_subdoc == SUBDOC_CAPTION))
  1287.                         {
  1288.                             tmp_state->subdoc_tags =
  1289.                                 state->subdoc_tags_end;
  1290.                         }
  1291.                     }
  1292.                     /*
  1293.                      * This can never happen.
  1294.                      */
  1295.                     else
  1296.                     {
  1297.                         PA_FreeTag(tag);
  1298.                     }
  1299.  
  1300.                     state = tmp_state;
  1301.                 }
  1302.                 else
  1303.                 {
  1304.                     PA_FreeTag(tag);
  1305.                 }
  1306.             }
  1307.         }
  1308. /*        pNextTag = pTag->next;
  1309.         PA_FreeTag(pTag);
  1310. */        if( pNextTag == 0 ){
  1311.             bFoundBreak = EDT_TagCursorAtBreak( pRd->pTagCursor, &bBreakIsEndTag );
  1312.         }
  1313.     }
  1314.  
  1315.     if( bFoundBreak )
  1316.     {
  1317.         if( bBreakIsEndTag ){
  1318.             pTag = EDT_TagCursorGetNext(pRd->pTagCursor);
  1319.             lo_LayoutTag(pRd->context, pRd->new_state, pTag);
  1320.             iEndLine = EDT_TagCursorCurrentLine( pRd->pTagCursor );
  1321.         }
  1322.         else {
  1323.             iEndLine = EDT_TagCursorCurrentLine( pRd->pTagCursor );
  1324.             pTag = EDT_TagCursorGetNext(pRd->pTagCursor);
  1325.             if ( pTag->type == P_TABLE ) {
  1326.                 lo_CloseOutLayout( pRd->context, pRd->new_state);
  1327.             }
  1328.             else {
  1329.                 lo_LayoutTag(pRd->context, pRd->new_state, pTag);
  1330.             }
  1331.         }
  1332.         EDT_DeleteTagChain(pTag);
  1333.  
  1334.         /* don't close layout.  We just flushed this line to the proper
  1335.          *  height, there is a start of a new tag in the buffer.
  1336.         */
  1337.     }
  1338.     else {
  1339.         lo_CloseOutLayout( pRd->context, pRd->new_state);
  1340.     }
  1341.  
  1342.     
  1343.     if( iEndLine == -1 ){
  1344.         iEndLine = pRd->old_state->line_num-1;
  1345.     }
  1346.  
  1347.  
  1348.     /* Go up the liststack, closing out any mquotes. */
  1349.     {
  1350.         lo_ListStack *lptr;
  1351.         lptr = pRd->new_state->list_stack;
  1352.         while (lptr->type != P_UNKNOWN && lptr->next != NULL)
  1353.         {
  1354.             if (lptr->quote_type == QUOTE_MQUOTE)
  1355.             {
  1356.                 lo_add_leading_bullets(context,pRd->new_state,
  1357.                                        lptr->mquote_line_num - 1,
  1358.                                        pRd->new_state->line_num - 2,
  1359.                                        lptr->mquote_x);
  1360.             }
  1361.                     
  1362.             lptr = lptr->next;
  1363.         }
  1364.     }
  1365.  
  1366. #ifdef DEBUG_shannon
  1367.     XP_TRACE(("\n\nELEMENTS TO MERGE - old relayout"));
  1368.     lo_VerifyStateLayoutImplementation( context, pRd->top_state, pRd->new_state, TRUE);
  1369. #endif
  1370.  
  1371.     lo_MergeState( context, pRd->old_state, iStartLine, iEndLine,
  1372.                 pRd->new_state, &changedY, &changedHeight );
  1373.  
  1374.     /* top_state->doc_state = new_state; */
  1375.     /* LTNOTE:need to destroy the new state        */
  1376.  
  1377.     /* kill the current state */
  1378.     /*
  1379.     FE_ClearView( context, FE_VIEW );
  1380.     lo_RefreshDocumentArea( context, state, 0, 0,new_state->win_width, 
  1381.             new_state->win_height);
  1382.     */
  1383.  
  1384.     /* Free the layout */
  1385.     lo_FreeRelayoutData(context, pRd);
  1386.     
  1387.     context->is_editor &= ~EDT_RELAYOUT_FLAG;
  1388.     FE_DocumentChanged( context, changedY, changedHeight );
  1389. }
  1390.  
  1391. PRIVATE
  1392. Bool lo_MovePosition( MWContext *pContext, 
  1393.             LO_Element *pElement, intn iOffset, 
  1394.             ED_Element **ppEdElement, intn* pOffset, Bool bForward)
  1395. {
  1396.     int32 iOffset32 = iOffset; /* Really should use a sythetic type for offsets. */
  1397.     Bool bResult = LO_ComputeNewPosition( pContext, LO_NA_CHARACTER,
  1398.         FALSE, FALSE, bForward, &pElement, &iOffset32);
  1399.  
  1400.     iOffset = (intn) iOffset32;
  1401.  
  1402.     if ( bResult ) {
  1403.         *ppEdElement = pElement->lo_any.edit_element;
  1404.         *pOffset = pElement->lo_any.edit_offset+iOffset;
  1405.     }
  1406.     return bResult;
  1407. }
  1408.  
  1409. Bool LO_PreviousPosition( MWContext *pContext, 
  1410.             LO_Element *pElement, intn iOffset, 
  1411.             ED_Element **ppEdElement, intn* pOffset)
  1412. {
  1413.     return lo_MovePosition(pContext, pElement, iOffset,
  1414.         ppEdElement, pOffset, FALSE);
  1415. }
  1416.  
  1417. Bool LO_NextPosition( MWContext *pContext, 
  1418.             LO_Element *pElement, intn iOffset, 
  1419.             ED_Element **ppEdElement, intn* pOffset)
  1420. {
  1421.     return lo_MovePosition( pContext, pElement, iOffset,
  1422.         ppEdElement, pOffset, TRUE);
  1423. }
  1424.  
  1425. /*
  1426.  * Find the first text element on a line.
  1427.  */
  1428. LO_Element* LO_FirstElementOnLine( MWContext *pContext, int32 x, int32 y, 
  1429.         int32* pLineNum )
  1430. {
  1431.     lo_TopState* top_state;
  1432.     lo_DocState *state;
  1433.     int32 iLine;
  1434.     LO_Element **line_array, *pElement;
  1435.     Bool bFound = FALSE;
  1436.  
  1437.     /*
  1438.      * Get the unique document ID, and retreive this
  1439.      * documents layout state.
  1440.      */
  1441.     top_state = lo_FetchTopState(XP_DOCID(pContext));
  1442.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  1443.     {
  1444.         return 0;
  1445.     }
  1446.     state = top_state->doc_state;
  1447.  
  1448.  
  1449.     XP_LOCK_BLOCK(line_array, LO_Element **, state->line_array);
  1450.  
  1451.     /* find the line we are currently on */
  1452.     iLine = lo_PointToLine( pContext, state, x, y);
  1453.     if( pLineNum ){
  1454.         *pLineNum = iLine;
  1455.     }
  1456.     pElement = line_array[iLine];
  1457.     while( !bFound )
  1458.     {
  1459.         /* if we've found a text element.  We've for sure, found the line. */
  1460.         if( lo_EditableElement( pElement->type ) && pElement->lo_any.edit_element != 0 ){
  1461.             bFound = TRUE;
  1462.         }
  1463.         else {
  1464.             pElement = pElement->lo_any.next;
  1465.         }
  1466.     }
  1467.  
  1468.     XP_UNLOCK_BLOCK(state->line_array);
  1469.     return pElement;
  1470. }
  1471.  
  1472. /*
  1473.  * Find the first text element on a line.
  1474.  */
  1475. PRIVATE
  1476. LO_Element* LO_LastElementOnLine( MWContext *pContext, int32 x, int32 y, 
  1477.         int32* pLineNum )
  1478. {
  1479.     lo_TopState* top_state;
  1480.     lo_DocState *state;
  1481.     int32 iLine;
  1482.     LO_Element **line_array, *pElement, *pFoundElement, *pEnd;
  1483.  
  1484.     /*
  1485.      * Get the unique document ID, and retreive this
  1486.      * documents layout state.
  1487.      */
  1488.     top_state = lo_FetchTopState(XP_DOCID(pContext));
  1489.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  1490.     {
  1491.         return 0;
  1492.     }
  1493.     state = top_state->doc_state;
  1494.  
  1495.  
  1496.     XP_LOCK_BLOCK(line_array, LO_Element **, state->line_array);
  1497.  
  1498.     /* find the line we are currently on */
  1499.     iLine = lo_PointToLine( pContext, state, x, y);
  1500.     if( pLineNum ){
  1501.         *pLineNum = iLine;
  1502.     }
  1503.  
  1504.     pElement = line_array[iLine];
  1505.     pFoundElement = 0;
  1506.     if( iLine == state->line_num-2 ){
  1507.         pEnd = NULL;
  1508.     }
  1509.     else {
  1510.         pEnd = line_array[iLine+1];
  1511.     }
  1512.  
  1513.     while( pElement != pEnd )
  1514.     {
  1515.         /* if we've found a text element.  We've for sure, found the line. */
  1516.         if( pElement->type == LO_TEXT ){
  1517.             pFoundElement = pElement;
  1518.         }
  1519.         pElement = pElement->lo_any.next;
  1520.     }
  1521.  
  1522.     XP_UNLOCK_BLOCK(state->line_array);
  1523.     return pFoundElement;
  1524. }
  1525.  
  1526.  
  1527. LO_Element* LO_BeginOfLine( MWContext *pContext, LO_Element *pElement){
  1528.     LO_Element *pFirst = LO_FirstElementOnLine( pContext, pElement->lo_any.x + pElement->lo_any.width / 2,
  1529.                 pElement->lo_any.y + pElement->lo_any.height / 2, 0 );
  1530.  
  1531.     return pFirst;
  1532. }
  1533.  
  1534. LO_Element* LO_EndOfLine( MWContext *pContext, LO_Element *pElement ){
  1535.     LO_Element *pLast = LO_LastElementOnLine( pContext, pElement->lo_any.x,
  1536.                 pElement->lo_any.y, 0 );
  1537.     return pLast;
  1538.  
  1539. }
  1540.  
  1541. void LO_PositionCaretBounded(MWContext *context, int32 x, int32 y,
  1542.             int32 minY, int32 maxY )
  1543. {
  1544.     int32 doc_id;
  1545.     lo_TopState *top_state;
  1546.     lo_DocState *state;
  1547.     int32 retX, retY;
  1548.     int32 iLine;
  1549.     int dir = 0;
  1550.     Bool bFound = FALSE;
  1551.  
  1552.     /*
  1553.      * Get the unique document ID, and retreive this
  1554.      * documents layout state.
  1555.      */
  1556.     doc_id = XP_DOCID(context);
  1557.     top_state = lo_FetchTopState(doc_id);
  1558.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  1559.     {
  1560.         return;
  1561.     }
  1562.     state = top_state->doc_state;
  1563.  
  1564.  
  1565.     iLine = lo_PointToLine( context, state, x, y );
  1566.  
  1567. #define FORWARD     1
  1568. #define BACKWARD    2
  1569.  
  1570.     while( iLine >= 0 && iLine < state->line_num -2 && !bFound)
  1571.     {
  1572.         bFound = lo_FindBestPositionOnLine( context, state, iLine, x, y, dir == FORWARD, &retX, &retY );
  1573.         if( bFound && dir != BACKWARD && retY < minY ){
  1574.             iLine++;
  1575.             dir = FORWARD;
  1576.             bFound = FALSE;
  1577.         }
  1578.         else if ( bFound && dir != FORWARD && retY > maxY ){
  1579.             iLine--;
  1580.             dir = BACKWARD;
  1581.             bFound = FALSE;
  1582.         }
  1583.     }
  1584.     if( bFound ){
  1585.         LO_PositionCaret( context, retX, retY, NULL );
  1586.     }
  1587. }
  1588.  
  1589. PRIVATE
  1590. void LO_Resize( MWContext *pContext ){
  1591. }
  1592.  
  1593. void LO_RefetchWindowDimensions( MWContext *pContext ){
  1594.     int32 doc_id;
  1595.     lo_TopState *top_state;
  1596.     lo_DocState *state;
  1597.     int32 topX,topY, winWidth, winHeight;
  1598.  
  1599.     /*
  1600.      * Get the unique document ID, and retreive this
  1601.      * documents layout state.
  1602.      */
  1603.     doc_id = XP_DOCID(pContext);
  1604.     top_state = lo_FetchTopState(doc_id);
  1605.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  1606.     {
  1607.         return;
  1608.     }
  1609.     state = top_state->doc_state;
  1610.     FE_GetDocAndWindowPosition( pContext, &topX, &topY, &winWidth, &winHeight );
  1611.     state->win_width = winWidth;
  1612.     state->win_height = winHeight;
  1613. }
  1614.  
  1615.  
  1616. #ifdef DEBUG
  1617.  
  1618. PRIVATE
  1619. Bool
  1620. lo_PrintLayoutElement(MWContext *pContext, lo_TopState* top_state,
  1621.                       lo_DocState* state, LO_Element *eptr, int32 index);
  1622.  
  1623.  
  1624. Bool
  1625. lo_VerifyList( MWContext *pContext, lo_TopState* top_state,
  1626.               lo_DocState* state,
  1627.               LO_Element* start,
  1628.               LO_Element* end,
  1629.               LO_Element* floating,
  1630.               Bool print)
  1631. {
  1632.     Bool result = TRUE;
  1633.     LO_Element *eptr;
  1634.     LO_Element *prev;
  1635.     int32 index;
  1636.     int32 elementID;
  1637.     prev = NULL;
  1638.     index = 0;
  1639.     elementID = -1;
  1640.  
  1641.     for ( eptr = start; eptr; eptr = eptr->lo_any.next )
  1642.     {
  1643.         if ( print )
  1644.         {
  1645.             result = lo_PrintLayoutElement(pContext, top_state, state, eptr, index);        
  1646.         }
  1647.         /*
  1648.          * Check next/prev pointer consistency
  1649.          */
  1650.         if ( eptr->lo_any.prev != prev )
  1651.         {
  1652.             XP_TRACE(("element %ld at address 0x%08x has a bad prev pointer.",
  1653.                 index, eptr));
  1654.             XP_TRACE((" prev is 0x%08x , should be 0x%08x.",
  1655.                 eptr->lo_any.prev, prev));
  1656.             result = FALSE;
  1657.         }
  1658.  
  1659.         /*
  1660.          * Does this element have a valid type?
  1661.          */
  1662.         if ( eptr->lo_any.type < 0 || eptr->lo_any.type > LO_SPACER ) {
  1663.             XP_TRACE(("element %ld at address 0x%08x has an unknown type %d.",
  1664.                 index, eptr, eptr->lo_any.type));
  1665.             result = FALSE;
  1666.         }
  1667.         /*
  1668.          * Does this element have a valid element id?
  1669.          */
  1670.         if ( eptr->lo_any.ele_id <= elementID ) {
  1671.             XP_TRACE(("element %ld at address 0x%08x has an invalid ele_id %ld.",
  1672.                 index, eptr, eptr->lo_any.ele_id));
  1673.             /* Exit because the pointers may be invalid. */
  1674.             return FALSE;
  1675.             
  1676.             /* Keep going for now, till you can because element ids might be
  1677.                getting corrupted by relayout even though pointers are valid */
  1678.             /* result = FALSE; */
  1679.         }
  1680.         elementID = eptr->lo_any.ele_id;
  1681.  
  1682.         /*
  1683.          *  Verify the edit element if it exists.
  1684.          */
  1685.         if ( eptr->lo_any.edit_element != NULL ) 
  1686.         {
  1687.             /*EDT_VerifyLayoutElement( pContext, eptr, print );*/
  1688.         }
  1689.         /*
  1690.          * Update our loop variables
  1691.          */
  1692.         prev = eptr;
  1693.         index++;
  1694.     }
  1695.     
  1696.  
  1697.     /* If we just verified the float list, don't check for end because
  1698.        end for float list is not stored in doc state */
  1699.     if (state->float_list != start)
  1700.     {
  1701.         /*
  1702.          * Check that the last "next" pointer points to the end of the document.
  1703.          */
  1704.         if ( prev != end ) {
  1705.             XP_TRACE(("end 0x%08x is not the same as the last linked-list element 0x%08x",
  1706.                 end, prev));
  1707.             result = FALSE;
  1708.         }
  1709.     }
  1710.  
  1711.     /* Verify floating element list */
  1712.     if (floating != NULL)
  1713.     {
  1714.         lo_VerifyList(pContext, top_state, state, floating, NULL, NULL, print);
  1715.     }
  1716.  
  1717.     return result;
  1718. }
  1719.  
  1720.  
  1721. PRIVATE
  1722. Bool
  1723. lo_PrintLayoutElement(MWContext *pContext, lo_TopState* top_state,
  1724.                       lo_DocState* state, LO_Element *eptr, int32 index)
  1725. {
  1726.     int16 type;
  1727.     Bool result = TRUE;
  1728.     const char* typeString;
  1729.     static const char* kTypeStrings[] = {"LO_UNKNOWN",
  1730.         "LO_NONE",
  1731.         "LO_TEXT",
  1732.         "LO_LINEFEED",
  1733.         "LO_HRULE",
  1734.         "LO_IMAGE",
  1735.         "LO_BULLET",
  1736.         "LO_FORM_ELE",
  1737.         "LO_SUBDOC",
  1738.         "LO_TABLE",
  1739.         "LO_CELL",
  1740.         "LO_EMBED",
  1741.         "LO_EDGE",
  1742.         "LO_JAVA",
  1743.         "LO_SCRIPT",
  1744.         "LO_OBJECT",
  1745.         "LO_PARAGRAPH",
  1746.         "LO_CENTER",
  1747.         "LO_MULTICOLUMN",
  1748.         "LO_FLOAT",
  1749.         "LO_TEXTBLOCK",
  1750.         "LO_LIST",
  1751.         "LO_DESCTITLE",
  1752.         "LO_DESCTEXT",
  1753.         "LO_BLOCKQUOTE",
  1754.         "LO_LAYER",
  1755.         "LO_HEADING",
  1756.         "LO_SPAN",
  1757.         "LO_DIV",
  1758.         "LO_SPACER"
  1759.     };
  1760.     type = eptr->lo_any.type;
  1761.     if ( type < LO_UNKNOWN || type > LO_SPACER ) typeString  = "Illegal type";
  1762.     else typeString = kTypeStrings[type + 1];
  1763.  
  1764.     XP_TRACE(("[%d] 0x%08x type %s(%d) ele_id %d",
  1765.         index, eptr, typeString, type, eptr->lo_any.ele_id));
  1766.     XP_TRACE(("\tposition %d,%d size %d, %d offset %d, %d, lineheight %d",
  1767.         eptr->lo_any.x,
  1768.         eptr->lo_any.y,
  1769.         eptr->lo_any.width,
  1770.         eptr->lo_any.height,
  1771.         eptr->lo_any.x_offset,
  1772.         eptr->lo_any.y_offset,
  1773.         eptr->lo_any.line_height));
  1774. #ifdef EDITOR
  1775.     XP_TRACE(("\tedit_element 0x%08x edit_offset %d",
  1776.         eptr->lo_any.edit_element,
  1777.         eptr->lo_any.edit_offset));
  1778. #endif
  1779.     switch ( type )
  1780.     {
  1781.     case LO_TEXT:
  1782.         {
  1783.             char* text;
  1784.             char saveChar;
  1785.  
  1786.             XP_LOCK_BLOCK(text, char *, eptr->lo_text.text);
  1787.             saveChar = text[ eptr->lo_text.text_len ];
  1788.             text[ eptr->lo_text.text_len ] = '\0';
  1789.             XP_TRACE(("\ttext \"%s\"", text));
  1790.             text[ eptr->lo_text.text_len ] = saveChar;
  1791.             XP_TRACE(("block offset: %d, doc_width: %d, attr mask: %d",
  1792.                 eptr->lo_text.block_offset,
  1793.                 eptr->lo_text.doc_width,
  1794.                 eptr->lo_text.ele_attrmask));
  1795.             XP_UNLOCK_BLOCK(eptr->lo_text.text);
  1796.         }
  1797.         break;
  1798.     case LO_LINEFEED:
  1799.         {
  1800.             XP_TRACE(("\tbreak_type: %d, baseline: %d, attr mask: %d", 
  1801.                 eptr->lo_linefeed.break_type,
  1802.                 eptr->lo_linefeed.baseline,
  1803.                 eptr->lo_linefeed.ele_attrmask));
  1804.         }
  1805.         break;
  1806.     case LO_HRULE:
  1807.         {
  1808.             XP_TRACE(("\tend_x: %d, end_y: %d, attr mask: %d, alignment: %d, thickness: %d, %% width: %d", 
  1809.                 eptr->lo_hrule.end_x,
  1810.                 eptr->lo_hrule.end_y,
  1811.                 eptr->lo_hrule.ele_attrmask,
  1812.                 eptr->lo_hrule.alignment,
  1813.                 eptr->lo_hrule.thickness,
  1814.                 eptr->lo_hrule.percent_width));
  1815.         }
  1816.         break;
  1817.     case LO_IMAGE:
  1818.         {
  1819.             XP_TRACE(("\tlayer_id: %d, layer-x: %d, layer-y: %d, %% width: %d, %% height: %d, border(width: %d, vert_space: %d, horiz_space: %d)", 
  1820.                 eptr->lo_image.layer_id,
  1821.                 CL_GetLayerXOrigin(eptr->lo_image.layer),
  1822.                 CL_GetLayerYOrigin(eptr->lo_image.layer),
  1823.                 eptr->lo_image.percent_width,
  1824.                 eptr->lo_image.percent_height,
  1825.                 eptr->lo_image.border_width,
  1826.                 eptr->lo_image.border_vert_space,
  1827.                 eptr->lo_image.border_horiz_space));
  1828.  
  1829.             XP_TRACE(("\timage_url: %s, attr mask: %d, alt: %s, seq_num: %d",
  1830.                 eptr->lo_image.image_url,
  1831.                 eptr->lo_image.ele_attrmask,
  1832.                 eptr->lo_image.alt,
  1833.                 eptr->lo_image.seq_num));
  1834.         }
  1835.         break;
  1836.     case LO_BULLET:
  1837.         {
  1838.             XP_TRACE(("\tbullet(type: %d, size: %d, attr mask: %d, level: %d)",
  1839.                 eptr->lo_bullet.bullet_type,
  1840.                 eptr->lo_bullet.bullet_size,
  1841.                 eptr->lo_bullet.ele_attrmask,
  1842.                 eptr->lo_bullet.level));
  1843.         }
  1844.         break;
  1845.     case LO_FORM_ELE:
  1846.         {
  1847.             const struct LO_FormElementStruct_struct* form;
  1848.             static const char* kFormTypeNames[] = {
  1849.                   "FORM_TYPE_NONE",
  1850.                   "FORM_TYPE_TEXT",
  1851.                   "FORM_TYPE_RADIO",
  1852.                   "FORM_TYPE_CHECKBOX",
  1853.                   "FORM_TYPE_HIDDEN",
  1854.                   "FORM_TYPE_SUBMIT",
  1855.                   "FORM_TYPE_RESET",
  1856.                   "FORM_TYPE_PASSWORD",
  1857.                   "FORM_TYPE_BUTTON",
  1858.                   "FORM_TYPE_JOT",
  1859.                   "FORM_TYPE_SELECT_ONE",
  1860.                   "FORM_TYPE_SELECT_MULT",
  1861.                   "FORM_TYPE_TEXTAREA",
  1862.                   "FORM_TYPE_ISINDEX",
  1863.                   "FORM_TYPE_IMAGE",
  1864.                   "FORM_TYPE_FILE",
  1865.                   "FORM_TYPE_KEYGEN",
  1866.                   "FORM_TYPE_READONLY",
  1867.                   "FORM_TYPE_OBJECT"
  1868.                   };
  1869.             const char* formTypeName;
  1870.             int16 form_id;
  1871.             form = & eptr->lo_form;
  1872.             form_id = form->form_id;
  1873.             formTypeName = "Unknown";
  1874.             if ( form_id >= FORM_TYPE_NONE && form_id <= FORM_TYPE_OBJECT ) {
  1875.                 formTypeName = kFormTypeNames[form_id];
  1876.             }
  1877.  
  1878.             XP_TRACE(("\tform_id %s(%d)", formTypeName, form_id));
  1879.             XP_TRACE(("\telement_index: %d, baseline: %d, sel(%d,%d), attr mask: %d, form_id: %d, border(horiz_space: %d, vert_space: %d)",
  1880.                 form->element_index, form->baseline, form->sel_start,
  1881.                 form->sel_end, form->ele_attrmask, form->form_id, 
  1882.                 form->border_vert_space, form->border_horiz_space));
  1883.         }
  1884.         break;
  1885.  
  1886.     case LO_TABLE:
  1887.         {
  1888.             if (eptr->lo_table.table == NULL) 
  1889.             {
  1890.                 XP_TRACE(("\tERROR: Table element contains a null pointer to its table state"));
  1891.                 result = FALSE;
  1892.             }
  1893.             else
  1894.             {
  1895.                 XP_TRACE(("\tlo_TableRec *: %p", eptr->lo_table.table));
  1896.             }
  1897.         }
  1898.         break;
  1899.     case LO_CELL:
  1900.         {
  1901.             const LO_CellStruct* cell
  1902.                 = & eptr->lo_cell;
  1903.             XP_TRACE(("------------- Beginning of cell (ele_id = %d) contents --------------------",
  1904.                 cell->ele_id));
  1905.  
  1906.             if (cell->table == NULL || cell->table_row == NULL || cell->table_cell == NULL)
  1907.             {
  1908.                 XP_TRACE(("\tERROR: Cell element contains a null pointer to its table state"));
  1909.                 result = FALSE;
  1910.             }
  1911.             XP_TRACE(("lo_TableRec ptr: %p, lo_TableRow ptr: lo_TableCell ptr:",
  1912.                 cell->table,
  1913.                 cell->table_row,
  1914.                 cell->table_cell));
  1915.             lo_VerifyList(pContext, top_state, state,
  1916.                 cell->cell_list,
  1917.                 cell->cell_list_end,
  1918.                 cell->cell_float_list, TRUE);
  1919.             XP_TRACE(("------------- End of cell (ele_id = %d) contents --------------------",
  1920.                 cell->ele_id));
  1921.         }
  1922.         break;
  1923.  
  1924.     case LO_PARAGRAPH:                
  1925.         {
  1926.             XP_TRACE(("\tis_end: %d, blank_lines: %d, implicit_end: %d, align_set: %d, align: %d",
  1927.                 eptr->lo_paragraph.is_end,
  1928.                 eptr->lo_paragraph.blank_lines,
  1929.                 eptr->lo_paragraph.implicit_end,
  1930.                 eptr->lo_paragraph.alignment_set,
  1931.                 eptr->lo_paragraph.alignment));
  1932.         }
  1933.         break;                
  1934.  
  1935.     case LO_CENTER:
  1936.         {
  1937.             XP_TRACE(("\tis_end: %d",
  1938.                 eptr->lo_center.is_end));
  1939.         }
  1940.         break;
  1941.  
  1942.     case LO_MULTICOLUMN:
  1943.         {
  1944.             XP_TRACE(("\tis_end: %d",
  1945.                 eptr->lo_multicolumn.is_end));
  1946.         }
  1947.         break;
  1948.  
  1949.     case LO_FLOAT:
  1950.         {
  1951.             XP_TRACE(("\tPoints to %s(%d), ele_id: %d",
  1952.                 kTypeStrings[eptr->lo_float.float_ele->lo_any.type + 1],
  1953.                 eptr->lo_float.float_ele->lo_any.type,
  1954.                 eptr->lo_float.float_ele->lo_any.ele_id));
  1955.         }
  1956.         break;
  1957.  
  1958.        case LO_TEXTBLOCK:
  1959.            {
  1960.             const LO_TextBlock* block = & eptr->lo_textBlock;
  1961.             XP_TRACE(("      buffer_length %lu", block->buffer_length));
  1962.             XP_TRACE(("      buffer_write_index %lu", block->buffer_write_index));
  1963.             XP_TRACE(("      break_table %lx", block->break_table));
  1964.             XP_TRACE(("      startTextElement %lx", block->startTextElement));
  1965.             XP_TRACE(("      endTextElement %lx", block->endTextElement));
  1966.            }
  1967.  
  1968.     case LO_LIST:
  1969.         {
  1970.             XP_TRACE(("\tbullet(type: %d, start: %d), quote_type: %d, compact: %d, is_end: %d",
  1971.                 eptr->lo_list.bullet_type,
  1972.                 eptr->lo_list.bullet_start,
  1973.                 eptr->lo_list.quote_type,
  1974.                 eptr->lo_list.compact,
  1975.                 eptr->lo_list.is_end));
  1976.         }
  1977.         break;
  1978.     case LO_BLOCKQUOTE:
  1979.         {
  1980.             XP_TRACE(("\tblockquote : end: %s, quote_type: %d",
  1981.                       eptr->lo_blockquote.is_end ? "True" : "False",
  1982.                       eptr->lo_blockquote.quote_type));
  1983.         }
  1984.         break;
  1985.     default:
  1986.         break;
  1987.     }
  1988.  
  1989.     return result;
  1990. }
  1991.  
  1992.  
  1993. PRIVATE
  1994. Bool
  1995. lo_VerifyStateLayoutImplementation( MWContext *pContext, lo_TopState *top_state, lo_DocState *state, Bool print) {
  1996.     /* Debugging aid. Checks consistency of the layout for this context.
  1997.      * returns TRUE if the layout is valid.
  1998.      * Prints information to stderr if the layout is invalid.
  1999.      */
  2000.      Bool result;
  2001.     int32 doc_id;
  2002.     LO_Element **array;
  2003.     LO_Element *eptr;
  2004.     LO_Element *start;
  2005.     LO_Element *end;
  2006. #ifdef XP_WIN16
  2007.     XP_Block *larray_array;
  2008. #endif /* XP_WIN16 */
  2009.  
  2010.     result = TRUE;
  2011.     if ( ! pContext ) {
  2012.         XP_TRACE(("context is NULL."));
  2013.         return FALSE;
  2014.     }
  2015.  
  2016.     /*
  2017.      * Get first element in doc.
  2018.      */
  2019. #ifdef XP_WIN16
  2020.     XP_LOCK_BLOCK(larray_array, XP_Block *, state->larray_array);
  2021.     state->line_array = larray_array[0];
  2022.     XP_UNLOCK_BLOCK(state->larray_array);
  2023. #endif /* XP_WIN16 */
  2024.  
  2025.     XP_LOCK_BLOCK(array, LO_Element **, state->line_array);
  2026.     eptr = array[0];
  2027.     XP_UNLOCK_BLOCK(state->line_array);
  2028.  
  2029.     if (eptr == NULL)
  2030.     {
  2031.         XP_TRACE(("No first element."));
  2032.         return FALSE;
  2033.     }
  2034.  
  2035.     start = eptr;
  2036.  
  2037.     /*
  2038.      * Get last element in doc.
  2039.      */
  2040.     end = state->end_last_line;
  2041.  
  2042.     if (end == NULL)
  2043.     {
  2044.         XP_TRACE(("No last element."));
  2045.         return FALSE;
  2046.     }
  2047.  
  2048.     return lo_VerifyList(pContext, top_state, state, start, end, state->float_list, print);
  2049. }
  2050.  
  2051. PRIVATE
  2052. Bool
  2053. lo_VerifyLayoutImplementation( MWContext *pContext, Bool print) {
  2054.     /* Debugging aid. Checks consistency of the layout for this context.
  2055.      * returns TRUE if the layout is valid.
  2056.      * Prints information to stderr if the layout is invalid.
  2057.      */
  2058.      Bool result;
  2059.     int32 doc_id;
  2060.     lo_TopState *top_state;
  2061.     lo_DocState *state;
  2062.     LO_Element **array;
  2063.     LO_Element *eptr;
  2064.     LO_Element *start;
  2065.     LO_Element *end;
  2066. #ifdef XP_WIN16
  2067.     XP_Block *larray_array;
  2068. #endif /* XP_WIN16 */
  2069.  
  2070.     result = TRUE;
  2071.     if ( ! pContext ) {
  2072.         XP_TRACE(("context is NULL."));
  2073.         return FALSE;
  2074.     }
  2075.     /*
  2076.      * Get the unique document ID, and retreive this
  2077.      * documents layout state.
  2078.      */
  2079.     doc_id = XP_DOCID(pContext);
  2080.     top_state = lo_FetchTopState(doc_id);
  2081.     if (top_state == NULL)
  2082.     {
  2083.         XP_TRACE(("top_state is NULL."));
  2084.         return FALSE;
  2085.     }
  2086.     state = top_state->doc_state;
  2087.     if (state == NULL)
  2088.     {
  2089.         XP_TRACE(("state is NULL."));
  2090.         return FALSE;
  2091.     }
  2092.  
  2093.     return lo_VerifyStateLayoutImplementation(pContext, top_state, state, print);
  2094. }
  2095.  
  2096. Bool
  2097. lo_VerifyLayout( MWContext *pContext) {
  2098.     return lo_VerifyLayoutImplementation(pContext, FALSE);
  2099. }
  2100.  
  2101. void
  2102. lo_PrintLayout( MWContext *pContext) {
  2103.     lo_VerifyLayoutImplementation(pContext, TRUE);
  2104. }
  2105. #endif /* DEBUG */
  2106.  
  2107. #endif /* editor */
  2108.  
  2109. void LO_SetBaseURL( MWContext *context, char *pURL ){
  2110.     int32 doc_id;
  2111.     lo_TopState *top_state;
  2112.  
  2113.     /*
  2114.      * Get the unique document ID, and retreive this
  2115.      * documents layout state.
  2116.      */
  2117.     doc_id = XP_DOCID(context);
  2118.     top_state = lo_FetchTopState(doc_id);
  2119.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  2120.     {
  2121.         return;
  2122.     }
  2123.     XP_FREE( top_state->base_url );
  2124.     top_state->base_url = XP_STRDUP( pURL );
  2125. }
  2126.  
  2127. char* LO_GetBaseURL( MWContext *context ){
  2128.     int32 doc_id;
  2129.     lo_TopState *top_state;
  2130.  
  2131.     /*
  2132.      * Get the unique document ID, and retreive this
  2133.      * documents layout state.
  2134.      */
  2135.     doc_id = XP_DOCID(context);
  2136.     top_state = lo_FetchTopState(doc_id);
  2137.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  2138.     {
  2139.         return 0;
  2140.     }
  2141.     return top_state->base_url;
  2142. }
  2143.  
  2144.