/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public License * Version 1.0 (the "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ /* Moved here so we can expose 2 functions outside of ifdef EDITOR */ #include "xp.h" #include "layout.h" #include "layers.h" #ifdef EDITOR /* define LAYEDIT so LO_RelayoutData is not defined. */ #define LAYEDIT 1 typedef struct LO_RelayoutData_struct LO_RelayoutData; #include "pa_parse.h" #include "edt.h" #include "libi18n.h" #ifdef max #undef max #endif #define max(a, b) (((a) > (b)) ? (a) : (b)) struct LO_RelayoutData_struct { MWContext *context; ED_TagCursor* pTagCursor; lo_TopState* top_state; lo_DocState* old_state; lo_DocState* new_state; int32 iStartLine; int iStartEditOffset; }; #define LINE_INC 100 #ifdef DEBUG PRIVATE Bool lo_VerifyStateLayoutImplementation( MWContext *pContext, lo_TopState *top_state, lo_DocState *state, Bool print); #endif #ifdef XP_WIN16 #define SIZE_LIMIT 32000 void lo_GrowLineArrayByOneForWin16( lo_DocState *state, intn lineNum) { /* This code is a modified version of the lo_FlushLineList * that grows the line array to hold more lines. */ intn a_size; intn a_indx; intn a_line; XP_Block *larray_array; LO_Element **line_array; a_size = SIZE_LIMIT / sizeof(LO_Element *); a_indx = (lineNum - 1) / a_size; a_line = lineNum - (a_indx * a_size); XP_LOCK_BLOCK(larray_array, XP_Block *, state->larray_array); state->line_array = larray_array[a_indx]; if (a_line == a_size) { state->line_array = XP_ALLOC_BLOCK(LINE_INC * sizeof(LO_Element *)); if (state->line_array == NULL) { XP_UNLOCK_BLOCK(state->larray_array); state->top_state->out_of_memory = TRUE; return; } XP_LOCK_BLOCK(line_array, LO_Element **, state->line_array); line_array[0] = NULL; XP_UNLOCK_BLOCK(state->line_array); state->line_array_size = LINE_INC; state->larray_array_size++; XP_UNLOCK_BLOCK(state->larray_array); state->larray_array = XP_REALLOC_BLOCK( state->larray_array, (state->larray_array_size * sizeof(XP_Block))); if (state->larray_array == NULL) { state->top_state->out_of_memory = TRUE; return; } XP_LOCK_BLOCK(larray_array, XP_Block *, state->larray_array); larray_array[state->larray_array_size - 1] = state->line_array; state->line_array = larray_array[a_indx]; } else if (a_line >= state->line_array_size) { state->line_array_size += LINE_INC; if (state->line_array_size > a_size) { state->line_array_size = (intn)a_size; } state->line_array = XP_REALLOC_BLOCK(state->line_array, (state->line_array_size * sizeof(LO_Element *))); if (state->line_array == NULL) { XP_UNLOCK_BLOCK(state->larray_array); state->top_state->out_of_memory = TRUE; return; } larray_array[a_indx] = state->line_array; } /* * Place this line of elements into the line array. */ XP_LOCK_BLOCK(line_array, LO_Element **, state->line_array); line_array[a_line - 1] = state->line_list; XP_UNLOCK_BLOCK(state->line_array); XP_UNLOCK_BLOCK(state->larray_array); } #endif PRIVATE Bool lo_GrowLineArray( lo_DocState *state, int32 iMaxLines ) { /* This code is a modified version of the lo_FlushLineList * that grows the line array to hold more lines. */ #ifdef XP_WIN16 intn linenum = state->line_array_size; while ( iMaxLines > linenum){ lo_GrowLineArrayByOneForWin16(state, linenum); if ( state->top_state->out_of_memory == TRUE) { return FALSE; } linenum++; } #else int32 line_inc = 0; while ( iMaxLines > state->line_array_size + line_inc) { if (state->line_array_size > (LINE_INC * 10)) { line_inc += state->line_array_size / 10; } else { line_inc += LINE_INC; } } if ( line_inc != 0 ) { state->line_array = XP_REALLOC_BLOCK(state->line_array, ((state->line_array_size + line_inc) * sizeof(LO_Element *))); if (state->line_array == NULL) { state->top_state->out_of_memory = TRUE; return FALSE; } state->line_array_size += line_inc; } #endif return TRUE; } void lo_MergeStateMoveElement(LO_Element* eptr, int32 yDelta, int32* pEle_id); PRIVATE void lo_MergeStateMoveCell(LO_CellStruct* cellPtr, int32 yDelta, int32* pEle_id) { /* Like lo_ShiftCell, only we also renumber the element ids. */ LO_Element* eptr; if (cellPtr->cell_bg_layer) CL_MoveLayer(cellPtr->cell_bg_layer, cellPtr->x, cellPtr->y); for( eptr = cellPtr->cell_list; eptr; eptr = eptr->lo_any.next) { lo_MergeStateMoveElement(eptr, yDelta, pEle_id); } for( eptr = cellPtr->cell_float_list; eptr; eptr = eptr->lo_any.next) { lo_MergeStateMoveElement(eptr, yDelta, pEle_id); } } void lo_MergeStateMoveElement(LO_Element* eptr, int32 yDelta, int32* pEle_id) { eptr->lo_any.ele_id = (*pEle_id)++; eptr->lo_any.y += yDelta; /* Descend into cells */ if ( eptr->type == LO_CELL ) { lo_MergeStateMoveCell(&eptr->lo_cell, yDelta, pEle_id); } else if (eptr->type == LO_IMAGE) { if (eptr->lo_image.layer) { CL_OffsetLayer(eptr->lo_image.layer, 0, yDelta); } } } PRIVATE void lo_MergeState( MWContext *context, lo_DocState *old_state, int32 iStartLine, int32 iEndLine, lo_DocState *new_state, int32* pRetY, int32* pRetHeight ) { LO_Element **old_line_array, **new_line_array; LO_Element *prev_element, *start_element, *eptr, *end_element, *new_end_element=NULL; int32 yDelta; int32 ele_id; int32 new_line_num; int32 new_changed_line_count; int32 old_changed_line_count; int32 old_delta; int32 old_lines_to_move; Bool relayout_to_end; Bool old_state_empty = (old_state->line_num < 2); new_changed_line_count = new_state->line_num-1; old_changed_line_count = iEndLine-iStartLine; new_line_num = old_state->line_num - old_changed_line_count + new_changed_line_count; /* * Grow the line array if we need to. */ if( !lo_GrowLineArray( old_state, new_line_num+1 ) ) { return; /* out of memory */ } /* lock down the line array and copy the lines from the new_state into the * old state. */ XP_LOCK_BLOCK(old_line_array, LO_Element **, old_state->line_array); XP_LOCK_BLOCK(new_line_array, LO_Element **, new_state->line_array); if( iEndLine == old_state->line_num-1 ){ /* ERIC: help! */ /* There is something very wrong here. We the layout engine is * telling us the wrong line count. Force the end by stuffing * a zero */ /*XP_ASSERT( old_line_array[iEndLine] == 0 );*/ old_line_array[iEndLine] = 0; } start_element = old_line_array[iStartLine]; end_element = old_line_array[iEndLine]; relayout_to_end = ( end_element == 0 ); /* * Break the chain so when we recycle these elements they don't continue * and deallocate the entire tree. */ if( end_element && end_element->lo_any.prev){ end_element->lo_any.prev->lo_any.next = 0; } /* * Shrink or grow the the old line array. XP_BCOPY is supposed to handle * overlaps. */ old_delta = new_changed_line_count - old_changed_line_count; old_lines_to_move = (old_state->line_num-1) - iEndLine; XP_BCOPY( &old_line_array[iEndLine], &old_line_array[iEndLine+old_delta], sizeof(LO_Element*)*old_lines_to_move ); old_state->line_num = new_line_num; /* if we shrunk the line array, make sure it is 0 filled */ if( old_delta < 0 ){ XP_BZERO( &old_line_array[iEndLine+old_delta+old_lines_to_move], sizeof(LO_Element**)*(old_delta * -1) ); } if( !old_state_empty ) { *pRetY = start_element->lo_any.y; } else { *pRetY = 0; } if( iStartLine != 0 ) { prev_element = start_element->lo_any.prev; } else { prev_element = 0; } if( new_changed_line_count ){ if( !old_state_empty ) { yDelta = start_element->lo_any.y - new_line_array[0]->lo_any.y; ele_id = start_element->lo_any.ele_id; } else { yDelta = 0; ele_id = 0; } XP_BCOPY( &new_line_array[0], &old_line_array[iStartLine], sizeof(LO_Element*)*new_changed_line_count ); if( prev_element ) { prev_element->lo_any.next = old_line_array[iStartLine]; old_line_array[iStartLine]->lo_any.prev = prev_element; } eptr = old_line_array[iStartLine]; while( eptr != NULL) { lo_MergeStateMoveElement(eptr, yDelta, &ele_id); new_end_element = eptr; eptr = eptr->lo_any.next; } } else { yDelta = 0; if( !old_state_empty ) { ele_id = start_element->lo_any.ele_id; } else { ele_id = 0; } new_end_element = prev_element; } /* * We now have to patch the the bottom of the newly generated tags into * the old tags. * */ if( relayout_to_end == FALSE ) { /* WARNING: THIS WILL CRASH WHEN new_end_element == NULL */ end_element->lo_any.prev = new_end_element; new_end_element->lo_any.next = end_element; yDelta = (new_end_element->lo_any.y+new_end_element->lo_any.line_height) - end_element->lo_any.y; ele_id = new_end_element->lo_any.ele_id+1; eptr = end_element; while( eptr != NULL) { lo_MergeStateMoveElement(eptr, yDelta, &ele_id); eptr = eptr->lo_any.next; } } else { /* trivia: The -2 is because lines are 1 based, and new_line_num is one * more than the allocated number of lines. The array is zero based. * So subtract 1 to get the count zero based, and another 1 to get the * last allocated line rather than the first un-allocated line. */ if( !old_state_empty ) { eptr = old_line_array[new_line_num - 2]; } else { eptr = new_end_element; } /* move to the last element of the last line line */ while( eptr != NULL && eptr->lo_any.next != NULL) { eptr = eptr->lo_any.next; } old_state->end_last_line = eptr; } if( new_changed_line_count ) { if( yDelta == 0 && !relayout_to_end ) { eptr = new_end_element; *pRetHeight = eptr->lo_any.y - *pRetY + eptr->lo_any.line_height; } else { /* the last line in the document. */ *pRetHeight = -1; } eptr = old_line_array[new_line_num-2]; /* INCREMENT_WIDTH_AMT: We used to add a "slop" factor to allow for * the cursor; that doesn't seem to be needed any more, and probably * should be taken out. */ #define INCREMENT_WIDTH_AMT 0 #define max(a, b) (((a) > (b)) ? (a) : (b)) /* HACK ALERT! For bug 94115 (inappropriate horizontal scrollbars). * lo_MergeState is used both for relayout after resizing * the window, and for relayout of small parts of a document. * In the former case, we need to be able to shrink the * document width; in the latter, we can't because some * other part of the document may be something like a * table or image which needs the larger width. * So we rely on the caller to tell us if we're resizing. * The comparable code in lo_MergeElements doesn't seem to need * this hack because it's never used for full-document resize. */ if (context->reSize) old_state->max_width = new_state->max_width; else old_state->max_width = max( old_state->max_width, new_state->max_width + INCREMENT_WIDTH_AMT ); #undef max old_state->y = eptr->lo_any.y +eptr->lo_any.line_height; LO_SetDocumentDimensions(context, old_state->max_width, eptr->lo_any.y +eptr->lo_any.line_height); } else { eptr = start_element; if( eptr ) { *pRetHeight = eptr->lo_any.y - *pRetY + eptr->lo_any.line_height; } else { *pRetHeight = -1; } } #if 0 XP_TRACE(( "line count = %d, end pointer = %d\n", new_line_num, old_line_array[new_line_num-2] )); XP_ASSERT( old_line_array[new_line_num-2] != 0 ); #endif XP_UNLOCK_BLOCK(new_state->line_array); XP_UNLOCK_BLOCK(old_state->line_array); /* LTNOTE: need to recycle element chain from start_element */ if( start_element ) { lo_relayout_recycle(context, old_state, start_element); } /* * XXX BUGBUG We don't deal with layers yet, but we will eventually have * to worry about layers that are in the old state. */ #ifdef DEBUG lo_VerifyLayout(context); #endif /* DEBUG */ } PRIVATE void lo_MergeElements( MWContext *context, lo_DocState *old_state, int32 iStartLine, int32 iEndLine, lo_DocState *new_state, int32* pRetY, int32* pRetHeight ) { LO_Element **old_line_array, **new_line_array; LO_Element *prev_element, *eptr, *end_element, *new_end_element=NULL; int32 yDelta=0; int32 ele_id; int32 new_line_num; int32 new_changed_line_count; int32 old_changed_line_count; int32 old_delta; int32 old_lines_to_move; Bool relayout_to_end; Bool old_state_empty = (old_state->line_num < 2); new_changed_line_count = new_state->line_num-1; old_changed_line_count = iEndLine-iStartLine; new_line_num = old_state->line_num - old_changed_line_count + new_changed_line_count; /* * Grow the line array if we need to. */ if( !lo_GrowLineArray( old_state, new_line_num+1 ) ) { return; /* out of memory */ } /* lock down the line array and copy the lines from the new_state into the * old state. */ XP_LOCK_BLOCK(old_line_array, LO_Element **, old_state->line_array); XP_LOCK_BLOCK(new_line_array, LO_Element **, new_state->line_array); if( iEndLine == old_state->line_num-1 ){ /* ERIC: help! */ /* There is something very wrong here. We the layout engine is * telling us the wrong line count. Force the end by stuffing * a zero */ /*XP_ASSERT( old_line_array[iEndLine] == 0 );*/ old_line_array[iEndLine] = 0; } end_element = old_line_array[iEndLine]; relayout_to_end = ( end_element == 0 ); /* * Break the chain so when we recycle these elements they don't continue * and deallocate the entire tree. */ if( end_element && end_element->lo_any.prev){ end_element->lo_any.prev->lo_any.next = 0; } /* * Shrink or grow the the old line array. XP_BCOPY is supposed to handle * overlaps. */ old_delta = new_changed_line_count - old_changed_line_count; old_lines_to_move = (old_state->line_num-1) - iEndLine; XP_BCOPY( &old_line_array[iEndLine], &old_line_array[iEndLine+old_delta], sizeof(LO_Element*)*old_lines_to_move ); old_state->line_num = new_line_num; /* if we shrunk the line array, make sure it is 0 filled */ if( old_delta < 0 ){ XP_BZERO( &old_line_array[iEndLine+old_delta+old_lines_to_move], sizeof(LO_Element**)*(old_delta * -1) ); } if( iStartLine != 0 ) { /* find the last element on the previous line */ prev_element = old_line_array[iStartLine - 1]; while ( prev_element != NULL ) { if ( prev_element->lo_any.next == NULL ) break; prev_element = prev_element->lo_any.next; } } else { prev_element = 0; } if( old_state_empty ) { *pRetY = 0; } ele_id = 0; if( new_changed_line_count ){ /* copy in new lines */ XP_BCOPY( &new_line_array[0], &old_line_array[iStartLine], sizeof(LO_Element*)*new_changed_line_count ); if( prev_element ) { prev_element->lo_any.next = old_line_array[iStartLine]; old_line_array[iStartLine]->lo_any.prev = prev_element; ele_id = prev_element->lo_any.ele_id + 1; } eptr = old_line_array[iStartLine]; while( eptr != NULL) { lo_MergeStateMoveElement(eptr, 0, &ele_id); new_end_element = eptr; eptr = eptr->lo_any.next; } } else { /* no new lines to copy in, so get ready to delete the old ones */ yDelta = 0; if( prev_element ) { ele_id = prev_element->lo_any.ele_id + 1; } else { ele_id = 0; } new_end_element = prev_element; } /* * We now have to patch the the bottom of the newly generated tags into * the old tags. * */ if( relayout_to_end == FALSE ) { end_element->lo_any.prev = new_end_element; new_end_element->lo_any.next = end_element; yDelta = (new_end_element->lo_any.y+new_end_element->lo_any.line_height) - end_element->lo_any.y; ele_id = new_end_element->lo_any.ele_id+1; eptr = end_element; while( eptr != NULL) { lo_MergeStateMoveElement(eptr, yDelta, &ele_id); eptr = eptr->lo_any.next; } } else { /* trivia: The -2 is because lines are 1 based, and new_line_num is one * more than the allocated number of lines. The array is zero based. * So subtract 1 to get the count zero based, and another 1 to get the * last allocated line rather than the first un-allocated line. */ if( !old_state_empty ) { eptr = old_line_array[new_line_num - 2]; } else { eptr = new_end_element; } /* move to the last element of the last line line */ while( eptr != NULL && eptr->lo_any.next != NULL) { eptr = eptr->lo_any.next; } old_state->end_last_line = eptr; } if( new_changed_line_count ) { if( yDelta == 0 && !relayout_to_end ) { eptr = new_end_element; *pRetHeight = eptr->lo_any.y - *pRetY + eptr->lo_any.line_height; } else { /* the last line in the document. */ *pRetHeight = -1; } eptr = old_line_array[new_line_num-2]; #define max(a, b) (((a) > (b)) ? (a) : (b)) old_state->max_width = max( old_state->max_width, new_state->max_width + INCREMENT_WIDTH_AMT ); old_state->y = eptr->lo_any.y +eptr->lo_any.line_height; #undef max LO_SetDocumentDimensions(context, old_state->max_width, eptr->lo_any.y +eptr->lo_any.line_height); } else { #if 0 eptr = start_element; if( eptr ) { *pRetHeight = eptr->lo_any.y - *pRetY + eptr->lo_any.line_height; } else { *pRetHeight = -1; } #else XP_ASSERT(0); *pRetHeight = -1; #endif } #if 0 XP_TRACE(( "line count = %d, end pointer = %d\n", new_line_num, old_line_array[new_line_num-2] )); XP_ASSERT( old_line_array[new_line_num-2] != 0 ); #endif XP_UNLOCK_BLOCK(new_state->line_array); XP_UNLOCK_BLOCK(old_state->line_array); /* * XXX BUGBUG We don't deal with layers yet, but we will eventually have * to worry about layers that are in the old state. */ #ifdef DEBUG lo_VerifyLayout(context); #endif /* DEBUG */ } PRIVATE LO_RelayoutData* lo_NewRelayoutData( MWContext* context, ED_TagCursor* pCursor, int32 iStartLine, int iStartEditOffset, lo_DocState *new_state ) { int32 doc_id; LO_RelayoutData *pRd; pRd = XP_NEW( LO_RelayoutData ); if( pRd == 0 ) { return 0; } pRd->pTagCursor = pCursor; pRd->context = context; /* * Get the unique document ID, and retreive this * documents layout state. */ doc_id = XP_DOCID(context); pRd->top_state = lo_FetchTopState(doc_id); if ((pRd->top_state == NULL)||(pRd->top_state->doc_state == NULL)) { return 0; } pRd->old_state = pRd->top_state->doc_state; pRd->iStartLine = iStartLine; pRd->iStartEditOffset = iStartEditOffset; /* new state was supplied */ if( new_state ) pRd->new_state = new_state; else pRd->new_state = lo_NewLayout( context, pRd->old_state->win_width, pRd->old_state->win_height, pRd->old_state->win_left, pRd->old_state->win_top, pRd->old_state ); pRd->new_state->display_blocked = TRUE; pRd->new_state->edit_relayout_display_blocked = TRUE; return pRd; } PRIVATE void lo_FreeRelayoutData( MWContext* context, LO_RelayoutData* pRd) { if ( pRd ) { if ( pRd->new_state ) { lo_DocState* state = pRd->new_state; /* * If there is a blocking element, we'll get into trouble later. * Check if CEditImageElement::FinishedLoad is being called. */ XP_ASSERT ( state->top_state->layout_blocking_element == NULL ); lo_FreeLayoutData(context, state); lo_InternalDiscardDocument(context, state, NULL, FALSE); } XP_FREE(pRd); } } /* Remove all mquote bullets from elist, returning the list of mquote bullets. */ PRIVATE LO_Element *lo_strip_mquotes(LO_Element **elist) { /* Return value. */ LO_Element *mquotes = NULL; LO_Element *mquotesTail = NULL; /* For traversing the elist. */ LO_Element *eptr = *elist; if (!elist || !*elist) { return NULL; } LO_LockLayout(); while (eptr) { /* Found a mquote bullet. */ if (eptr->type == LO_BULLET && eptr->lo_bullet.bullet_type == BULLET_MQUOTE) { /* Delete from the passed-in element list. */ if (eptr->lo_any.prev) { XP_ASSERT(eptr->lo_any.prev->lo_any.next == eptr); eptr->lo_any.prev->lo_any.next = eptr->lo_any.next; } else { /* beginning of list, change the head. */ XP_ASSERT(*elist == eptr); *elist = eptr->lo_any.next; } if (eptr->lo_any.next) { eptr->lo_any.next->lo_any.prev = eptr->lo_any.prev; } eptr->lo_any.prev = mquotesTail; /* Add to mquotes list. */ if (mquotes) { XP_ASSERT(mquotesTail); mquotesTail->lo_any.next = eptr; } else { /* Start the mquotes list. */ mquotes = eptr; } mquotesTail = eptr; /* Move eptr to next element, eptrPrev stays the same. */ eptr = eptr->lo_any.next; /* NULL-terminate mquotes list. */ mquotesTail->lo_any.next = NULL; } /* Not a mquote bullet, just skip it. */ else { eptr = eptr->lo_any.next; } } LO_UnlockLayout(); return mquotes; } void lo_EditorCellReflow(MWContext *context, ED_TagCursor *pCursor, LO_CellStruct *pCell) { PA_Tag *pTag; PA_Tag *pNextTag = 0; LO_Element *eptr; LO_Element **line_array; int32 changedY, changedHeight; LO_RelayoutData* pRd; Bool bFoundBreak, bBreakIsEndTag; int32 iEndLine = -1; LO_Element *leadingMquotes = NULL; int32 x = pCell->x + 1; int32 y = pCell->y + 1; lo_DocState *new_state; int iStartEditOffset = 0; int32 iStartLine; if( !context || !pCursor || !pCell ) return; LO_FirstElementOnLine(context, x, y, &iStartLine); context->is_editor |= EDT_RELAYOUT_FLAG; /* Relayout flag */ /* Create a new doc state to layout into */ new_state = lo_CreateStateForCellLayout(context, pCell); pRd = lo_NewRelayoutData( context, pCursor, iStartLine, iStartEditOffset, new_state ); /*************************************************************/ /* This block was copied from lo_Relayout TODO: When things work, extract common code into a separate function */ /* save the floating element list for later deletion. */ if( pRd->old_state->float_list != 0 ) { lo_relayout_recycle(context, pRd->new_state, pRd->old_state->float_list); pRd->old_state->float_list = NULL; } while( (pTag = pNextTag) != 0 || (pTag = EDT_TagCursorGetNextState(pCursor)) != 0 ) { lo_LayoutTag(pRd->context, pRd->new_state, pTag); pNextTag = pTag->next; PA_FreeTag(pTag); } /* What the heck is this doing? */ eptr = NULL; XP_LOCK_BLOCK(line_array, LO_Element **, pRd->new_state->line_array); eptr = line_array[0]; if (eptr != NULL ) { lo_relayout_recycle(context, pRd->new_state, eptr); } XP_UNLOCK_BLOCK(pRd->old_state->line_array); /* Get list of any mailing quote bullets, removing them from line_list. These are the only elements that we want to preserve. lo_strip_mquotes deals properly with a NULL input. Note that this can change the value of pRd->new_state->line_list. */ leadingMquotes = lo_strip_mquotes(&pRd->new_state->line_list); if ( pRd->new_state->line_list ) { lo_relayout_recycle(context, pRd->new_state, pRd->new_state->line_list); } /* Only leave the leading mquote bullets, if any. */ pRd->new_state->line_list = leadingMquotes; pRd->new_state->line_num = 1; /* while there are tags to parse... */ bFoundBreak = FALSE; pNextTag = NULL; while( !bFoundBreak ) { if( pNextTag == NULL ) { pTag = EDT_TagCursorGetNext(pRd->pTagCursor); } else { pTag = pNextTag; } if( pTag == NULL ){ break; } pRd->new_state->display_blocked = TRUE; pNextTag = pTag->next; if( iStartEditOffset && pTag->type == P_TEXT ) { pRd->new_state->edit_current_offset = iStartEditOffset; pRd->new_state->edit_force_offset = TRUE; lo_LayoutTag(pRd->context, pRd->new_state, pTag); iStartEditOffset = 0; pRd->new_state->edit_force_offset = FALSE; PA_FreeTag(pTag); } else { /* lo_LayoutTag(pRd->context, pRd->new_state, pTag);*/ /* Matches code at end of LO_ProcessTag */ lo_DocState *state = pRd->new_state; lo_DocState *orig_state; lo_DocState *up_state; PA_Tag* tag = pTag; /* * Divert all tags to the current sub-document if there is one. */ up_state = NULL; orig_state = state; /* Note: we always display tables, so we ignore bDisplayTables now */ while (state->sub_state != NULL) { lo_DocState *new_state; up_state = state; new_state = state->sub_state; state = new_state; } /* orig_state->top_state->layout_status = status; */ { lo_DocState *tmp_state; Bool may_save; if ((state->is_a_subdoc == SUBDOC_CELL)|| (state->is_a_subdoc == SUBDOC_CAPTION)) { may_save = TRUE; } else { may_save = FALSE; } /* Some table routines reach out to find the top doc state. * So we replace it for the duration of this call. */ pRd->top_state->doc_state = pRd->new_state; state->edit_relayout_display_blocked = TRUE; state->display_blocked = TRUE; lo_LayoutTag(context, state, tag); pRd->top_state->doc_state = pRd->old_state; tmp_state = lo_CurrentSubState(orig_state); if (may_save != FALSE) { /* * That tag popped us up one state level. If this new * state is still a subdoc, save the tag there. */ if (tmp_state == up_state) { if ((tmp_state->is_a_subdoc == SUBDOC_CELL)|| (tmp_state->is_a_subdoc == SUBDOC_CAPTION)) { lo_SaveSubdocTags(context, tmp_state, tag); } else { PA_FreeTag(tag); } } /* * Else that tag put us in a new subdoc on the same * level. It needs to be saved one level up, * if the parent is also a subdoc. */ else if ((up_state != NULL)&& (tmp_state == up_state->sub_state)&& (tmp_state != state)) { if ((up_state->is_a_subdoc == SUBDOC_CELL)|| (up_state->is_a_subdoc == SUBDOC_CAPTION)) { lo_SaveSubdocTags(context, up_state, tag); } else { PA_FreeTag(tag); } } /* * Else we are still in the same subdoc */ else if (tmp_state == state) { lo_SaveSubdocTags(context, state, tag); } /* * Else that tag started a new, nested subdoc. * Add the starting tag to the parent. */ else if (tmp_state == state->sub_state) { lo_SaveSubdocTags(context, state, tag); /* * Since we have extended the parent chain, * we need to reset the child to the new * parent end-chain. */ if ((tmp_state->is_a_subdoc == SUBDOC_CELL)|| (tmp_state->is_a_subdoc == SUBDOC_CAPTION)) { tmp_state->subdoc_tags = state->subdoc_tags_end; } } /* * This can never happen. */ else { PA_FreeTag(tag); } state = tmp_state; } else { PA_FreeTag(tag); } } } /* pNextTag = pTag->next; PA_FreeTag(pTag); */ if( pNextTag == 0 ){ bFoundBreak = EDT_TagCursorAtBreak( pRd->pTagCursor, &bBreakIsEndTag ); } } if( bFoundBreak ) { if( bBreakIsEndTag ){ pTag = EDT_TagCursorGetNext(pRd->pTagCursor); lo_LayoutTag(pRd->context, pRd->new_state, pTag); iEndLine = EDT_TagCursorCurrentLine( pRd->pTagCursor ); } else { iEndLine = EDT_TagCursorCurrentLine( pRd->pTagCursor ); pTag = EDT_TagCursorGetNext(pRd->pTagCursor); if ( pTag->type == P_TABLE ) { lo_CloseOutLayout( pRd->context, pRd->new_state); } else { lo_LayoutTag(pRd->context, pRd->new_state, pTag); } } EDT_DeleteTagChain(pTag); /* don't close layout. We just flushed this line to the proper * height, there is a start of a new tag in the buffer. */ } else { lo_CloseOutLayout( pRd->context, pRd->new_state); } if( iEndLine == -1 ){ iEndLine = pRd->old_state->line_num-1; } /* Go up the liststack, closing out any mquotes. */ { lo_ListStack *lptr; lptr = pRd->new_state->list_stack; while (lptr->type != P_UNKNOWN && lptr->next != NULL) { if (lptr->quote_type == QUOTE_MQUOTE) { lo_add_leading_bullets(context,pRd->new_state, lptr->mquote_line_num - 1, pRd->new_state->line_num - 2, lptr->mquote_x); } lptr = lptr->next; } } /**************************************************************/ lo_RebuildCell(context, new_state, pCell); /* TODO -- RESET POINTERS SO NEW TAGS ARE NOT DELETED AND DELETE new_state */ /* For now, assume same Y and height doesn't change */ changedY = pCell->y; /* Note: using -1 for this will redraw to the end of the window */ changedHeight = pCell->line_height; context->is_editor &= ~EDT_RELAYOUT_FLAG; FE_DocumentChanged( context, changedY, changedHeight ); } void lo_EditorReflow(MWContext *context, ED_TagCursor *pCursor, int32 iStartLine, int iStartEditOffset) { PA_Tag *pTag; PA_Tag *pNextTag = 0; LO_Element *eptr; LO_Element **line_array; int32 changedY, changedHeight; LO_RelayoutData* pRd; Bool bFoundBreak, bBreakIsEndTag; int32 iEndLine = -1; LO_Element *leadingMquotes = NULL; LO_Element * startElement; LO_Element * endElement; LO_Element * prevElement; LO_Element ** old_line_array; context->is_editor |= EDT_RELAYOUT_FLAG; /* Relayout flag */ pRd = lo_NewRelayoutData( context, pCursor, iStartLine, iStartEditOffset, 0 ); /* save the floating element list for later deletion. */ if( pRd->old_state->float_list != 0 ) { lo_relayout_recycle(context, pRd->new_state, pRd->old_state->float_list); pRd->old_state->float_list = NULL; } /* unhook the line array at our start line */ XP_LOCK_BLOCK(old_line_array, LO_Element **, pRd->old_state->line_array); startElement = old_line_array[ iStartLine ]; #ifdef UNHOOK if ( ( startElement != NULL ) && ( startElement->lo_any.prev != NULL ) ) { startElement->lo_any.prev->lo_any.next = NULL; startElement->lo_any.prev = NULL; } #endif prevElement = NULL; if ( startElement != NULL ) { prevElement = startElement->lo_any.prev; } /* assume we'll layout to the end */ endElement = NULL; if ( startElement != NULL ) { changedY = startElement->lo_any.y; } else { changedY = 0; } /* set up our state's y position so we don't have to shift the new elements down */ pRd->new_state->y = startElement->lo_any.y; pRd->top_state->element_id = startElement->lo_any.ele_id; /* set up any external state (such as lists) */ while( (pTag = pNextTag) != 0 || (pTag = EDT_TagCursorGetNextState(pCursor)) != 0 ) { lo_LayoutTag(pRd->context, pRd->new_state, pTag); pNextTag = pTag->next; PA_FreeTag(pTag); } eptr = NULL; XP_LOCK_BLOCK(line_array, LO_Element **, pRd->new_state->line_array); eptr = line_array[0]; if (eptr != NULL ) { lo_relayout_recycle(context, pRd->new_state, eptr); } XP_UNLOCK_BLOCK(pRd->old_state->line_array); /* Get list of any mailing quote bullets, removing them from line_list. These are the only elements that we want to preserve. lo_strip_mquotes deals properly with a NULL input. Note that this can change the value of pRd->new_state->line_list. */ leadingMquotes = lo_strip_mquotes(&pRd->new_state->line_list); if ( pRd->new_state->line_list ) { lo_relayout_recycle(context, pRd->new_state, pRd->new_state->line_list); } /* Only leave the leading mquote bullets, if any. */ pRd->new_state->line_list = leadingMquotes; pRd->new_state->line_num = 1; /* hack to find the endline */ /* run through the tags with the tag cursor until we get to the end line */ bFoundBreak = FALSE; pNextTag = NULL; while( !bFoundBreak ) { if( pNextTag == NULL ) { pTag = EDT_TagCursorGetNext(pRd->pTagCursor); } else { pTag = pNextTag; } if( pTag == NULL ) { break; } PA_FreeTag(pTag); if( pNextTag == 0 ) { bFoundBreak = EDT_TagCursorAtBreak( pRd->pTagCursor, &bBreakIsEndTag ); } } iEndLine = EDT_TagCursorCurrentLine( pRd->pTagCursor ); if( iEndLine == -1 ) { /* BRAIN DAMAGE: figger this out - why do we need -2? */ iEndLine = pRd->old_state->line_num - 2; if ( iEndLine < 0 ) { iEndLine = pRd->old_state->line_num - 1; } } endElement = old_line_array[ iEndLine ]; if ( endElement != NULL ) { if ( endElement->lo_any.prev != NULL ) { endElement->lo_any.prev->lo_any.next = NULL; endElement->lo_any.prev = NULL; } } /* unhook our elements from the old state record */ if ( pRd->old_state->line_list == startElement ) { pRd->old_state->line_list = NULL; } if ( pRd->old_state->end_last_line == startElement ) { pRd->old_state->end_last_line = NULL; } /* reflow from our start to our end element */ LO_Reflow(pRd->context, pRd->new_state, startElement, endElement ); /* layout any cleanup tags */ if( bFoundBreak ) { if( bBreakIsEndTag ){ pTag = EDT_TagCursorGetNext(pRd->pTagCursor); lo_LayoutTag(pRd->context, pRd->new_state, pTag); } else { pTag = EDT_TagCursorGetNext(pRd->pTagCursor); if ( pTag->type == P_TABLE ) { lo_CloseOutLayout( pRd->context, pRd->new_state); } else { lo_LayoutTag(pRd->context, pRd->new_state, pTag); } } EDT_DeleteTagChain(pTag); /* don't close layout. We just flushed this line to the proper * height, there is a start of a new tag in the buffer. */ } /* cleanup the element list for the old elements before the reflow */ if ( prevElement ) { prevElement->lo_any.next = NULL; } #if defined( DEBUG_shannon ) XP_TRACE(("\n\nELEMENTS TO MERGE - new reflow")); lo_VerifyStateLayoutImplementation( context, pRd->top_state, pRd->new_state, TRUE); #endif lo_MergeElements( context, pRd->old_state, iStartLine, iEndLine, pRd->new_state, &changedY, &changedHeight ); /* Free the layout */ lo_FreeRelayoutData(context, pRd); context->is_editor &= ~EDT_RELAYOUT_FLAG; FE_DocumentChanged( context, changedY, changedHeight ); } void LO_Relayout(MWContext *context, ED_TagCursor *pCursor, int32 iStartLine, int iStartEditOffset, XP_Bool bDisplayTables) { PA_Tag *pTag; PA_Tag *pNextTag = 0; LO_Element *eptr; LO_Element **line_array; int32 changedY, changedHeight; LO_RelayoutData* pRd; Bool bFoundBreak, bBreakIsEndTag; int32 iEndLine = -1; LO_Element *leadingMquotes = NULL; context->is_editor |= EDT_RELAYOUT_FLAG; /* Relayout flag */ pRd = lo_NewRelayoutData( context, pCursor, iStartLine, iStartEditOffset, 0 ); /* We need to keep images loaded during relayout. Images are reference counted. * When the total number of layout elements that use an image drops to zero, * the image is kicked out of the cache. We have to have the images in the cache, * or else the layout engine will start an asynchronous load. The asynchronous load * is bad because we don't have any way of waiting for it to complete. And that in * turn means we would have to leave LO_Relayout with an inconsistent CEditElement / * LO_Element state. * * Here's how there might come to be elements on the float list: When the document has * right-aligned images, they are placed on the float list during the original load. * * Happily, there's a way around this problem: We make sure we don't delete the old * image tags until we've created the new image tags. That's why we use lo_relayout_recycle, * which puts the image elements on the "trash" list of pRd->new_state. Then we clean the * trash in lo_InternalDiscardDocument. * */ /* save the floating element list for later deletion. */ if( pRd->old_state->float_list != 0 ) { lo_relayout_recycle(context, pRd->new_state, pRd->old_state->float_list); pRd->old_state->float_list = NULL; } while( (pTag = pNextTag) != 0 || (pTag = EDT_TagCursorGetNextState(pCursor)) != 0 ) { lo_LayoutTag(pRd->context, pRd->new_state, pTag); pNextTag = pTag->next; PA_FreeTag(pTag); } eptr = NULL; XP_LOCK_BLOCK(line_array, LO_Element **, pRd->new_state->line_array); eptr = line_array[0]; if (eptr != NULL ) { lo_relayout_recycle(context, pRd->new_state, eptr); } XP_UNLOCK_BLOCK(pRd->old_state->line_array); /* Get list of any mailing quote bullets, removing them from line_list. These are the only elements that we want to preserve. lo_strip_mquotes deals properly with a NULL input. Note that this can change the value of pRd->new_state->line_list. */ leadingMquotes = lo_strip_mquotes(&pRd->new_state->line_list); if ( pRd->new_state->line_list ) { lo_relayout_recycle(context, pRd->new_state, pRd->new_state->line_list); } /* Only leave the leading mquote bullets, if any. */ pRd->new_state->line_list = leadingMquotes; pRd->new_state->line_num = 1; /* while there are tags to parse... */ bFoundBreak = FALSE; pNextTag = NULL; while( !bFoundBreak ) { if( pNextTag == NULL ) { pTag = EDT_TagCursorGetNext(pRd->pTagCursor); } else { pTag = pNextTag; } if( pTag == NULL ){ break; } pRd->new_state->display_blocked = TRUE; pNextTag = pTag->next; if( iStartEditOffset && pTag->type == P_TEXT ) { pRd->new_state->edit_current_offset = iStartEditOffset; pRd->new_state->edit_force_offset = TRUE; lo_LayoutTag(pRd->context, pRd->new_state, pTag); iStartEditOffset = 0; pRd->new_state->edit_force_offset = FALSE; PA_FreeTag(pTag); } else { /* lo_LayoutTag(pRd->context, pRd->new_state, pTag);*/ /* Matches code at end of LO_ProcessTag */ lo_DocState *state = pRd->new_state; lo_DocState *orig_state; lo_DocState *up_state; PA_Tag* tag = pTag; /* * Divert all tags to the current sub-document if there is one. */ up_state = NULL; orig_state = state; if ( bDisplayTables ) { while (state->sub_state != NULL) { lo_DocState *new_state; up_state = state; new_state = state->sub_state; state = new_state; } } /* orig_state->top_state->layout_status = status; */ { lo_DocState *tmp_state; Bool may_save; if ((state->is_a_subdoc == SUBDOC_CELL)|| (state->is_a_subdoc == SUBDOC_CAPTION)) { may_save = TRUE; } else { may_save = FALSE; } /* Some table routines reach out to find the top doc state. * So we replace it for the duration of this call. */ pRd->top_state->doc_state = pRd->new_state; state->edit_relayout_display_blocked = TRUE; state->display_blocked = TRUE; lo_LayoutTag(context, state, tag); pRd->top_state->doc_state = pRd->old_state; tmp_state = lo_CurrentSubState(orig_state); if (may_save != FALSE) { /* * That tag popped us up one state level. If this new * state is still a subdoc, save the tag there. */ if (tmp_state == up_state) { if ((tmp_state->is_a_subdoc == SUBDOC_CELL)|| (tmp_state->is_a_subdoc == SUBDOC_CAPTION)) { lo_SaveSubdocTags(context, tmp_state, tag); } else { PA_FreeTag(tag); } } /* * Else that tag put us in a new subdoc on the same * level. It needs to be saved one level up, * if the parent is also a subdoc. */ else if ((up_state != NULL)&& (tmp_state == up_state->sub_state)&& (tmp_state != state)) { if ((up_state->is_a_subdoc == SUBDOC_CELL)|| (up_state->is_a_subdoc == SUBDOC_CAPTION)) { lo_SaveSubdocTags(context, up_state, tag); } else { PA_FreeTag(tag); } } /* * Else we are still in the same subdoc */ else if (tmp_state == state) { lo_SaveSubdocTags(context, state, tag); } /* * Else that tag started a new, nested subdoc. * Add the starting tag to the parent. */ else if (tmp_state == state->sub_state) { lo_SaveSubdocTags(context, state, tag); /* * Since we have extended the parent chain, * we need to reset the child to the new * parent end-chain. */ if ((tmp_state->is_a_subdoc == SUBDOC_CELL)|| (tmp_state->is_a_subdoc == SUBDOC_CAPTION)) { tmp_state->subdoc_tags = state->subdoc_tags_end; } } /* * This can never happen. */ else { PA_FreeTag(tag); } state = tmp_state; } else { PA_FreeTag(tag); } } } /* pNextTag = pTag->next; PA_FreeTag(pTag); */ if( pNextTag == 0 ){ bFoundBreak = EDT_TagCursorAtBreak( pRd->pTagCursor, &bBreakIsEndTag ); } } if( bFoundBreak ) { if( bBreakIsEndTag ){ pTag = EDT_TagCursorGetNext(pRd->pTagCursor); lo_LayoutTag(pRd->context, pRd->new_state, pTag); iEndLine = EDT_TagCursorCurrentLine( pRd->pTagCursor ); } else { iEndLine = EDT_TagCursorCurrentLine( pRd->pTagCursor ); pTag = EDT_TagCursorGetNext(pRd->pTagCursor); if ( pTag->type == P_TABLE ) { lo_CloseOutLayout( pRd->context, pRd->new_state); } else { lo_LayoutTag(pRd->context, pRd->new_state, pTag); } } EDT_DeleteTagChain(pTag); /* don't close layout. We just flushed this line to the proper * height, there is a start of a new tag in the buffer. */ } else { lo_CloseOutLayout( pRd->context, pRd->new_state); } if( iEndLine == -1 ){ iEndLine = pRd->old_state->line_num-1; } /* Go up the liststack, closing out any mquotes. */ { lo_ListStack *lptr; lptr = pRd->new_state->list_stack; while (lptr->type != P_UNKNOWN && lptr->next != NULL) { if (lptr->quote_type == QUOTE_MQUOTE) { lo_add_leading_bullets(context,pRd->new_state, lptr->mquote_line_num - 1, pRd->new_state->line_num - 2, lptr->mquote_x); } lptr = lptr->next; } } #ifdef DEBUG_shannon XP_TRACE(("\n\nELEMENTS TO MERGE - old relayout")); lo_VerifyStateLayoutImplementation( context, pRd->top_state, pRd->new_state, TRUE); #endif lo_MergeState( context, pRd->old_state, iStartLine, iEndLine, pRd->new_state, &changedY, &changedHeight ); /* top_state->doc_state = new_state; */ /* LTNOTE:need to destroy the new state */ /* kill the current state */ /* FE_ClearView( context, FE_VIEW ); lo_RefreshDocumentArea( context, state, 0, 0,new_state->win_width, new_state->win_height); */ /* Free the layout */ lo_FreeRelayoutData(context, pRd); context->is_editor &= ~EDT_RELAYOUT_FLAG; FE_DocumentChanged( context, changedY, changedHeight ); } PRIVATE Bool lo_MovePosition( MWContext *pContext, LO_Element *pElement, intn iOffset, ED_Element **ppEdElement, intn* pOffset, Bool bForward) { int32 iOffset32 = iOffset; /* Really should use a sythetic type for offsets. */ Bool bResult = LO_ComputeNewPosition( pContext, LO_NA_CHARACTER, FALSE, FALSE, bForward, &pElement, &iOffset32); iOffset = (intn) iOffset32; if ( bResult ) { *ppEdElement = pElement->lo_any.edit_element; *pOffset = pElement->lo_any.edit_offset+iOffset; } return bResult; } Bool LO_PreviousPosition( MWContext *pContext, LO_Element *pElement, intn iOffset, ED_Element **ppEdElement, intn* pOffset) { return lo_MovePosition(pContext, pElement, iOffset, ppEdElement, pOffset, FALSE); } Bool LO_NextPosition( MWContext *pContext, LO_Element *pElement, intn iOffset, ED_Element **ppEdElement, intn* pOffset) { return lo_MovePosition( pContext, pElement, iOffset, ppEdElement, pOffset, TRUE); } /* * Find the first text element on a line. */ LO_Element* LO_FirstElementOnLine( MWContext *pContext, int32 x, int32 y, int32* pLineNum ) { lo_TopState* top_state; lo_DocState *state; int32 iLine; LO_Element **line_array, *pElement; Bool bFound = FALSE; /* * Get the unique document ID, and retreive this * documents layout state. */ top_state = lo_FetchTopState(XP_DOCID(pContext)); if ((top_state == NULL)||(top_state->doc_state == NULL)) { return 0; } state = top_state->doc_state; XP_LOCK_BLOCK(line_array, LO_Element **, state->line_array); /* find the line we are currently on */ iLine = lo_PointToLine( pContext, state, x, y); if( pLineNum ){ *pLineNum = iLine; } pElement = line_array[iLine]; while( !bFound ) { /* if we've found a text element. We've for sure, found the line. */ if( lo_EditableElement( pElement->type ) && pElement->lo_any.edit_element != 0 ){ bFound = TRUE; } else { pElement = pElement->lo_any.next; } } XP_UNLOCK_BLOCK(state->line_array); return pElement; } /* * Find the first text element on a line. */ PRIVATE LO_Element* LO_LastElementOnLine( MWContext *pContext, int32 x, int32 y, int32* pLineNum ) { lo_TopState* top_state; lo_DocState *state; int32 iLine; LO_Element **line_array, *pElement, *pFoundElement, *pEnd; /* * Get the unique document ID, and retreive this * documents layout state. */ top_state = lo_FetchTopState(XP_DOCID(pContext)); if ((top_state == NULL)||(top_state->doc_state == NULL)) { return 0; } state = top_state->doc_state; XP_LOCK_BLOCK(line_array, LO_Element **, state->line_array); /* find the line we are currently on */ iLine = lo_PointToLine( pContext, state, x, y); if( pLineNum ){ *pLineNum = iLine; } pElement = line_array[iLine]; pFoundElement = 0; if( iLine == state->line_num-2 ){ pEnd = NULL; } else { pEnd = line_array[iLine+1]; } while( pElement != pEnd ) { /* if we've found a text element. We've for sure, found the line. */ if( pElement->type == LO_TEXT ){ pFoundElement = pElement; } pElement = pElement->lo_any.next; } XP_UNLOCK_BLOCK(state->line_array); return pFoundElement; } LO_Element* LO_BeginOfLine( MWContext *pContext, LO_Element *pElement){ LO_Element *pFirst = LO_FirstElementOnLine( pContext, pElement->lo_any.x + pElement->lo_any.width / 2, pElement->lo_any.y + pElement->lo_any.height / 2, 0 ); return pFirst; } LO_Element* LO_EndOfLine( MWContext *pContext, LO_Element *pElement ){ LO_Element *pLast = LO_LastElementOnLine( pContext, pElement->lo_any.x, pElement->lo_any.y, 0 ); return pLast; } void LO_PositionCaretBounded(MWContext *context, int32 x, int32 y, int32 minY, int32 maxY ) { int32 doc_id; lo_TopState *top_state; lo_DocState *state; int32 retX, retY; int32 iLine; int dir = 0; Bool bFound = FALSE; /* * Get the unique document ID, and retreive this * documents layout state. */ doc_id = XP_DOCID(context); top_state = lo_FetchTopState(doc_id); if ((top_state == NULL)||(top_state->doc_state == NULL)) { return; } state = top_state->doc_state; iLine = lo_PointToLine( context, state, x, y ); #define FORWARD 1 #define BACKWARD 2 while( iLine >= 0 && iLine < state->line_num -2 && !bFound) { bFound = lo_FindBestPositionOnLine( context, state, iLine, x, y, dir == FORWARD, &retX, &retY ); if( bFound && dir != BACKWARD && retY < minY ){ iLine++; dir = FORWARD; bFound = FALSE; } else if ( bFound && dir != FORWARD && retY > maxY ){ iLine--; dir = BACKWARD; bFound = FALSE; } } if( bFound ){ LO_PositionCaret( context, retX, retY, NULL ); } } #if 0 PRIVATE void LO_Resize( MWContext * pContext ){ } #endif void LO_RefetchWindowDimensions( MWContext *pContext ){ int32 doc_id; lo_TopState *top_state; lo_DocState *state; int32 topX,topY, winWidth, winHeight; /* * Get the unique document ID, and retreive this * documents layout state. */ doc_id = XP_DOCID(pContext); top_state = lo_FetchTopState(doc_id); if ((top_state == NULL)||(top_state->doc_state == NULL)) { return; } state = top_state->doc_state; FE_GetDocAndWindowPosition( pContext, &topX, &topY, &winWidth, &winHeight ); state->win_width = winWidth; state->win_height = winHeight; } #ifdef DEBUG PRIVATE Bool lo_PrintLayoutElement(MWContext *pContext, lo_TopState* top_state, lo_DocState* state, LO_Element *eptr, int32 index); Bool lo_VerifyList( MWContext *pContext, lo_TopState* top_state, lo_DocState* state, LO_Element* start, LO_Element* end, LO_Element* floating, Bool print) { Bool result = TRUE; LO_Element *eptr; LO_Element *prev; int32 index; int32 elementID; prev = NULL; index = 0; elementID = -1; for ( eptr = start; eptr; eptr = eptr->lo_any.next ) { if ( print ) { result = lo_PrintLayoutElement(pContext, top_state, state, eptr, index); } /* * Check next/prev pointer consistency */ if ( eptr->lo_any.prev != prev ) { XP_TRACE(("element %ld at address 0x%08x has a bad prev pointer.", index, eptr)); XP_TRACE((" prev is 0x%08x , should be 0x%08x.", eptr->lo_any.prev, prev)); result = FALSE; } /* * Does this element have a valid type? */ if ( eptr->lo_any.type < 0 || eptr->lo_any.type > LO_SUB ) { XP_TRACE(("element %ld at address 0x%08x has an unknown type %d.", index, eptr, eptr->lo_any.type)); result = FALSE; } /* * Does this element have a valid element id? */ if ( eptr->lo_any.ele_id <= elementID ) { XP_TRACE(("element %ld at address 0x%08x has an invalid ele_id %ld.", index, eptr, eptr->lo_any.ele_id)); /* Exit because the pointers may be invalid. */ return FALSE; /* Keep going for now, till you can because element ids might be getting corrupted by relayout even though pointers are valid */ /* result = FALSE; */ } elementID = eptr->lo_any.ele_id; /* * Verify the edit element if it exists. */ if ( eptr->lo_any.edit_element != NULL ) { /*EDT_VerifyLayoutElement( eptr );*/ } /* * Update our loop variables */ prev = eptr; index++; } /* If we just verified the float list, don't check for end because end for float list is not stored in doc state */ if (state->float_list != start) { /* * Check that the last "next" pointer points to the end of the document. */ if ( prev != end ) { XP_TRACE(("end 0x%08x is not the same as the last linked-list element 0x%08x", end, prev)); result = FALSE; } } /* Verify floating element list */ if (floating != NULL) { lo_VerifyList(pContext, top_state, state, floating, NULL, NULL, print); } return result; } PRIVATE Bool lo_PrintLayoutElement(MWContext *pContext, lo_TopState* top_state, lo_DocState* state, LO_Element *eptr, int32 index) { int16 type; Bool result = TRUE; const char* typeString; static const char* kTypeStrings[] = {"LO_UNKNOWN", "LO_NONE", "LO_TEXT", "LO_LINEFEED", "LO_HRULE", "LO_IMAGE", "LO_BULLET", "LO_FORM_ELE", "LO_SUBDOC", "LO_TABLE", "LO_CELL", "LO_EMBED", "LO_EDGE", "LO_JAVA", "LO_SCRIPT", "LO_OBJECT", "LO_PARAGRAPH", "LO_CENTER", "LO_MULTICOLUMN", "LO_FLOAT", "LO_TEXTBLOCK", "LO_LIST", "LO_DESCTITLE", "LO_DESCTEXT", "LO_BLOCKQUOTE", "LO_LAYER", "LO_HEADING", "LO_SPAN", "LO_DIV", "LO_BUILTIN", "LO_SPACER", "LO_SUPER", "LO_SUB" }; type = eptr->lo_any.type; if ( type < LO_UNKNOWN || type > LO_SUB ) typeString = "Illegal type"; else typeString = kTypeStrings[type + 1]; XP_TRACE(("[%d] 0x%08x type %s(%d) ele_id %d", index, eptr, typeString, type, eptr->lo_any.ele_id)); XP_TRACE(("\tposition %d,%d size %d, %d offset %d, %d, lineheight %d", eptr->lo_any.x, eptr->lo_any.y, eptr->lo_any.width, eptr->lo_any.height, eptr->lo_any.x_offset, eptr->lo_any.y_offset, eptr->lo_any.line_height)); #ifdef EDITOR XP_TRACE(("\tedit_element 0x%08x edit_offset %d", eptr->lo_any.edit_element, eptr->lo_any.edit_offset)); #endif switch ( type ) { case LO_TEXT: { char* text; char saveChar; XP_LOCK_BLOCK(text, char *, eptr->lo_text.text); saveChar = text[ eptr->lo_text.text_len ]; text[ eptr->lo_text.text_len ] = '\0'; XP_TRACE(("\ttext \"%s\"", text)); text[ eptr->lo_text.text_len ] = saveChar; XP_TRACE(("block offset: %d, doc_width: %d, attr mask: %d", eptr->lo_text.block_offset, eptr->lo_text.doc_width, eptr->lo_text.ele_attrmask)); XP_UNLOCK_BLOCK(eptr->lo_text.text); } break; case LO_LINEFEED: { XP_TRACE(("\tbreak_type: %d, baseline: %d, attr mask: %d", eptr->lo_linefeed.break_type, eptr->lo_linefeed.baseline, eptr->lo_linefeed.ele_attrmask)); } break; case LO_HRULE: { XP_TRACE(("\tend_x: %d, end_y: %d, attr mask: %d, alignment: %d, thickness: %d, %% width: %d", eptr->lo_hrule.end_x, eptr->lo_hrule.end_y, eptr->lo_hrule.ele_attrmask, eptr->lo_hrule.alignment, eptr->lo_hrule.thickness, eptr->lo_hrule.percent_width)); } break; case LO_IMAGE: { XP_TRACE(("\tlayer_id: %d, layer-x: %d, layer-y: %d, %% width: %d, %% height: %d, border(width: %d, vert_space: %d, horiz_space: %d)", eptr->lo_image.layer_id, CL_GetLayerXOrigin(eptr->lo_image.layer), CL_GetLayerYOrigin(eptr->lo_image.layer), eptr->lo_image.percent_width, eptr->lo_image.percent_height, eptr->lo_image.border_width, eptr->lo_image.border_vert_space, eptr->lo_image.border_horiz_space)); XP_TRACE(("\timage_url: %s, attr mask: %d, alt: %s, seq_num: %d", eptr->lo_image.image_url, eptr->lo_image.ele_attrmask, eptr->lo_image.alt, eptr->lo_image.seq_num)); } break; case LO_BULLET: { XP_TRACE(("\tbullet(type: %d, size: %d, attr mask: %d, level: %d)", eptr->lo_bullet.bullet_type, eptr->lo_bullet.bullet_size, eptr->lo_bullet.ele_attrmask, eptr->lo_bullet.level)); } break; case LO_FORM_ELE: { const struct LO_FormElementStruct_struct* form; static const char* kFormTypeNames[] = { "FORM_TYPE_NONE", "FORM_TYPE_TEXT", "FORM_TYPE_RADIO", "FORM_TYPE_CHECKBOX", "FORM_TYPE_HIDDEN", "FORM_TYPE_SUBMIT", "FORM_TYPE_RESET", "FORM_TYPE_PASSWORD", "FORM_TYPE_BUTTON", "FORM_TYPE_JOT", "FORM_TYPE_SELECT_ONE", "FORM_TYPE_SELECT_MULT", "FORM_TYPE_TEXTAREA", "FORM_TYPE_ISINDEX", "FORM_TYPE_IMAGE", "FORM_TYPE_FILE", "FORM_TYPE_KEYGEN", "FORM_TYPE_READONLY", "FORM_TYPE_OBJECT" #ifdef ENDER ,"FORM_TYPE_HTML" #endif /*ENDER*/ }; const char* formTypeName; int16 form_id; form = & eptr->lo_form; form_id = form->form_id; formTypeName = "Unknown"; if ( form_id >= FORM_TYPE_NONE && form_id <= FORM_TYPE_OBJECT ) { formTypeName = kFormTypeNames[form_id]; } XP_TRACE(("\tform_id %s(%d)", formTypeName, form_id)); XP_TRACE(("\telement_index: %d, baseline: %d, sel(%d,%d), attr mask: %d, form_id: %d, border(horiz_space: %d, vert_space: %d)", form->element_index, form->baseline, form->sel_start, form->sel_end, form->ele_attrmask, form->form_id, form->border_vert_space, form->border_horiz_space)); } break; case LO_TABLE: { if (eptr->lo_table.table == NULL) { XP_TRACE(("\tERROR: Table element contains a null pointer to its table state")); result = FALSE; } else { XP_TRACE(("\tlo_TableRec *: %p", eptr->lo_table.table)); } } break; case LO_CELL: { const LO_CellStruct* cell = & eptr->lo_cell; XP_TRACE(("------------- Beginning of cell (ele_id = %d) contents --------------------", cell->ele_id)); if (cell->table == NULL || cell->table_row == NULL || cell->table_cell == NULL) { XP_TRACE(("\tERROR: Cell element contains a null pointer to its table state")); result = FALSE; } XP_TRACE(("lo_TableRec ptr: %p, lo_TableRow ptr: lo_TableCell ptr:", cell->table, cell->table_row, cell->table_cell)); lo_VerifyList(pContext, top_state, state, cell->cell_list, cell->cell_list_end, cell->cell_float_list, TRUE); XP_TRACE(("------------- End of cell (ele_id = %d) contents --------------------", cell->ele_id)); } break; case LO_PARAGRAPH: { XP_TRACE(("\tis_end: %d, blank_lines: %d, implicit_end: %d, align_set: %d, align: %d", eptr->lo_paragraph.is_end, eptr->lo_paragraph.blank_lines, eptr->lo_paragraph.implicit_end, eptr->lo_paragraph.alignment_set, eptr->lo_paragraph.alignment)); } break; case LO_CENTER: { XP_TRACE(("\tis_end: %d", eptr->lo_center.is_end)); } break; case LO_MULTICOLUMN: { XP_TRACE(("\tis_end: %d", eptr->lo_multicolumn.is_end)); } break; case LO_FLOAT: { XP_TRACE(("\tPoints to %s(%d), ele_id: %d", kTypeStrings[eptr->lo_float.float_ele->lo_any.type + 1], eptr->lo_float.float_ele->lo_any.type, eptr->lo_float.float_ele->lo_any.ele_id)); } break; case LO_TEXTBLOCK: { const LO_TextBlock* block = & eptr->lo_textBlock; XP_TRACE((" buffer_length %lu", block->buffer_length)); XP_TRACE((" buffer_write_index %lu", block->buffer_write_index)); XP_TRACE((" break_table %lx", block->break_table)); XP_TRACE((" startTextElement %lx", block->startTextElement)); XP_TRACE((" endTextElement %lx", block->endTextElement)); } case LO_LIST: { XP_TRACE(("\tbullet(type: %d, start: %d), quote_type: %d, compact: %d, is_end: %d", eptr->lo_list.bullet_type, eptr->lo_list.bullet_start, eptr->lo_list.quote_type, eptr->lo_list.compact, eptr->lo_list.is_end)); } break; case LO_BLOCKQUOTE: { XP_TRACE(("\tblockquote : end: %s, quote_type: %d", eptr->lo_blockquote.is_end ? "True" : "False", eptr->lo_blockquote.quote_type)); } break; default: break; } return result; } PRIVATE Bool lo_VerifyStateLayoutImplementation( MWContext *pContext, lo_TopState *top_state, lo_DocState *state, Bool print) { /* Debugging aid. Checks consistency of the layout for this context. * returns TRUE if the layout is valid. * Prints information to stderr if the layout is invalid. */ Bool result; LO_Element **array; LO_Element *eptr; LO_Element *start; LO_Element *end; #ifdef XP_WIN16 XP_Block *larray_array; #endif /* XP_WIN16 */ result = TRUE; if ( ! pContext ) { XP_TRACE(("context is NULL.")); return FALSE; } /* * Get first element in doc. */ #ifdef XP_WIN16 XP_LOCK_BLOCK(larray_array, XP_Block *, state->larray_array); state->line_array = larray_array[0]; XP_UNLOCK_BLOCK(state->larray_array); #endif /* XP_WIN16 */ XP_LOCK_BLOCK(array, LO_Element **, state->line_array); eptr = array[0]; XP_UNLOCK_BLOCK(state->line_array); if (eptr == NULL) { XP_TRACE(("No first element.")); return FALSE; } start = eptr; /* * Get last element in doc. */ end = state->end_last_line; if (end == NULL) { XP_TRACE(("No last element.")); return FALSE; } return lo_VerifyList(pContext, top_state, state, start, end, state->float_list, print); } PRIVATE Bool lo_VerifyLayoutImplementation( MWContext *pContext, Bool print) { /* Debugging aid. Checks consistency of the layout for this context. * returns TRUE if the layout is valid. * Prints information to stderr if the layout is invalid. */ Bool result; int32 doc_id; lo_TopState *top_state; lo_DocState *state; result = TRUE; if ( ! pContext ) { XP_TRACE(("context is NULL.")); return FALSE; } /* * Get the unique document ID, and retreive this * documents layout state. */ doc_id = XP_DOCID(pContext); top_state = lo_FetchTopState(doc_id); if (top_state == NULL) { XP_TRACE(("top_state is NULL.")); return FALSE; } state = top_state->doc_state; if (state == NULL) { XP_TRACE(("state is NULL.")); return FALSE; } return lo_VerifyStateLayoutImplementation(pContext, top_state, state, print); } Bool lo_VerifyLayout( MWContext *pContext) { return lo_VerifyLayoutImplementation(pContext, FALSE); } void lo_PrintLayout( MWContext *pContext) { lo_VerifyLayoutImplementation(pContext, TRUE); } #endif /* DEBUG */ #endif /* editor */ void LO_SetBaseURL( MWContext *context, char *pURL ){ int32 doc_id; lo_TopState *top_state; /* * Get the unique document ID, and retreive this * documents layout state. */ doc_id = XP_DOCID(context); top_state = lo_FetchTopState(doc_id); if ((top_state == NULL)||(top_state->doc_state == NULL)) { return; } XP_FREE( top_state->base_url ); top_state->base_url = XP_STRDUP( pURL ); } char* LO_GetBaseURL( MWContext *context ){ int32 doc_id; lo_TopState *top_state; /* * Get the unique document ID, and retreive this * documents layout state. */ doc_id = XP_DOCID(context); top_state = lo_FetchTopState(doc_id); if ((top_state == NULL)||(top_state->doc_state == NULL)) { return 0; } return top_state->base_url; }