home *** CD-ROM | disk | FTP | other *** search
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- *
- * The contents of this file are subject to the Netscape Public License
- * Version 1.0 (the "NPL"); you may not use this file except in
- * compliance with the NPL. You may obtain a copy of the NPL at
- * http://www.mozilla.org/NPL/
- *
- * Software distributed under the NPL is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
- * for the specific language governing rights and limitations under the
- * NPL.
- *
- * The Initial Developer of this code under the NPL is Netscape
- * Communications Corporation. Portions created by Netscape are
- * Copyright (C) 1998 Netscape Communications Corporation. All Rights
- * Reserved.
- */
-
-
- #include "xp.h"
- #include "layout.h"
- #include "laylayer.h"
- #include "libi18n.h"
- #include "edt.h"
- #include "layers.h"
- #include <ctype.h> /* For lo_CharacterClassOf */
-
- #ifndef XP_TRACE
- # define XP_TRACE(X) fprintf X
- #endif
-
- #ifdef TEST_16BIT
- #define XP_WIN16
- #endif /* TEST_16BIT */
-
- #ifdef XP_WIN16
- #define SIZE_LIMIT 32000
- #endif /* XP_WIN16 */
-
- #ifdef PROFILE
- #pragma profile on
- #endif
-
- #ifdef DEBUG
- void lo_ValidatePosition(MWContext *context, LO_Position* position);
- void lo_ValidateSelection(MWContext *context, LO_Selection* selection);
- void lo_ValidatePosition2(MWContext *context, LO_Element* element, int32 position);
- void lo_ValidateSelection2(MWContext *context, LO_Element* elementB, int32 positionB, LO_Element* elementE, int32 positionE);
-
- #define LO_ASSERT_POSITION(context, p) lo_ValidatePosition(context, p)
- #define LO_ASSERT_SELECTION(context, s) lo_ValidateSelection(context, s)
- #define LO_ASSERT_POSITION2(context, e, p) lo_ValidatePosition2(context, e, p)
- #define LO_ASSERT_SELECTION2(context, eb, pb, ee, pe) lo_ValidateSelection2(context, eb, pb, ee, pe)
-
- #else
-
- #define LO_ASSERT_POSITION(context, p) {}
- #define LO_ASSERT_SELECTION(context, s) {}
- #define LO_ASSERT_POSITION2(context, e, p) {}
- #define LO_ASSERT_SELECTION2(context, eb, pb, ee, pe) {}
-
- #endif
-
- static void
- lo_bump_position(MWContext *context, lo_DocState *state,
- LO_Element *sel, int32 pos,
- LO_Element **new_sel, int32 *new_pos, Bool forward);
-
- /*
- * Utility functions to convert between insert point and selection end.
- * Returns FALSE if the conversion can't be done.
- * (Only happens when the insert point is off the beginning of the document, or the
- * selection end is off the end of the document.) In those cases, the input position is
- * returned unchanged.
- *
- */
-
- Bool lo_ConvertInsertPointToSelectionEnd(MWContext* context, lo_DocState * state,
- LO_Element** element, int32* position);
- Bool lo_ConvertSelectionEndToInsertPoint(MWContext* context, lo_DocState * state,
- LO_Element** element, int32* position);
-
- void lo_HighlightSelect(MWContext *context, lo_DocState *state,
- LO_Element *start, int32 start_pos, LO_Element *end, int32 end_pos,
- Bool on);
- void lo_SetSelect(MWContext *context, lo_DocState *state,
- LO_Element *start, int32 start_pos, LO_Element *end, int32 end_pos,
- Bool on);
- void
- lo_StartNewSelection(MWContext *context, lo_DocState * state,
- LO_Element* eptr, int32 position);
-
- void
- lo_ExtendSelectionToPosition2(MWContext *context, lo_TopState* top_state, lo_DocState *state, LO_Element* eptr, int32 position);
-
- /* Makes sure *eptr points to an editable element. If *eptr doesn't, searches towards
- * the end of the document.
- * If no editiable element is found, the function
- * returns FALSE.
- */
- Bool
- lo_EnsureEditableSearchNext(MWContext *context, lo_DocState *state, LO_Element** eptr);
-
- Bool
- lo_EnsureEditableSearchNext2(MWContext *context, lo_DocState *state, LO_Element** eptr, int32* ePositionPtr);
-
- /* Same as lo_EnsureEditableSearchNext, except searches towards the start of document.
- */
-
- Bool
- lo_EnsureEditableSearchPrev(MWContext *context, lo_DocState *state, LO_Element** eptr);
-
- Bool
- lo_EnsureEditableSearchPrev2(MWContext *context, lo_DocState *state, LO_Element** eptr, int32* ePositionPtr);
-
- Bool
- lo_EnsureEditableSearch(MWContext *context, lo_DocState* state, LO_Position* p, Bool forward);
-
- Bool
- lo_EnsureEditableSearch2(MWContext *context, lo_DocState* state, LO_Element** eptr, int32* ePositionPtr, Bool forward);
-
- Bool
- lo_IsValidEditableInsertPoint2(MWContext *context, lo_DocState* state, LO_Element* eptr, int32 ePositionPtr);
-
-
- /* Normalizes the selection, if editing is turned on. Returns TRUE if the selection
- * is non empty.
- */
-
- Bool lo_NormalizeSelection(MWContext *context);
-
- /*
- * Return FALSE if the selection point can't be normalized.
- * (i.e. if it's at the end of the document.)
- */
-
- Bool
- lo_NormalizeSelectionPoint(MWContext *context, lo_DocState *state, LO_Element** pEptr, int32* pPosition);
-
- void
- lo_NormalizeSelectionEnd(MWContext *context, lo_DocState *state, LO_Element** pEptr, int32* pPosition);
-
- int32
- lo_GetTextAttrMask(LO_Element* eptr);
-
- LO_AnchorData*
- lo_GetAnchorData(LO_Element* eptr);
-
- LO_Element*
- lo_GetNeighbor(LO_Element* element, Bool forward);
-
- int32
- lo_GetElementEdge(LO_Element* element, Bool forward);
-
- int32
- lo_GetMaximumInsertPointPosition(LO_Element* eptr);
-
- int32
- lo_GetLastCharEndPosition(LO_Element* eptr);
-
- int32
- lo_GetLastCharBeginPosition(LO_Element* eptr);
-
- int32
- lo_IncrementPosition(LO_Element* eptr, int32 position);
-
- int32
- lo_DecrementPosition(LO_Element* eptr, int32 position);
-
- Bool
- lo_IsEndOfParagraph2(MWContext* context, LO_Element* element, int32 position);
-
- Bool
- lo_FindDocumentEdge(MWContext* context, lo_DocState *state, LO_Position* edge, Bool select, Bool forward);
-
- Bool
- lo_IsEdgeOfDocument2(MWContext* context, lo_DocState *state, LO_Element* element, int32 position, Bool forward);
-
- Bool
- lo_IsEdgeOfDocument(MWContext* context, lo_DocState *state, LO_Position* where, Bool forward);
-
- void
- lo_SetInsertPoint(MWContext *context, lo_TopState *top_state, LO_Element* eptr, int32 position, CL_Layer *layer);
-
- /*
- * Implements the UI policy for a click on an element. Returns TRUE if the result is an insertion point.
- */
-
- Bool lo_ProcessClick(MWContext *context, lo_TopState *top_state, lo_DocState *state, LO_HitResult* result, Bool requireCaret, CL_Layer *layer);
- Bool lo_ProcessDoubleClick(MWContext *context, lo_TopState *top_state, lo_DocState *state, LO_HitResult* result, CL_Layer *layer);
- Bool lo_ProcessAnchorClick(MWContext *context, lo_TopState *top_state, lo_DocState *state, LO_HitResult* result);
-
- Bool
- lo_SelectAnchor(MWContext* context, lo_DocState *state, LO_Element* eptr);
-
- void
- lo_SetSelection(MWContext *context, LO_Selection* selection, Bool extendingStart);
-
- void
- lo_FullSetSelection(MWContext *context, lo_DocState * state,
- LO_Element* start, int32 start_pos,
- LO_Element* end, int32 end_pos, Bool extendStart);
-
- /* Get various selection parts, expressed as insert points. */
-
- void
- lo_GetAnchorPoint(MWContext *context, lo_DocState * state, LO_Element** pElement, int32* pPosition);
- void
- lo_GetExtensionPoint(MWContext *context, lo_DocState * state, LO_Element** pElement, int32* pPosition);
-
- Bool lo_FindBestPositionInTable(MWContext *context, lo_DocState* state, LO_TableStruct* pTable, int32 iDesiredX,
- int32 iDesiredY, Bool bForward, int32* pRetX, int32 *pRetY );
- Bool lo_FindClosestUpDown_Cell(MWContext *pContext, lo_DocState* state,
- LO_CellStruct* cell, int32 x, int32 y,
- Bool bForward, int32* ret_x, int32* ret_y);
- LO_Element *
- lo_search_element_list_WideMatch(MWContext *context, LO_Element *eptr, LO_Element* eEndPtr, int32 x, int32 y,
- Bool bForward);
-
- Bool lo_EditableElement( int iType )
- {
- if( iType == LO_TEXT
- || iType == LO_TEXTBLOCK
- || iType == LO_IMAGE
- || iType == LO_HRULE
- || iType == LO_LINEFEED
- || iType == LO_FORM_ELE )
- {
- return TRUE;
- }
- else
- {
- return FALSE;
- }
- }
-
- PRIVATE
- int32
- lo_ElementToCharOffset2(MWContext *context, lo_DocState *state,
- LO_Element *element, int32 x, int32* returnCharStart, int32* returnCharEnd)
- {
-
- LO_TextStruct *text_ele;
- LO_TextInfo text_info;
- int32 orig_len;
- int32 xpos;
- int32 cpos;
- int32 start, end;
- int16 charset;
- int32 startX;
-
- if ((element == NULL)||(element->type != LO_TEXT))
- {
- return(-1);
- }
-
- text_ele = (LO_TextStruct *)element;
- if ((text_ele->text == NULL)||(text_ele->text_len <= 0))
- {
- return -1;
- }
-
- startX = text_ele->x + text_ele->x_offset;
- xpos = x - startX;
- if (xpos < 0)
- {
- return -1;
- }
-
- if (xpos >= text_ele->width && returnCharStart == NULL)
- {
- return lo_GetLastCharEndPosition(element);
- }
-
- start = 0;
- end = lo_GetLastCharEndPosition(element);
-
- /* Make a guess as to where to start searching, assuming characters are
- * all roughly the same width.
- */
-
- if ( text_ele->width > 0 )
- {
- cpos = xpos * text_ele->text_len / text_ele->width;
- }
- else
- {
- cpos = 0;
- }
- if (cpos > end)
- {
- cpos = end;
- }
- orig_len = text_ele->text_len;
-
- charset = text_ele->text_attr->charset;
- if (INTL_CharSetType(charset) == SINGLEBYTE)
- {
- while (start < end)
- {
- XP_ASSERT(cpos >= 0);
- text_ele->text_len = (intn) cpos + 1;
- FE_GetTextInfo(context, text_ele, &text_info);
- if (xpos > text_info.max_width)
- {
- start = cpos + 1;
- }
- else
- {
- end = cpos;
- }
- cpos = (start + end) / 2;
- }
-
- /* Find character width by trickery */
- text_ele->text_len = (intn) cpos;
- FE_GetTextInfo(context, text_ele, &text_info);
- if ( returnCharStart != NULL ) *returnCharStart = startX + text_info.max_width;
- text_ele->text_len = (intn) cpos+1;
- FE_GetTextInfo(context, text_ele, &text_info);
- if ( returnCharEnd != NULL ) *returnCharEnd = startX + text_info.max_width;
- }
- else
- { /* slow multibyte finding, I feel sad we can't use the
- beautiful single byte algorithm above
- */
- int32 prev_xpos, last_xpos, last_pos;
- char *tptr;
-
- PA_LOCK(tptr, char *, text_ele->text);
- cpos = 0;
- prev_xpos = 0;
- last_xpos = text_ele->width ;
- last_pos = end ;
- while ((start <= end) && (cpos <= end))
- {
- text_ele->text_len = (intn) cpos ;
- FE_GetTextInfo(context, text_ele, &text_info);
- if (xpos > text_info.max_width)
- {
- prev_xpos = text_info.max_width ;
- start = text_ele->text_len;
- }
- else /* since it goes char by char, finding ends here */
- {
- last_xpos = text_info.max_width ;
- end = cpos;
- break ;
- }
- /* go to next char */
- cpos = start +
- (int32)INTL_CharLen(charset,
- (unsigned char *)(tptr + start));
- }
- cpos = start ;
- if (cpos > last_pos)
- cpos = last_pos ;
-
- PA_UNLOCK(text_ele->text);
-
- if ( returnCharStart != NULL ) *returnCharStart = startX + prev_xpos;
- if ( returnCharEnd != NULL) *returnCharEnd = startX + last_xpos;
- }
-
- text_ele->text_len = (intn) orig_len;
-
- return(cpos);
- }
-
- /*
- * For clients that don't care about the bounds of what they hit.
- */
- PRIVATE
- int32
- lo_ElementToCharOffset(MWContext *context, lo_DocState *state,
- LO_Element *element, int32 x)
- {
- return lo_ElementToCharOffset2(context, state, element, x, NULL, NULL);
- }
-
- void
- LO_StartSelection(MWContext *context, int32 x, int32 y, CL_Layer *layer)
- {
- #if 0
- int32 doc_id;
- lo_TopState *top_state;
- lo_DocState *state;
- LO_Element *eptr;
- int32 position;
- int32 ret_x, ret_y;
-
- /*
- * 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;
-
- LO_HighlightSelection(context, FALSE);
- state->selection_start = NULL;
- state->selection_start_pos = 0;
- state->selection_end = NULL;
- state->selection_end_pos = 0;
-
- position = 0;
- eptr = lo_XYToDocumentElement(context, state, x, y, FALSE, TRUE, TRUE,
- &ret_x, &ret_y);
- if (eptr != NULL)
- {
- position = lo_ElementToCharOffset(context, state, eptr, ret_x);
- if (position < 0)
- {
- position = 0;
- }
- }
-
- state->selection_new = eptr;
- state->selection_new_pos = position;
- #endif
-
- LO_Click(context, x, y, ! EDT_IS_EDITOR(context),
- layer); /* Only allow selections in editor */
- }
-
- /*
- * Used for right-clicks.
- */
- void LO_SelectObject( MWContext *context, int32 x, int32 y, CL_Layer *layer)
- {
- int32 doc_id;
- lo_TopState *top_state;
- lo_DocState *state;
- LO_HitResult result;
-
- /*
- * 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;
-
- LO_Hit(context, x, y, FALSE, &result, layer);
-
- if ( ! lo_ProcessAnchorClick(context, top_state, state, &result ) ) {
- lo_ProcessClick(context, top_state, state, &result, FALSE, layer );
- }
- }
-
-
- void LO_StartSelectionFromElement( MWContext *context, LO_Element *eptr, int32 new_pos, CL_Layer *layer )
- {
- int32 doc_id;
- lo_TopState *top_state;
- lo_DocState *state;
-
- /*
- * Get the unique document ID, and retreive this
- * documents layout state.
- */
- doc_id = XP_DOCID(context);
- top_state = lo_FetchTopState(doc_id);
- if ((top_state == NULL)||(top_state->doc_state == NULL))
- {
- return;
- }
- state = top_state->doc_state;
-
- if ( eptr ) {
- if ( ! lo_EnsureEditableSearchPrev2(context, state, &eptr, &new_pos) )
- {
- XP_ASSERT(FALSE);
- return;
- }
- LO_ASSERT_POSITION2(context, eptr, new_pos);
- }
-
- LO_HighlightSelection(context, FALSE);
-
- state->selection_start = NULL;
- state->selection_start_pos = 0;
- state->selection_end = NULL;
- state->selection_end_pos = 0;
-
- state->selection_new = eptr;
- state->selection_new_pos = new_pos;
- state->selection_layer = layer;
- }
-
- #ifdef DEBUG
-
- PRIVATE
- void
- LO_DUMP_SELECTIONSTATE(MWContext *context)
- {
- int32 doc_id;
- lo_TopState *top_state;
- lo_DocState *state;
-
- /*
- * Get the unique document ID, and retreive this
- * documents layout state.
- */
- doc_id = XP_DOCID(context);
- top_state = lo_FetchTopState(doc_id);
- if ((top_state == NULL)||(top_state->doc_state == NULL))
- {
- return;
- }
- state = top_state->doc_state;
-
- XP_TRACE(("selection 0x%08x %d 0x%08x %d new 0x%08x %d extending_start %d\n",
- state->selection_start,
- state->selection_start_pos,
- state->selection_end,
- state->selection_end_pos,
- state->selection_new,
- state->selection_new_pos,
- state->extending_start));
- }
-
- PRIVATE
- void
- LO_DUMP_INSERT_POINT(char* s, LO_Element* element, int32 position)
- {
- if ( element )
- {
- XP_TRACE(("%s %d:%d.%d ", s, element->type, element->lo_any.ele_id, position));
- }
- else
- {
- XP_TRACE(("%s - NIL", s));
- }
- }
-
- PRIVATE
- void
- LO_DUMP_POSITION(char* s, LO_Position* position)
- {
- LO_DUMP_INSERT_POINT(s, position->element, position->position);
- }
-
- PRIVATE
- void
- LO_DUMP_SELECTION2(char* s, LO_Element* a, int32 ap, LO_Element* b, int32 bp)
- {
- /* const char* kElementCodes = "?tlhi?????????????????";*/
- XP_TRACE(("%s %d:%d.%d %d:%d.%d ", s,
- a->type, a->lo_any.ele_id, ap,
- b->type, b->lo_any.ele_id, bp));
- }
-
- PRIVATE
- void
- LO_DUMP_SELECTION(char* s, LO_Selection* selection)
- {
- LO_DUMP_SELECTION2(s, selection->begin.element, selection->begin.position,
- selection->end.element, selection->end.position);
- }
-
- PRIVATE
- void
- LO_DUMP_HIT_RESULT(LO_HitResult* result)
- {
- switch ( result->type )
- {
- case LO_HIT_UNKNOWN:
- XP_TRACE(("Unknown"));
- break;
- case LO_HIT_LINE:
- {
- char* s;
- switch ( result->lo_hitLine.region )
- {
- case LO_HIT_LINE_REGION_BEFORE:
- {
- s = "before line";
- }
- break;
- case LO_HIT_LINE_REGION_AFTER:
- {
- s = "after line";
- }
- break;
- default:
- s = "LO_HIT_LINE Bad region";
- break;
- }
- LO_DUMP_SELECTION(s, & result->lo_hitLine.selection);
- }
- break;
- case LO_HIT_ELEMENT:
- {
- char* s;
- switch ( result->lo_hitElement.region )
- {
- case LO_HIT_ELEMENT_REGION_BEFORE:
- {
- s = "before element";
- }
- break;
- case LO_HIT_ELEMENT_REGION_MIDDLE:
- {
- s = "middle element";
- }
- break;
- case LO_HIT_ELEMENT_REGION_AFTER:
- {
- s = "after element";
- }
- break;
- default:
- {
- s = "LO_HIT_ELEMENT unknown region";
- }
- break;
- }
- LO_DUMP_POSITION(s, & result->lo_hitElement.position);
- }
- break;
- default:
- {
- XP_TRACE(("LO_HIT unknown result "));
- }
- break;
- }
- }
-
- #endif
-
- Bool
- LO_IsSelected(MWContext *context)
- {
- int32 doc_id;
- lo_TopState *top_state;
- lo_DocState *state;
-
- /*
- * Get the unique document ID, and retreive this
- * documents layout state.
- */
- doc_id = XP_DOCID(context);
- top_state = lo_FetchTopState(doc_id);
- if ((top_state == NULL)||(top_state->doc_state == NULL))
- {
- return FALSE;
- }
- state = top_state->doc_state;
-
- return (state->selection_start != NULL);
- }
-
- Bool
- LO_IsSelectionStarted(MWContext *context)
- {
- int32 doc_id;
- lo_TopState *top_state;
- lo_DocState *state;
-
- /*
- * Get the unique document ID, and retreive this
- * documents layout state.
- */
- doc_id = XP_DOCID(context);
- top_state = lo_FetchTopState(doc_id);
- if ((top_state == NULL)||(top_state->doc_state == NULL))
- {
- return FALSE;
- }
- state = top_state->doc_state;
-
- return (state->selection_new != NULL);
- }
-
- PRIVATE
- void lo_JiggleToMakeEditable( MWContext *context,
- lo_DocState *state,
- LO_Element** ppElement,
- int32 *pPosition,
- Bool bForward)
- {
- /* If it's not an editable element, move to find one that is.
- * We don't call the ensure routines because we don't want to
- * select end-of-paragraph linefeeds here.
- *
- */
- LO_Element* element = *ppElement;
- int32 position = *pPosition;
-
- if ( (! EDT_IS_EDITOR(context)) || (element && element->lo_any.edit_element) ) {
- return;
- }
-
- while( element && ! element->lo_any.edit_element ){
- LO_Element* newElement;
- int32 newPosition;
- lo_bump_position(context, state, element, position, &newElement, &newPosition, bForward);
- if ( element == newElement && position == newPosition ) {
- /* We got stuck */
- element = NULL;
- break;
- }
- element = newElement;
- position = newPosition;
- }
- if ( ! element ) {
- /* Ran off end of document */
- element = *ppElement;
- position = *pPosition;
- while( element && ! element->lo_any.edit_element ){
- LO_Element* newElement;
- int32 newPosition;
- lo_bump_position(context, state, element, position, &newElement, &newPosition, !bForward);
- if ( element == newElement && position == newPosition ) {
- /* We got stuck */
- element = NULL;
- break;
- }
- element = newElement;
- position = newPosition;
- }
- }
- if ( element ) {
- *ppElement = element;
- *pPosition = lo_GetElementEdge(element, !bForward);
- }
- }
-
- /* This is only called from the editor. As a convienience to the
- * editor, we return the selection as a 1/2 open selection. That
- * means we convert the selection end into an insert point.
- */
-
- void LO_GetSelectionEndPoints( MWContext *context,
- LO_Element** ppStart,
- intn *pStartOffset,
- LO_Element** ppEnd,
- intn *pEndOffset,
- Bool* pbFromStart,
- Bool* pbSingleItemSelection )
- {
- int32 doc_id;
- lo_TopState *top_state;
- lo_DocState *state;
- LO_Element* startElement;
- int32 startPosition;
- LO_Element* endElement;
- int32 endPosition;
- /*
- * 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;
-
- startElement = state->selection_start;
- startPosition = state->selection_start_pos;
- /* If it's not an editable element, move backward to find one that is.
- * We don't call the ensure routines because we don't want to
- * select end-of-paragraph linefeeds here.
- *
- * But now that we have tables, we need to bump.
- */
- lo_JiggleToMakeEditable( context, state, &startElement, &startPosition, FALSE);
- if( ppStart ) *ppStart = startElement;
- if( pStartOffset ) *pStartOffset = startPosition;
-
-
- /* Convert the selection end into an insert point. */
- endElement = state->selection_end;
- endPosition = state->selection_end_pos;
- if ( endElement )
- {
- lo_ConvertSelectionEndToInsertPoint(context, state, &endElement, &endPosition);
- }
-
- /* If it's not an editable element, move forward to find one that is.
- * We don't call the ensure routines because we don't want to
- * select end-of-paragraph linefeeds here.
- */
- lo_JiggleToMakeEditable( context, state, &endElement, &endPosition, TRUE);
-
- if( ppEnd ) *ppEnd = endElement;
- if( pEndOffset ) *pEndOffset = endPosition;
-
- if( pbFromStart ) *pbFromStart = state->extending_start;
-
- /*
- * the editor needs to know if the select is just a single thing like a
- * image or a HRULE.
- */
- if( pbSingleItemSelection ) *pbSingleItemSelection = (
- startElement
- && startElement->type != LO_TEXT
- && startElement->type != LO_TEXTBLOCK
- && startElement == endElement );
-
- /*
- * For lloyd's edification, if we have a single item seleciton, start_pos
- * is 0 and end_pos is 0 or 1
- */
- XP_ASSERT( pbSingleItemSelection && *pbSingleItemSelection
- ?
- startPosition == 0
- && ( endPosition == 0
- || endPosition == 1 )
- :
- TRUE);
- }
-
- void LO_GetSelectionNewPoint( MWContext *context,
- LO_Element** ppNew,
- intn *pNewOffset)
- {
- int32 doc_id;
- lo_TopState *top_state;
- lo_DocState *state;
-
- /*
- * Get the unique document ID, and retreive this
- * documents layout state.
- */
- doc_id = XP_DOCID(context);
- top_state = lo_FetchTopState(doc_id);
- if ((top_state == NULL)||(top_state->doc_state == NULL))
- {
- return;
- }
- state = top_state->doc_state;
-
- if( ppNew ) *ppNew = state->selection_new;
- if( pNewOffset ) *pNewOffset = state->selection_new_pos;
- }
-
- static intn
- lo_compare_selections(LO_Element *sel1, int32 pos1,
- LO_Element *sel2, int32 pos2)
- {
- if ((sel1 == NULL)||(sel2 == NULL))
- {
- if (sel1 == sel2)
- {
- return(0);
- }
- else if (sel1 == NULL)
- {
- return(-1);
- }
- else
- {
- return(1);
- }
- }
-
- if (sel1->lo_any.ele_id < sel2->lo_any.ele_id)
- {
- return(-1);
- }
- else if (sel1->lo_any.ele_id > sel2->lo_any.ele_id)
- {
- return(1);
- }
- else if (sel1->lo_any.ele_id == sel2->lo_any.ele_id)
- {
- if (pos1 < pos2)
- {
- return(-1);
- }
- else if (pos1 > pos2)
- {
- return(1);
- }
- else if (pos1 == pos2)
- {
- return(0);
- }
- }
- #ifdef DEBUG
- assert (FALSE);
- #endif
- return(-1);
- }
-
- PRIVATE
- intn
- lo_ComparePositions(LO_Position* a, LO_Position* b)
- {
- return lo_compare_selections( a->element, a->position, b->element, b->position);
- }
-
- PRIVATE
- LO_Element*
- lo_BoundaryJumpingNext(MWContext *context, lo_DocState *state, LO_Element *eptr)
- {
- int32 last_id;
- LO_Element *cell_parent;
- /*
- * If no next element, see if we need to jump the cell wall.
- */
- Bool success=FALSE;
- while(!success)
- {
- LO_Element* next = eptr->lo_any.next;
- last_id = eptr->lo_any.ele_id;
- cell_parent = NULL;
- while ( eptr && ! next )
- {
- LO_Element* oldEptr = eptr;
- eptr = lo_JumpCellWall(context, state, eptr);
- if ( ! eptr || eptr == oldEptr )
- break; /* Ran off the end of the document */
- next = eptr->lo_any.next;
- }
- eptr = next;
- /*
- * When we walk onto a cell,
- * we need to walk into it if
- * it isn't empty.
- */
- if ((eptr != NULL)&&
- (eptr->type == LO_CELL)&&
- (eptr->lo_cell.cell_list != NULL))
- {
- cell_parent = eptr;
- eptr = eptr->lo_cell.cell_list;
- }
-
- /*
- * Heuristic: For some difficult tables, you may come back
- * to the same cell you left instead of progressing.
- * If so, try manually moving the cell forward.
- */
- if ((eptr != NULL)&&(eptr->lo_any.ele_id == last_id)&&
- (cell_parent != NULL))
- {
- LO_Element *guess;
-
- guess = cell_parent->lo_any.next;
- /*
- * If our guessed next element after the parent cell is
- * a non-empty cell, make the first element in that
- * cell our new element.
- */
- if ((guess != NULL)&&(guess->type == LO_CELL)&&
- (guess->lo_cell.cell_list != NULL))
- {
- eptr = guess->lo_cell.cell_list;
- }
- }
-
- /*
- * We don't want infinite loops.
- * If the element ID hasn't progressed, something
- * serious is wrong, and we should punt.
- */
- if ((eptr != NULL)&&(eptr->lo_any.ele_id <= last_id))
- {
- #ifdef DEBUG
- XP_TRACE(("Cell Jump loop avoidance 1\n"));
- #endif /* DEBUG */
- return NULL;
- }
-
- success=TRUE; /* unless we detect a LO_TEXTBLOCK */
- /* check textblock until not textblock leave prev as null if you hit
- null */
- if (( next && eptr->type == LO_TEXTBLOCK ) || ( next && eptr->type == LO_DESCTITLE ))
- {
- success=FALSE;
- }
-
- }
- return eptr;
- }
-
-
- /*
- * We changed celljumping to boundary jumping to include type 19/LO_TEXTBLOCK as a type to skip past. anthonyd
- */
-
- PRIVATE
- LO_Element*
- lo_BoundaryJumpingPrev(MWContext *context, lo_DocState *state, LO_Element *eptr)
- {
- /*
- * If no previous element, see if we need to jump the cell wall.
- */
- LO_Element* prev;
- Bool success=FALSE;
- while(!success)
- {
- prev = eptr->lo_any.prev;
- while ( eptr && ! prev )
- {
- LO_Element* oldEptr = eptr;
- eptr = lo_JumpCellWall(context, state, eptr);
- if ( ! eptr || eptr == oldEptr )
- break; /* Ran off the front of the document */
- prev = eptr->lo_any.prev;
- if ( ! prev )
- continue; /* Ran off the front of the cell/document */
- if ( prev->type == LO_TABLE )
- {
- /* That was the first cell of the table. */
- eptr = prev;
- prev = eptr->lo_any.prev;
- /* Repeat the check that was made above since we've moved eptr. */
- if ( eptr == oldEptr )
- break;
- }
- }
- success=TRUE; /* successfull until textblock check */
- /*
- * When we walk onto a cell,
- * we need to walk into it if
- * it isn't empty.
- */
- if ((prev != NULL)&&
- (prev->type == LO_CELL)&&
- (prev->lo_cell.cell_list_end != NULL))
- {
- prev = prev->lo_cell.cell_list_end;
- }
-
- eptr = prev;
-
- /* check textblock until not textblock leave prev as null if you hit
- null */
- if (( prev && eptr->type == LO_TEXTBLOCK ) || ( prev && eptr->type == LO_DESCTITLE ))
- {
- success=FALSE;
- }
- }
- return eptr;
- }
-
-
-
- static void
- lo_bump_position(MWContext *context, lo_DocState *state,
- LO_Element *sel, int32 pos,
- LO_Element **new_sel, int32 *new_pos, Bool forward)
- {
- LO_Element *eptr;
- int32 position;
-
- eptr = sel;
- position = pos;
-
- /*
- * Moving forward one selection position
- */
- if (forward != FALSE)
- {
- /*
- * If it is not a text element, just move
- * to the next element.
- */
- if (eptr->lo_any.type != LO_TEXT)
- {
- eptr = lo_BoundaryJumpingNext(context, state, eptr);
- if (eptr == NULL)
- {
- eptr = sel;
- }
- /*
- * Else start at the beginning of the next element
- */
- else
- {
- position = 0;
- }
- }
- /*
- * Else this is a text element, so check moving
- * forward within the element.
- */
- else
- {
- intn maxPosition = lo_GetLastCharBeginPosition(eptr);
- /*
- * If we are already at the end of the text
- * element we are back to moving on to the
- * next element.
- */
- if (position < maxPosition)
- {
- position = lo_IncrementPosition(eptr, position);
- }
- else
- {
- eptr = lo_BoundaryJumpingNext(context, state, eptr);
- if (eptr == NULL)
- {
- eptr = sel;
- }
- /*
- * Else start at the beginning of the next element
- */
- else
- {
- position = 0;
- }
- }
- }
- }
- /*
- * Else moving backward one selection position
- */
- else
- {
- /*
- * If it is not a text element, just move
- * to the previous element.
- */
- if (eptr->lo_any.type != LO_TEXT)
- {
- eptr = lo_BoundaryJumpingPrev(context, state, eptr);
-
- /*
- * If no previous element, don't move it.
- */
- if (eptr == NULL)
- {
- eptr = sel;
- }
- /*
- * Else start at the end of the previous element.
- * (only matters for text elements)
- */
- else
- {
- position = lo_GetLastCharBeginPosition(eptr);
- }
- }
- /*
- * Else this is a text element, so check moving
- * backward within the element.
- */
- else
- {
- /*
- * If we are already at the beginning of the text
- * element we are back to moving back to the
- * previous element.
- */
- if (position == 0)
- {
- eptr = lo_BoundaryJumpingPrev(context, state, eptr);
-
- /*
- * If no previous element, don't move it.
- */
- if (eptr == NULL)
- {
- eptr = sel;
- }
- /*
- * Else start at the end of the previous
- * element.
- * (only matters for text elements)
- */
- else
- {
- position = lo_GetLastCharBeginPosition(eptr);
- }
- }
- /*
- * This is the easy case, move one position
- * backward in the selected text element.
- */
- else
- {
- position = lo_DecrementPosition(eptr, position);
- }
- }
- }
-
- *new_sel = eptr;
- *new_pos = position;
- }
-
- PRIVATE
- Bool
- lo_BumpPosition(MWContext *context, lo_DocState *state, LO_Position* position, Bool forward)
- {
- LO_Element* newElement;
- int32 newPosition;
- lo_bump_position(context, state, position->element, position->position,
- &newElement, &newPosition, forward);
- if ( newElement == NULL
- || lo_compare_selections(newElement, newPosition, position->element, position->position) == 0)
- {
- /* We ran out of room. */
- return FALSE;
- }
- position->element = newElement;
- position->position = newPosition;
- return TRUE;
- }
-
- PRIVATE
- Bool lo_ValidEditableElement(MWContext *context, LO_Element* eptr)
- {
- return lo_EditableElement(eptr->type) &&
- ( (!EDT_IS_EDITOR(context) ) ||
- (eptr->lo_any.edit_element != 0 &&
- eptr->lo_any.edit_offset >= 0));
- }
-
- PRIVATE
- Bool lo_ValidEditableElementIncludingParagraphMarks(MWContext *context, LO_Element* pElement)
- {
- return lo_ValidEditableElement(context, pElement)
- || (pElement->type== LO_LINEFEED
- && pElement->lo_linefeed.break_type == LO_LINEFEED_BREAK_PARAGRAPH);
- }
-
- /* Moves by one editable position forward or backward. Returns FALSE if it couldn't.
- * Assumes that position is normalized, doesn't denormalize it.
- */
-
- PRIVATE
- Bool
- lo_BumpNormalizedEditablePosition(MWContext *context, lo_DocState *state,
- LO_Element **pEptr, int32 *pPosition, Bool direction)
- {
- LO_Element* newEptr;
- int32 newPosition;
- if ( ! ( pEptr && *pEptr && pPosition ) )
- {
- XP_ASSERT(FALSE);
- return FALSE;
- }
-
- newEptr = *pEptr;
- newPosition = *pPosition;
-
- {
- int32 length = lo_GetElementLength(newEptr);
- if ( ! (newPosition >= 0 && ( newPosition < length || length == 0 ) ) )
- {
- XP_ASSERT(FALSE);
- return FALSE;
- }
- }
-
- do {
- LO_Element* oldEptr = newEptr;
- int32 oldPosition = newPosition;
- lo_bump_position(context, state, newEptr, newPosition,
- &newEptr, &newPosition, direction);
- if ( newEptr == NULL
- || lo_compare_selections(newEptr, newPosition, oldEptr, oldPosition) == 0)
- {
- /* We ran out of room. */
- return FALSE;
- }
- } while ( ! lo_ValidEditableElementIncludingParagraphMarks(context, newEptr) );
- *pEptr = newEptr;
- *pPosition = newPosition;
- return TRUE;
- }
-
- /* This function is only intended for the internal use of
- * lo_BumpEditablePosition. It handles the quick case where
- * the position doesn't cross over the object boundary.
- */
-
- PRIVATE
- Bool
- lo_QuickBumpEditablePosition(MWContext *context, lo_DocState *state,
- LO_Element **pEptr, int32 *pPosition, Bool direction)
- {
- if ( direction )
- {
- if ( *pPosition < lo_GetMaximumInsertPointPosition(*pEptr) )
- {
- /* Easy case: Moving forward within an element. */
- *pPosition = lo_IncrementPosition(*pEptr, *pPosition);
-
- /* XP_TRACE(("new position = %d", *pPosition)); */
- return TRUE;
- }
- }
- else
- {
- if ( *pPosition > 0 )
- {
- /* Easy case: Moving backward within an element. */
- *pPosition = lo_DecrementPosition(*pEptr, *pPosition);
-
- /* XP_TRACE(("new position = %d", *pPosition)); */
- return TRUE;
- }
- }
- return FALSE;
- }
-
- /* Moves by one editable position forward or backward. Returns FALSE if it couldn't.
- * position can be denormalized on entry and may be denormalized on exit.
- */
-
- Bool
- lo_BumpEditablePosition(MWContext *context, lo_DocState *state,
- LO_Element **pEptr, int32 *pPosition, Bool direction)
- {
- LO_Element* newEptr;
- int32 newPosition;
- if ( ! ( pEptr && *pEptr && pPosition ) )
- {
- XP_ASSERT(FALSE);
- return FALSE;
- }
- newEptr = *pEptr;
- newPosition = *pPosition;
-
- if ( ! lo_QuickBumpEditablePosition(context, state, &newEptr, &newPosition, direction ) )
- {
- /* Normalize, and try again. */
- if ( ! lo_NormalizeSelectionPoint(context, state, &newEptr, &newPosition) )
- return FALSE;
-
- if ( ! lo_QuickBumpEditablePosition(context, state, &newEptr, &newPosition, direction ) )
- {
- /* Now that it's normalized, try the heavy-duty bump. */
- if ( ! lo_BumpNormalizedEditablePosition(context, state, &newEptr, &newPosition, direction) )
- {
- return FALSE;
- }
- }
- }
-
- *pEptr = newEptr;
- *pPosition = newPosition;
- return TRUE;
- }
-
- PRIVATE
- Bool
- lo_BumpEditablePositionForward(MWContext *context, lo_DocState *state,
- LO_Element **pEptr, int32 *pPosition)
- {
- return lo_BumpEditablePosition(context, state, pEptr, pPosition, TRUE);
- }
-
-
- PRIVATE
- Bool
- lo_BumpEditablePositionBackward(MWContext *context, lo_DocState *state,
- LO_Element **pEptr, int32 *pPosition)
- {
- return lo_BumpEditablePosition(context, state, pEptr, pPosition, FALSE);
- }
-
- PRIVATE
- void
- lo_ChangeSelection(MWContext *context, lo_DocState *state,
- LO_Element *changed_start, LO_Element *changed_end,
- int32 changed_start_pos, int32 changed_end_pos)
- {
- LO_Element *eptr;
- int32 position;
- LO_Element *start, *end;
- int32 start_pos, end_pos;
- intn compare_start;
- intn compare_end;
-
- /*
- * Handle case where there's no existing selection
- */
-
- if ( state->selection_start == NULL )
- {
- lo_HighlightSelect(context, state,
- changed_start, changed_start_pos,
- changed_end, changed_end_pos, TRUE);
- return;
- }
-
- start = state->selection_start;
- start_pos = state->selection_start_pos;
- end = state->selection_end;
- end_pos = state->selection_end_pos;
- lo_NormalizeSelectionEnd(context, state, &end, &end_pos);
-
- /*
- * If the end of the old selection is less than
- * the start of the new one. Or the end of the new one is
- * less than the start of the old one.
- * Erase the old one, draw the new one.
- */
- if ((lo_compare_selections(end, end_pos, changed_start,
- changed_start_pos) < 0)||
- (lo_compare_selections(changed_end, changed_end_pos, start,
- start_pos) < 0))
- {
- lo_HighlightSelect(context, state,
- start, start_pos, end, end_pos, FALSE);
- lo_HighlightSelect(context, state,
- changed_start, changed_start_pos,
- changed_end, changed_end_pos, TRUE);
- }
- else
- {
- compare_start = lo_compare_selections(changed_start,
- changed_start_pos, start, start_pos);
- compare_end = lo_compare_selections(changed_end,
- changed_end_pos, end, end_pos);
- /*
- * If the start position has moved either draw more,
- * or erase some of the old.
- */
- if (compare_start < 0)
- {
- lo_HighlightSelect(context, state,
- changed_start, changed_start_pos,
- start, start_pos, TRUE);
- }
- else if (compare_start > 0)
- {
- lo_bump_position(context, state,
- changed_start, changed_start_pos,
- &eptr, &position, FALSE);
- lo_HighlightSelect(context, state,
- start, start_pos, eptr, position, FALSE);
- }
-
- /*
- * If the end position has moved either draw more,
- * or erase some of the old.
- */
- if (compare_end > 0)
- {
- lo_HighlightSelect(context, state, end, end_pos,
- changed_end, changed_end_pos, TRUE);
- }
- else if (compare_end < 0)
- {
- lo_bump_position(context, state,
- changed_end, changed_end_pos,
- &eptr, &position, TRUE);
- lo_HighlightSelect(context, state,
- eptr, position, end, end_pos, FALSE);
- }
- }
- }
-
- Bool LO_SelectElement( MWContext *context, LO_Element *eptr,
- int32 position, Bool bFromStart )
- {
- int32 doc_id;
- lo_TopState *top_state;
- lo_DocState *state;
-
- /*
- * Get the unique document ID, and retreive this
- * documents layout state.
- */
- doc_id = XP_DOCID(context);
- top_state = lo_FetchTopState(doc_id);
- if ((top_state == NULL)||(top_state->doc_state == NULL))
- {
- return FALSE;
- }
- state = top_state->doc_state;
-
- /* If we've been handed a non-editable item, go to the previous editable item */
- if ( ! lo_EnsureEditableSearchPrev2(context, state, &eptr, &position) )
- {
- return FALSE;
- }
-
- if ( lo_IsEdgeOfDocument2(context, state, eptr, position, ! bFromStart ) )
- {
- return FALSE;
- }
-
- /* If we're selecting backwards, actually select one lower */
- if ( bFromStart )
- {
- if ( ! lo_BumpEditablePositionBackward(context, state,
- &eptr, &position) ) return FALSE;
- }
- /* End is the same as the beginning. */
- lo_FullSetSelection(context, state, eptr, position, eptr, position, bFromStart);
- return TRUE;
- }
-
- /* Internal routine that should not be exported */
- void LO_ExtendSelectionFromElement( MWContext *context, LO_Element *eptr,
- int32 position, Bool bFromStart )
- {
- int32 doc_id;
- lo_TopState *top_state;
- lo_DocState *state;
- LO_Element *changed_start, *changed_end;
- int32 changed_start_pos, changed_end_pos;
-
- /*
- * Get the unique document ID, and retreive this
- * documents layout state.
- */
- doc_id = XP_DOCID(context);
- top_state = lo_FetchTopState(doc_id);
- if ((top_state == NULL)||(top_state->doc_state == NULL))
- {
- return;
- }
- state = top_state->doc_state;
-
- /*
- * If we have started a new selection, replace the old one with
- * this new one before extending
- */
- if (state->selection_new != NULL)
- {
- lo_StartNewSelection(context, state, eptr, position);
- return;
- }
-
- /*
- * If we somehow got here without having any selection or started
- * selection, start it now where we are.
- */
- if (state->selection_start == NULL)
- {
- state->selection_start = eptr;
- state->selection_start_pos = position;
- state->selection_end = state->selection_start;
- state->selection_end_pos = state->selection_start_pos;
- state->extending_start = FALSE;
- }
-
- /*
- * By some horrendous bug the start was set and the end was not,
- * and there was no newly started selection. Correct this now.
- */
- if (state->selection_end == NULL)
- {
- state->selection_end = state->selection_start;
- state->selection_end_pos = state->selection_start_pos;
- state->extending_start = FALSE;
- }
-
- /*
- * Extend the same side we extended last time.
- */
- if (state->extending_start != FALSE)
- {
- changed_start = eptr;
- changed_start_pos = position;
- changed_end = state->selection_end;
- changed_end_pos = state->selection_end_pos;
- }
- else
- {
- changed_start = state->selection_start;
- changed_start_pos = state->selection_start_pos;
- changed_end = eptr;
- changed_end_pos = position;
- }
-
- /*
- * If we crossed our start and end positions, switch
- * them, and switch which end we are extending from.
- */
- if (changed_start->lo_any.ele_id > changed_end->lo_any.ele_id)
- {
- eptr = changed_start;
- position = changed_start_pos;
- changed_start = changed_end;
- changed_start_pos = changed_end_pos;
- changed_end = eptr;
- changed_end_pos = position;
- if (state->extending_start != FALSE)
- {
- state->extending_start = FALSE;
- }
- else
- {
- state->extending_start = TRUE;
- }
- }
- else if ((changed_start->lo_any.ele_id == changed_end->lo_any.ele_id)&&
- (changed_start_pos > changed_end_pos))
- {
- position = changed_start_pos;
- changed_start_pos = changed_end_pos;
- changed_end_pos = position;
- if (state->extending_start != FALSE)
- {
- state->extending_start = FALSE;
- }
- else
- {
- state->extending_start = TRUE;
- }
- }
-
- lo_ChangeSelection(context, state, changed_start, changed_end,
- changed_start_pos, changed_end_pos);
-
- lo_SetSelect(context, state, changed_start, changed_start_pos,
- changed_end, changed_end_pos, TRUE);
-
- state->selection_start = changed_start;
- state->selection_start_pos = changed_start_pos;
- state->selection_end = changed_end;
- state->selection_end_pos = changed_end_pos;
-
- /*
- * Cannot have a new selection after an extend.
- */
- state->selection_new = NULL;
- state->selection_new_pos = 0;
-
- #if 0
- /*
- * If the beginning and the endpoints were not the beginning and end points
- * then the editors notion of bFromStart is backwards.
- */
- if( state->extending_start ){
- state->extending_start = !bFromStart;
- }
- else {
- state->extending_start = bFromStart;
- }
- #endif
- }
-
- void LO_SelectRegion( MWContext *context, LO_Element *begin, int32 beginPosition,
- LO_Element* end, int32 endPosition, Bool fromStart , Bool forward)
- {
- int32 doc_id;
- lo_TopState *top_state;
- lo_DocState *state;
- /*
- * Get the unique document ID, and retreive this
- * documents layout state.
- */
- doc_id = XP_DOCID(context);
- top_state = lo_FetchTopState(doc_id);
- if ((top_state == NULL)||(top_state->doc_state == NULL))
- {
- return;
- }
- state = top_state->doc_state;
-
- if ( ! begin || !end ) {
- XP_ASSERT(FALSE);
- return;
- }
-
- #if 0
- lo_NormalizeSelectionPoint(context, state, &begin, &beginPosition);
- lo_NormalizeSelectionPoint(context, state, &end, &endPosition);
- #endif
-
- #if 0
- XP_TRACE(("begin %d %d %d end %d %d %d start %d forward %d",
- begin->type, begin->lo_any.ele_id, beginPosition,
- end->type, end->lo_any.ele_id, endPosition,
- fromStart, forward));
- #endif
- /* Normalize end-points for end-of line conditions */
- if ( fromStart )
- {
- if ( !forward && begin->type == LO_LINEFEED
- && beginPosition == 0 )
- {
- lo_BumpEditablePositionBackward(context, state, &begin, &beginPosition);
- }
- }
- else
- {
- LO_Element* prev;
- if ( forward && endPosition == 0 &&
- ( end->type == LO_LINEFEED ||
- ((prev = lo_BoundaryJumpingPrev(context, state, end)) != NULL && prev->type == LO_LINEFEED ) ) )
- {
- lo_BumpEditablePositionForward(context, state, &end, &endPosition);
- }
- }
- LO_StartSelectionFromElement( context, begin, beginPosition, NULL );
- LO_ExtendSelectionFromElement( context, end, endPosition, fromStart );
- state->extending_start = fromStart;
- }
-
- void
- lo_SetSelection(MWContext *context, LO_Selection* selection, Bool extendingStart)
- {
- int32 doc_id;
- lo_TopState *top_state;
- lo_DocState *state;
- /*
- * Get the unique document ID, and retreive this
- * documents layout state.
- */
- doc_id = XP_DOCID(context);
- top_state = lo_FetchTopState(doc_id);
- if ((top_state == NULL)||(top_state->doc_state == NULL))
- {
- return;
- }
- state = top_state->doc_state;
-
- lo_FullSetSelection( context, state,
- selection->begin.element,
- selection->begin.position,
- selection->end.element,
- selection->end.position, extendingStart);
- }
-
- void
- lo_FullSetSelection(MWContext *context, lo_DocState * state,
- LO_Element* start, int32 start_pos,
- LO_Element* end, int32 end_pos,
- Bool extendFromStart)
- {
- LO_Element* normalizedStart = start;
- int32 normalizedStartPosition = start_pos;
- LO_Element* normalizedEnd = end;
- int32 normalizedEndPosition = end_pos;
-
- LO_ASSERT_POSITION2(context, start, start_pos);
- LO_ASSERT_POSITION2(context, end, end_pos);
-
- /* The non-editor portions of layout can't deal with a
- * de-normalized positions. So we normalize it here.
- */
-
- /* LO_DUMP_SELECTION2("FullSetSelection", start, start_pos, end, end_pos); */
- #if 0
- lo_NormalizeSelectionPoint(context, state, &normalizedStart, &normalizedStartPosition);
- #endif
-
- if ( lo_GetElementLength(normalizedStart) <= normalizedStartPosition )
- {
- normalizedStart = lo_BoundaryJumpingNext(context, state, normalizedStart);
- normalizedStartPosition = 0;
- }
- lo_NormalizeSelectionEnd(context, state, &normalizedEnd, &normalizedEndPosition);
-
- /* If we've been handed a bad selection, just return. */
- if ( ! ( normalizedStart && normalizedEnd &&
- lo_compare_selections(normalizedStart, normalizedStartPosition,
- normalizedEnd, normalizedEndPosition) <= 0 ) )
- {
- XP_TRACE(("lo_FullSetSelection: bad selection."));
- #ifdef DEBUG_EDIT_LAYOUT
- XP_ASSERT(FALSE);
- #endif
- return;
- }
-
- lo_ChangeSelection(context, state, normalizedStart, normalizedEnd,
- normalizedStartPosition, normalizedEndPosition);
-
- lo_SetSelect(context, state, normalizedStart, normalizedStartPosition,
- normalizedEnd, normalizedEndPosition, TRUE);
-
- state->selection_start = start;
- state->selection_start_pos = start_pos;
- state->selection_end = end;
- state->selection_end_pos = end_pos;
- state->extending_start = extendFromStart;
-
- /*
- * Cannot have a new selection after an extend.
- */
- state->selection_new = NULL;
- state->selection_new_pos = 0;
- }
-
- /* Get the selection edges, expressed as insert points. */
-
- PRIVATE
- void
- lo_GetSelectionEdge(MWContext *context, lo_DocState * state, LO_Element** pElement, int32* pPosition, Bool bForward)
- {
- if(state->selection_start && state->selection_end )
- {
- if ( !bForward )
- {
- *pElement = state->selection_start;
- *pPosition = state->selection_start_pos;
- }
- else
- {
- *pElement = state->selection_end;
- *pPosition = state->selection_end_pos;
- lo_ConvertSelectionEndToInsertPoint(context, state, pElement, pPosition);
- }
- }
- else
- {
- /* Use insert point */
- *pElement = state->selection_new;
- *pPosition = state->selection_new_pos;
- }
-
- }
-
- void
- lo_GetAnchorPoint(MWContext *context, lo_DocState * state, LO_Element** pElement, int32* pPosition)
- {
- lo_GetSelectionEdge(context, state, pElement, pPosition, state->extending_start);
- }
- void
- lo_GetExtensionPoint(MWContext *context, lo_DocState * state, LO_Element** pElement, int32* pPosition)
- {
- lo_GetSelectionEdge(context, state, pElement, pPosition, !state->extending_start);
- }
-
- Bool
- lo_IsEndOfParagraph2(MWContext *context, LO_Element* element, int32 position)
- {
- if ( ! element )
- {
- XP_ASSERT( FALSE );
- return FALSE;
- }
-
- /* The browser doesn't set break_type, so every line is a paragraph to it. */
- if ( element->type == LO_LINEFEED &&
- ( ! EDT_IS_EDITOR(context)
- || element->lo_linefeed.break_type == LO_LINEFEED_BREAK_PARAGRAPH)) {
- return TRUE;
- }
- return FALSE;
- }
-
- PRIVATE
- Bool
- lo_IsEndOfParagraph(MWContext *context, LO_Position* position)
- {
- XP_ASSERT( position );
- return lo_IsEndOfParagraph2(context, position->element, position->position);
- }
-
- PRIVATE
- Bool
- lo_IsHardBreak2(MWContext *context, LO_Element* element, int32 position)
- {
- if ( ! element )
- {
- XP_ASSERT( FALSE );
- return FALSE;
- }
-
- /* The browser doesn't set break_type, so every line is a hard break to it. */
- if ( element->type == LO_LINEFEED &&
- ( ! EDT_IS_EDITOR(context)
- || element->lo_linefeed.break_type != LO_LINEFEED_BREAK_SOFT)) {
- return TRUE;
- }
- return FALSE;
- }
-
- PRIVATE
- Bool
- lo_ExtendToIncludeHardBreak(MWContext* context, lo_DocState *state, LO_Selection* selection)
- {
- /* If this is a selection that ends in a hard break, extend the
- * selection to include the hard break.
- */
- Bool result = FALSE;
- if ( EDT_IS_EDITOR(context) )
- {
- LO_Position* end = &selection->end;
- if ( lo_IsHardBreak2(context, end->element,0) )
- {
- result = TRUE; /* Already at a break character. */
- }
- else
- {
- if ( end->position >= lo_GetLastCharEndPosition(end->element) )
- {
- LO_Element* eol = lo_BoundaryJumpingNext(context, state, end->element);
- if ( lo_IsHardBreak2(context, eol,0) )
- {
- /* Extend to include the paragraph end element */
- end->element = eol;
- end->position = 0;
- result = TRUE;
- }
- }
- }
- }
- return result;
- }
-
- #ifdef DEBUG
- void
- lo_ValidatePosition(MWContext *context, LO_Position* position)
- {
- XP_ASSERT( position );
- if ( position ) {
- lo_ValidatePosition2(context, position->element, position->position);
- }
- }
-
- void
- lo_ValidatePosition2(MWContext *context, LO_Element* element, int32 position)
- {
- int32 length;
- XP_ASSERT( element );
- XP_ASSERT( context );
- if ( context && element )
- {
- length = lo_GetElementLength(element);
- XP_ASSERT( 0 <= position );
- XP_ASSERT( position <= length );
- if ( EDT_IS_EDITOR(context) )
- {
- /* Either it's a line-feed with an eop, or it has an edit_element. */
- if ( ! element->lo_any.edit_element )
- {
- if (element->type == LO_LINEFEED)
- {
- if ( ! lo_IsEndOfParagraph2(context, element, position) )
- {
- XP_TRACE(("lo_ValidatePosition2: illegal position: a line feed without an end-of-paragraph marker."));
- #ifdef DEBUG_EDIT_LAYOUT
- XP_ASSERT(FALSE);
- #endif
- }
- }
- else
- {
- #ifdef DEBUG_EDIT_LAYOUT
- XP_ASSERT(FALSE);
- #endif
- }
- }
- }
- }
- }
-
- void
- lo_ValidateSelection(MWContext *context, LO_Selection* selection)
- {
- XP_ASSERT( selection );
- lo_ValidateSelection2( context, selection->begin.element, selection->begin.position,
- selection->end.element, selection->end.position );
- }
-
- void
- lo_ValidateSelection2(MWContext *context, LO_Element* beginElement, int32 beginPosition, LO_Element* endElement, int32 endPosition)
- {
- lo_ValidatePosition2( context, beginElement, beginPosition );
- lo_ValidatePosition2( context, endElement, endPosition );
- XP_ASSERT( lo_compare_selections(beginElement, beginPosition,
- endElement, endPosition) <= 0 );
- }
-
- #endif
-
- /*
- * An insert point is one edit position greater than a selection end. But there are
- * two insert points for the edit position that falls between elements. One
- * insert point is for the end of the previous element. The other is for the
- * beginning of the next element. There are two corresponding selection ends.
- *
- * end of previous element:
- * insertion point: element = <previous>, position = <previous.length>
- * selection end: element = <previous>, position = <previous.length> - 1
- *
- * start of next element:
- * insertion point: element = <next>, position = 0
- * selection end: element = <previous>, position = <previous.length>
- */
-
- PRIVATE
- Bool lo_IsAtStartOfLine(MWContext* context, lo_DocState* state,
- LO_Element* element, int32 position)
- {
- LO_Element* prev;
- if ( position != 0 || ! element )
- return FALSE;
- prev = element->lo_any.prev;
- if ( ! prev )
- return TRUE;
- if ( prev->type == LO_LINEFEED ||
- prev->type == LO_BULLET )
- return TRUE;
- /* Numbered list test. */
- if ( EDT_IS_EDITOR(context) && prev->type == LO_TEXT && prev->lo_text.edit_element == 0 )
- return TRUE;
- return FALSE;
- }
-
- Bool lo_ConvertInsertPointToSelectionEnd(MWContext* context, lo_DocState * state,
- LO_Element** pElement, int32* pPosition)
- {
- LO_Element* eptr = *pElement;
- int32 position = *pPosition;
-
- /* Special case for insert points at beginning of lines. */
- if ( lo_IsAtStartOfLine(context, state, eptr, position) )
- {
- /* Skip backwards to line feed that has end-of-paragraph set */
- while ( eptr && lo_BoundaryJumpingPrev(context, state, eptr) ) {
- lo_bump_position(context, state, eptr, position, &eptr, &position, FALSE);
- if ( ! eptr )
- {
- break;
- }
-
- if ( eptr->type == LO_LINEFEED )
- {
- if ( eptr->lo_linefeed.break_type != LO_LINEFEED_BREAK_SOFT )
- {
- break;
- }
- }
- /* Linefeeds are editable, but the if test above filters them out. */
- else if (lo_EditableElement(eptr->type) )
- {
- break;
- }
- }
- position = lo_GetLastCharBeginPosition(eptr);
- }
- else
- if ( lo_BumpEditablePositionBackward(context, state, &eptr, &position) )
- {
- if ( *pPosition == 0 )
- {
- /* This is the "Start of next element" case. */
- lo_BumpEditablePositionForward(context, state, &eptr, &position);
- }
- }
- else
- {
- /* If we can't go backwards, it means that the insert point was at the beginning of the document.
- * This is an error condition, because there is no legal selection end that corresponds
- * to this insert point.
- */
- return FALSE;
- }
-
- /* It turns out that the end is the address of the last byte selected, not just the position of the
- * first byte of the two-byte character. So we have to bump forward and subtract one.
- *
- * (If we're a denormalized selection end, we don't increment our position.
- * that's what the "if" is for.)
- */
- if ( position < lo_GetElementLength(eptr) )
- {
- position = lo_IncrementPosition(eptr, position) - 1;
- }
- *pElement = eptr;
- *pPosition = position;
- return TRUE;
- }
-
- Bool lo_ConvertSelectionEndToInsertPoint(MWContext* context, lo_DocState * state,
- LO_Element** pElement, int32* pPosition)
- {
- LO_Element* eptr = *pElement;
- int32 position = *pPosition;
- int32 length = lo_GetElementLength(*pElement);
-
- LO_ASSERT_POSITION2( context, eptr, position );
- /* It turns out that the end is the address of the last byte selected, not just the position of the
- * first byte of the two-byte character. So we have to add one and bump backward.
- */
- if ( position >= length )
- {
- /* this is the end-of-previous-element case. */
- XP_ASSERT( position == length );
- position = lo_GetLastCharBeginPosition(*pElement);
-
- /* If we can't go forward, it means that the selection end was beyond the end of the document.
- * This is an error condition.
- */
- if ( ! lo_BumpEditablePositionForward(context, state, &eptr, &position) )
- {
- return FALSE;
- }
- }
- else
- {
- position = lo_DecrementPosition(eptr, position + 1);
- /* This maps 0..length-1 into 1..length, which is OK. */
- position = lo_IncrementPosition(eptr, position);
- }
- *pElement = eptr;
- *pPosition = position;
-
- LO_ASSERT_POSITION2( context, eptr, position );
- return TRUE;
- }
-
- Bool
- lo_NormalizeSelectionPoint(MWContext *context, lo_DocState * state, LO_Element** pEptr, int32* pPosition)
- {
- /*
- * If the insert point is off the end of the element, make it the 0th selection of the next element.
- * If we can't normalize it, leave it alone.
- */
- Bool result = TRUE;
- if ( *pEptr != NULL )
- {
- int32 length = lo_GetElementLength(*pEptr);
- int32 newPosition = *pPosition;
- XP_ASSERT( 0 <= newPosition && newPosition <= length );
- if ( newPosition >= length )
- {
- if ( length <= 0 )
- {
- /* There are some zero-length text elements in the browser, when
- * tables are around. Also, the editor uses zero-length text
- * elements for empty paragraphs and empty lines.
- */
- newPosition = 0;
- }
- else {
- /* Needs to be internationalized */
- newPosition = lo_GetLastCharBeginPosition(*pEptr);
- result = lo_BumpNormalizedEditablePosition(context, state, pEptr, &newPosition, TRUE);
- }
- if ( result ) {
- *pPosition = newPosition;
- }
- }
- }
- return result;
- }
-
- void
- lo_NormalizeSelectionEnd(MWContext *context, lo_DocState * state, LO_Element** pEptr, int32* pPosition)
- {
- /*
- * If the selection end point is off the end of the element, make it the last position of the element.
- */
- if ( *pEptr != NULL )
- {
- int32 length = lo_GetElementLength(*pEptr);
- XP_ASSERT( 0 <= *pPosition && *pPosition <= length );
- if ( *pPosition >= length )
- {
- *pPosition = lo_GetLastCharEndPosition(*pEptr);
- }
- }
- }
-
- PRIVATE
- intn
- lo_FancyHalfCompareSelections(LO_Element* start, int32 startPosition, LO_Element* end, int32 endPosition)
- {
- int32 startID = start->lo_any.ele_id;
- int32 endID = end->lo_any.ele_id;
- if ( startID < endID )
- {
- if ( startID < (endID - 1) )
- return -1; /* Has to be less */
- if ( startPosition < lo_GetElementLength(start) )
- return -1;
- if ( endPosition > 0 )
- return -1;
- return 0;
- }
- XP_ASSERT(FALSE);
- return -1;
- }
-
- PRIVATE
- intn
- lo_FancyCompareSelections(MWContext *context, lo_DocState * state,
- LO_Element* start, int32 startPosition, LO_Element* end, int32 endPosition)
- {
- #if 0 /* We can't normalize past the end of the document. */
- Bool startIsEOD = ! lo_NormalizeSelectionPoint(context, state, &start, &startPosition);
- Bool endIsEOD = ! lo_NormalizeSelectionPoint(context, state, &end, &endPosition);
- if ( startIsEOD && endIsEOD ) return 0;
- else if ( startIsEOD ) return 1;
- else if ( endIsEOD ) return -1;
-
- return lo_compare_selections(start, startPosition, end, endPosition);
- #endif
- int32 startID = 0;
- int32 endID = 0;
-
- /* Can not perform if no start or end element */
- if(!start || !end)
- {
- return 0;
- }
-
- /* Now safe to set IDs */
- startID = start->lo_any.ele_id;
- endID = end->lo_any.ele_id;
-
- if ( startID < endID )
- {
- return lo_FancyHalfCompareSelections(start, startPosition, end, endPosition);
- }
- else if ( startID == endID )
- {
- if ( startPosition < endPosition )
- return -1;
- else if ( startPosition == endPosition )
- return 0;
- return 1;
- }
- else /* startID > endID */
- {
- return - lo_FancyHalfCompareSelections(end, endPosition, start, startPosition);
- }
- }
-
-
- void
- lo_StartNewSelection(MWContext *context, lo_DocState * state,
- LO_Element* eptr, int32 position)
- {
- /*
- * If the user hasn't moved the insertion point yet, start a selection.
- */
- LO_Element* i_eptr = state->selection_new;
- int32 i_position = state->selection_new_pos;
- intn compare = lo_FancyCompareSelections(context, state, i_eptr, i_position, eptr, position);
- if ( compare < 0 )
- {
- /* old < now, so they're dragging towards the end of the document */
- lo_ConvertInsertPointToSelectionEnd(context, state, &eptr, &position);
- state->extending_start = FALSE;
- lo_FullSetSelection(context, state, i_eptr, i_position, eptr, position, FALSE);
- }
- else if ( compare == 0 )
- {
- /* The user hasn't moved the mouse far enough to select anything. */
- return;
- }
- else
- {
- /* Dragging towards start of document */
- lo_ConvertInsertPointToSelectionEnd(context, state, &i_eptr, &i_position);
- state->extending_start = TRUE;
- lo_FullSetSelection(context, state, eptr, position, i_eptr, i_position, TRUE);
- }
- }
-
- void
- LO_ExtendSelection(MWContext *context, int32 x, int32 y)
- {
- int32 doc_id;
- lo_TopState *top_state;
- lo_DocState *state;
- LO_Element *eptr;
- int32 position;
- LO_HitResult result;
-
- /*
- * 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;
-
- LO_Hit(context, x, y, FALSE, &result, state->selection_layer);
-
- position = 0;
-
- eptr = NULL;
- switch ( result.type )
- {
- case LO_HIT_LINE:
- {
- switch ( result.lo_hitLine.region )
- {
- case LO_HIT_LINE_REGION_BEFORE:
- {
- /* Insertion point before first element of line */
- eptr = result.lo_hitLine.selection.begin.element;
- position = result.lo_hitLine.selection.begin.position;
- }
- break;
- case LO_HIT_LINE_REGION_AFTER:
- {
- /* Insertion point after last element of line */
- eptr = result.lo_hitLine.selection.end.element;
- position = result.lo_hitLine.selection.end.position;
- lo_ConvertSelectionEndToInsertPoint(context, state, &eptr, &position);
- if ( eptr->type == LO_LINEFEED ) position = 0; /* For editable line-feeds */
- }
- break;
- default:
- break;
- }
- }
- break;
- case LO_HIT_ELEMENT:
- {
- eptr = result.lo_hitElement.position.element;
- position = result.lo_hitElement.position.position;
- switch ( result.lo_hitElement.region )
- {
- case LO_HIT_ELEMENT_REGION_BEFORE:
- break;
- case LO_HIT_ELEMENT_REGION_MIDDLE:
- {
- /* The user wants this item selected.
- *
- * If the anchor point is before or at this item, the insert point should be
- * after this item.
- *
- */
- LO_Element* anchorElement;
- int32 anchorPosition;
- lo_GetAnchorPoint(context, state, &anchorElement, &anchorPosition);
- if ( lo_FancyCompareSelections(context, state, anchorElement, anchorPosition, eptr, position) <= 0 )
- {
- lo_BumpEditablePositionForward(context, state, &eptr, &position);
- }
- }
- break;
- case LO_HIT_ELEMENT_REGION_AFTER:
- {
- lo_BumpEditablePositionForward(context, state, &eptr, &position);
- }
- break;
- default:
- break;
- }
- }
- break;
- default:
- break;
- }
- if (eptr == NULL)
- {
- return;
- }
-
- lo_ExtendSelectionToPosition2(context, top_state, state, eptr, position);
- }
-
- void
- lo_ExtendSelectionToPosition2(MWContext *context, lo_TopState* top_state, lo_DocState *state, LO_Element* eptr, int32 position)
- {
- /*
- * Are we starting a new selection?
- */
- if (state->selection_new != NULL)
- {
- lo_StartNewSelection(context, state, eptr, position);
- return;
- }
-
- /*
- * If we somehow got here without having any selection or started
- * selection, start it now where we are.
- */
- if (state->selection_start == NULL)
- {
- state->selection_start = eptr;
- state->selection_start_pos = position;
- state->selection_end = state->selection_start;
- state->selection_end_pos = state->selection_start_pos;
- state->extending_start = FALSE;
- }
-
- /*
- * By some horrendous bug the start was set and the end was not,
- * and there was no newly started selection. Correct this now.
- */
- if (state->selection_end == NULL)
- {
- state->selection_end = state->selection_start;
- state->selection_end_pos = state->selection_start_pos;
- state->extending_start = FALSE;
- }
-
- {
- LO_Element* anchorElement;
- int32 anchorPosition;
- intn compare;
-
- lo_GetAnchorPoint(context, state, &anchorElement, &anchorPosition);
- compare = lo_FancyCompareSelections(context, state, eptr, position, anchorElement, anchorPosition );
-
- if ( compare == 0 )
- {
- /* The anchor point might not be editable -- it might be the start of a line feed.
- * So Jiggle it.
- */
- lo_JiggleToMakeEditable(context, state, &anchorElement, &anchorPosition, FALSE);
- lo_SetInsertPoint(context, top_state, anchorElement, anchorPosition, state->selection_layer);
- }
- else
- {
- LO_Element *changed_start, *changed_end;
- int32 changed_start_pos, changed_end_pos;
- Bool extendingStart;
- if ( compare < 0 )
- {
- /* new position is less than anchor. So it's the start */
- changed_start = eptr;
- changed_start_pos = position;
- changed_end = anchorElement;
- changed_end_pos = anchorPosition;
- extendingStart = TRUE;
- }
- else
- {
- /* new position is greater than anchor. So it's the end */
- changed_start = anchorElement;
- changed_start_pos = anchorPosition;
- changed_end = eptr;
- changed_end_pos = position;
- extendingStart = FALSE;
- }
- lo_ConvertInsertPointToSelectionEnd(context, state, &changed_end, &changed_end_pos);
-
- lo_FullSetSelection(context, state, changed_start, changed_start_pos,
- changed_end, changed_end_pos, extendingStart);
- }
- }
- }
-
-
- void
- LO_EndSelection(MWContext *context)
- {
- int32 doc_id;
- lo_TopState *top_state;
- lo_DocState *state;
-
- /*
- * Get the unique document ID, and retreive this
- * documents layout state.
- */
- doc_id = XP_DOCID(context);
- top_state = lo_FetchTopState(doc_id);
- if ((top_state == NULL)||(top_state->doc_state == NULL))
- {
- return;
- }
- state = top_state->doc_state;
- }
-
-
- #ifdef NOT_USED
- static void
- lo_copy_color(LO_Color *from_color, LO_Color *to_color)
- {
- to_color->red = from_color->red;
- to_color->green = from_color->green;
- to_color->blue = from_color->blue;
- }
-
- #endif /* NOT_USED */
-
-
- /*
- * If this element is part of a cell, return that cell.
- */
- LO_Element *
- lo_JumpCellWall(MWContext *context, lo_DocState *state, LO_Element *eptr)
- {
- LO_Element *parent;
- LO_Element *cell = NULL;
- LO_Element *prev_eptr;
- LO_Element *stop_eptr;
- int32 x, y;
- int32 ret_x, ret_y;
- Bool guess_mode;
- CL_Layer *layer;
-
- /*
- * We only turn this on if we are desparate and think
- * we are in a cell with NO selectable elements!
- */
- guess_mode = FALSE;
-
- prev_eptr = NULL;
- x = eptr->lo_any.x + eptr->lo_any.x_offset;
- y = eptr->lo_any.y + eptr->lo_any.y_offset;
-
- /*
- * If this element is a zero width linefeed, it is unselectable, so
- * we need to special case this to work off the previous element
- * in the cell (if any).
- */
- if (eptr->lo_any.width <= 0)
- {
- prev_eptr = eptr->lo_any.prev;
- /*
- * Gads, you have have zero width text followed by a
- * zero width linefeed. Move back until we get a real
- * width or can't go any furthur.
- */
- while ((prev_eptr != NULL)&&(prev_eptr->lo_any.width <= 0))
- {
- prev_eptr = prev_eptr->lo_any.prev;
- }
-
- if (prev_eptr != NULL)
- {
- x = prev_eptr->lo_any.x + prev_eptr->lo_any.x_offset;
- y = prev_eptr->lo_any.y + prev_eptr->lo_any.y_offset;
- }
- /*
- * If there is no previous element to the zero-width
- * one, guess in desperation by moving back one.
- * Probably there are no other elements in this
- * cell, go into guess_mode.
- */
- else
- {
- guess_mode = TRUE;
- /*
- * We used to subtract 1 from the x pixel position, but
- * that didn't work in all cases and had the potential to
- * cause infinite loops. Now we don't and I feel a lot
- * happier.
- */
- }
- }
-
- /*
- * figure out when to stop traversing inward.
- * If we have a prev_eptr then that is where we stop,
- * otherwise stop if we hit the starting element.
- */
- if (prev_eptr != NULL)
- {
- stop_eptr = prev_eptr;
- }
- else
- {
- stop_eptr = eptr;
- }
-
- parent = NULL;
- /* This deals with the case where the table is in a layer.
- * We want to search within the corresponding block and
- * not in the base document for the enclosing cell.
- * BUGBUG This makes the assumption that if this table is
- * in a layer, that layer is part of the current selection
- * state. I don't know if this assumption is always true
- * for all invocations of this function.
- * joki: changing function to work based on layer key focus
- * instead of selected layer so that form elements will be
- * tabable in tables. 5/25/97.
- */
- if (context->compositor && !(CL_IsKeyEventGrabber(context->compositor, NULL))) {
- layer = CL_GetKeyEventGrabber(context->compositor);
-
- if (layer && LO_GetIdFromLayer(context, layer) != LO_DOCUMENT_LAYER_ID) {
- LO_CellStruct *layer_cell = lo_GetCellFromLayer(context, layer);
- if (layer_cell)
- cell = lo_XYToCellElement(context,
- state,
- layer_cell,
- x, y, TRUE, FALSE, FALSE);
- }
- }
-
- if (cell == NULL)
- cell = lo_XYToDocumentElement(context, state,
- x, y, TRUE, FALSE, FALSE,
- &ret_x, &ret_y);
- while ((cell != NULL)&&(cell != stop_eptr)&&(cell->type == LO_CELL))
- {
- parent = cell;
- cell = lo_XYToCellElement(context, state, (LO_CellStruct *)cell,
- x, y, TRUE, FALSE, FALSE);
- }
-
- /*
- * If we've traversed to our stopping point.
- */
- if (cell == stop_eptr)
- {
- /*
- * If the stopping point has a cell parent, return it.
- */
- if ((parent != NULL)&&(parent->type == LO_CELL))
- {
- return(parent);
- }
- /*
- * Else we aren't in a cell, return the original element.
- */
- else
- {
- return(eptr);
- }
- }
- /*
- * Else if we are in guess mode, we found a valid parent
- * cell that appears to have no children, then we need to check
- * if the first child of the parent is our stop_eptr, and if
- * so, return the cell parent.
- */
- else if ((guess_mode != FALSE)&&(cell == NULL)&&(parent != NULL)&&
- (parent->type == LO_CELL))
- {
- if (parent->lo_cell.cell_list == stop_eptr)
- {
- return(parent);
- }
-
- /*
- * If we get here we don't know what we found, so just
- * return.
- */
- return(eptr);
- }
- /* If we found a parent that's a cell, then return it.
- * I think this case happens when we hit an element
- * that has the same coordinates are the element we
- * were given. An example would be a zero-length
- * text element before a linefeed. Maybe we should
- * change the search algorithm to skip zero-lenght elements?
- *
- */
- else if ( (parent != NULL) && (parent->type == LO_CELL))
- {
- return parent;
- }
- /*
- * Else, we've missed the stopping point for some reason.
- * Assume we aren't in a cell.
- */
- else
- {
- return(eptr);
- }
- }
-
-
- void
- lo_SetSelect(MWContext *context, lo_DocState *state,
- LO_Element *start, int32 start_pos, LO_Element *end, int32 end_pos,
- Bool on)
- {
- LO_Element *eptr;
-
- if ((start == NULL)||(end == NULL))
- {
- return;
- }
-
- eptr = start;
- while ((eptr != NULL)&&(eptr->lo_any.ele_id <= end->lo_any.ele_id))
- {
- int32 last_id;
-
- switch (eptr->type)
- {
- case LO_TEXT:
- if (eptr->lo_text.text != NULL)
- {
- int32 p1, p2;
-
- if (eptr == start)
- {
- p1 = start_pos;
- }
- else
- {
- p1 = 0;
- }
-
- if (eptr == end)
- {
- p2 = end_pos;
- }
- else
- {
- p2 = lo_GetLastCharEndPosition(eptr);
- }
- if (p2 < p1)
- {
- p2 = p1;
- }
-
- if (on != FALSE)
- {
- eptr->lo_text.ele_attrmask |= LO_ELE_SELECTED;
- eptr->lo_text.sel_start = (intn) p1;
- eptr->lo_text.sel_end = (intn) p2;
- }
- else
- {
- eptr->lo_text.ele_attrmask &= (~(LO_ELE_SELECTED));
- eptr->lo_text.sel_start = -1;
- eptr->lo_text.sel_end = -1;
- }
- }
- break;
- case LO_LINEFEED:
- if (on != FALSE)
- {
- eptr->lo_linefeed.ele_attrmask |= LO_ELE_SELECTED;
- eptr->lo_linefeed.sel_start = 0;
- eptr->lo_linefeed.sel_end = 0;
- }
- else
- {
- eptr->lo_linefeed.ele_attrmask &= (~(LO_ELE_SELECTED));
- eptr->lo_linefeed.sel_start = -1;
- eptr->lo_linefeed.sel_end = -1;
- }
- break;
- case LO_IMAGE:
- #ifdef EDITOR
- if ( EDT_IS_EDITOR(context) )
- {
- if (on != FALSE)
- {
- eptr->lo_image.ele_attrmask |= LO_ELE_SELECTED;
- eptr->lo_image.sel_start = 0;
- eptr->lo_image.sel_end = 0;
- }
- else
- {
- eptr->lo_image.ele_attrmask &= (~(LO_ELE_SELECTED));
- eptr->lo_image.sel_start = -1;
- eptr->lo_image.sel_end = -1;
- }
- }
- break;
- #endif /* EDITOR */
- case LO_HRULE:
- #ifdef EDITOR
- if ( EDT_IS_EDITOR(context) )
- {
- if (on != FALSE)
- {
- eptr->lo_hrule.ele_attrmask |= LO_ELE_SELECTED;
- eptr->lo_hrule.sel_start = 0;
- eptr->lo_hrule.sel_end = 0;
- }
- else
- {
- eptr->lo_hrule.ele_attrmask &= (~(LO_ELE_SELECTED));
- eptr->lo_hrule.sel_start = -1;
- eptr->lo_hrule.sel_end = -1;
- }
- }
- break;
- #endif /* EDITOR */
- case LO_FORM_ELE:
- #ifdef EDITOR
- if ( EDT_IS_EDITOR(context) )
- {
- if (on != FALSE)
- {
- eptr->lo_form.ele_attrmask |= LO_ELE_SELECTED;
- eptr->lo_form.sel_start = 0;
- eptr->lo_form.sel_end = 0;
- }
- else
- {
- eptr->lo_form.ele_attrmask &= (~(LO_ELE_SELECTED));
- eptr->lo_form.sel_start = -1;
- eptr->lo_form.sel_end = -1;
- }
- }
- break;
- #endif /* EDITOR */
- case LO_BULLET:
- case LO_SUBDOC:
- case LO_TABLE:
- default:
- break;
- }
-
- last_id = eptr->lo_any.ele_id;
-
- /*
- * Jump cell boundries if there is one between start
- * and end.
- */
- if ((eptr->lo_any.next == NULL)&&(eptr != end))
- {
- eptr = lo_JumpCellWall(context, state, eptr);
- }
-
- eptr = eptr->lo_any.next;
-
- /*
- * When we walk onto a cell, we need to walk into
- * it if it isn't empty.
- */
- if ((eptr != NULL)&&(eptr->type == LO_CELL)&&
- (eptr->lo_cell.cell_list != NULL))
- {
- eptr = eptr->lo_cell.cell_list;
- }
-
- /*
- * We don't want infinite loops.
- * If the element ID hasn't progressed, something
- * serious is wrong, and we should punt.
- */
- if ((eptr != NULL)&&(eptr->lo_any.ele_id <= last_id))
- {
- #ifdef DEBUG
- XP_TRACE(("Selection loop avoidance 1\n"));
- #endif /* DEBUG */
- break;
- }
- }
- }
-
- typedef enum {
- HL_STARTED,
- HL_NOT_STARTED,
- HL_COMPLETED
- } LO_HighlightState;
-
- /* Highlight a range of elements from within a list of layout
- elements. Return values:
-
- HL_STARTED At least one element highlighted from list, but
- last element in range was not encountered on list.
-
- HL_NOT_STARTED Specified range did not contain any of the range
- of elements specified in the arguments.
-
- HL_COMPLETED Specified range of elements has been highlighted. */
- static LO_HighlightState
- lo_HighlightElementList(MWContext *context,
- int32 base_x, int32 base_y,
- LO_Element *list,
- LO_Element *start, int32 start_pos,
- LO_Element *end, int32 end_pos,
- CL_Layer *layer,
- Bool on)
- {
- LO_Element *eptr;
- int16 charset;
- int32 p1, p2;
- LO_HighlightState highlight_state;
-
- if (list == NULL)
- return HL_COMPLETED;
-
- /* Locate first element to be highlighted in list */
- eptr = list;
- while (eptr && eptr != start) {
- if (eptr->type == LO_CELL) {
- int32 x_offset = 0;
- int32 y_offset = 0;
- CL_Layer *cell_layer = layer;
-
- /* LO_CELLs can be either ordinary table cells or
- containers for in-flow layers */
- if (eptr->lo_cell.cell_inflow_layer) {
- cell_layer = eptr->lo_cell.cell_inflow_layer;
- lo_GetLayerXYShift(cell_layer, &x_offset, &y_offset);
- }
-
- if (eptr->lo_cell.cell_list) {
- highlight_state=lo_HighlightElementList(context,
- base_x - x_offset,
- base_y - y_offset,
- eptr->lo_cell.cell_list,
- start, start_pos,
- end, end_pos,
- cell_layer, on);
- if (highlight_state == HL_COMPLETED)
- return HL_COMPLETED;
- if (highlight_state == HL_STARTED) {
- eptr = eptr->lo_any.next;
- break;
- }
- }
- }
- eptr = eptr->lo_any.next;
- }
- if (!eptr)
- return HL_NOT_STARTED;
-
- /* Paint highlighted elements */
- while ((eptr != NULL) && (eptr->lo_any.ele_id <= end->lo_any.ele_id))
- {
- int32 last_id;
-
- switch (eptr->type)
- {
- case LO_TEXT:
- if (eptr->lo_text.text == NULL)
- break;
-
- if (eptr == start)
- p1 = start_pos;
- else
- p1 = 0;
-
- if (eptr == end)
- p2 = end_pos;
- else
- p2 = lo_GetLastCharEndPosition(eptr);
-
- if (p2 < p1)
- p2 = p1;
-
- if ( p2 >= eptr->lo_text.text_len )
- p2 = lo_GetLastCharEndPosition(eptr);
-
- if (on != FALSE)
- {
- eptr->lo_text.ele_attrmask |= LO_ELE_SELECTED;
- eptr->lo_text.sel_start = (intn) p1;
- eptr->lo_text.sel_end = (intn) p2;
- }
- else
- {
- eptr->lo_text.ele_attrmask &= (~(LO_ELE_SELECTED));
- eptr->lo_text.sel_start = -1;
- eptr->lo_text.sel_end = -1;
- }
-
- charset = ((LO_TextStruct *) eptr)->text_attr->charset;
- if ((eptr == start) || ((eptr == end) &&
- INTL_CharSetType(charset) != SINGLEBYTE))
- {
- /* ugly processing for multibyte here */
- char *string;
- int n;
-
- PA_LOCK(string, char *, eptr->lo_text.text);
-
- /*
- * find beginning of first character
- */
- switch (n = INTL_NthByteOfChar(charset, string, (int)(p1+1)))
- {
- case 0:
- case 1:
- break;
- default:
- p1 -= (n - 1);
- if (p1 < 0)
- p1 = 0;
-
- break;
- }
-
- /*
- * find end of last character
- */
- switch (n = INTL_NthByteOfChar(charset, string, (int)(p2+1)))
- {
- case 0:
- break;
- default:
- p2 -= (n - 1);
- if (p2 < 0)
- p2 = 0;
-
- /* FALL THROUGH */
- case 1:
- p2 += INTL_IsLeadByte(charset, string[p2]);
- if (p2 > lo_GetLastCharEndPosition(eptr))
- {
- p2 = lo_GetLastCharEndPosition(eptr);
- }
- break;
- }
-
- PA_UNLOCK(eptr->lo_text.text);
- }
-
- if (p1 <= p2)
- {
- lo_DisplaySubtext(context,
- (LO_TextStruct *)eptr, p1, p2,
- !on,
- layer);
- }
- break;
-
- case LO_LINEFEED:
- if (on != FALSE)
- {
- eptr->lo_linefeed.ele_attrmask |= LO_ELE_SELECTED;
- eptr->lo_linefeed.sel_start = 0;
- eptr->lo_linefeed.sel_end = 0;
- }
- else
- {
- eptr->lo_linefeed.ele_attrmask &= (~(LO_ELE_SELECTED));
- eptr->lo_linefeed.sel_start = -1;
- eptr->lo_linefeed.sel_end = -1;
- }
-
- lo_RefreshElement(eptr, layer, FALSE);
- break;
-
- case LO_HRULE:
- #ifdef EDITOR
- if ( EDT_IS_EDITOR(context) )
- {
- if (on != FALSE)
- {
- eptr->lo_hrule.ele_attrmask |= LO_ELE_SELECTED;
- eptr->lo_hrule.sel_start = 0;
- eptr->lo_hrule.sel_end = 0;
- }
- else
- {
- eptr->lo_hrule.ele_attrmask &= (~(LO_ELE_SELECTED));
- eptr->lo_hrule.sel_start = -1;
- eptr->lo_hrule.sel_end = -1;
- }
-
- lo_RefreshElement(eptr, layer, FALSE);
- }
- break;
- #endif
- case LO_FORM_ELE:
- #ifdef EDITOR
- if ( EDT_IS_EDITOR(context) )
- {
- if (on != FALSE)
- {
- eptr->lo_form.ele_attrmask |= LO_ELE_SELECTED;
- eptr->lo_form.sel_start = 0;
- eptr->lo_form.sel_end = 0;
- }
- else
- {
- eptr->lo_form.ele_attrmask &= (~(LO_ELE_SELECTED));
- eptr->lo_form.sel_start = -1;
- eptr->lo_form.sel_end = -1;
- }
-
- lo_DisplayFormElement(context, (LO_FormElementStruct *)eptr);
- }
- break;
- #endif
- case LO_IMAGE:
- #ifdef EDITOR
- if ( EDT_IS_EDITOR(context) )
- {
- if (on != FALSE)
- {
- eptr->lo_image.ele_attrmask |= LO_ELE_SELECTED;
- eptr->lo_image.sel_start = 0;
- eptr->lo_image.sel_end = 0;
- }
- else
- {
- eptr->lo_image.ele_attrmask &= (~(LO_ELE_SELECTED));
- eptr->lo_image.sel_start = -1;
- eptr->lo_image.sel_end = -1;
- }
-
- {
- LO_ImageStruct *lo_image = (LO_ImageStruct*)eptr;
- XP_Rect rect;
-
- rect.left = 0;
- rect.top = 0;
- rect.right = lo_image->width;
- rect.bottom = lo_image->height;
-
- CL_UpdateLayerRect(context->compositor, lo_image->layer,
- &rect, (PRBool)FALSE);
- }
- }
- #endif
- break;
-
- case LO_CELL:
- {
- LO_CellStruct *cell = &eptr->lo_cell;
- int32 x_offset = 0;
- int32 y_offset = 0;
- CL_Layer *cell_layer = layer;
-
- /* LO_CELLs can be either ordinary table cells or
- containers for in-flow layers */
- if (cell->cell_inflow_layer) {
- cell_layer = cell->cell_inflow_layer;
- lo_GetLayerXYShift(cell_layer, &x_offset, &y_offset);
- }
-
- if (!eptr->lo_cell.cell_list)
- break;
-
- highlight_state = lo_HighlightElementList(context,
- base_x - x_offset,
- base_y - y_offset,
- eptr->lo_cell.cell_list,
- eptr->lo_cell.cell_list, 0,
- end, end_pos,
- cell_layer, on);
- if (highlight_state == HL_COMPLETED) {
- return HL_COMPLETED;
- }
- break;
- }
-
- case LO_SUBDOC:
- case LO_TABLE:
- case LO_BULLET:
- default:
- break;
- }
-
- last_id = eptr->lo_any.ele_id;
-
- eptr = eptr->lo_any.next;
-
- /*
- * We don't want infinite loops.
- * If the element ID hasn't progressed, something
- * serious is wrong, and we should punt.
- */
- if ((eptr != NULL)&&(eptr->lo_any.ele_id <= last_id))
- {
- #ifdef DEBUG
- XP_TRACE(("Selection loop avoidance 2\n"));
- #endif /* DEBUG */
- break;
- }
- }
-
- /* We ran off the list without reaching the last element in the
- selection range, so tell our caller that there's more work to do. */
- if (!eptr)
- return HL_STARTED;
-
- /* We reached the last element in the selection range. We're done. */
- return HL_COMPLETED;
- }
-
-
- void
- lo_HighlightSelect(MWContext *context, lo_DocState *state,
- LO_Element *start, int32 start_pos,
- LO_Element *end, int32 end_pos,
- Bool on)
- {
- LO_Element **line_array, *list;
- LO_CellStruct *layer_cell;
-
- if ((start == NULL) || (end == NULL))
- return;
-
- /*
- * Do a synchronous composite to flush any other drawing
- * request. Since we're going to replace the painter_func
- * of state->selection_layer, we don't want to ignore other
- * requests that might require this layer to draw. This call
- * must happen before the text element is modified in any way.
- */
- if (context->compositor)
- CL_CompositeNow(context->compositor);
-
- /* The stinkin' editor doesn't set the selection layer yet. */
- if (!state->selection_layer)
- state->selection_layer = CL_FindLayer(context->compositor, LO_BODY_LAYER_NAME);
-
- layer_cell = lo_GetCellFromLayer(context, state->selection_layer);
- if (layer_cell == NULL) {
- XP_LOCK_BLOCK(line_array, LO_Element**, state->line_array );
- if (line_array)
- list = line_array[0];
- else
- list = NULL;
- XP_UNLOCK_BLOCK( state->line_array );
- } else
- list = layer_cell->cell_list;
-
- lo_HighlightElementList(context, state->base_x, state->base_y, list,
- start, start_pos, end, end_pos,
- state->selection_layer, on);
-
- /* On the Mac, we don't run timeouts during a selection, so the
- compositor won't run. Therefore we need to force any new
- selection to paint now. */
- if (context->compositor)
- CL_CompositeNow(context->compositor);
- }
-
- void
- LO_HighlightSelection(MWContext *context, Bool on)
- {
- int32 doc_id;
- lo_TopState *top_state;
- lo_DocState *state;
- LO_Element *start, *end;
- int32 start_pos, end_pos;
-
- /*
- * Get the unique document ID, and retreive this
- * documents layout state.
- */
- doc_id = XP_DOCID(context);
- top_state = lo_FetchTopState(doc_id);
- if ((top_state == NULL)||(top_state->doc_state == NULL))
- {
- return;
- }
- state = top_state->doc_state;
-
- if ((state->selection_start == NULL)||(state->selection_end == NULL))
- {
- return;
- }
-
- start = state->selection_start;
- start_pos = state->selection_start_pos;
- end = state->selection_end;
- end_pos = state->selection_end_pos;
-
- lo_HighlightSelect(context, state, start, start_pos, end, end_pos,
- on);
- }
-
-
- void
- LO_ClearSelection(MWContext *context)
- {
- int32 doc_id;
- lo_TopState *top_state;
- lo_DocState *state;
-
- /*
- * Get the unique document ID, and retreive this
- * documents layout state.
- */
- doc_id = XP_DOCID(context);
- top_state = lo_FetchTopState(doc_id);
- if ((top_state == NULL)||(top_state->doc_state == NULL))
- {
- return;
- }
- state = top_state->doc_state;
-
- LO_HighlightSelection(context, FALSE);
- state->selection_start = NULL;
- state->selection_start_pos = 0;
- state->selection_end = NULL;
- state->selection_end_pos = 0;
- state->selection_layer = NULL;
- }
-
-
- PRIVATE
- XP_Block
- lo_SelectionToText(MWContext *context, lo_DocState *state,
- LO_Element *start, int32 start_pos, LO_Element *end, int32 end_pos)
- {
- LO_Element *eptr;
- LO_TextStruct tmp_text;
- LO_TextInfo text_info;
- LO_TextAttr tmp_attr;
- int32 space_width;
- int32 length;
- Bool indent_counts;
- PA_Block sbuff;
- XP_Block buff;
- char *str;
- char *tptr;
- /* int16 charset;*/
-
- if ((start == NULL)||(end == NULL))
- {
- return(NULL);
- }
-
- /*
- * All this work is to get the width of a " " in the default
- * fixed font.
- */
- memset (&tmp_text, 0, sizeof (tmp_text));
- sbuff = PA_ALLOC(1);
- if (sbuff == NULL)
- {
- return(NULL);
- }
- PA_LOCK(str, char *, sbuff);
- str[0] = ' ';
- PA_UNLOCK(sbuff);
- tmp_text.text = sbuff;
- tmp_text.text_len = 1;
- /*
- * Fill in default font information.
- */
- lo_SetDefaultFontAttr(state, &tmp_attr, context);
- tmp_attr.fontmask |= LO_FONT_FIXED;
- tmp_text.text_attr = lo_FetchTextAttr(state, &tmp_attr);
- FE_GetTextInfo(context, &tmp_text, &text_info);
- PA_FREE(sbuff);
- space_width = text_info.max_width;
- if (space_width <= 0)
- {
- space_width = 2;
- }
-
- indent_counts = FALSE;
- length = 0;
- eptr = start;
- while ((eptr != NULL)&&(eptr->lo_any.ele_id <= end->lo_any.ele_id))
- {
- int32 last_id;
-
- switch (eptr->type)
- {
- case LO_TEXT:
- if (eptr->lo_text.text != NULL)
- {
- if (indent_counts != FALSE)
- {
- int32 width;
-
- width = eptr->lo_text.x +
- eptr->lo_text.x_offset -
- state->win_left;
- width = (width + space_width - 1) / space_width;
- if (width < 0)
- {
- width = 0;
- }
- length += width;
- indent_counts = FALSE;
- }
-
- if ((eptr == start)||(eptr == end))
- {
- int32 p1, p2;
-
- if (eptr == start)
- {
- p1 = start_pos;
- }
- else
- {
- p1 = 0;
- }
-
- if (eptr == end)
- {
- char *string;
-
- PA_LOCK(string, char *, eptr->lo_text.text);
- /* charset = eptr->lo_text.text_attr->charset;
- * p2 = end_pos + INTL_IsLeadByte(charset, string[end_pos]);
- *
- * p2 already point to the end of the selection
- * calling INTL_IsLeadByte(charset, string[end_pos]) is wrong here.
- */
- p2 = end_pos;
- PA_UNLOCK(eptr->lo_text.text);
- if (p2 > lo_GetLastCharEndPosition(eptr))
- {
- p2 = lo_GetLastCharEndPosition(eptr);
- }
- }
- else
- {
- p2 = lo_GetLastCharEndPosition(eptr);
- }
- if (p2 < p1)
- {
- p2 = p1;
- }
-
- length += (p2 - p1 + 1);
- }
- else
- {
- length += eptr->lo_text.text_len;
- }
- }
- break;
- case LO_LINEFEED:
- length += LINEBREAK_LEN;
- indent_counts = TRUE;
- break;
- case LO_HRULE:
- case LO_FORM_ELE:
- case LO_BULLET:
- case LO_IMAGE:
- case LO_SUBDOC:
- case LO_TABLE:
- default:
- break;
- }
-
- last_id = eptr->lo_any.ele_id;
-
- /*
- * Jump cell boundries if there is one between start
- * and end.
- */
- if ((eptr->lo_any.next == NULL)&&(eptr != end))
- {
- eptr = lo_JumpCellWall(context, state, eptr);
- }
-
- eptr = eptr->lo_any.next;
-
- /*
- * When we walk onto a cell, we need to walk into
- * it if it isn't empty.
- */
- if ((eptr != NULL)&&(eptr->type == LO_CELL)&&
- (eptr->lo_cell.cell_list != NULL))
- {
- eptr = eptr->lo_cell.cell_list;
- }
-
- /*
- * We don't want infinite loops.
- * If the element ID hasn't progressed, something
- * serious is wrong, and we should punt.
- */
- if ((eptr != NULL)&&(eptr->lo_any.ele_id <= last_id))
- {
- #ifdef DEBUG
- XP_TRACE(("Selection loop avoidance 3\n"));
- #endif /* DEBUG */
- break;
- }
- }
- length++;
-
- #ifdef XP_WIN16
- if (length > SIZE_LIMIT)
- {
- length = SIZE_LIMIT;
- }
- #endif /* XP_WIN16 */
-
- buff = XP_ALLOC_BLOCK(length * sizeof(char));
- if (buff == NULL)
- {
- return(NULL);
- }
- XP_LOCK_BLOCK(str, char *, buff);
-
- tptr = str;
- indent_counts = FALSE;
- length = 0;
- eptr = start;
- while ((eptr != NULL)&&(eptr->lo_any.ele_id <= end->lo_any.ele_id))
- {
- int32 last_id;
-
- switch (eptr->type)
- {
- case LO_TEXT:
- if (eptr->lo_text.text != NULL)
- {
- char *text;
-
- if (indent_counts != FALSE)
- {
- int32 width;
- int32 i;
-
- width = eptr->lo_text.x +
- eptr->lo_text.x_offset -
- state->win_left;
- width = (width + space_width - 1) / space_width;
- if (width < 0)
- {
- width = 0;
- }
- length += width;
- #ifdef XP_WIN16
- if (length > SIZE_LIMIT)
- {
- length -= width;
- break;
- }
- #endif /* XP_WIN16 */
- for (i=0; i<width; i++)
- {
- XP_BCOPY(" ", tptr, 1);
- tptr++;
- }
- indent_counts = FALSE;
- }
-
- if ((eptr == start)||(eptr == end))
- {
- int32 p1, p2, len;
- int32 maxPos = lo_GetLastCharEndPosition(eptr);
-
- if (eptr == start)
- {
- p1 = start_pos;
- }
- else
- {
- p1 = 0;
- }
-
- if (eptr == end)
- {
- char *string;
-
- PA_LOCK(string, char *, eptr->lo_text.text);
- /* charset = eptr->lo_text.text_attr->charset;
- * p2 = end_pos + INTL_IsLeadByte(charset, string[end_pos]);
- *
- * p2 already point to the end of the selection
- * calling INTL_IsLeadByte(charset, string[end_pos]) is wrong here.
- */
- p2 = end_pos;
- PA_UNLOCK(eptr->lo_text.text);
- if (p2 > maxPos)
- {
- p2 = maxPos;
- }
- }
- else
- {
- p2 = maxPos;
- }
- if (p2 < p1)
- {
- p2 = p1;
- }
-
- len = p2 - p1 + 1;
- length += len;
- #ifdef XP_WIN16
- if (length > SIZE_LIMIT)
- {
- length -= len;
- break;
- }
- #endif /* XP_WIN16 */
- PA_LOCK(text, char *, eptr->lo_text.text);
- XP_BCOPY((char *)(text + p1), tptr, len);
- tptr = (char *)(tptr + len);
- PA_UNLOCK(eptr->lo_text.text);
- }
- else
- {
- length += eptr->lo_text.text_len;
- #ifdef XP_WIN16
- if (length > SIZE_LIMIT)
- {
- length -= eptr->lo_text.text_len;
- break;
- }
- #endif /* XP_WIN16 */
- PA_LOCK(text, char *, eptr->lo_text.text);
- XP_BCOPY(text, tptr, eptr->lo_text.text_len);
- tptr = (char *)(tptr + eptr->lo_text.text_len);
- PA_UNLOCK(eptr->lo_text.text);
- }
- }
- break;
- case LO_LINEFEED:
- length += LINEBREAK_LEN;
- #ifdef XP_WIN16
- if (length > SIZE_LIMIT)
- {
- length -= LINEBREAK_LEN;
- break;
- }
- #endif /* XP_WIN16 */
- XP_BCOPY(LINEBREAK, tptr, LINEBREAK_LEN);
- tptr = (char *)(tptr + LINEBREAK_LEN);
- indent_counts = TRUE;
- break;
- case LO_HRULE:
- case LO_FORM_ELE:
- case LO_BULLET:
- case LO_IMAGE:
- case LO_SUBDOC:
- case LO_TABLE:
- default:
- break;
- }
-
- last_id = eptr->lo_any.ele_id;
-
- /*
- * Jump cell boundries if there is one between start
- * and end.
- */
- if ((eptr->lo_any.next == NULL)&&(eptr != end))
- {
- eptr = lo_JumpCellWall(context, state, eptr);
- }
-
- eptr = eptr->lo_any.next;
-
- /*
- * When we walk onto a cell, we need to walk into
- * it if it isn't empty.
- */
- if ((eptr != NULL)&&(eptr->type == LO_CELL)&&
- (eptr->lo_cell.cell_list != NULL))
- {
- eptr = eptr->lo_cell.cell_list;
- }
-
- /*
- * We don't want infinite loops.
- * If the element ID hasn't progressed, something
- * serious is wrong, and we should punt.
- */
- if ((eptr != NULL)&&(eptr->lo_any.ele_id <= last_id))
- {
- #ifdef DEBUG
- XP_TRACE(("Selection loop avoidance 4\n"));
- #endif /* DEBUG */
- break;
- }
- }
- str[length] = '\0';
- length++;
- XP_UNLOCK_BLOCK(buff);
-
- return(buff);
- }
-
-
- XP_Block
- LO_GetSelectionText(MWContext *context)
- {
- int32 doc_id;
- lo_TopState *top_state;
- lo_DocState *state;
- LO_Element *start, *end;
- int32 start_pos, end_pos;
- XP_Block buff;
-
- /*
- * 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(NULL);
- }
- state = top_state->doc_state;
-
- if ((state->selection_start == NULL)||(state->selection_end == NULL))
- {
- return(NULL);
- }
-
- start = state->selection_start;
- start_pos = state->selection_start_pos;
- end = state->selection_end;
- end_pos = state->selection_end_pos;
-
- /* Make sure the selection is normalized */
- lo_NormalizeSelectionPoint(context, state, &start, &start_pos);
- lo_NormalizeSelectionEnd(context, state, &end, &end_pos);
-
- buff = lo_SelectionToText(context, state, start, start_pos,
- end, end_pos);
-
- return(buff);
- }
-
-
- Bool
- LO_HaveSelection(MWContext *context)
- {
- int32 doc_id;
- lo_TopState *top_state;
- lo_DocState *state;
-
- /*
- * Get the unique document ID, and retreive this
- * documents layout state.
- */
- doc_id = XP_DOCID(context);
- top_state = lo_FetchTopState(doc_id);
- if ((top_state == NULL)||(top_state->doc_state == NULL))
- {
- return(FALSE);
- }
- state = top_state->doc_state;
-
- if ((state->selection_start == NULL)||(state->selection_end == NULL))
- {
- return(FALSE);
- }
- else
- {
- return(TRUE);
- }
- }
-
- /* return true if there is a current selection */
-
- PRIVATE
- Bool
- lo_GetSelection(MWContext *context, LO_Selection* selection)
- {
- int32 beginPosition;
- int32 endPosition;
- CL_Layer *sel_layer;
- /* LO_GetSelectionEndPoints uses int32. LO_Selection uses intn. So we need to
- * do the conversion here.
- */
-
- LO_GetSelectionEndpoints(context, &selection->begin.element, &selection->end.element,
- &beginPosition, &endPosition, &sel_layer);
- selection->begin.position = (intn) beginPosition;
- selection->end.position = (intn) endPosition;
- return selection->begin.element != NULL && selection->end.element != NULL;
- }
-
- void
- LO_GetSelectionEndpoints(MWContext *context, LO_Element **start, LO_Element **end,
- int32 *start_pos, int32 *end_pos, CL_Layer **sel_layer)
- {
- int32 doc_id;
- lo_TopState *top_state;
- lo_DocState *state;
-
- *start = NULL;
- *end = NULL;
- *start_pos = 0;
- *end_pos = 0;
- *sel_layer = NULL;
-
- /*
- * Get the unique document ID, and retreive this
- * documents layout state.
- */
- doc_id = XP_DOCID(context);
- top_state = lo_FetchTopState(doc_id);
- if ((top_state == NULL)||(top_state->doc_state == NULL))
- {
- return;
- }
- state = top_state->doc_state;
-
- if ((state->selection_start == NULL)||(state->selection_end == NULL))
- {
- return;
- }
-
- *start = state->selection_start;
- *start_pos = state->selection_start_pos;
- *end = state->selection_end;
- *end_pos = state->selection_end_pos;
- *sel_layer = state->selection_layer;
- }
-
- /* Returns TRUE if there was anything to select. If there is no data, or if
- * we're in editor mode and there is no editable data, then FALSE is returned.
- */
-
- Bool
- LO_SelectAll(MWContext *context)
- {
- int32 doc_id;
- lo_TopState *top_state;
- lo_DocState *state;
- LO_Selection selection;
-
- /*
- * 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 FALSE;
- }
- state = top_state->doc_state;
-
- LO_HighlightSelection(context, FALSE);
- selection.begin.element = NULL;
- selection.begin.position = 0;
- selection.end.element = NULL;
- selection.end.position = 0;
-
- if ( ! ( lo_FindDocumentEdge(context, state, &selection.begin, TRUE, FALSE)
- && lo_FindDocumentEdge(context, state, &selection.end, TRUE, TRUE) ) )
- {
- return FALSE;
- }
- #if 0
- lo_ConvertInsertPointToSelectionEnd(context, state, &selection.end.element, &selection.end.position);
- #endif
- LO_ASSERT_SELECTION(context, &selection);
- /*
- * Nothing to select.
- */
- if (selection.begin.element == NULL)
- {
- return FALSE;
- }
-
- state->selection_start = selection.begin.element;
- state->selection_start_pos = selection.begin.position;
- state->selection_end = selection.end.element;
- state->selection_end_pos = selection.end.position;
-
- #if 0
- if ( ! lo_NormalizeSelection(context) ){
- return FALSE;
- }
- #endif
- LO_HighlightSelection(context, TRUE);
- return TRUE;
- }
-
- #ifdef EDITOR
-
- /* This routine normalizes a selection so that it only selects editable elements.
- * It returns TRUE if the resulting selection is not empty.
- */
- Bool
- lo_NormalizeSelection(MWContext *context)
- {
- Bool result;
- int32 doc_id;
- lo_TopState *top_state;
- lo_DocState *state;
- intn comparisonResult;
-
- #ifdef DEBUG
- lo_VerifyLayout(context);
- #endif /* DEBUG */
- result = FALSE;
- /*
- * Get the unique document ID, and retreive this
- * document's layout state.
- */
- doc_id = XP_DOCID(context);
- top_state = lo_FetchTopState(doc_id);
- if ((top_state == NULL)||(top_state->doc_state == NULL))
- {
- return FALSE;
- }
- state = top_state->doc_state;
- if ( ! ( lo_EnsureEditableSearchNext2(context, state, &state->selection_start, & state->selection_start_pos)
- && lo_EnsureEditableSearchPrev2(context, state, &state->selection_end, & state->selection_end_pos) ) )
- {
- XP_ASSERT(FALSE);
- return FALSE;
- }
- /*
- * Ensure that the start position is before the end position
- */
- comparisonResult = lo_compare_selections(
- state->selection_start, state->selection_start_pos,
- state->selection_end, state->selection_end_pos );
- result = comparisonResult <= 0;
- if ( comparisonResult > 0 )
- {
- /*
- * It's not a legal selection. This can happen if we're editing and
- * the selected element is not editable. In this case we null out the selection
- */
- state->selection_start = NULL;
- state->selection_end = NULL;
- state->selection_start_pos = 0;
- state->selection_end_pos = 0;
- }
- return result;
- }
- #endif
-
- Bool
- lo_EnsureEditableSearchNext(MWContext *context, lo_DocState* state, LO_Element** eptr)
- {
- int32 position = 0;
- return lo_EnsureEditableSearch2(context, state, eptr, &position, TRUE);
- }
-
- Bool
- lo_EnsureEditableSearchNext2(MWContext *context, lo_DocState* state, LO_Element** eptr,
- int32* ePositionPtr)
- {
- return lo_EnsureEditableSearch2(context, state, eptr, ePositionPtr, TRUE);
- }
-
- Bool
- lo_EnsureEditableSearchPrev(MWContext *context, lo_DocState* state, LO_Element** eptr)
- {
- int32 position = lo_GetMaximumInsertPointPosition(*eptr);
- return lo_EnsureEditableSearch2(context, state, eptr, &position, FALSE);
- }
-
- Bool
- lo_EnsureEditableSearchPrev2(MWContext *context, lo_DocState* state, LO_Element** eptr, int32* ePositionPtr)
- {
- return lo_EnsureEditableSearch2(context, state, eptr, ePositionPtr, FALSE);
- }
-
- /* Returns TRUE if the result is editable. Doesn't change position if can't find editable. */
-
- Bool
- lo_EnsureEditableSearch(MWContext *context, lo_DocState* state, LO_Position* p, Bool forward)
- {
- return lo_EnsureEditableSearch2(context, state, &p->element, &p->position, forward);
- }
-
- PRIVATE
- Bool
- lo_BumpToNextElement(MWContext *context, lo_DocState* state, LO_Element** eptr, int32* ePositionPtr, Bool forward)
- {
- LO_Element* element = *eptr;
- int32 position = *ePositionPtr;
- if ( forward )
- {
- element = lo_BoundaryJumpingNext(context, state, element);
- }
- else
- {
- element = lo_BoundaryJumpingPrev(context, state, element);
- }
-
- if ( ! element ) return FALSE;
-
- if ( forward )
- {
- position = 0;
- }
- else
- {
- position = lo_GetMaximumInsertPointPosition(element);
- }
-
- *eptr = element;
- *ePositionPtr = position;
- return TRUE;
- }
-
- Bool
- lo_EnsureEditableSearch2(MWContext *context, lo_DocState* state, LO_Element** eptr, int32* ePositionPtr, Bool forward)
- {
- Bool moved = FALSE;
- LO_Element* element = *eptr;
- int32 position = *ePositionPtr;
-
- if ( ! element ) return FALSE;
-
- while ( ! lo_IsValidEditableInsertPoint2(context, state, element, position ) )
- {
- if ( ! lo_BumpToNextElement(context, state, &element, &position, forward ) )
- {
- return FALSE;
- }
- moved = TRUE;
- }
-
- if ( moved )
- {
- *eptr = element;
- *ePositionPtr = position;
- }
- return TRUE;
- }
-
- Bool
- lo_IsValidEditableInsertPoint2(MWContext *context, lo_DocState* state, LO_Element* eptr, int32 position)
- {
- return lo_ValidEditableElement(context, eptr)
- && position >= 0 && position <= lo_GetMaximumInsertPointPosition(eptr);
- }
-
- #ifdef EDITOR
- /*
- * This routine has too much knowledge of the layout engine. Need to revisit
- */
- void LO_PositionCaret(MWContext *context, int32 x, int32 y, CL_Layer *layer)
- {
- LO_Click(context, x, y, TRUE, layer);
- }
-
- void LO_DoubleClick(MWContext *context, int32 x, int32 y, CL_Layer *layer)
- {
- int32 doc_id;
- lo_TopState *top_state;
- lo_DocState *state;
- LO_HitResult result;
-
- /*
- * 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;
-
- LO_Hit(context, x, y, FALSE, &result, layer);
-
- lo_ProcessDoubleClick(context, top_state, state, &result, layer);
- }
-
-
- #endif
-
- PRIVATE
- void lo_GetHorizontalBounds(LO_Element* eptr, int32* returnBegin, int32* returnEnd)
- {
- int32 width = eptr->lo_any.width;
- /*
- * Images need to account for border width
- */
- if (eptr->type == LO_IMAGE)
- {
- width = width + (2 * eptr->lo_image.border_width);
- }
- if (width <= 0)
- {
- width = 1;
- }
- *returnBegin = eptr->lo_any.x + eptr->lo_any.x_offset;
- *returnEnd = *returnBegin + width;
- }
-
- int32
- lo_GetTextAttrMask(LO_Element* eptr)
- {
- int32 mask = 0;
- LO_TextAttr* textAttr = 0;
- if ( ! eptr )
- {
- XP_ASSERT(FALSE);
- return mask;
- }
- switch ( eptr->type ) {
- case LO_TEXT:
- textAttr = eptr->lo_text.text_attr;
- break;
- case LO_IMAGE:
- textAttr = eptr->lo_image.text_attr;
- break;
- case LO_LINEFEED:
- textAttr = eptr->lo_linefeed.text_attr;
- break;
- default:
- break;
- }
- if ( textAttr ) {
- mask = textAttr->attrmask;
- }
- return mask;
- }
-
- int32
- lo_GetElementLength(LO_Element* eptr)
- {
- int32 length;
- if ( ! eptr )
- {
- XP_ASSERT(FALSE);
- return 1;
- }
- switch ( eptr->type ) {
- case LO_TEXT:
- length = eptr->lo_text.text_len;
- break;
- case LO_TEXTBLOCK:
- case LO_DESCTITLE:
- length = 0; /*MJUDGE 2-5-98*/
- break;
- case LO_HRULE:
- case LO_IMAGE:
- case LO_LINEFEED:
- default:
- length = 1;
- break;
- }
- return length;
- }
-
- LO_AnchorData*
- lo_GetAnchorData(LO_Element* eptr)
- {
- LO_AnchorData* result = 0;
- if ( ! eptr )
- {
- XP_ASSERT(FALSE);
- return result;
- }
- switch ( eptr->type ) {
- case LO_TEXT:
- result = eptr->lo_text.anchor_href;
- break;
- case LO_IMAGE:
- result = eptr->lo_image.anchor_href;
- break;
- case LO_LINEFEED:
- result = eptr->lo_linefeed.anchor_href;
- break;
- default:
- break;
- }
- return result;
- }
-
- LO_Element*
- lo_GetNeighbor(LO_Element* element, Bool forward)
- {
- LO_Element* result = NULL;
- if ( element ) {
- if ( forward )
- result = element->lo_any.next;
- else
- result = element->lo_any.prev;
- }
- return result;
- }
-
- int32
- lo_GetElementEdge(LO_Element* element, Bool forward)
- {
- int32 result = 0;
- if ( element ) {
- if ( forward )
- result = lo_GetElementLength(element);
- }
- return result;
- }
-
- int32
- lo_GetMaximumInsertPointPosition(LO_Element* eptr)
- {
- if ( ! eptr )
- {
- XP_ASSERT(FALSE);
- return 0;
- }
- #if 0
- if ( eptr->type == LO_LINEFEED )
- {
- return 0;
- }
- else
- {
- return lo_GetElementLength(eptr);
- }
- #endif
- return lo_GetElementLength(eptr);
- }
-
- int32
- lo_IncrementPosition(LO_Element* eptr, int32 position)
- {
- int32 length = lo_GetElementLength(eptr);
- if ( position < length )
- {
- if (eptr->type == LO_TEXT)
- {
- unsigned char *string;
-
- PA_LOCK(string, unsigned char *, eptr->lo_text.text);
- position = INTL_NextCharIdx(eptr->lo_text.text_attr->charset,
- string, position);
- PA_UNLOCK(eptr->lo_text.text);
- }
- else
- position++;
- }
- if ( position > length ) {
- position = length;
- }
- return position;
- }
-
- int32
- lo_DecrementPosition(LO_Element* eptr, int32 position)
- {
- if ( position > 0 )
- {
- if (eptr->type == LO_TEXT)
- {
- unsigned char *string;
-
- PA_LOCK(string, unsigned char *, eptr->lo_text.text);
- position = INTL_PrevCharIdx(eptr->lo_text.text_attr->charset,
- string, position);
- PA_UNLOCK(eptr->lo_text.text);
- }
- else
- position--;
- }
- if ( position < 0 ) {
- position = 0;
- }
- return position;
- }
-
- int32
- lo_GetLastCharBeginPosition(LO_Element* eptr)
- {
- return lo_DecrementPosition(eptr,
- lo_GetElementLength(eptr));
- }
-
- int32
- lo_GetLastCharEndPosition(LO_Element* eptr)
- {
- return lo_GetElementLength(eptr) - 1;
- }
-
- void lo_HitLine(MWContext *context, lo_DocState *state, int32 x, int32 y, Bool requireCaret,
- LO_HitResult* result);
- void lo_HitLine2(MWContext *context, lo_DocState *state, LO_Element* element, int32 position,
- int32 x, LO_HitResult* result);
- Bool lo_PositionIsOffEndOfLine(LO_HitElementResult* elementResult);
-
- void lo_FullHitElement(MWContext *context, lo_DocState* state, int32 x, int32 y,
- Bool requireCaret,
- LO_Element* eptr, int32 ret_x, int32 ret_y, LO_HitResult* result);
-
- PRIVATE
- void lo_HitElement(MWContext *context, lo_DocState* state, int32 x, int32 y,
- Bool requireCaret,
- LO_Element* eptr, int32 ret_x, int32 ret_y, LO_HitResult* result)
- {
- /* We hit something */
- result->type = LO_HIT_ELEMENT;
- result->lo_hitElement.position.element = eptr;
- switch ( eptr->lo_any.type )
- {
- case LO_TEXT:
- {
- int32 charStart;
- int32 charEnd;
- result->lo_hitElement.position.position = lo_ElementToCharOffset2(context, state, eptr, ret_x,
- &charStart, &charEnd);
- if (result->lo_hitElement.position.position < 0)
- {
- result->lo_hitElement.position.position = 0;
- result->lo_hitElement.region = LO_HIT_ELEMENT_REGION_BEFORE;
- }
- else {
- /*
- * eptr points to the element
- * region is the part of the element
- */
- if( x > eptr->lo_any.x + eptr->lo_text.width )
- {
- result->lo_hitElement.region = LO_HIT_ELEMENT_REGION_AFTER;
- }
- else
- {
- int32 charMiddle = (charStart + charEnd) / 2;
- if ( ret_x < charMiddle ) {
- result->lo_hitElement.region = LO_HIT_ELEMENT_REGION_BEFORE;
- } else {
- result->lo_hitElement.region = LO_HIT_ELEMENT_REGION_AFTER;
- }
- }
- }
- }
- break;
- default:
- {
- int32 begin;
- int32 end;
- result->lo_hitElement.position.position = 0;
- lo_GetHorizontalBounds(eptr, &begin, &end);
- if ( requireCaret )
- {
- int32 middle = (begin + end) / 2;
- result->lo_hitElement.region =
- ( x < middle ) ? LO_HIT_ELEMENT_REGION_BEFORE
- : LO_HIT_ELEMENT_REGION_AFTER;
- }
- else
- {
- int32 midBegin;
- int32 midEnd;
- midBegin = begin + 3;
- midEnd = end - 3;
- if ( midEnd - midBegin < 6 ) {
- if ( end - begin <= 6 ) {
- /* Very narrow picture. 0..6 pixels */
- midBegin = begin;
- midEnd = end;
- } else {
- /* Narrow picture. 6+..12 pixels */
- int32 margin = end - begin - 6;
- int32 halfMargin = margin / 2;
- midBegin = begin + halfMargin;
- midEnd = end - (margin - halfMargin);
- }
- }
- if ( x < midBegin ) {
- result->lo_hitElement.region = LO_HIT_ELEMENT_REGION_BEFORE;
- } else if ( x < midEnd ) {
- result->lo_hitElement.region = LO_HIT_ELEMENT_REGION_MIDDLE;
- } else {
- result->lo_hitElement.region = LO_HIT_ELEMENT_REGION_AFTER;
- }
- }
- }
- break;
- }
-
- }
-
- void lo_FullHitElement(MWContext *context, lo_DocState* state, int32 x, int32 y,
- Bool requireCaret,
- LO_Element* eptr, int32 ret_x, int32 ret_y, LO_HitResult* result)
- {
- if ( eptr->type != LO_LINEFEED ) {
- /* Seek forward to find an editable element */
- if ( ! lo_EnsureEditableSearchNext(context, state, &eptr) )
- {
- lo_EnsureEditableSearchPrev(context, state, &eptr);
- }
- lo_HitElement(context, state, x, y, requireCaret, eptr, ret_x, ret_y, result);
- /* Check if we ran off the end of the line */
- if ( result->type == LO_HIT_UNKNOWN )
-
- #if 0 /* leads to infinite recursion when draging around tables. */
- if ( result->type == LO_HIT_UNKNOWN
- || result->type == LO_HIT_ELEMENT
- && lo_PositionIsOffEndOfLine(& result->lo_hitElement) )
- #endif
-
- {
- /* XP_TRACE(("Element off end of line.")); */
- lo_HitLine(context, state, x, y, requireCaret, result);
- }
- }
- else
- {
- /* There's a bug selecting the last character of text in a table
- * that bites us if we use lo_HitLine. So rather than starting the
- * search from the beginning, we start from the linefeed.
- */
- lo_HitLine2(context, state, eptr, 0, x, result);
- }
- }
-
- PRIVATE
- void lo_HitCellWideMatch(MWContext *context, lo_DocState *state, LO_CellStruct* cellPtr, int32 x, int32 y, Bool requireCaret, LO_HitResult* result)
- {
- #if 0
- LO_Element* eptr = lo_XYToNearestCellElement(context, state, cellPtr, x, y);
- #endif
- /*
- * The last argument is FALSE so that we search upwards. This helps us in tables
- * in the editor.
- */
-
- LO_Element* eptr = lo_search_element_list_WideMatch(context, cellPtr->cell_list, NULL, x, y, FALSE);
- if ( eptr )
- {
- lo_FullHitElement(context, state, x, y, requireCaret, eptr, x, y, result);
- }
- }
-
- PRIVATE
- void lo_HitCell(MWContext *context, lo_DocState *state, LO_CellStruct* cellPtr, int32 x, int32 y, Bool requireCaret, LO_HitResult* result)
- {
- LO_Element* eptr = lo_XYToNearestCellElement(context, state, cellPtr, x, y);
- if ( eptr )
- {
- lo_FullHitElement(context, state, x, y, requireCaret, eptr, x, y, result);
- }
- }
-
- void lo_HitLine(MWContext *context, lo_DocState *state, int32 x, int32 y, Bool requireCaret,
- LO_HitResult* result)
- {
- int32 line;
- result->type = LO_HIT_UNKNOWN;
-
- /*
- * Search from current line backwards to find something to edit.
- */
- for ( line = lo_PointToLine(context, state, x, y);
- line >= 0;
- line-- )
- {
- LO_Element* begin;
- LO_Element* end;
- LO_Element* tptr;
- lo_GetLineEnds(context, state, line, & begin, & end);
- /* lo_GetLineEnds returns the start of the next line for 'end' */
- if ( end ) {
- end = end->lo_any.prev;
- } else {
- /* Last line. We know that the last line only has one element. */
- end = begin;
- }
- /* Except for cases where the entire line is a line feed, don't select the end line-feed. */
- if ( begin->type != LO_LINEFEED && end->type == LO_LINEFEED ) {
- end = end->lo_any.prev;
- }
-
- if ( begin->type == LO_TABLE )
- {
- /* Search inside the table to find which cell/caption we hit */
- LO_Element* tptr;
- tptr = begin->lo_any.next;
- while ((tptr != NULL)&&(tptr->type == LO_CELL))
- {
- if ( tptr->lo_any.x <= x &&
- x < tptr->lo_any.x + tptr->lo_any.width &&
- tptr->lo_any.y <= y &&
- y < tptr->lo_any.y + tptr->lo_any.height ) {
- /* We hit this cell */
- /* lo_HitCellWideMatch(context, state, (LO_CellStruct*) tptr, x, y, requireCaret, result); */
- /* Replacing call to lo_HitCellWideMatch with lo_HitCell because the former function does
- not drill down into cell lists to find the closest document element to x,y. This fixes
- selection in tables. */
- lo_HitCell(context, state, (LO_CellStruct*) tptr, x, y, requireCaret, result);
- break;
- }
- tptr = tptr->lo_any.next;
- }
- return;
- }
-
- /*
- * Loop through the elements in the line and, if any are inflow
- * layers, go into them. We bail out if we've reached the end of
- * the line (or null for the last line).
- */
- for( tptr = begin; tptr && ((tptr != end) || (begin == end));
- tptr = tptr->lo_any.next) {
- if ( tptr->type == LO_CELL && tptr->lo_cell.cell_inflow_layer &&
- tptr->lo_any.x <= x &&
- x < tptr->lo_any.x + tptr->lo_any.width &&
- tptr->lo_any.y <= y &&
- y < tptr->lo_any.y + tptr->lo_any.height ) {
- lo_HitCell(context, state, (LO_CellStruct *)tptr,
- x, y, requireCaret, result);
- return;
- }
- }
-
- /* Make the end-points editable */
- if ( ! lo_EnsureEditableSearchNext(context, state, &begin) )
- continue;
- if ( ! lo_EnsureEditableSearchPrev(context, state, &end) )
- return;
- if ( begin && end && begin->lo_any.ele_id <= end->lo_any.ele_id )
- {
- result->type = LO_HIT_LINE;
- if ( x < begin->lo_any.x ) {
- result->lo_hitLine.region = LO_HIT_LINE_REGION_BEFORE;
- } else {
- result->lo_hitLine.region = LO_HIT_LINE_REGION_AFTER;
- }
- result->lo_hitLine.selection.begin.element = begin;
- result->lo_hitLine.selection.begin.position = 0;
- result->lo_hitLine.selection.end.element = end;
- if ( end->type == LO_LINEFEED )
- {
- result->lo_hitLine.selection.end.position = 0;
- }
- else
- {
- result->lo_hitLine.selection.end.position = lo_GetMaximumInsertPointPosition(end);
- }
- #if 0
- XP_TRACE(("b %d e %d\n", result->lo_hitLine.begin->lo_any.ele_id,
- result->lo_hitLine.end->lo_any.ele_id));
- #endif
- break;
- }
- }
- }
-
-
- #ifdef MQUOTE
- /* Returns true if the only elements between begin and end are bullets, not including endpoints. */
- Bool lo_OnlyBulletsBetween(LO_Element *begin,LO_Element *end)
- {
- /* Both begin and end should exist, and begin should be strictly before end. */
- if (!begin || !end || (begin->lo_any.ele_id >= end->lo_any.ele_id))
- {
- XP_ASSERT(FALSE);
- return FALSE;
- }
-
- /* We should never get into an infinite loop because we made sure begin was before end. */
- do {
- if (begin->lo_any.next == end)
- {
- return TRUE;
- }
-
- begin = begin->lo_any.next;
- } while (begin->type == LO_BULLET);
-
- /* Hit something that's not a bullet between begin and end. */
- return FALSE;
- }
- #endif
-
- void lo_HitLine2(MWContext *context, lo_DocState *state, LO_Element* element,
- int32 position, int32 x, LO_HitResult* result)
- {
- LO_Element* begin;
- LO_Element* end;
-
- result->type = LO_HIT_UNKNOWN;
-
- end = element;
-
- for(;;)
- {
- /* If this is a non-editable line feed, go backwards to the previous editable line. */
- while ( end && end->type == LO_LINEFEED
- && end->lo_linefeed.break_type == LO_LINEFEED_BREAK_SOFT)
- {
- end = end->lo_any.prev;
- }
- if ( ! end )
- return;
-
- /* Search forward to find the end of line */
- for ( ;
- end;
- end = end->lo_any.next)
- {
- if ( end->type == LO_LINEFEED ) break;
- }
-
- if ( ! end )
- {
- return;
- }
- /* Search backwards to find the beginning of the line. */
- for ( begin = end->lo_any.prev;
- begin;
- begin = begin->lo_any.prev)
- {
- if ( begin->type == LO_LINEFEED )
- {
- #ifdef MQUOTE
- /* We have the case of an editable linefeed on a line by itself when the only
- thing on the line are bullets. I.e. from a <mquote> tag. */
- if ( lo_OnlyBulletsBetween(begin,end) )
- #else
- if ( begin->lo_any.next == end )
- #endif
- {
- /* editable linefeed on a line by itself. */
- result->type = LO_HIT_LINE;
- if ( x < end->lo_any.x ) {
- result->lo_hitLine.region = LO_HIT_LINE_REGION_BEFORE;
- } else {
- result->lo_hitLine.region = LO_HIT_LINE_REGION_AFTER;
- }
- result->lo_hitLine.selection.begin.element = end;
- result->lo_hitLine.selection.begin.position = 0;
- result->lo_hitLine.selection.end = result->lo_hitLine.selection.begin;
- return;
- }
- begin = begin->lo_any.next;
- break;
- }
- if ( ! begin->lo_any.prev )
- break; /* Start of document */
- }
-
- if ( ! begin )
- {
- /* Must be a line-feed at the beginning of the document */
- begin = end;
- }
-
-
- /* Except for cases where the entire line is a line feed, don't select the end line-feed. */
- if ( begin->type != LO_LINEFEED && end->type == LO_LINEFEED ) {
- end = end->lo_any.prev;
- }
-
- if ( ( begin->type == LO_TABLE ) ||
- (begin->type == LO_CELL && begin->lo_cell.cell_inflow_layer) )
- {
- /* If this is a table or an inflow layer, give up.
- We don't understand them, yet. */
- return;
- }
-
- /* Make the end-points editable */
- if ( ! lo_EnsureEditableSearchNext(context, state, & begin) ) {
- /* This is the last, unselectable line of a document in edit mode.
- * Select the previous line.
- */
- if ( lo_EnsureEditableSearchPrev(context, state, & begin) ){
- lo_HitLine2(context, state, begin, 0, 0, result);
- }
- return;
- }
- if ( ! lo_EnsureEditableSearchPrev(context, state, & end) )
- return;
-
- if ( begin && end && begin->lo_any.ele_id <= end->lo_any.ele_id )
- {
- /* This is a good line */
- break;
- }
- /* At this point "end" points to the previous editable line. So try again. */
- }
-
- result->type = LO_HIT_LINE;
- if ( x < begin->lo_any.x ) {
- result->lo_hitLine.region = LO_HIT_LINE_REGION_BEFORE;
- } else {
- result->lo_hitLine.region = LO_HIT_LINE_REGION_AFTER;
- }
- result->lo_hitLine.selection.begin.element = begin;
- result->lo_hitLine.selection.begin.position = 0;
- result->lo_hitLine.selection.end.element = end;
- if ( end->type == LO_LINEFEED )
- {
- result->lo_hitLine.selection.end.position = 0;
- }
- else
- {
- result->lo_hitLine.selection.end.position = lo_GetMaximumInsertPointPosition(end);
- }
- }
-
-
- Bool
- lo_PositionIsOffEndOfLine(LO_HitElementResult* elementResult)
- {
- if ( elementResult->region == LO_HIT_ELEMENT_REGION_AFTER )
- {
- LO_Element* eptr = elementResult->position.element;
- int32 position = elementResult->position.position;
- if ( eptr && eptr->lo_any.next && eptr->lo_any.next->type == LO_LINEFEED )
- {
- return position >= lo_GetLastCharEndPosition(eptr);
- }
- }
- return FALSE;
- }
-
- /*
- * LO_Hit
- * Determines what semantic part of the layout was hit for a given x/y position.
- * This is intended to be called by cursor tracking, drag & drop, etc.
- *
- */
-
- void LO_Hit(MWContext *context, int32 x, int32 y, Bool requireCaret,
- LO_HitResult* result, CL_Layer *layer)
- {
- int32 doc_id;
- lo_TopState *top_state;
- lo_DocState *state;
- LO_Element *eptr;
- int32 position;
- int32 ret_x, ret_y;
- LO_CellStruct *layer_cell;
-
- #if 0
- lo_PrintLayout(context);
- #endif /* DEBUG */
-
- result->type = LO_HIT_UNKNOWN;
- /*
- * 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;
-
- layer_cell = lo_GetCellFromLayer(context, layer);
- if (layer_cell != NULL) {
- lo_HitCell(context,
- state,
- layer_cell,
- x, y, requireCaret, result);
- }
- else {
-
- /* Clip Y to the last line of the document */
- {
- LO_Position endOfDocument;
- int32 endY;
- LO_Element *last_eptr;
- last_eptr = LO_getFirstLastElement(context, FALSE);
- if (last_eptr == NULL)
- {
- return;
- }
- endY = last_eptr->lo_any.y
- + last_eptr->lo_any.y_offset
- + last_eptr->lo_any.height;
- if ( y >= endY ){
- y = endY - 1;
- }
- }
-
- /* Clip Y to the first line of the document */
- {
- LO_Position startOfDocument;
- int32 startY;
- LO_Element *first_eptr;
- first_eptr = LO_getFirstLastElement(context, TRUE);
- if (first_eptr == NULL)
- {
- return;
- }
- /* Curiously, tables have an offset which excludes their captions. So don't
- * add in the offset, or else you won't be able to select the caption of a
- * table at the start of a document.
- */
- startY = first_eptr->lo_any.y;
- if ( y < startY ){
- y = startY;
- }
- }
-
- /* Note: Setting the first Boolean to TRUE allows you to hit-select into floating elements,
- * which basicly means into floating tables. Unfortunately, floating element ids are not numbered
- * consecutively with respect to the main document flow, which means that selections that
- * cross from below the floating element into the floating element will compare and draw wrong.
- */
-
- position = 0;
- eptr = lo_XYToDocumentElement2(context, state, x, y, FALSE, TRUE, TRUE,
- TRUE, &ret_x, &ret_y);
-
- /* LO_DUMP_INSERT_POINT("lo_XYToDocumentElement2", eptr, 0); */
-
- if ( eptr ) {
- lo_FullHitElement(context, state, x, y, requireCaret, eptr, ret_x, ret_y, result);
- }
- else {
- lo_HitLine(context, state, x, y, requireCaret, result);
- }
-
- }
-
- #ifdef DEBUG
- {
- /* const char* kTypes[] = { "LO_HIT_UNKNOWN", "LO_HIT_LINE", "LO_HIT_ELEMENT"};*/
- XP_ASSERT ( result->type >= LO_HIT_UNKNOWN && result->type <= LO_HIT_ELEMENT );
- }
-
- #if 0
- /* Useful for debugging mouse selection, */
- LO_DUMP_HIT_RESULT(result);
- #endif
-
- #endif
- }
-
- void
- lo_SetInsertPoint(MWContext *context, lo_TopState *top_state, LO_Element* eptr, int32 position, CL_Layer *layer)
- {
- if ( EDT_IS_EDITOR(context) )
- {
- #ifdef EDITOR
- LO_ASSERT_POSITION2(context, eptr, position);
- if ( ! lo_IsValidEditableInsertPoint2(context, top_state->doc_state, eptr, position) )
- {
- XP_ASSERT(FALSE);
- }
- else
- {
- EDT_SetInsertPoint( top_state->edit_buffer,
- eptr->lo_any.edit_element,
- eptr->lo_any.edit_offset + position,
- position == 0);
- }
- #endif
- }
- LO_StartSelectionFromElement( context, eptr, position, layer );
- }
-
- /*
- * Like LO_PositionCaret, except it selects non-text items as well.
- * Returns TRUE if the result is a selection, FALSE if the result is
- * an insertion point. (The layout level
- * does not understand the concept of insertion point.)
- */
-
- Bool LO_Click(MWContext *context, int32 x, int32 y, Bool requireCaret,
- CL_Layer *layer)
- {
- int32 doc_id;
- lo_TopState *top_state;
- lo_DocState *state;
- LO_HitResult result;
-
- /*
- * 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 FALSE;
- }
- state = top_state->doc_state;
-
- LO_Hit(context, x, y, requireCaret, &result, layer);
-
- return lo_ProcessClick(context, top_state, state, &result, requireCaret,
- layer);
- }
-
- Bool lo_ProcessClick(MWContext *context, lo_TopState *top_state, lo_DocState *state, LO_HitResult* result, Bool requireCaret, CL_Layer *layer)
- {
- switch ( result->type )
- {
- case LO_HIT_LINE:
- {
- switch ( result->lo_hitLine.region )
- {
- case LO_HIT_LINE_REGION_BEFORE:
- {
- if ( requireCaret )
- /* Insertion point before first element of line */
- {
- LO_Element* eptr = result->lo_hitLine.selection.begin.element;
- int32 position = result->lo_hitLine.selection.begin.position;
- lo_SetInsertPoint(context, top_state, eptr, position, layer);
- return FALSE;
- }
- else
- {
- /* Select the line */
- lo_ExtendToIncludeHardBreak(context, state, & result->lo_hitLine.selection);
- lo_SetSelection(context, & result->lo_hitLine.selection, FALSE);
- return TRUE;
- }
- }
- break;
- case LO_HIT_LINE_REGION_AFTER:
- {
- /* Set the insertion point after the last element of line */
- LO_Element* eptr = result->lo_hitLine.selection.end.element;
- int32 position = result->lo_hitLine.selection.end.position;
- lo_EnsureEditableSearchPrev2(context, state, &eptr, &position);
- lo_SetInsertPoint(context, top_state, eptr,
- position, layer);
- return FALSE;
- }
- break;
- default:
- break;
- }
- }
- break;
- case LO_HIT_ELEMENT:
- {
- LO_Element* eptr = result->lo_hitElement.position.element;
- int32 position = result->lo_hitElement.position.position;
- switch ( result->lo_hitElement.region )
- {
- case LO_HIT_ELEMENT_REGION_BEFORE:
- {
- lo_SetInsertPoint(context, top_state, eptr, position,
- layer);
- return FALSE;
- }
- break;
- case LO_HIT_ELEMENT_REGION_MIDDLE:
- {
- /* Select the element */
- LO_StartSelectionFromElement( context, eptr, position,
- layer );
- lo_BumpEditablePositionForward(context, state, &eptr, &position);
- LO_ExtendSelectionFromElement( context, eptr, position, FALSE );
- LO_HighlightSelection(context, TRUE);
- return TRUE;
- }
- break;
- case LO_HIT_ELEMENT_REGION_AFTER:
- {
- lo_BumpEditablePositionForward(context, state,
- &eptr, &position);
- lo_SetInsertPoint(context, top_state, eptr,
- position, layer);
- return FALSE;
- }
- break;
- default:
- break;
- }
- }
- break;
- default:
- break;
- }
- return FALSE;
- }
-
- /* Shares much functionality with lo_ProcessClick
- * If any changes made in logic above, please check
- * this and make corresponding changes if relevant
- * Returns the element that we will drop at and position
- * within this element for text data
- */
- LO_Element * lo_PositionDropCaret(MWContext *pContext, int32 x, int32 y, int32 * pPosition)
- {
- LO_Element* eptr = NULL;
- #ifdef EDITOR
- lo_TopState *top_state = lo_FetchTopState(XP_DOCID(pContext));
- int32 position;
- LO_HitResult result;
- lo_DocState *state;
-
- if ((top_state == NULL)||(top_state->doc_state == NULL)) {
- return NULL;
- }
- state = top_state->doc_state;
-
- LO_Hit(pContext, x, y, FALSE /*requireCaret*/, &result, 0);
-
- /* This was copied from lo_ProcessClick above
- * We want to execute most of the same logic to locate the caret position
- * without calling lo_SetInsertPoint, which sets the regular caret
- * and is incompatable with a selection
- */
-
- switch ( result.type )
- {
- case LO_HIT_LINE:
- switch ( result.lo_hitLine.region )
- {
- case LO_HIT_LINE_REGION_BEFORE:
- /* Drop point before first element of line */
- eptr = result.lo_hitLine.selection.begin.element;
- position = result.lo_hitLine.selection.begin.position;
- break;
- case LO_HIT_LINE_REGION_AFTER:
- /* Set the drop point after the last element of line */
- eptr = result.lo_hitLine.selection.end.element;
- position = result.lo_hitLine.selection.end.position;
- lo_EnsureEditableSearchPrev2(pContext, state, &eptr, &position);
- break;
- default:
- break;
- }
- break;
- case LO_HIT_ELEMENT:
- eptr = result.lo_hitElement.position.element;
- position = result.lo_hitElement.position.position;
-
- /* Move to just past the element if after it */
- if( result.lo_hitElement.region == LO_HIT_ELEMENT_REGION_AFTER ){
- lo_BumpEditablePositionForward(pContext, state, &eptr, &position);
- }
- break;
- default:
- break;
- }
-
- if( eptr )
- {
- LO_ASSERT_POSITION2(pContext, eptr, position);
- if ( lo_IsValidEditableInsertPoint2(pContext, state, eptr, position) )
- {
- if( EDT_IsSelected(pContext) )
- {
- FE_DestroyCaret(pContext);
- }
-
- switch ( eptr->type )
- {
- case LO_TEXT:
- FE_DisplayTextCaret( pContext, FE_VIEW,
- &eptr->lo_text,
- (ED_CaretObjectPosition)position );
- break;
- case LO_IMAGE:
- FE_DisplayImageCaret( pContext,
- &eptr->lo_image,
- (ED_CaretObjectPosition)position );
- break;
- default:
- FE_DisplayGenericCaret( pContext,
- &eptr->lo_any,
- (ED_CaretObjectPosition)position );
- break;
- }
- }
- else
- {
- XP_ASSERT(FALSE);
- }
- }
- if( pPosition ){
- *pPosition = position;
- }
- #endif /* EDITOR */
- return eptr;
- }
-
-
- /* Returns TRUE if the result is an anchor. Also selects the entire anchor. */
- Bool lo_ProcessAnchorClick(MWContext *context, lo_TopState *top_state, lo_DocState *state, LO_HitResult* result)
- {
- switch ( result->type )
- {
- case LO_HIT_LINE:
- {
- return FALSE;
- }
- break;
- case LO_HIT_ELEMENT:
- {
- LO_Element* eptr = result->lo_hitElement.position.element;
- switch ( result->lo_hitElement.region )
- {
- case LO_HIT_ELEMENT_REGION_BEFORE:
- case LO_HIT_ELEMENT_REGION_AFTER:
- {
- return lo_SelectAnchor(context, state, eptr);
- }
- break;
- case LO_HIT_ELEMENT_REGION_MIDDLE:
- {
- return FALSE;
- }
- break;
- default:
- break;
- }
- }
- break;
- default:
- break;
- }
- return FALSE;
- }
-
- PRIVATE
- void
- lo_FindStartOfParagraph(MWContext* context, lo_DocState * state, LO_Position* where, LO_Position* paragraphStart)
- {
- /*
- * Search backwards to find the beginning of the paragraph. A paragraph starts
- * with the beginning of the document, or after a forced break.
- */
- LO_Element* element;
- if ( ! (where && where->element ) )
- {
- XP_ASSERT(FALSE);
- return;
- }
-
- element = where->element;
-
- for(;;)
- {
- LO_Element* prev = element->lo_any.prev;
- if ( ! prev ) break;
- if ( lo_IsEndOfParagraph2(context, prev, 0) )
- {
- break;
- }
- element = prev;
- }
-
- paragraphStart->element = element;
- paragraphStart->position = 0;
- lo_EnsureEditableSearchNext2(context, state, ¶graphStart->element, & paragraphStart->position);
- }
-
- PRIVATE
- void
- lo_FindEndOfParagraph(MWContext* context, lo_DocState * state, LO_Position* where, LO_Position* paragraphEnd)
- {
- /*
- * Search forward to find the end of the paragraph. A paragraph ends
- * at the end of the document, or before a forced break.
- */
- LO_Element* element;
- if ( ! (where && where->element ) )
- {
- XP_ASSERT(FALSE);
- return;
- }
-
- element = where->element;
- for(;;) {
- LO_Element* p = element->lo_any.next;
- if ( ! p ) break;
- element = p;
- if ( lo_IsEndOfParagraph2(context, element, 0) ) break;
- }
- paragraphEnd->element = element;
- paragraphEnd->position = lo_GetElementLength(element);
- if ( paragraphEnd->element->type == LO_LINEFEED )
- {
- paragraphEnd->position = 1;
- }
- /* lo_EnsureEditableSearchPrev2(context, state, ¶graphEnd->element, & paragraphEnd->position); */
- }
-
- PRIVATE
- void
- lo_FindParagraph(MWContext* context, lo_DocState *state, LO_Position* where, LO_Selection* paragraph)
- {
- lo_FindStartOfParagraph(context, state, where, & paragraph->begin);
- lo_FindEndOfParagraph(context, state, where, ¶graph->end);
- lo_ConvertInsertPointToSelectionEnd(context, state, ¶graph->end.element, ¶graph->end.position);
- LO_ASSERT_SELECTION(context, paragraph);
- }
-
- PRIVATE
- void
- lo_SelectParagraph(MWContext* context, lo_DocState *state, LO_Position* where)
- {
- LO_Selection selection;
- lo_FindParagraph(context, state, where, &selection);
- lo_SetSelection(context, &selection, FALSE);
- }
-
- PRIVATE
- intn pa_strcmp( PA_Block p1, PA_Block p2 )
- {
- char *s1, *s2;
- intn ret;
-
- PA_LOCK( s1, char*, p1 );
- PA_LOCK( s2, char*, p2 );
- ret = XP_STRCMP( s1, s2 );
- PA_UNLOCK( p1 );
- PA_UNLOCK( p2 );
- return ret;
- }
-
- PRIVATE
- Bool lo_AnchorsEqual( LO_AnchorData *p1, LO_AnchorData *p2 )
- {
- if( pa_strcmp( p1->anchor, p2->anchor ) != 0 )
- {
- return FALSE;
- }
-
- if( p1->target == 0 && p2->target == 0 )
- {
- return TRUE;
- }
-
- if( p1->target && p2->target && pa_strcmp( p1->target, p2->target ) == 0 )
- {
- return TRUE;
- }
-
- return FALSE;
- }
-
- PRIVATE
- Bool
- lo_FindAnchorEdge(MWContext* context, lo_DocState *state, LO_Position* where, LO_Position* result, Bool forward)
- {
- LO_Element* element = where->element;
- LO_AnchorData* goal = lo_GetAnchorData(element);
- if ( ! goal ) return FALSE;
- while(1) {
- LO_Element* next = lo_GetNeighbor(element, forward);
- LO_AnchorData* next_anchor;
- if ( next == 0
- || (next_anchor = lo_GetAnchorData(next)) == 0
- || !lo_AnchorsEqual( next_anchor,goal ))
- {
- break;
- }
- element = next;
- }
- /* LINEFEED elements can have anchor information. Backup.
- */
- while ( element && element->type == LO_LINEFEED ) {
- element = lo_GetNeighbor(element, !forward);
- }
- result->element = element;
- result->position = lo_GetElementEdge(element, forward);
- return TRUE;
- }
-
- PRIVATE
- Bool
- lo_FindAnchor(MWContext* context, lo_DocState *state, LO_Position* where, LO_Selection* anchor)
- {
- LO_Selection selection;
- Bool result = lo_FindAnchorEdge(context, state, where, & selection.begin, FALSE) &&
- lo_FindAnchorEdge(context, state, where, &selection.end, TRUE);
- if ( result ) {
- lo_ConvertInsertPointToSelectionEnd(context, state, &selection.end.element, &selection.end.position);
- LO_ASSERT_SELECTION(context, &selection);
- *anchor = selection;
- }
- return result;
- }
-
- Bool
- lo_SelectAnchor(MWContext* context, lo_DocState *state, LO_Element* eptr)
- {
- /* Select the entire anchor associated with eptr */
- Bool result = FALSE;
- LO_Selection selection;
- LO_Position where;
- where.element = eptr;
- where.position = 0;
- result = lo_FindAnchor(context, state, &where, &selection);
- if ( result ) {
- lo_SetSelection(context, &selection, FALSE);
- }
- return result;
- }
-
- /*
- * This should move to a public header so that the routine can be internationalized more easily.
- *
- * The word-finding algorithm assumes that characters can be divided into different classes.
- * The classes are: single, space, and grouping. There are currently only two grouping
- * classes, because that's enough for english text. Some languages (e.g. Japanese, which has
- * romanji, katakana and hirigana, might encourage us to define more grouping classes.)
-
- */
-
- #define LO_CC_SINGLE 0
- #define LO_CC_SPACE 1
- #define LO_CC_ALPHA 2
- #define LO_CC_PUNCT 3
- #define LO_CC_KANJI 4
- #define LO_CC_KANA 5
- #define LO_CC_OTHERS 6
-
-
- PRIVATE
- intn
- lo_CharacterClassOf(MWContext* context, lo_DocState *state, LO_Position* where)
- {
- LO_Position position = *where;
- if ( ! lo_NormalizeSelectionPoint(context, state, &position.element, &position.position) )
- {
- /* We've gone off the end of the world. */
- return LO_CC_SINGLE;
- }
- switch ( position.element->type )
- {
- case LO_LINEFEED:
- return LO_CC_SPACE;
- case LO_TEXT:
- {
- int16 charset;
- if ( position.element->lo_text.text_len <= 0 ) {
- return LO_CC_SPACE;
- }
- charset = position.element->lo_text.text_attr->charset;
- XP_ASSERT(position.position < position.element->lo_text.text_len);
- if (INTL_CharSetType(charset) == SINGLEBYTE)
- {
- /* Need to handle this on a character-set basis. */
- char *tptr;
- char c;
-
- /* To do: The lock should be outside of the inner loop. */
- PA_LOCK(tptr, char *, position.element->lo_text.text);
- if ( tptr )
- {
- c = tptr[position.position];
- }
- else
- {
- XP_ASSERT(FALSE);
- c = '\0';
- }
- PA_UNLOCK(where->element->lo_text.text);
- if ( XP_IS_SPACE(c) ) return LO_CC_SPACE;
- else if ( isalnum(c) || ((unsigned char)c > 0x7F)) return LO_CC_ALPHA;
- else return LO_CC_PUNCT;
- }
- else {
- /* Do something smarter here. */
- char *tptr;
- char c;
- intn iRet;
-
- /* To do: The lock should be outside of the inner loop. */
- PA_LOCK(tptr, char *, position.element->lo_text.text);
- if ( tptr == NULL )
- {
- XP_ASSERT(FALSE);
- c = '\0';
- iRet = LO_CC_SINGLE;
- }
- else
- {
- c = tptr[position.position];
- /* Don't know how much detail we want to do for multibyte,
- right now it divide into pronounce character and kanji character
- */
- switch (INTL_CharClass(charset, (unsigned char *)(tptr+position.position)))
- {
- case SEVEN_BIT_CHAR:
- if ( XP_IS_SPACE(c) ) iRet = LO_CC_SPACE;
- else if ( isalnum(c) ) iRet = LO_CC_ALPHA;
- else iRet = LO_CC_PUNCT;
- break;
- case HALFWIDTH_PRONOUNCE_CHAR:
- case FULLWIDTH_PRONOUNCE_CHAR:
- iRet = LO_CC_KANA;
- break;
- case FULLWIDTH_ASCII_CHAR:
- iRet = LO_CC_ALPHA;
- break;
- case KANJI_CHAR:
- iRet = LO_CC_KANJI;
- break;
- case UNCLASSIFIED_CHAR:
- iRet = LO_CC_OTHERS;
- break;
- default:
- iRet = LO_CC_PUNCT;
- }
- }
-
- PA_UNLOCK(where->element->lo_text.text);
-
- return iRet;
-
- }
- }
-
- default:
- return LO_CC_SINGLE;
- }
- }
-
- Bool
- lo_IsEdgeOfDocument2(MWContext* context, lo_DocState *state, LO_Element* element, int32 position, Bool forward)
- {
- /* If we can bump the pointer, then we're not at the end of the document. */
- if ( forward )
- {
- LO_Element* next;
- if ( element->type != LO_LINEFEED
- && position < lo_GetElementLength(element) )
- return FALSE;
- next = lo_BoundaryJumpingNext(context, state, element);
- if ( next == NULL ||
- lo_BoundaryJumpingNext(context, state, next) == NULL )
- return TRUE;
- }
- else
- {
- if ( position > 0 )
- return FALSE;
- }
-
- {
- return ! lo_BumpEditablePosition(context, state, &element, &position, forward);
- }
- }
-
- Bool
- lo_IsEdgeOfDocument(MWContext* context, lo_DocState *state, LO_Position* where, Bool forward)
- {
- return lo_IsEdgeOfDocument2(context, state, where->element, where->position, forward);
- }
-
- PRIVATE
- Bool
- lo_TraverseElement(MWContext* context, lo_DocState *state, LO_Element** element, Bool forward)
- {
- if ( ! element || ! *element ) return FALSE;
- if ( forward )
- {
- *element = lo_BoundaryJumpingNext(context, state, *element);
- }
- else
- {
- *element = lo_BoundaryJumpingPrev(context, state, *element);
- }
- return *element != NULL;
- }
-
- /* Stop for linefeeds */
- PRIVATE
- Bool
- lo_IsLineEdge(MWContext* context, lo_DocState *state, LO_Element* element, int32 position,
- Bool skipSoftBreaks, Bool forward)
- {
- LO_Element* next = element;
- int32 nextPosition = position;
- if ( ! lo_BumpEditablePosition(context, state, &next, &nextPosition, forward) )
- {
- /* Hit end of document. That counts as a linefeed. */
- return TRUE;
- }
-
- /* Increment element towards next, looking for a linefeed */
-
- if ( next == element )
- {
- return element->type == LO_LINEFEED;
- }
-
- if ( next->type == LO_LINEFEED )
- {
- return TRUE;
- }
-
- if ( ! lo_TraverseElement(context, state, &element, forward) ) return TRUE;
- while ( next != element )
- {
- if ( element->type == LO_LINEFEED &&
- (forward ? position <= 0 : position > 0)
- && ! lo_ValidEditableElement(context, element) )
- {
- Bool hardBreak = element->lo_linefeed.break_type != LO_LINEFEED_BREAK_SOFT;
- if ( !skipSoftBreaks || hardBreak )
- return TRUE;
- }
-
- /* Go on to next element */
- if ( ! lo_TraverseElement(context, state, &element, forward) ) return TRUE;
- }
-
- /* Didn't find a linefeed yet. */
- return FALSE;
- }
-
-
- /* As long as there's room and where is of the target class, move forward.
- * returns with where not equal to the character class, unless edge of line or document.
- * returns TRUE if not edge of document, else false.
- */
-
- #define LO_SO_DOCUMENT_EDGE 0
- #define LO_SO_LINEFEED 1
- #define LO_SO_NEW_CHARACTER_CLASS 2
-
- PRIVATE
- intn
- lo_SkipOver(MWContext* context, lo_DocState *state, LO_Position* where, intn targetCharacterClass, Bool forward)
- {
- while ( lo_CharacterClassOf(context, state, where) == targetCharacterClass )
- {
- /* Stop for linefeeds */
- if ( lo_IsLineEdge(context, state, where->element, where->position,
- targetCharacterClass == LO_CC_SPACE, forward) )
- {
- /* Skip to the next character */
- return LO_SO_LINEFEED;
- }
- if ( ! lo_BumpEditablePosition(context, state, &where->element, &where->position, forward) ) return LO_SO_DOCUMENT_EDGE;
- }
- return LO_SO_NEW_CHARACTER_CLASS;
- }
-
- PRIVATE
- Bool
- lo_MoveToNearestEdgeOfNextLine(MWContext* context, lo_DocState *state, LO_Position* where, Bool forward)
- {
- LO_Position caret = *where;
- Bool bHardBreak;
- while ( ! lo_IsLineEdge(context, state, caret.element, caret.position, FALSE, forward) )
- {
- if (! lo_BumpEditablePosition ( context, state, &caret.element, &caret.position, forward ) )
- return FALSE;
- }
- bHardBreak = caret.element && caret.element->type == LO_LINEFEED
- && caret.element->lo_linefeed.break_type == LO_LINEFEED_BREAK_HARD;
-
- if ( bHardBreak && ! forward && caret.position == 1 )
- {
- /* This is the after-a-hard-break-and-before-a-paragraph-end case. */
- caret.position = 0;
- }
- else if ( bHardBreak && forward && caret.position == 0 && caret.element->lo_any.next
- && caret.element->lo_any.next->type == LO_LINEFEED
- && caret.element->lo_any.next->lo_linefeed.break_type == LO_LINEFEED_BREAK_PARAGRAPH )
- {
- /* This is the before-a-hard-break-before-a-paragraph-end case. */
- caret.position = 1;
- }
- else
- {
- do {
- lo_TraverseElement(context, state, &caret.element, forward);
- #ifdef MQUOTE
- } while ( caret.element &&
- ((caret.element->type == LO_LINEFEED &&
- caret.element->lo_linefeed.break_type == LO_LINEFEED_BREAK_SOFT) ||
- caret.element->type == LO_BULLET));
- /* Added code above to skip over leading bullets resulting from <mquote> */
- #else
- } while ( caret.element && caret.element->type == LO_LINEFEED
- && caret.element->lo_linefeed.break_type == LO_LINEFEED_BREAK_SOFT);
- #endif
-
-
- if ( ! caret.element )
- return FALSE;
- caret.position = 0;
- if ( ! forward && caret.element ){
- if ( ! (caret.element->type == LO_LINEFEED
- && caret.element->lo_linefeed.break_type == LO_LINEFEED_BREAK_HARD ) ){
- caret.position = lo_GetMaximumInsertPointPosition(caret.element);
- }
- }
- {
- /* Bump by one position if crossing from one line of wrapped text to another.
- * This is because the editor can't represent the difference between one edge
- * and the next for wrapped text. *sigh*
- */
- if ( caret.element && where->element
- && caret.element->type == LO_TEXT && where->element->type == LO_TEXT
- && caret.element->lo_any.edit_element == where->element->lo_any.edit_element )
- {
- if (! lo_BumpEditablePosition ( context, state, &caret.element, &caret.position, forward ) )
- return FALSE;
- }
- }
- }
- /* Skip over junk at edge of line. (For example, bullets.) */
- if ( ! lo_EnsureEditableSearch2(context, state, &caret.element, &caret.position, forward) )
- {
- /* Either the end of the document, or the end of a cell. */
- #if 0
- if (! lo_BumpEditablePosition ( context, state, &caret.element, &caret.position, forward ) )
- return FALSE;
- #endif
- return FALSE;
- }
- XP_ASSERT(caret.element != NULL);
- *where = caret;
- return TRUE;
- }
-
- /* Return TRUE if we actually found an edge. FALSE if we're off the start or end of the document. */
-
- PRIVATE
- Bool
- lo_FindEdgeOfWord(MWContext* context, lo_DocState *state, LO_Position* where, LO_Position* wordEdge,
- Bool bSelect, Bool forward, Bool bIncludeSpacesAtEndOfWord)
- {
- /*
- * Search to find the edge of the word.
- * word = { single_word | alpha_word | punct_word | space_word }
- * single_word = LO_CC_SINGLE (e.g. chinese character for the season "autumn".)
- * alpha_word = LO_CC_ALPHA + LO_CC_SPACE * (e.g. "foo10 ")
- * punct_word = LO_CC_PUNCT + LO_CC_SPACE * (e.g. "---() ")
- * space_word = LO_CC_SPACE + (e.g. " ")
- */
-
- intn characterClass;
- *wordEdge = *where;
-
- /* Check for end of document */
-
- if ( lo_IsEdgeOfDocument(context, state, wordEdge, forward) )
- {
- lo_FindDocumentEdge(context, state, wordEdge, bSelect, forward);
- return TRUE;
- }
-
- if ( lo_IsLineEdge(context, state, where->element, where->position, FALSE, forward) )
- {
- return TRUE;
- }
-
- if ( !forward && lo_IsLineEdge(context, state, where->element, where->position, FALSE, !forward) )
- {
- /* Starting at end of line. Move backward. */
- lo_BumpEditablePosition(context, state, &wordEdge->element, &wordEdge->position, forward);
- }
-
- characterClass = lo_CharacterClassOf(context, state, wordEdge);
- if ( characterClass == LO_CC_SINGLE ) {
- if ( forward )
- {
- lo_BumpEditablePositionForward(context, state, &wordEdge->element, &wordEdge->position);
- }
- }
- else {
- /* Match more of this class */
- if ( ! forward )
- {
- /* Skip over spaces */
- if ( lo_SkipOver(context, state, wordEdge, LO_CC_SPACE, forward ) != LO_SO_NEW_CHARACTER_CLASS )
- {
- goto exit;
- }
- characterClass = lo_CharacterClassOf(context, state, wordEdge);
- }
- if ( characterClass != LO_CC_SPACE )
- {
- if ( lo_SkipOver(context, state, wordEdge, characterClass, forward) != LO_SO_NEW_CHARACTER_CLASS )
- goto exit;
- }
- if ( forward )
- {
- if ( bIncludeSpacesAtEndOfWord )
- {
- /* Skip over spaces */
- if ( lo_SkipOver(context, state, wordEdge, LO_CC_SPACE, forward ) != LO_SO_NEW_CHARACTER_CLASS )
- goto exit;
- }
- }
- else
- {
- /* If we didn't hit the edge of the document, we are now one element further than we want to be. So back up one position. */
- lo_BumpEditablePositionForward(context, state, &wordEdge->element, &wordEdge->position);
- }
- }
-
- exit:
- /* The edge might be a line feed. If so, move to an editable position. */
- lo_EnsureEditableSearch(context, state, wordEdge, forward);
- return TRUE;
- }
-
- PRIVATE
- Bool
- lo_IsEmptyText(LO_Element* eptr)
- {
- if ( eptr && eptr->type == LO_TEXT &&
- lo_GetElementLength(eptr) == 0 )
- {
- return TRUE;
- }
- return FALSE;
- }
-
- PRIVATE
- Bool
- lo_FindLineEdge(MWContext* context, lo_DocState *state, LO_Position* where, LO_Position* lineEdge, Bool select, Bool forward)
- {
- /* Special case for hard breaks at end of paragraph */
-
- Bool bHardBreakAtEndOfParagraph;
-
- if ( lo_IsEdgeOfDocument(context, state, lineEdge, forward) )
- {
- lo_FindDocumentEdge(context, state, lineEdge, select, forward);
- return TRUE;
- }
-
- bHardBreakAtEndOfParagraph = lo_IsHardBreak2(context, where->element, 0)
- || (lo_IsEmptyText(where->element) && lo_IsHardBreak2(context, where->element->lo_any.next, 0));
-
- if ( bHardBreakAtEndOfParagraph && forward) {
- /* "where" is the correct edge */
- *lineEdge = *where;
- }
- else {
- LO_HitResult result;
- lo_HitLine2(context, state, where->element, where->position, where->element->lo_any.x, &result);
- if ( result.type != LO_HIT_LINE )
- {
- XP_ASSERT(FALSE);
- return FALSE;
- }
-
- if ( forward )
- {
- if ( select )
- {
- lo_ExtendToIncludeHardBreak(context, state, & result.lo_hitLine.selection);
- }
- *lineEdge = result.lo_hitLine.selection.end;
- lo_ConvertSelectionEndToInsertPoint(context, state, &lineEdge->element, &lineEdge->position);
- }
- else
- {
- *lineEdge = result.lo_hitLine.selection.begin;
- }
- }
- return TRUE;
- }
-
- Bool
- lo_FindDocumentEdge(MWContext* context, lo_DocState *state, LO_Position* edge, Bool select, Bool forward)
- {
- LO_Element **array;
- LO_Element* eptr = NULL;
- #ifdef XP_WIN16
- XP_Block *larray_array;
- #endif /* XP_WIN16 */
- /*
- * Nothing to select.
- */
- if (state->line_num <= 1)
- {
- return FALSE;
- }
-
- if ( forward )
- {
- /*
- * Here we deal with the case where the current selection is within
- * a layer. In that case, we need to find the edge of the layer.
- */
- if (state->selection_layer) {
- LO_CellStruct *layer_cell = lo_GetCellFromLayer(context,
- state->selection_layer);
-
- if (layer_cell)
- eptr = layer_cell->cell_list_end;
- }
-
- if (!eptr)
- /*
- * Get last element in doc.
- */
- eptr = state->end_last_line;
- if (eptr == NULL)
- {
- eptr = state->selection_start;
- }
-
- /*
- * Since the last element is always a
- * linefeed, it is safe to set selection_end_pos to 0.
- */
- edge->element = eptr;
- edge->position = 0;
- if ( EDT_IS_EDITOR(context) ) {
- /* Skip over end-of-document elements. */
- int32 position = 0;
- while ( eptr && eptr->type == LO_LINEFEED
- && eptr->lo_linefeed.break_type == LO_LINEFEED_BREAK_PARAGRAPH ) {
- if ( ! lo_BumpEditablePositionBackward(context, state, &eptr, &position) ) break;
- }
- edge->element = eptr;
- edge->position = position;
- /* Skip forward to end of paragraph. */
- while ( eptr && ! lo_IsEndOfParagraph2(context, eptr,0) ) {
- eptr = eptr->lo_any.next;
- }
- if ( eptr ) {
- edge->element = eptr;
- edge->position = select ? 1 : 0;
- }
- else {
- XP_ASSERT(FALSE);
- }
- }
-
- }
- else
- {
- /*
- * Here we deal with the case where the current selection is within
- * a layer. In that case, we need to find the edge of the layer.
- */
- if (state->selection_layer) {
- LO_CellStruct *layer_cell = lo_GetCellFromLayer(context,
- state->selection_layer);
-
- if (layer_cell)
- eptr = layer_cell->cell_list;
- }
-
- if (!eptr)
- {
- /*
- * 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);
- }
-
- /*
- * No elements.
- */
- if (eptr == NULL)
- {
- return FALSE;
- }
-
- edge->element = eptr;
- edge->position = 0;
- }
- /* Backtrack to ensure editability. */
- lo_EnsureEditableSearch(context, state, edge, !forward);
- /* However, for selecting forward, we need to go over the edge. */
- if ( select && forward ) {
- if ( edge->element->lo_any.next && edge->element->lo_any.next->type == LO_LINEFEED) {
- edge->element = edge->element->lo_any.next;
- edge->position = 1;
- }
- }
- return TRUE;
- }
-
- Bool
- lo_FindWord(MWContext* context, lo_DocState *state, LO_Position* where, LO_Selection* word)
- {
- lo_FindEdgeOfWord(context, state, where, &word->begin, TRUE, FALSE, TRUE);
- LO_ASSERT_POSITION(context, &word->begin);
- lo_FindEdgeOfWord(context, state, where, &word->end, TRUE, TRUE, FALSE);
- if ( word->begin.element == word->end.element && word->begin.position == word->end.position ) {
- /* For some reason the result is an insert point. Perhaps there are no words in
- * this document.
- */
- return FALSE;
- }
- lo_ConvertInsertPointToSelectionEnd(context, state, &word->end.element, &word->end.position);
- LO_ASSERT_SELECTION(context, word);
- return TRUE;
- }
-
- PRIVATE
- void
- lo_SelectWord(MWContext* context, lo_DocState *state, LO_Position* where)
- {
- LO_Selection selection;
- Bool success = lo_FindWord(context, state, where, &selection);
- if ( success ) {
- lo_SetSelection(context, &selection, FALSE);
- }
- else {
- /* No word available. */
- lo_SelectParagraph(context, state, where );
- }
- }
-
- Bool lo_ProcessDoubleClick(MWContext *context, lo_TopState *top_state, lo_DocState *state, LO_HitResult* result, CL_Layer *layer)
- {
- switch ( result->type )
- {
- case LO_HIT_LINE:
- {
- switch ( result->lo_hitLine.region )
- {
- case LO_HIT_LINE_REGION_BEFORE:
- {
- lo_SelectParagraph(context, state, & result->lo_hitLine.selection.begin );
- return TRUE;
- }
- break;
- case LO_HIT_LINE_REGION_AFTER:
- {
- LO_Selection selection = result->lo_hitLine.selection;
- /* If they double clicked after the end of the document, back them up. */
- lo_EnsureEditableSearchPrev2(context, state, &selection.end.element, &selection.end.position);
- if ( lo_ExtendToIncludeHardBreak(context, state, &selection) )
- {
- /* Select the paragraph end */
- selection.begin = selection.end;
- lo_SetSelection(context, &selection, FALSE);
- }
- else
- {
- lo_SelectWord(context, state, &selection.end );
- }
-
- return FALSE;
- }
- break;
- default:
- break;
- }
- }
- break;
- case LO_HIT_ELEMENT:
- {
- switch ( result->lo_hitElement.region )
- {
- case LO_HIT_ELEMENT_REGION_BEFORE:
- case LO_HIT_ELEMENT_REGION_AFTER:
- {
- if ( ! lo_SelectAnchor(context, state, result->lo_hitElement.position.element ) )
- lo_SelectWord(context, state, &result->lo_hitElement.position );
- return FALSE;
- }
- break;
- case LO_HIT_ELEMENT_REGION_MIDDLE:
- {
- /* To do: Open editor on image. For now, act like click */
- return lo_ProcessClick( context, top_state, state, result, FALSE, layer);
- }
- break;
- default:
- break;
- }
- }
- break;
- default:
- break;
- }
- return FALSE;
- }
-
- PRIVATE
- Bool lo_FindCharacterEdge(MWContext* context, lo_DocState *state, LO_Position* where, LO_Position* edge, Bool bSelect, Bool forward)
- {
- *edge = *where;
- if ( forward )
- {
- if ( lo_IsEdgeOfDocument(context, state, edge, forward) )
- {
- lo_FindDocumentEdge(context, state, edge, bSelect, forward);
- return where->element != edge->element && where->position != edge->position;
- }
- return lo_BumpEditablePosition(context, state, &edge->element, &edge->position, forward);
- }
- else
- {
- /* We're already at the edge of the character. */
- return TRUE;
- }
- }
-
- PRIVATE
- Bool
- lo_FindChunkEdge(MWContext* context, lo_DocState *state, LO_Position* where, LO_Position* wordEdge, intn chunkType, Bool select, Bool forward)
- {
- Bool result;
- *wordEdge = *where;
- switch ( chunkType )
- {
- case LO_NA_CHARACTER:
- result = lo_FindCharacterEdge(context, state, where, wordEdge, select, forward);
- break;
- case LO_NA_WORD:
- result = lo_FindEdgeOfWord(context, state, where, wordEdge, select, forward, TRUE);
- break;
- case LO_NA_LINEEDGE:
- result = lo_FindLineEdge(context, state, where, wordEdge, select, forward);
- break;
- case LO_NA_DOCUMENT:
- /* Doesn't care where we start. */
- result = lo_FindDocumentEdge(context, state, wordEdge, select, forward);
- break;
- default:
- XP_ASSERT(FALSE);
- return FALSE;
- }
- /* Did we go off the end of the world? */
- /* if ( result && ! lo_EnsureEditableSearch(context, state, wordEdge, forward) ) result = FALSE; */
- return result;
- }
-
- PRIVATE
- Bool
- lo_GapWithBothSidesAllowed( MWContext *context, lo_DocState *state, LO_Position* caret)
- {
- LO_Position prev = *caret;
- LO_Position next = *caret;
- Bool result = FALSE;
- if ( caret->position == 0 )
- {
- result = lo_BumpEditablePosition(context, state, &prev.element, &prev.position, FALSE);
- }
- else if ( caret->position == lo_GetElementLength(caret->element) )
- {
- result = lo_BumpEditablePosition(context, state, &next.element, &next.position, TRUE);
- }
- if ( ! result )
- return FALSE;
-
- /* OK, we're in the gap. */
- if ( prev.element->type == LO_HRULE
- || next.element->type == LO_HRULE )
- {
- /* It's a gap with hrules. Now, if there isn't a hard break, we're all set. */
- LO_Element* element;
- for ( element = prev.element;
- element && element != next.element;
- element = element->lo_any.next)
- {
- if ( lo_IsHardBreak2(context, element, 0) )
- {
- return FALSE;
- }
- }
- return TRUE;
- }
- return FALSE;
- }
-
- /* For logical navigation (e.g. next / prev word, character, paragraph) this function
- * performs all the book keeping to implement extension vs. moving the cursor.
- *
- * target - the unit of navigation -- the word / character that is selected.
- * bSelect - true if the current selection is to be extended.
- * bDeselecting - true if there used to be a selection, but it's going away.
- * bForward - true if the end of the target is to be used.
- *
- */
-
- /* Compute new position
- */
-
- Bool
- LO_ComputeNewPosition( MWContext *context, intn chunkType,
- Bool bSelect, Bool bDeselecting, Bool bForward,
- LO_Element** pElement, int32* pPosition )
- {
- lo_TopState* top_state;
- lo_DocState *state;
- LO_Position wordEdge;
- LO_Position caret;
- Bool result = TRUE;
-
- /* LO_DUMP_SELECTIONSTATE(context); */
-
- /*
- * Get the unique document ID, and retreive this
- * documents layout state.
- */
- top_state = lo_FetchTopState(XP_DOCID(context));
- if ((top_state == NULL)||(top_state->doc_state == NULL))
- {
- return FALSE;
- }
- state = top_state->doc_state;
-
- if ( ! pElement || ! pPosition )
- return FALSE;
-
- caret.element = *pElement;
- caret.position = *pPosition;
- wordEdge = caret;
-
- switch ( chunkType )
- {
- case LO_NA_WORD:
- {
- Bool isLineEdge = lo_IsLineEdge(context, state, caret.element, caret.position, FALSE, bForward);
- Bool isHardBreak = lo_IsLineEdge(context, state, caret.element, caret.position, TRUE, bForward);
- if ( isLineEdge && bDeselecting ) break;
-
- if ( lo_IsEdgeOfDocument(context, state, &caret, bForward) )
- {
- lo_FindDocumentEdge(context, state, &caret, bSelect, bForward);
- wordEdge = caret;
- }
- else
- {
- if ( isLineEdge )
- {
- Bool bOverEdge = caret.element->type == LO_LINEFEED && caret.position > 0;
- lo_MoveToNearestEdgeOfNextLine(context, state, &caret, bForward);
- wordEdge = caret;
- if ( isHardBreak )
- {
- if ( bSelect && bOverEdge && bForward )
- {
- /* If the next line is a single line feed. */
- if ( lo_GetElementLength(caret.element) == 0 )
- {
- lo_BumpEditablePosition(context, state, &caret.element, &caret.position, bForward);
- wordEdge = caret;
- }
- else
- {
- lo_BumpEditablePosition(context, state, &caret.element, &caret.position, bForward);
- lo_FindChunkEdge(context, state, &caret, &wordEdge, chunkType, bSelect, bForward);
- }
- }
- }
- else
- {
- lo_FindChunkEdge(context, state, &caret, &wordEdge, chunkType, bSelect, bForward);
- }
- }
- else {
- if ( isLineEdge && ! bSelect )
- {
- if ( isHardBreak )
- {
- wordEdge = caret;
- }
- else
- {
- lo_FindChunkEdge(context, state, &caret, &wordEdge, chunkType, bSelect, bForward);
- }
- }
- else
- {
- /* If we're at the beginning of a word, and we are searching backwards,
- * we want the word before the word we're currently on.
- */
- if ( ! bDeselecting && !isLineEdge && !bForward )
- {
- lo_BumpEditablePosition(context, state, &caret.element, &caret.position, bForward);
- }
- lo_FindChunkEdge(context, state, &caret, &wordEdge, chunkType, bSelect, bForward);
- }
- }
- }
- }
- break;
- case LO_NA_CHARACTER:
- {
- Bool isLineEdge;
- /* Special rule: If navigating by unselected character, and there is a selection, then the
- * result is the edge in the direction of travel.
- */
- if ( bDeselecting )
- {
- break;
- }
-
- isLineEdge = lo_IsLineEdge(context, state, caret.element, caret.position, FALSE, bForward);
- if ( isLineEdge )
- {
- Bool bDoubleGap = lo_GapWithBothSidesAllowed(context, state, &caret);
- result = lo_MoveToNearestEdgeOfNextLine(context, state, &caret, bForward);
- if ( ! result && bSelect && bForward )
- {
- /* End of document case. Allowed to select the paragraph. */
- lo_FindDocumentEdge(context, state, &caret, TRUE, TRUE);
- result = TRUE;
- }
- else if ( ! result )
- {
- return FALSE;
- }
- else
- {
- /* If we are shift-selecting an hrule, bump an extra position,
- because the pseudo-position before and after an hrule doesn't
- really exist. */
- if ( result && bSelect && bDoubleGap )
- {
- result = lo_BumpEditablePosition(context, state, &caret.element, &caret.position, bForward);
- if ( ! result )
- {
- /* End of document */
- lo_FindDocumentEdge(context, state, &caret, bSelect, bForward);
- result = TRUE;
- }
- }
- }
-
- result = TRUE; /* Bogus -- should remove. */
- wordEdge = caret;
- }
- else
- {
- /* If we are searching backwards,
- * we want the character before the character we're currently on.
- */
- result = TRUE;
- if ( !bForward )
- {
- result = lo_BumpEditablePosition(context, state, &caret.element, &caret.position, bForward);
- }
- if ( result )
- {
- result = lo_FindChunkEdge(context, state, &caret, &wordEdge, chunkType, bSelect, bForward);
- }
- }
- }
- break;
-
- default:
- {
- result = lo_FindChunkEdge(context, state, &caret, &wordEdge, chunkType, bSelect, bForward);
- }
- break;
- }
-
- result = result && (bDeselecting || wordEdge.element != *pElement || wordEdge.position != *pPosition);
- if ( result ){
- *pElement = wordEdge.element;
- *pPosition = wordEdge.position;
- }
- return result;
- }
-
- /*
- * Find the next or previous word and possibly extend the selection.
- */
- Bool
- LO_NavigateChunk( MWContext *context, intn chunkType, Bool bSelect, Bool bForward )
- {
- lo_TopState* top_state;
- lo_DocState *state;
- LO_Position caret;
- LO_Element *element;
- int32 position;
- Bool bSelectionExists;
- Bool bDeselecting;
- Bool bResult;
-
- /* LO_DUMP_SELECTIONSTATE(context); */
-
- /*
- * Get the unique document ID, and retreive this
- * documents layout state.
- */
- top_state = lo_FetchTopState(XP_DOCID(context));
- if ((top_state == NULL)||(top_state->doc_state == NULL))
- {
- return FALSE;
- }
- state = top_state->doc_state;
-
- bSelectionExists = state->selection_new == NULL;
- bDeselecting = bSelectionExists && ! bSelect;
- if ( bSelectionExists )
- {
- /* There is no insert point. Use the extension point. */
- if ( bSelect )
- {
- lo_GetExtensionPoint(context, state, &element, &position);
- }
- else
- {
- lo_GetSelectionEdge(context, state, &element, &position, bForward);
- }
- }
- else
- {
- element = state->selection_new;
- position = state->selection_new_pos;
- }
-
- bResult = LO_ComputeNewPosition(context, chunkType, bSelect, bDeselecting, bForward, &element, &position);
- if ( bResult ) {
- caret.element = element;
- caret.position = position;
- if ( bSelect )
- {
- lo_ExtendSelectionToPosition2(context, top_state, state, caret.element, caret.position);
- }
- else
- {
- if ( ! lo_EnsureEditableSearch(context, state, &caret, bForward) ) {
- /* Off the edge of the world. */
- lo_EnsureEditableSearch(context, state, &caret, !bForward);
- }
- lo_SetInsertPoint(context, top_state, caret.element, caret.position, state->selection_layer);
- }
- }
-
- return bResult;
- }
-
- void LO_GetEffectiveCoordinates( MWContext *pContext, LO_Element *pElement, int32 position,
- int32* pX, int32* pY, int32* pWidth, int32* pHeight )
- {
- *pY = pElement->lo_any.y + pElement->lo_any.y_offset;
- *pX = pElement->lo_any.x + pElement->lo_any.x_offset;
- *pWidth = pElement->lo_any.width;
- *pHeight = pElement->lo_any.height;
- switch ( pElement->type )
- {
- case LO_IMAGE:
- *pWidth += 2 * pElement->lo_image.border_width;
- *pHeight += 2 * pElement->lo_image.border_width;
- if ( position > 0 ) {
- *pX = *pX + *pWidth;
- }
- break;
- case LO_TEXT:
- {
- int32 start = LO_TextElementWidth( pContext, (LO_TextStruct*)pElement,
- position);
- *pX += start;
- }
- break;
- case LO_HRULE:
- /* The effective y and height is the height of the following linefeed. */
- {
- LO_Element* pNext = pElement->lo_any.next;
- if ( pNext && pNext->type == LO_LINEFEED )
- {
- int32 dummyWidth;
- int32 dummyX;
- LO_GetEffectiveCoordinates(pContext, pNext, 0, &dummyX, pY, &dummyWidth, pHeight);
- }
- if ( position > 0 ) {
- *pX = *pX + *pWidth;
- }
- }
- break;
- case LO_LINEFEED:
- {
- if ( position > 0 )
- {
- /* At start of next element. */
- LO_Element* pNext = pElement->lo_any.next;
- if ( pNext )
- { /* Just one level of recursion, since position = 0 */
- LO_GetEffectiveCoordinates(pContext, pNext, 0, pX, pY, pWidth, pHeight);
- }
- }
- }
- break;
- default:
- break;
- }
- }
-
- PRIVATE
- Bool lo_FindClosestUpDown(MWContext *pContext, lo_DocState* state, int32 x, int32 y,
- Bool bForward, int32* ret_x, int32* ret_y)
- {
- int32 iLine;
- Bool bFound = FALSE;
- lo_TopState* top_state = lo_FetchTopState(XP_DOCID(pContext));
- if ((top_state == NULL)||(top_state->doc_state == NULL))
- {
- return FALSE;
- }
-
- /* find the line we are currently on */
- iLine = lo_PointToLine( pContext, state, x, y );
-
- if ( bForward )
- {
- int32 maxLine;
- /* Down. MaxLine is - 2 for the lines data structure */
- maxLine = state->line_num - 2;
- if ( EDT_IS_EDITOR(pContext) && top_state->doc_state == state )
- {
- maxLine -= 2; /* and -2 for the phantom end-of-doc hrule*/
- }
- for( ;
- iLine <= maxLine && !bFound;
- iLine++)
- {
- bFound = lo_FindBestPositionOnLine( pContext, state, iLine, x, y, bForward, ret_x, ret_y );
- }
- }
- else
- {
- /* Up */ /*changed iline comparisson to include 0. is this correct? uparrow was not working*/
- for( ;
- iLine >= 0 && !bFound;
- --iLine)
- {
- bFound = lo_FindBestPositionOnLine( pContext, state, iLine, x, y, bForward, ret_x, ret_y );
- }
- }
- return bFound;
- }
-
-
- /*
- * Find a element up from this and set the cursor there.
- */
- void LO_UpDown( MWContext *pContext, LO_Element *pElement, int32 position, int32 iDesiredX, Bool bSelect, Bool bForward )
- {
- #ifdef EDITOR
- lo_TopState* top_state;
- lo_DocState *state;
- int32 x, y, width, height;
- Bool bFound = FALSE;
- LO_Element *pNext;/*used to look ahead for correct y*/
- /*
- * 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;
- }
- state = top_state->doc_state;
-
- /* Special case. If the element is a linefeed and the position is 1,
- * we have to move the cursor to the next element.
- */
- if ( pElement && pElement->type == LO_LINEFEED && position > 0 ) {
- LO_Element* pNext = pElement->lo_linefeed.next;
- if ( pNext ) {
- pElement = pNext;
- position = 0;
- }
- }
-
- {
- LO_Position p;
- p.element = pElement;
- p.position = position;
- if ( lo_IsEdgeOfDocument(pContext, state, &p, bForward) )
- {
- LO_NavigateChunk( pContext, LO_NA_DOCUMENT, bSelect, bForward);
- return;
- }
- }
-
- /* Find the effective coordinate of our current position */
- LO_GetEffectiveCoordinates(pContext, pElement, position, &x, &y, &width, &height);
-
- /* Skip over this element */
- pNext=NULL; /*if not forward, let the old way handle it.*/
- if (bForward)
- {
- pNext=pElement->lo_any.next;
- while (pNext && (pNext->type!=LO_CELL) && (!lo_ValidEditableElement(pContext,pNext) || (pNext->lo_any.y == pElement->lo_any.y)) )
- pNext=pNext->lo_any.next;
- /*if we find a cell, we will need more work.*/
- if (pNext)
- y=pNext->lo_any.y;
- }
- if (!pNext)
- {
- if ( bForward) {
- y = pElement->lo_any.y + pElement->lo_any.line_height;
- }
- else {
- y = pElement->lo_any.y - 1;
- }
- }
-
- bFound = lo_FindClosestUpDown(pContext, state, iDesiredX, y, bForward, &x, &y);
-
- /* We found a text element on this line. Now, lets re-scan the line for
- * the best fit.
- */
- if( bFound ){
- if( bSelect ){
- EDT_ExtendSelection( pContext, x, y );
- EDT_EndSelection( pContext, x, y );
- }
- else {
- LO_PositionCaret( pContext, x, y, NULL );
- }
- }
- else {
- /* No more elements. */
- /* Navigate to the edge of the document. */
- LO_NavigateChunk( pContext, LO_NA_DOCUMENT, bSelect, bForward);
- }
- #endif
- }
-
- /* like lo_search_element_list, but matches nearest X */
- LO_Element *
- lo_search_element_list_WideMatch(MWContext *context, LO_Element *eptr, LO_Element* eEndPtr, int32 x, int32 y,
- Bool bForward)
- {
- LO_Element* pFound = NULL;
- int32 bestDistanceX = 2000000;
- int32 bestDistanceY = 2000000;
- int32 distanceY;
-
- for ( ;
- eptr != eEndPtr && eptr != NULL;
- eptr = eptr->lo_any.next
- )
- {
- int32 width, height;
-
- if( eptr->type != LO_CELL
- && eptr->type != LO_TABLE
- && ! lo_ValidEditableElementIncludingParagraphMarks(context, eptr)){
- continue;
- }
- width = eptr->lo_any.width;
- /*
- * Images need to account for border width
- */
- if (eptr->type == LO_IMAGE)
- {
- width = width + (2 * eptr->lo_image.border_width);
- }
- if (width <= 0)
- {
- width = 1;
- }
-
- height = eptr->lo_any.height;
- /*
- * Images need to account for border height
- */
- if (eptr->type == LO_IMAGE)
- {
- height = height + (2 * eptr->lo_image.border_width);
- }
-
- distanceY = bestDistanceY + 1;
- if ( y < eptr->lo_any.y ) {
- /* This element is below the target. Consider it if we're searching forward. */
- if ( bForward ) {
- distanceY = eptr->lo_any.y - y;
- }
- }
- else if ( eptr->lo_any.y + eptr->lo_any.y_offset + height <= y ) {
- /* This element is above the target. Consider it if we're searching backwards. */
- if ( ! bForward ) {
- distanceY = y - (eptr->lo_any.y + eptr->lo_any.y_offset + height) + 1;
- }
- }
- else {
- distanceY = 0;
- }
- if ( distanceY <= bestDistanceY ) {
- if ( distanceY < bestDistanceY ) {
- /* We have a new winner in Y, so forget our X */
- bestDistanceX = 2000000;
- bestDistanceY = distanceY;
- }
-
-
- /* We're looking for the closest match */
- if ((x < (eptr->lo_any.x + eptr->lo_any.x_offset +
- width))&&(x >= eptr->lo_any.x))
- {
- /* Can't get closer than containing. */
- pFound = eptr;
- bestDistanceX = 0;
- if ( distanceY == 0 ) {
- break;
- }
- }
- else {
- int32 distance;
- if ( x < eptr->lo_any.x ) {
- distance = eptr->lo_any.x - x;
- }
- else {
- distance = x - (eptr->lo_any.x + eptr->lo_any.x_offset +
- width) + 1;
- }
- if ( distance < bestDistanceX ) {
- pFound = eptr;
- bestDistanceX = distance;
- }
- }
- }
- /* Skip over cells while searchig for the closest item */
- if (eptr->type == LO_TABLE) {
- while ( eptr->lo_any.next && eptr->lo_any.next->type == LO_CELL )
- {
- eptr = eptr->lo_any.next;
- }
- }
- }
- return pFound;
- }
-
- PRIVATE
- Bool lo_FindClosestUpDown_SubDoc(MWContext *pContext, lo_DocState* state,
- LO_SubDocStruct* pSubdoc, int32 x, int32 y,
- Bool bForward, int32* ret_x, int32* ret_y)
- {
- /* Cribbed from the end of lo_XYToDocumentElement2 */
- int32 new_x, new_y;
-
- new_x = x - (pSubdoc->x + pSubdoc->x_offset +
- pSubdoc->border_width);
- new_y = y - (pSubdoc->y + pSubdoc->y_offset +
- pSubdoc->border_width);
- return lo_FindClosestUpDown(pContext, (lo_DocState *)pSubdoc->state, new_x, new_y, bForward, ret_x, ret_y);
- }
-
- Bool lo_FindClosestUpDown_Cell(MWContext *pContext, lo_DocState* state,
- LO_CellStruct* cell, int32 x, int32 y,
- Bool bForward, int32* ret_x, int32* ret_y)
- {
- /* Cells don't have line arrays. So we linearly search for our element.
- * This code was inspired by the code in lo_search_element_list and
- * lo_XYToCellElement
- */
- LO_Element *eptr;
- LO_Element *element;
-
- eptr = cell->cell_list;
- element = lo_search_element_list_WideMatch(pContext, eptr, NULL, x, y, bForward);
- if (element == NULL)
- {
- eptr = cell->cell_float_list;
- element = lo_search_element_list_WideMatch(pContext, eptr, NULL, x, y, bForward);
- }
- if ((element != NULL)&&(element->type == LO_SUBDOC))
- {
- return lo_FindClosestUpDown_SubDoc(pContext, state, (LO_SubDocStruct *)element,
- x, y, bForward, ret_x, ret_y);
- }
- else if ((element != NULL)&&(element->type == LO_CELL))
- {
- return lo_FindClosestUpDown_Cell(pContext, state,
- (LO_CellStruct *)element, x, y,
- bForward, ret_x, ret_y);
- }
- else if ((element != NULL)&&(element->type == LO_TABLE))
- {
- if ( lo_FindBestPositionInTable(pContext, state,
- (LO_TableStruct *)element, x, y,
- bForward, ret_x, ret_y) )
- return TRUE;
- else {
- /* no more stuff in the table. Skip out of the table and try again. */
- if ( bForward ) {
- y = element->lo_table.y + element->lo_table.y_offset + element->lo_table.height;
- }
- else {
- y = element->lo_table.y - 1;
- }
- /* Tail recursion. Maybe we should use goto? */
- return lo_FindClosestUpDown_Cell(pContext, state, cell, x, y, bForward, ret_x, ret_y);
- }
- }
- else if (element != NULL) {
- /* Return this element's Y. */
- int32 eHeight;
- int32 eWidth;
- int32 eX;
- int32 eY;
- LO_GetEffectiveCoordinates(pContext, element, 0, &eX, &eY, & eWidth, &eHeight);
- /* Clip x to be inside the bounds of the element */
- if ( x >= eX + eWidth ) x = eX + eWidth - 1;
- if ( x < eX ) x = eX;
- *ret_x = x;
- *ret_y = eY;
- return TRUE;
- }
- else
- return FALSE;
- }
-
- PRIVATE
- LO_CellStruct* lo_FindBestCellInTable(MWContext *context, lo_DocState* state, LO_TableStruct* pTable, int32 iDesiredX,
- int32 iDesiredY, Bool bForward )
- {
- /* Find end of table */
- LO_Element* pClosest = NULL;
- LO_CellStruct* result = NULL;
- if ( pTable ) {
- LO_Element* pStart = pTable->next;
- LO_Element* pEnd = pStart;
- while ( pEnd && pEnd->type == LO_CELL ) {
- pEnd = pEnd->lo_any.next;
- }
-
- if ( pStart ) {
- pClosest = lo_search_element_list_WideMatch(context, pStart, pEnd, iDesiredX, iDesiredY, bForward);
-
- if ( pClosest && pClosest->type == LO_CELL ) {
- return result = &pClosest->lo_cell;
- }
- }
- }
- return result;
- }
-
-
- Bool lo_FindBestPositionInTable(MWContext *context, lo_DocState* state, LO_TableStruct* pTable, int32 iDesiredX,
- int32 iDesiredY, Bool bForward, int32* pRetX, int32 *pRetY )
- {
- /* Up/Down can skip over cell boundaries. So we may have to search in two cells, the one
- * we started in, and the next one in the direction we're searching.
- * For simplicity, we search a cell, and if we don't hit anything, we bump the
- * y coordinate by the cell height, and search again.
- */
-
- while(1) {
- LO_CellStruct* pCell = lo_FindBestCellInTable(context, state, pTable, iDesiredX, iDesiredY, bForward);
- if ( ! pCell ) return FALSE;
- if ( lo_FindClosestUpDown_Cell(context, state, pCell, iDesiredX, iDesiredY, bForward, pRetX, pRetY) ) {
- return TRUE;
- }
- /* Search the next cell */
- if ( ! bForward ) {
- iDesiredY = pCell->y - 1;
- }
- else {
- iDesiredY = pCell->y + pCell->y_offset + pCell->height;
- }
- }
- }
-
- Bool lo_FindBestPositionOnLine(MWContext *context, lo_DocState* state, int32 iLine, int32 iDesiredX,
- int32 iDesiredY, Bool bForward, int32* pRetX, int32 *pRetY )
- {
- LO_Element **line_array;
- LO_Element *pElement, *pEnd;
- LO_Element *pFound = NULL;
- Bool bDone = FALSE;
-
- XP_LOCK_BLOCK(line_array, LO_Element **, state->line_array);
-
- if( iLine == state->line_num - 2 ){
- pEnd = 0;
- }
- else {
- pEnd = line_array[iLine+1];
- }
-
- pElement = line_array[iLine];
-
- XP_UNLOCK_BLOCK(state->line_array);
-
- if ( pElement->type == LO_TABLE ) {
- return lo_FindBestPositionInTable(context, state, & pElement->lo_table, iDesiredX, iDesiredY, bForward, pRetX, pRetY);
- }
-
- for( ;
- pElement != pEnd && !bDone;
- pElement = pElement->lo_any.next){
-
- if( lo_ValidEditableElementIncludingParagraphMarks(context, pElement)){
- if( pFound ){
- /* what is desired is before the element, take the absolute
- * value of the distance between the two elements
- */
- if( iDesiredX < pElement->lo_any.x ){
- if( pElement->lo_any.x - iDesiredX < iDesiredX - (*pRetX) ){
- *pRetX = pElement->lo_any.x;
- *pRetY = pElement->lo_any.y;
- pFound = pElement;
- }
- bDone = TRUE;
- }
- else if( iDesiredX < pElement->lo_any.x + pElement->lo_any.width ) {
- *pRetY = pElement->lo_any.y;
- *pRetX = iDesiredX;
- pFound = pElement;
- bDone = TRUE;
- }
- else {
- *pRetX = iDesiredX;
- /* *pRetX = pElement->lo_any.x + pElement->lo_any.width; */
- *pRetY = pElement->lo_any.y;
- pFound = pElement;
- }
- }
- else {
- if( iDesiredX < pElement->lo_any.x ){
- *pRetX = pElement->lo_any.x;
- *pRetY = pElement->lo_any.y;
- bDone = TRUE;
- }
- else if( iDesiredX < pElement->lo_any.x + pElement->lo_any.width ) {
- *pRetY = pElement->lo_any.y;
- *pRetX = iDesiredX;
- bDone = TRUE;
- }
- else {
- *pRetX = iDesiredX;
- /* *pRetX = pElement->lo_any.x + pElement->lo_any.width; */
- *pRetY = pElement->lo_any.y;
- }
- pFound = pElement;
- }
- }
- }
-
- if ( pFound && pFound->type == LO_CELL ) {
- return lo_FindClosestUpDown_Cell(context, state, &pFound->lo_cell, iDesiredX, iDesiredY, bForward, pRetX, pRetY);
- }
- else if ( pFound && pFound->type == LO_SUBDOC ) {
- return lo_FindClosestUpDown_SubDoc(context, state, &pFound->lo_subdoc, iDesiredX, iDesiredY, bForward, pRetX, pRetY);
- }
- return pFound != NULL;
- }
-
- ED_Buffer* LO_GetEDBuffer( MWContext *context)
- {
- #ifdef EDITOR
- 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 NULL;
- }
- return top_state->edit_buffer;
- #else
- return 0;
- #endif
- }
-
- /* #endif */
-
- /*
- * Given a Text element and a character offset, find the width of upto that
- * offset.
- */
- int32
- LO_TextElementWidth(MWContext *context, LO_TextStruct *text_ele, int charOffset)
- {
- LO_TextInfo text_info;
- int orig_len;
- int32 retval;
-
- if ((text_ele->text == NULL)||(text_ele->text_len <= 0))
- {
- return(0);
- }
-
- orig_len = text_ele->text_len;
- text_ele->text_len = charOffset;
- FE_GetTextInfo(context, text_ele, &text_info);
- retval = text_info.max_width;
- text_ele->text_len = orig_len;
- return retval;
- }
-
- #ifdef TEST_16BIT
- #undef XP_WIN16
- #endif /* TEST_16BIT */
-
- #ifdef PROFILE
- #pragma profile off
- #endif
-
- /* #ifndef NO_TAB_NAVIGATION */
-
- /*
- Arthur Liu, 9/13/96
- Reading through this file, I found LO_getDocState() should
- be a separate function.
-
- TODO, check this file(may be also other files), to replace
- the duplicated code with this function.
- */
- PRIVATE
- lo_DocState *
- LO_getDocState(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 NULL ;
- }
- return( top_state->doc_state );
- }
-
- /*
- Arthur Liu, 9/13/96
- LO_getFirstLastElement() was created for Keyboard Navigation.
-
- TODO: Its function is a subset of lo_FindDocumentEdge(..), and the
- latter may call LO_getFirstLastElement() to avoid code duplication.
- */
-
- LO_Element *
- LO_getFirstLastElement(MWContext *context, int forward )
- {
- lo_DocState *state;
- LO_Element **array;
- LO_Element* eptr;
- #ifdef XP_WIN16
- XP_Block *larray_array;
- #endif /* XP_WIN16 */
-
- if( NULL == ( state = LO_getDocState(context) ) )
- return( NULL );
-
- /*
- * Nothing to select.
- */
- if (state->line_num <= 1)
- {
- return NULL ;
- }
-
- if ( ! forward )
- {
- /*
- * Get last element in doc.
- */
- eptr = state->end_last_line;
- if (eptr == NULL)
- {
- eptr = state->selection_start; /* ??? */
- }
- }
- else /* wantFirst */
- {
- /*
- * 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);
- /*
- * No elements.
- */
- if (eptr == NULL)
- {
- return NULL ;
- }
-
- }
- return( eptr );
- } /* LO_getFirstLastElement() */
-
- /*
- return TRUE if pCurrentFocus->pElement is a tabable element,
- may also fill in pCurrentFocus->pAnchor, and pCurrentFocus->mapAreaIndex
- */
- Bool
- LO_isTabableElement(MWContext *context, LO_TabFocusData *pCurrentFocus )
- {
- /*
- For all LO_ types which update status bar, see CWinCX::OnMouseMoveForLayerCX(UINT uFlags, CPoint& cpPoint,
- for all links see CWinCX::OnLButtonUpForLayerCX(UINT uFlags, CPoint& cpPoint, XY& Point,
- */
- Bool bIsTabable;
- LO_TextStruct *pText;
- LO_ImageStruct *pImage;
- lo_MapAreaRec *tempArea;
- LO_Element *pElement;
- LO_FormElementStruct *formEleStruct;
-
- if( pCurrentFocus == NULL )
- return( FALSE );
-
- pCurrentFocus->mapAreaIndex = 0; /* no area */
- pCurrentFocus->pAnchor = NULL;
-
- pElement = pCurrentFocus->pElement;
- if( pElement == NULL )
- return( FALSE );
-
- bIsTabable = FALSE;
-
- switch( pElement->type ) {
-
- case LO_NONE : /* 0 */
- break;
- case LO_TEXT : /* 1 */
- pText = (LO_TextStruct *) pElement;
- if( pText )
- bIsTabable = (pText->anchor_href && pText->anchor_href->anchor);
- if( bIsTabable )
- pCurrentFocus->pAnchor = pText->anchor_href;
- break;
-
- case LO_LINEFEED : /* 2 */
- break;
- case LO_HRULE : /* 3 */
- break;
- case LO_IMAGE : /* 4 */
- pImage = ( LO_ImageStruct *) pElement;
-
- /* first test image with a link */
- bIsTabable = (pImage->anchor_href != NULL) && (pImage->anchor_href->anchor != NULL);
- if( bIsTabable ) {
- pCurrentFocus->pAnchor = pImage->anchor_href;
- break;
- }
-
- /* test use map */
- tempArea = LO_getTabableMapArea(context, pImage, 1 ); /* 1 means get the first */
- if( tempArea != NULL ) {
- pCurrentFocus->mapAreaIndex = 1; /* the first area */
- pCurrentFocus->pAnchor = tempArea->anchor;
- bIsTabable = TRUE;
- }
- break;
- case LO_BULLET : /* 5 */
- break;
- case LO_FORM_ELE : /* 6 */
- /*TODO get anchor for buttons...*/
- formEleStruct = (LO_FormElementStruct *) pElement;
- if( formEleStruct )
- bIsTabable = LO_isTabableFormElement( formEleStruct );
- break;
-
- case LO_SUBDOC : /* 7 */
- break;
- case LO_TABLE : /* 8 */
- break;
- case LO_CELL : /* 9 */
- break;
- case LO_EMBED : /* 10 */
- break;
- case LO_EDGE : /* 11 */
- break;
- #ifdef JAVA
- case LO_JAVA : /* 12 */
- break;
- #endif
- case LO_SCRIPT : /* 13 */
- break;
- default : /* something wrong!! */
- bIsTabable = FALSE;
- break;
- }
- return( bIsTabable );
- }
-
-
- /*
- get first(last) if current element is NULL.
- */
- Bool
- LO_getNextTabableElement( MWContext *context, LO_TabFocusData *pCurrentFocus, int forward )
- {
-
- lo_DocState *state;
- LO_Element *currentElement;
- lo_MapAreaRec *tempArea;
- LO_AnchorData *pLastAnchorData;
-
- if( NULL == ( state = LO_getDocState(context) ) )
- return( FALSE );
-
- /*
- A text may have been fragmented into multiple lines because the text folding.
- If last tab focus is a LO_Text, remember its anchor to skip continuious text fragments.
-
- When the focus is drawn, all continuious fragments will hav efocus, See
- CWinCX::setTextTabFocusDrawFlag() in cmd\winfe\cxwin.cpp file.
- */
-
- pLastAnchorData = NULL; /* assuming no need to skip continuious text fragments. */
-
- currentElement = pCurrentFocus->pElement;
- /* find the first candidate, */
- if( currentElement == NULL ) {
- /* find the first(last) element in the Doc as candidate, */
- pCurrentFocus->pElement = LO_getFirstLastElement( context, forward ); /* forward is getting first */
-
- /*TODO go down to cell_list if pElement has subtree */
-
- } else {
- if( pCurrentFocus->pElement->lo_any.type == LO_TEXT )
- pLastAnchorData = pCurrentFocus->pAnchor; /* need to skip continuious text fragments. */
-
- /* if the last element is a image map, try go to next area in the map */
- if( currentElement->lo_any.type == LO_IMAGE ) {
- if( forward ) {
- pCurrentFocus->mapAreaIndex++;
- tempArea = LO_getTabableMapArea(context, (LO_ImageStruct *)currentElement, pCurrentFocus->mapAreaIndex );
- if( tempArea ) {
- pCurrentFocus->pAnchor = tempArea->anchor;
- return( TRUE );
- }
- } else { /* if( forward ) */
- pCurrentFocus->mapAreaIndex--;
- if( pCurrentFocus->mapAreaIndex > 0 ) { /* otherwise no need to search. */
- tempArea = LO_getTabableMapArea(context, (LO_ImageStruct *)currentElement, pCurrentFocus->mapAreaIndex );
- if( tempArea ) {
- pCurrentFocus->pAnchor = tempArea->anchor;
- return( TRUE );
- }
- }
- }
-
- }
-
- /* advance the pointer, use the next(previous) as first candidate.
- pElement = lo_GetNeighbor( currentElement, forward);
- pElement = currentElement;
- */
- lo_TraverseElement(context, state, &(pCurrentFocus->pElement), forward);
- }
-
- /*todo if not forward, get last area. */
- pCurrentFocus->mapAreaIndex = 0; /* assumming no focus area */
- pCurrentFocus->pAnchor = NULL;
- while ( pCurrentFocus->pElement != NULL ) {
- if( LO_isTabableElement(context, pCurrentFocus) ) {
-
- if( pLastAnchorData == NULL /* no need to skip continuious text fragments. */
- || pCurrentFocus->pElement->lo_any.type != LO_TEXT
- || pLastAnchorData != pCurrentFocus->pAnchor )
- return( TRUE );
- /* else it is a text fragment with the same anchor as the last tabFocus text.
- In such case, continue search.
- */
- }
-
- lo_TraverseElement(context, state, &(pCurrentFocus->pElement), forward);
- }
-
- return( FALSE );
- } /* LO_getNextTabableElement */
-
- /* NO_TAB_NAVIGATION */
-
-
-
-