gecko-dev/lib/layout/layedit.c

2444 строки
64 KiB
C

/* -*- 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;
}